From e8524198e3931373f5e097dece21e36d21c4a537 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Fri, 5 Dec 2014 15:53:30 -0800 Subject: [PATCH] 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. --- src/libcore/lib.rs | 2 +- src/librustc_typeck/check/callee.rs | 46 +++++++++++++++++++ src/librustc_typeck/check/method/confirm.rs | 11 ++--- src/librustc_typeck/check/mod.rs | 13 +++++- src/librustc_typeck/coherence/mod.rs | 25 ++++++++++ src/librustc_typeck/diagnostics.rs | 6 ++- .../compile-fail/explicit-call-to-dtor.rs | 2 +- .../explicit-call-to-supertrait-dtor.rs | 2 +- ...ture-gate-unboxed-closures-manual-impls.rs | 26 +++++++++++ ...ture-gate-unboxed-closures-method-calls.rs | 19 ++++++++ ...eature-gate-unboxed-closures-ufcs-calls.rs | 19 ++++++++ src/test/compile-fail/illegal-ufcs-drop.rs | 20 ++++++++ src/test/run-pass/ufcs-type-params.rs | 22 +++++++++ 13 files changed, 199 insertions(+), 14 deletions(-) create mode 100644 src/librustc_typeck/check/callee.rs create mode 100644 src/test/compile-fail/feature-gate-unboxed-closures-manual-impls.rs create mode 100644 src/test/compile-fail/feature-gate-unboxed-closures-method-calls.rs create mode 100644 src/test/compile-fail/feature-gate-unboxed-closures-ufcs-calls.rs create mode 100644 src/test/compile-fail/illegal-ufcs-drop.rs create mode 100644 src/test/run-pass/ufcs-type-params.rs diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 5ad9462daf274..51bf7ba0a2aec 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -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; diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs new file mode 100644 index 0000000000000..ee93c896433a1 --- /dev/null +++ b/src/librustc_typeck/check/callee.rs @@ -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 or the MIT license +// , 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"); + } +} diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 1fe73f0478d56..b6a9e2cbc59ea 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -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 diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a0f3f2734d976..dc6de01ff90eb 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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(..) | diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index b8642ddde4082..8fd383802d1f0 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -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>, diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 36e81f18103b8..e026fbd05c7f0 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -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 ) diff --git a/src/test/compile-fail/explicit-call-to-dtor.rs b/src/test/compile-fail/explicit-call-to-dtor.rs index 3f69cb6e51ea2..6b334dd6ecdfc 100644 --- a/src/test/compile-fail/explicit-call-to-dtor.rs +++ b/src/test/compile-fail/explicit-call-to-dtor.rs @@ -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 } diff --git a/src/test/compile-fail/explicit-call-to-supertrait-dtor.rs b/src/test/compile-fail/explicit-call-to-supertrait-dtor.rs index 3d987bf4c58a2..d0dd0e68da6bd 100644 --- a/src/test/compile-fail/explicit-call-to-supertrait-dtor.rs +++ b/src/test/compile-fail/explicit-call-to-supertrait-dtor.rs @@ -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 } } diff --git a/src/test/compile-fail/feature-gate-unboxed-closures-manual-impls.rs b/src/test/compile-fail/feature-gate-unboxed-closures-manual-impls.rs new file mode 100644 index 0000000000000..8999da5b17555 --- /dev/null +++ b/src/test/compile-fail/feature-gate-unboxed-closures-manual-impls.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/compile-fail/feature-gate-unboxed-closures-method-calls.rs b/src/test/compile-fail/feature-gate-unboxed-closures-method-calls.rs new file mode 100644 index 0000000000000..5a066c441cfe5 --- /dev/null +++ b/src/test/compile-fail/feature-gate-unboxed-closures-method-calls.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +fn foo>(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() {} diff --git a/src/test/compile-fail/feature-gate-unboxed-closures-ufcs-calls.rs b/src/test/compile-fail/feature-gate-unboxed-closures-ufcs-calls.rs new file mode 100644 index 0000000000000..8efaf00c9c848 --- /dev/null +++ b/src/test/compile-fail/feature-gate-unboxed-closures-ufcs-calls.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] + +fn foo>(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() {} diff --git a/src/test/compile-fail/illegal-ufcs-drop.rs b/src/test/compile-fail/illegal-ufcs-drop.rs new file mode 100644 index 0000000000000..f4c653bd57323 --- /dev/null +++ b/src/test/compile-fail/illegal-ufcs-drop.rs @@ -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 or the MIT license +// , 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 +} diff --git a/src/test/run-pass/ufcs-type-params.rs b/src/test/run-pass/ufcs-type-params.rs new file mode 100644 index 0000000000000..f4ad78da4877e --- /dev/null +++ b/src/test/run-pass/ufcs-type-params.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Foo { + fn get(&self) -> T; +} + +impl Foo for i32 { + fn get(&self) -> i32 { *self } +} + +fn main() { + let x: i32 = 1; + Foo::::get(&x) +}