Skip to content

Feature-gate explicit unboxed closure method calls & manual impls, #19587

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 9, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
#![allow(unknown_features)]
#![feature(globs, intrinsics, lang_items, macro_rules, phase)]
#![feature(simd, unsafe_destructor, slicing_syntax)]
#![feature(default_type_params)]
#![feature(default_type_params, unboxed_closures)]
#![deny(missing_docs)]

mod macros;
Expand Down
46 changes: 46 additions & 0 deletions src/librustc_typeck/check/callee.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use syntax::ast;
use syntax::codemap::Span;
use CrateCtxt;

/// Check that it is legal to call methods of the trait corresponding
/// to `trait_id` (this only cares about the trait, not the specific
/// method that is called)
pub fn check_legal_trait_for_method_call(ccx: &CrateCtxt, span: Span, trait_id: ast::DefId) {
let tcx = ccx.tcx;
let did = Some(trait_id);
let li = &tcx.lang_items;

if did == li.drop_trait() {
span_err!(tcx.sess, span, E0040, "explicit use of destructor method");
} else if !tcx.sess.features.borrow().unboxed_closures {
// the #[feature(unboxed_closures)] feature isn't
// activated so we need to enforce the closure
// restrictions.

let method = if did == li.fn_trait() {
"call"
} else if did == li.fn_mut_trait() {
"call_mut"
} else if did == li.fn_once_trait() {
"call_once"
} else {
return // not a closure method, everything is OK.
};

span_err!(tcx.sess, span, E0174,
"explicit use of unboxed closure method `{}` is experimental",
method);
span_help!(tcx.sess, span,
"add `#![feature(unboxed_closures)]` to the crate attributes to enable");
}
}
11 changes: 4 additions & 7 deletions src/librustc_typeck/check/method/confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

use super::probe;

use check::{mod, FnCtxt, NoPreference, PreferMutLvalue};
use check::{mod, FnCtxt, NoPreference, PreferMutLvalue, callee};
use middle::subst::{mod, Subst};
use middle::traits;
use middle::ty::{mod, Ty};
Expand Down Expand Up @@ -90,7 +90,7 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
let self_ty = self.adjust_self_ty(unadjusted_self_ty, &pick.adjustment);

// Make sure nobody calls `drop()` explicitly.
self.enforce_drop_trait_limitations(&pick);
self.enforce_illegal_method_limitations(&pick);

// Create substitutions for the method's type parameters.
let (rcvr_substs, method_origin) =
Expand Down Expand Up @@ -624,14 +624,11 @@ impl<'a,'tcx> ConfirmContext<'a,'tcx> {
self.fcx.infcx()
}

