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

detect UFCS drop and allow UFCS methods to have explicit type parameters.

Work towards #18875.

Since code could previously call the methods & implement the traits
manually, this is a

[breaking-change]

Closes #19586. Closes #19375.
This commit is contained in:
Huon Wilson 2014-12-05 15:53:30 -08:00
parent 4573da6f4f
commit e8524198e3
13 changed files with 199 additions and 14 deletions

View File

@ -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;

View File

@ -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");
}
}

View File

@ -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};
@ -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) =
@ -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

View File

@ -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:
@ -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));
@ -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(..) |

View File

@ -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);
}
@ -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>,

View File

@ -53,7 +53,7 @@ register_diagnostics!(
E0035,
E0036,
E0038,
E0040,
E0040, // explicit use of destructor method
E0044,
E0045,
E0046,
@ -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
)

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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
}

View File

@ -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)
}