fn enforce_drop_trait_limitations(&self, pick: &probe::Pick) {
fn enforce_illegal_method_limitations(&self, pick: &probe::Pick) {
// Disallow calls to the method `drop` defined in the `Drop` trait.
match pick.method_ty.container {
ty::TraitContainer(trait_def_id) => {
if Some(trait_def_id) == self.tcx().lang_items.drop_trait() {
span_err!(self.tcx().sess, self.span, E0040,
"explicit call to destructor");
}
callee::check_legal_trait_for_method_call(self.fcx.ccx, self.span, trait_def_id)
}
ty::ImplContainer(..) => {
// Since `drop` is a trait method, we expect that any
Expand Down
13 changes: 11 additions & 2 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ pub mod demand;
pub mod method;
pub mod wf;
mod closure;
mod callee;

/// Fields that are part of a `FnCtxt` which are inherited by
/// closures defined within the function. For example:
Expand Down Expand Up @@ -5088,8 +5089,17 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}

// Case 3. Reference to a method.
def::DefStaticMethod(..) => {
def::DefStaticMethod(_, providence) |
def::DefMethod(_, _, providence) => {
assert!(path.segments.len() >= 2);

match providence {
def::FromTrait(trait_did) => {
callee::check_legal_trait_for_method_call(fcx.ccx, span, trait_did)
}
def::FromImpl(_) => {}
}

segment_spaces = Vec::from_elem(path.segments.len() - 2, None);
segment_spaces.push(Some(subst::TypeSpace));
segment_spaces.push(Some(subst::FnSpace));
Expand All @@ -5101,7 +5111,6 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
def::DefMod(..) |
def::DefForeignMod(..) |
def::DefLocal(..) |
def::DefMethod(..) |
def::DefUse(..) |
def::DefRegion(..) |
def::DefLabel(..) |
Expand Down
25 changes: 25 additions & 0 deletions src/librustc_typeck/coherence/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
trait_ref.repr(self.crate_context.tcx),
token::get_ident(item.ident));

enforce_trait_manually_implementable(self.crate_context.tcx,
item.span,
trait_ref.def_id);
self.add_trait_impl(trait_ref.def_id, impl_did);
}

Expand Down Expand Up @@ -476,6 +479,28 @@ impl<'a, 'tcx> CoherenceChecker<'a, 'tcx> {
}
}

fn enforce_trait_manually_implementable(tcx: &ty::ctxt, sp: Span, trait_def_id: ast::DefId) {
if tcx.sess.features.borrow().unboxed_closures {
// the feature gate allows all of them
return
}
let did = Some(trait_def_id);
let li = &tcx.lang_items;

let trait_name = if did == li.fn_trait() {
"Fn"
} else if did == li.fn_mut_trait() {
"FnMut"
} else if did == li.fn_once_trait() {
"FnOnce"
} else {
return // everything OK
};
span_err!(tcx.sess, sp, E0173, "manual implementations of `{}` are experimental", trait_name);
span_help!(tcx.sess, sp,
"add `#![feature(unboxed_closures)]` to the crate attributes to enable");
}

fn subst_receiver_types_in_method_ty<'tcx>(tcx: &ty::ctxt<'tcx>,
impl_id: ast::DefId,
impl_poly_type: &ty::Polytype<'tcx>,
Expand Down
6 changes: 4 additions & 2 deletions src/librustc_typeck/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ register_diagnostics!(
E0035,
E0036,
E0038,
E0040,
E0040, // explicit use of destructor method
E0044,
E0045,
E0046,
Expand Down Expand Up @@ -147,5 +147,7 @@ register_diagnostics!(
E0168,
E0169,
E0171,
E0172
E0172,
E0173, // manual implementations of unboxed closure traits are experimental
E0174 // explicit use of unboxed closure methods are experimental
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious, any particular reason why we want these codes commented here? This file isn't really meant for any sort of read consumption, it's more of a manifest.

)
2 changes: 1 addition & 1 deletion src/test/compile-fail/explicit-call-to-dtor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ impl Drop for Foo {

fn main() {
let x = Foo { x: 3 };
x.drop(); //~ ERROR explicit call to destructor
x.drop(); //~ ERROR explicit use of destructor method
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/explicit-call-to-supertrait-dtor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl Drop for Foo {

impl Bar for Foo {
fn blah(&self) {
self.drop(); //~ ERROR explicit call to destructor
self.drop(); //~ ERROR explicit use of destructor method
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(dead_code)]

struct Foo;
impl Fn<(), ()> for Foo { //~ ERROR manual implementations of `Fn` are experimental
extern "rust-call" fn call(&self, args: ()) -> () {}
}
struct Bar;
impl FnMut<(), ()> for Bar { //~ ERROR manual implementations of `FnMut` are experimental
extern "rust-call" fn call_mut(&self, args: ()) -> () {}
}
struct Baz;
impl FnOnce<(), ()> for Baz { //~ ERROR manual implementations of `FnOnce` are experimental
extern "rust-call" fn call_once(&self, args: ()) -> () {}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(dead_code)]

fn foo<F: Fn<(), ()>>(mut f: F) {
f.call(()); //~ ERROR explicit use of unboxed closure method `call`
f.call_mut(()); //~ ERROR explicit use of unboxed closure method `call_mut`
f.call_once(()); //~ ERROR explicit use of unboxed closure method `call_once`
}

fn main() {}
19 changes: 19 additions & 0 deletions src/test/compile-fail/feature-gate-unboxed-closures-ufcs-calls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(dead_code)]

fn foo<F: Fn<(), ()>>(mut f: F, mut g: F) {
Fn::call(&g, ()); //~ ERROR explicit use of unboxed closure method `call`
FnMut::call_mut(&mut g, ()); //~ ERROR explicit use of unboxed closure method `call_mut`
FnOnce::call_once(g, ()); //~ ERROR explicit use of unboxed closure method `call_once`
}

fn main() {}
20 changes: 20 additions & 0 deletions src/test/compile-fail/illegal-ufcs-drop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.


struct Foo;

impl Drop for Foo {
fn drop(&mut self) {}
}

fn main() {
Drop::drop(&mut Foo) //~ ERROR explicit use of destructor method
}
22 changes: 22 additions & 0 deletions src/test/run-pass/ufcs-type-params.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

trait Foo<T> {
fn get(&self) -> T;
}

impl Foo<i32> for i32 {
fn get(&self) -> i32 { *self }
}

fn main() {
let x: i32 = 1;
Foo::<i32>::get(&x)
}