From c9e1c445dbcbfc9c938488a79ef12595e0f99c8d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 2 Feb 2015 11:52:08 -0500 Subject: [PATCH 1/6] Allow closure arguments types to unify even if we can't fully resolve a trait obligation. Partial fix for #16440 -- closure return types are not handled yet. --- src/librustc/middle/traits/select.rs | 83 +++++++++++++++++++------- src/test/run-pass/closure-inference.rs | 2 +- 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 000572cdd40a3..7a59909e13166 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -233,9 +233,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // is `Vec:Iterable`, but the impl specifies // `impl Iterable for Vec`, than an error would result. - /// Evaluates whether the obligation can be satisfied. Returns an indication of whether the - /// obligation can be satisfied and, if so, by what means. Never affects surrounding typing - /// environment. + /// Attempts to satisfy the obligation. If successful, this will affect the surrounding + /// type environment by performing unification. pub fn select(&mut self, obligation: &TraitObligation<'tcx>) -> SelectionResult<'tcx, Selection<'tcx>> { debug!("select({})", obligation.repr(self.tcx())); @@ -243,11 +242,69 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let stack = self.push_stack(None, obligation); match try!(self.candidate_from_obligation(&stack)) { - None => Ok(None), + None => { + self.consider_unification_despite_ambiguity(obligation); + Ok(None) + } Some(candidate) => Ok(Some(try!(self.confirm_candidate(obligation, candidate)))), } } + /// In the particular case of unboxed closure obligations, we can + /// sometimes do some amount of unification for the + /// argument/return types even though we can't yet fully match obligation. + /// The particular case we are interesting in is an obligation of the form: + /// + /// C : FnFoo + /// + /// where `C` is an unboxed closure type and `FnFoo` is one of the + /// `Fn` traits. Because we know that users cannot write impls for closure types + /// themselves, the only way that `C : FnFoo` can fail to match is under two + /// conditions: + /// + /// 1. The closure kind for `C` is not yet known, because inference isn't complete. + /// 2. The closure kind for `C` *is* known, but doesn't match what is needed. + /// For example, `C` may be a `FnOnce` closure, but a `Fn` closure is needed. + /// + /// In either case, we always know what argument types are + /// expected by `C`, no matter what kind of `Fn` trait it + /// eventually matches. So we can go ahead and unify the argument + /// types, even though the end result is ambiguous. + /// + /// Note that this is safe *even if* the trait would never be + /// matched (case 2 above). After all, in that case, an error will + /// result, so it kind of doesn't matter what we do --- unifying + /// the argument types can only be helpful to the user, because + /// once they patch up the kind of closure that is expected, the + /// argment types won't really change. + fn consider_unification_despite_ambiguity(&mut self, obligation: &TraitObligation<'tcx>) + { + // Is this a `C : FnFoo(...)` trait reference for some trait binding `FnFoo`? + match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) { + Some(_) => { } + None => { return; } + } + + // Is the self-type a closure type? We ignore bindings here + // because if it is a closure type, it must be a closure type from + // within this current fn, and hence none of the higher-ranked + // lifetimes can appear inside the self-type. + let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + let (closure_def_id, substs) = match self_ty.sty { + ty::ty_closure(id, _, ref substs) => (id, substs.clone()), + _ => { return; } + }; + assert!(!substs.has_escaping_regions()); + + let closure_trait_ref = self.closure_trait_ref(obligation, closure_def_id, substs); + match self.confirm_poly_trait_refs(obligation.cause.clone(), + obligation.predicate.to_poly_trait_ref(), + closure_trait_ref) { + Ok(()) => { } + Err(_) => { /* Silently ignore errors. */ } + } + } + /////////////////////////////////////////////////////////////////////////// // EVALUATION // @@ -1003,7 +1060,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates: &mut SelectionCandidateSet<'tcx>) -> Result<(),SelectionError<'tcx>> { - let kind = match self.fn_family_trait_kind(obligation.predicate.0.def_id()) { + let kind = match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) { Some(k) => k, None => { return Ok(()); } }; @@ -2303,22 +2360,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { impl_obligations } - fn fn_family_trait_kind(&self, - trait_def_id: ast::DefId) - -> Option - { - let tcx = self.tcx(); - if Some(trait_def_id) == tcx.lang_items.fn_trait() { - Some(ty::FnClosureKind) - } else if Some(trait_def_id) == tcx.lang_items.fn_mut_trait() { - Some(ty::FnMutClosureKind) - } else if Some(trait_def_id) == tcx.lang_items.fn_once_trait() { - Some(ty::FnOnceClosureKind) - } else { - None - } - } - #[allow(unused_comparisons)] fn derived_cause(&self, obligation: &TraitObligation<'tcx>, diff --git a/src/test/run-pass/closure-inference.rs b/src/test/run-pass/closure-inference.rs index 893003dd99722..51996ddfbe800 100644 --- a/src/test/run-pass/closure-inference.rs +++ b/src/test/run-pass/closure-inference.rs @@ -14,6 +14,6 @@ fn foo(i: int) -> int { i + 1 } fn apply(f: F, v: A) -> A where F: FnOnce(A) -> A { f(v) } pub fn main() { - let f = {|: i| foo(i)}; + let f = {|i| foo(i)}; assert_eq!(apply(f, 2), 3); } From 498595a3dc90b7df08e90b04278b4de33c5ab3cf Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Feb 2015 06:12:43 -0500 Subject: [PATCH 2/6] Teach project to unify the return type even if a precise match is not possible. There is some amount of duplication as a result (similar to select) -- I am not happy about this but not sure how to fix it without deeper rewrites. --- src/librustc/middle/traits/project.rs | 75 +++++++++++++------ src/librustc/middle/traits/select.rs | 3 +- src/test/run-pass/closure-inference.rs | 2 +- src/test/run-pass/last-use-in-cap-clause.rs | 4 +- .../run-pass/unboxed-closures-zero-args.rs | 4 +- 5 files changed, 60 insertions(+), 28 deletions(-) diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index 3ede6bbb965ef..c2a451b405bb5 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -80,37 +80,23 @@ pub fn poly_project_and_unify_type<'cx,'tcx>( obligation.repr(selcx.tcx())); let infcx = selcx.infcx(); - let result = infcx.try(|snapshot| { + infcx.try(|snapshot| { let (skol_predicate, skol_map) = infcx.skolemize_late_bound_regions(&obligation.predicate, snapshot); let skol_obligation = obligation.with(skol_predicate); match project_and_unify_type(selcx, &skol_obligation) { - Ok(Some(obligations)) => { + Ok(result) => { match infcx.leak_check(&skol_map, snapshot) { - Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, &obligations)), - Err(e) => Err(Some(MismatchedProjectionTypes { err: e })), + Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, &result)), + Err(e) => Err(MismatchedProjectionTypes { err: e }), } } - Ok(None) => { - // Signal ambiguity using Err just so that infcx.try() - // rolls back the snapshot. We adapt below. - Err(None) - } Err(e) => { - Err(Some(e)) + Err(e) } } - }); - - // Above, we use Err(None) to signal ambiguity so that the - // snapshot will be rolled back. But here, we want to translate to - // Ok(None). Kind of weird. - match result { - Ok(obligations) => Ok(Some(obligations)), - Err(None) => Ok(None), - Err(Some(e)) => Err(e), - } + }) } /// Evaluates constraints of the form: @@ -132,7 +118,10 @@ fn project_and_unify_type<'cx,'tcx>( obligation.cause.clone(), obligation.recursion_depth) { Some(n) => n, - None => { return Ok(None); } + None => { + consider_unification_despite_ambiguity(selcx, obligation); + return Ok(None); + } }; debug!("project_and_unify_type: normalized_ty={} obligations={}", @@ -147,6 +136,50 @@ fn project_and_unify_type<'cx,'tcx>( } } +fn consider_unification_despite_ambiguity<'cx,'tcx>(selcx: &mut SelectionContext<'cx,'tcx>, + obligation: &ProjectionObligation<'tcx>) { + debug!("consider_unification_despite_ambiguity(obligation={})", + obligation.repr(selcx.tcx())); + + let def_id = obligation.predicate.projection_ty.trait_ref.def_id; + match selcx.tcx().lang_items.fn_trait_kind(def_id) { + Some(_) => { } + None => { return; } + } + + let infcx = selcx.infcx(); + let self_ty = obligation.predicate.projection_ty.trait_ref.self_ty(); + let self_ty = infcx.shallow_resolve(self_ty); + debug!("consider_unification_despite_ambiguity: self_ty.sty={:?}", + self_ty.sty); + match self_ty.sty { + ty::ty_closure(closure_def_id, _, substs) => { + let closure_typer = selcx.closure_typer(); + let closure_type = closure_typer.closure_type(closure_def_id, substs); + let ty::Binder((_, ret_type)) = + util::closure_trait_ref_and_return_type(infcx.tcx, + def_id, + self_ty, + &closure_type.sig, + util::TupleArgumentsFlag::No); + let (ret_type, _) = + infcx.replace_late_bound_regions_with_fresh_var( + obligation.cause.span, + infer::AssocTypeProjection(obligation.predicate.projection_ty.item_name), + &ty::Binder(ret_type)); + debug!("consider_unification_despite_ambiguity: ret_type={:?}", + ret_type.repr(selcx.tcx())); + let origin = infer::RelateOutputImplTypes(obligation.cause.span); + let obligation_ty = obligation.predicate.ty; + match infer::mk_eqty(infcx, true, origin, obligation_ty, ret_type) { + Ok(()) => { } + Err(_) => { /* ignore errors */ } + } + } + _ => { } + } +} + /// Normalizes any associated type projections in `value`, replacing /// them with a fully resolved type where possible. The return value /// combines the normalized result and any additional obligations that diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 7a59909e13166..b8af91add9efb 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -277,8 +277,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// the argument types can only be helpful to the user, because /// once they patch up the kind of closure that is expected, the /// argment types won't really change. - fn consider_unification_despite_ambiguity(&mut self, obligation: &TraitObligation<'tcx>) - { + fn consider_unification_despite_ambiguity(&mut self, obligation: &TraitObligation<'tcx>) { // Is this a `C : FnFoo(...)` trait reference for some trait binding `FnFoo`? match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) { Some(_) => { } diff --git a/src/test/run-pass/closure-inference.rs b/src/test/run-pass/closure-inference.rs index 51996ddfbe800..3bd0273216de3 100644 --- a/src/test/run-pass/closure-inference.rs +++ b/src/test/run-pass/closure-inference.rs @@ -9,7 +9,7 @@ // except according to those terms. -fn foo(i: int) -> int { i + 1 } +fn foo(i: isize) -> isize { i + 1 } fn apply(f: F, v: A) -> A where F: FnOnce(A) -> A { f(v) } diff --git a/src/test/run-pass/last-use-in-cap-clause.rs b/src/test/run-pass/last-use-in-cap-clause.rs index 0cdd4d3889c06..0cd8c13a4e10a 100644 --- a/src/test/run-pass/last-use-in-cap-clause.rs +++ b/src/test/run-pass/last-use-in-cap-clause.rs @@ -14,9 +14,9 @@ #![feature(box_syntax)] #![feature(unboxed_closures)] -struct A { a: Box } +struct A { a: Box } -fn foo() -> Box int + 'static> { +fn foo() -> Box isize + 'static> { let k = box 22; let _u = A {a: k.clone()}; let result = |&mut:| 22; diff --git a/src/test/run-pass/unboxed-closures-zero-args.rs b/src/test/run-pass/unboxed-closures-zero-args.rs index f2eddd84af832..8e3d44df798a7 100644 --- a/src/test/run-pass/unboxed-closures-zero-args.rs +++ b/src/test/run-pass/unboxed-closures-zero-args.rs @@ -11,7 +11,7 @@ #![feature(unboxed_closures)] fn main() { - let mut zero = |&mut:| {}; - let () = zero.call_mut(()); + let mut zero = || {}; + let () = zero(); } From 47f18659ff629792f49b5ed87e870687703831fe Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Feb 2015 11:32:26 -0500 Subject: [PATCH 3/6] Update compile-fail tests to use the expected type to force the closure kind, thereby detecting what happens if there are mismatches. Simply removing the `:` annotations caused most of these tests to pass or produce other errors, because the inference would convert the closure into a more appropriate kind. (The ability to override the inference by using the expected type is an important backdoor partly for this reason.) --- .../borrow-immutable-upvar-mutation.rs | 18 +++++++----- .../compile-fail/borrowck-move-by-capture.rs | 11 ++++--- .../cannot-mutate-captured-non-mut-var.rs | 8 +++-- src/test/compile-fail/issue-11925.rs | 6 ++-- src/test/compile-fail/issue-12127.rs | 7 +++-- ...type-move-out-of-closure-env-issue-1965.rs | 6 ++-- .../unboxed-closer-non-implicit-copyable.rs | 4 ++- .../unboxed-closure-illegal-move.rs | 16 ++++++---- .../unboxed-closures-mutate-upvar.rs | 29 +++++++++++-------- ...nboxed-closures-static-call-wrong-trait.rs | 4 ++- .../unboxed-closures-vtable-mismatch.rs | 4 ++- .../unboxed-closures-wrong-trait.rs | 23 --------------- 12 files changed, 72 insertions(+), 64 deletions(-) delete mode 100644 src/test/compile-fail/unboxed-closures-wrong-trait.rs diff --git a/src/test/compile-fail/borrow-immutable-upvar-mutation.rs b/src/test/compile-fail/borrow-immutable-upvar-mutation.rs index 12555c550729c..7033f5caef6c6 100644 --- a/src/test/compile-fail/borrow-immutable-upvar-mutation.rs +++ b/src/test/compile-fail/borrow-immutable-upvar-mutation.rs @@ -8,34 +8,38 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(unboxed_closures, overloaded_calls)] +#![feature(unboxed_closures)] // Tests that we can't assign to or mutably borrow upvars from `Fn` // closures (issue #17780) fn set(x: &mut usize) { *x = 5; } +fn to_fn>(f: F) -> F { f } +fn to_fn_mut>(f: F) -> F { f } + fn main() { // By-ref captures { let mut x = 0us; - let _f = |&:| x = 42; //~ ERROR cannot assign + let _f = to_fn(|| x = 42); //~ ERROR cannot assign let mut y = 0us; - let _g = |&:| set(&mut y); //~ ERROR cannot borrow + let _g = to_fn(|| set(&mut y)); //~ ERROR cannot borrow let mut z = 0us; - let _h = |&mut:| { set(&mut z); |&:| z = 42; }; //~ ERROR cannot assign + let _h = to_fn_mut(|| { set(&mut z); to_fn(|| z = 42); }); //~ ERROR cannot assign } + // By-value captures { let mut x = 0us; - let _f = move |&:| x = 42; //~ ERROR cannot assign + let _f = to_fn(move || x = 42); //~ ERROR cannot assign let mut y = 0us; - let _g = move |&:| set(&mut y); //~ ERROR cannot borrow + let _g = to_fn(move || set(&mut y)); //~ ERROR cannot borrow let mut z = 0us; - let _h = move |&mut:| { set(&mut z); move |&:| z = 42; }; //~ ERROR cannot assign + let _h = to_fn_mut(move || { set(&mut z); to_fn(move || z = 42); }); //~ ERROR cannot assign } } diff --git a/src/test/compile-fail/borrowck-move-by-capture.rs b/src/test/compile-fail/borrowck-move-by-capture.rs index b0d546cd5c803..a1708e7f49728 100644 --- a/src/test/compile-fail/borrowck-move-by-capture.rs +++ b/src/test/compile-fail/borrowck-move-by-capture.rs @@ -8,11 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(box_syntax)] +#![feature(box_syntax,unboxed_closures)] + +fn to_fn_mut>(f: F) -> F { f } +fn to_fn_once>(f: F) -> F { f } pub fn main() { let bar = box 3; - let _g = |&mut:| { - let _h = move |:| -> isize { *bar }; //~ ERROR cannot move out of captured outer variable - }; + let _g = to_fn_mut(|| { + let _h = to_fn_once(move || -> isize { *bar }); //~ ERROR cannot move out of + }); } diff --git a/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs b/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs index 2951c63828d5c..738755855c070 100644 --- a/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs +++ b/src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs @@ -8,12 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(unboxed_closures)] + +fn to_fn_once>(f: F) -> F { f } + fn main() { let x = 1; - move|:| { x = 2; }; + to_fn_once(move|:| { x = 2; }); //~^ ERROR: cannot assign to immutable captured outer variable let s = std::old_io::stdin(); - move|:| { s.read_to_end(); }; + to_fn_once(move|:| { s.read_to_end(); }); //~^ ERROR: cannot borrow immutable captured outer variable } diff --git a/src/test/compile-fail/issue-11925.rs b/src/test/compile-fail/issue-11925.rs index 69f7b46009c38..df4dab2552e7d 100644 --- a/src/test/compile-fail/issue-11925.rs +++ b/src/test/compile-fail/issue-11925.rs @@ -8,12 +8,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(box_syntax)] +#![feature(box_syntax, unboxed_closures)] + +fn to_fn_once>(f: F) -> F { f } fn main() { let r = { let x = box 42; - let f = move|:| &x; //~ ERROR: `x` does not live long enough + let f = to_fn_once(move|| &x); //~ ERROR: `x` does not live long enough f() }; diff --git a/src/test/compile-fail/issue-12127.rs b/src/test/compile-fail/issue-12127.rs index c06082de3cd08..40d446b91a5a8 100644 --- a/src/test/compile-fail/issue-12127.rs +++ b/src/test/compile-fail/issue-12127.rs @@ -8,14 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(box_syntax)] +#![feature(box_syntax, unboxed_closures)] +fn to_fn_once>(f: F) -> F { f } fn do_it(x: &isize) { } fn main() { let x = box 22; - let f = move|:| do_it(&*x); - (move|:| { + let f = to_fn_once(move|| do_it(&*x)); + to_fn_once(move|| { f(); f(); //~^ ERROR: use of moved value: `f` diff --git a/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs b/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs index 5dfe7f0c71f14..4251be36ab438 100644 --- a/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs +++ b/src/test/compile-fail/moves-based-on-type-move-out-of-closure-env-issue-1965.rs @@ -8,13 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(box_syntax)] +#![feature(box_syntax, unboxed_closures)] use std::usize; +fn to_fn>(f: F) -> F { f } + fn test(_x: Box) {} fn main() { let i = box 3; - let _f = |&:| test(i); //~ ERROR cannot move out + let _f = to_fn(|| test(i)); //~ ERROR cannot move out } diff --git a/src/test/compile-fail/unboxed-closer-non-implicit-copyable.rs b/src/test/compile-fail/unboxed-closer-non-implicit-copyable.rs index 182c632d06261..2d55979491981 100644 --- a/src/test/compile-fail/unboxed-closer-non-implicit-copyable.rs +++ b/src/test/compile-fail/unboxed-closer-non-implicit-copyable.rs @@ -10,8 +10,10 @@ #![feature(unboxed_closures)] +fn to_fn_once>(f: F) -> F { f } + fn main() { - let f = move|:| (); + let f = to_fn_once(move|| ()); f(); f(); //~ ERROR use of moved value } diff --git a/src/test/compile-fail/unboxed-closure-illegal-move.rs b/src/test/compile-fail/unboxed-closure-illegal-move.rs index 1312b42fb82d4..224cbc2bef324 100644 --- a/src/test/compile-fail/unboxed-closure-illegal-move.rs +++ b/src/test/compile-fail/unboxed-closure-illegal-move.rs @@ -15,31 +15,35 @@ // if the upvar is captured by ref or the closure takes self by // reference. +fn to_fn>(f: F) -> F { f } +fn to_fn_mut>(f: F) -> F { f } +fn to_fn_once>(f: F) -> F { f } + fn main() { // By-ref cases { let x = box 0us; - let f = |&:| drop(x); //~ ERROR cannot move + let f = to_fn(|| drop(x)); //~ ERROR cannot move } { let x = box 0us; - let f = |&mut:| drop(x); //~ ERROR cannot move + let f = to_fn_mut(|| drop(x)); //~ ERROR cannot move } { let x = box 0us; - let f = |:| drop(x); // OK -- FnOnce + let f = to_fn_once(|| drop(x)); // OK -- FnOnce } // By-value cases { let x = box 0us; - let f = move |&:| drop(x); //~ ERROR cannot move + let f = to_fn(move || drop(x)); //~ ERROR cannot move } { let x = box 0us; - let f = move |&mut:| drop(x); //~ ERROR cannot move + let f = to_fn_mut(move || drop(x)); //~ ERROR cannot move } { let x = box 0us; - let f = move |:| drop(x); // this one is ok + let f = to_fn_once(move || drop(x)); // this one is ok } } diff --git a/src/test/compile-fail/unboxed-closures-mutate-upvar.rs b/src/test/compile-fail/unboxed-closures-mutate-upvar.rs index 96c7948dcb046..650bb17bb7758 100644 --- a/src/test/compile-fail/unboxed-closures-mutate-upvar.rs +++ b/src/test/compile-fail/unboxed-closures-mutate-upvar.rs @@ -12,51 +12,56 @@ // as `mut` through a closure. Also test that we CAN mutate a moved copy, // unless this is a `Fn` closure. Issue #16749. +#![feature(unboxed_closures)] + use std::mem; +fn to_fn>(f: F) -> F { f } +fn to_fn_mut>(f: F) -> F { f } + fn a() { let n = 0u8; - let mut f = |&mut:| { //~ ERROR closure cannot assign + let mut f = to_fn_mut(|| { //~ ERROR closure cannot assign n += 1; - }; + }); } fn b() { let mut n = 0u8; - let mut f = |&mut:| { + let mut f = to_fn_mut(|| { n += 1; // OK - }; + }); } fn c() { let n = 0u8; - let mut f = move |&mut:| { + let mut f = to_fn_mut(move || { // If we just did a straight-forward desugaring, this would // compile, but we do something a bit more subtle, and hence // we get an error. n += 1; //~ ERROR cannot assign - }; + }); } fn d() { let mut n = 0u8; - let mut f = move |&mut:| { + let mut f = to_fn_mut(move || { n += 1; // OK - }; + }); } fn e() { let n = 0u8; - let mut f = move |&:| { + let mut f = to_fn(move || { n += 1; //~ ERROR cannot assign - }; + }); } fn f() { let mut n = 0u8; - let mut f = move |&:| { + let mut f = to_fn(move || { n += 1; //~ ERROR cannot assign - }; + }); } fn main() { } diff --git a/src/test/compile-fail/unboxed-closures-static-call-wrong-trait.rs b/src/test/compile-fail/unboxed-closures-static-call-wrong-trait.rs index 8d3721f28db50..f430e9fc75902 100644 --- a/src/test/compile-fail/unboxed-closures-static-call-wrong-trait.rs +++ b/src/test/compile-fail/unboxed-closures-static-call-wrong-trait.rs @@ -10,8 +10,10 @@ #![feature(unboxed_closures)] +fn to_fn_mut>(f: F) -> F { f } + fn main() { - let mut_ = |&mut: x| x; + let mut_ = to_fn_mut(|x| x); mut_.call((0, )); //~ ERROR does not implement any method in scope named `call` } diff --git a/src/test/compile-fail/unboxed-closures-vtable-mismatch.rs b/src/test/compile-fail/unboxed-closures-vtable-mismatch.rs index 305dd33e5a05a..c2a2e5162ace0 100644 --- a/src/test/compile-fail/unboxed-closures-vtable-mismatch.rs +++ b/src/test/compile-fail/unboxed-closures-vtable-mismatch.rs @@ -12,12 +12,14 @@ use std::ops::FnMut; +fn to_fn_mut>(f: F) -> F { f } + fn call_itisize>(y: isize, mut f: F) -> isize { f(2, y) } pub fn main() { - let f = |&mut: x: usize, y: isize| -> isize { (x as isize) + y }; + let f = to_fn_mut(|x: usize, y: isize| -> isize { (x as isize) + y }); let z = call_it(3, f); //~^ ERROR type mismatch //~| ERROR type mismatch diff --git a/src/test/compile-fail/unboxed-closures-wrong-trait.rs b/src/test/compile-fail/unboxed-closures-wrong-trait.rs deleted file mode 100644 index 2ada0dd22e75f..0000000000000 --- a/src/test/compile-fail/unboxed-closures-wrong-trait.rs +++ /dev/null @@ -1,23 +0,0 @@ -// 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. - -#![feature(lang_items, overloaded_calls, unboxed_closures)] - -fn c isize>(f: F) -> isize { - f(5, 6) -} - -fn main() { - let z: isize = 7; - assert_eq!(c(|&mut: x: isize, y| x + y + z), 10); - //~^ ERROR not implemented - //~| ERROR not implemented -} - From 04311341197ddabc420a8cffc0d26c12228d445b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Feb 2015 11:34:05 -0500 Subject: [PATCH 4/6] Remove the explicit closure kind syntax from the parser and AST; upgrade the inference based on expected type so that it is able to infer the fn kind in isolation even if the full signature is not available (and we could perhaps do better still in some cases, such as extracting just the types of the arguments but not the return value). --- src/librustc/middle/check_loop.rs | 2 +- src/librustc/middle/liveness.rs | 2 +- src/librustc/middle/mem_categorization.rs | 2 +- src/librustc_borrowck/borrowck/mod.rs | 2 +- src/librustc_resolve/lib.rs | 2 +- src/librustc_trans/save/mod.rs | 2 +- src/librustc_trans/trans/base.rs | 2 +- src/librustc_trans/trans/debuginfo.rs | 4 +- src/librustc_trans/trans/expr.rs | 2 +- src/librustc_typeck/check/closure.rs | 151 +++++++++++++--------- src/librustc_typeck/check/mod.rs | 4 +- src/librustc_typeck/check/regionck.rs | 2 +- src/librustc_typeck/check/upvar.rs | 2 +- src/librustc_typeck/check/writeback.rs | 2 +- src/libsyntax/ast.rs | 10 +- src/libsyntax/ast_map/blocks.rs | 2 +- src/libsyntax/ext/build.rs | 4 +- src/libsyntax/ext/expand.rs | 3 +- src/libsyntax/fold.rs | 3 +- src/libsyntax/parse/obsolete.rs | 5 + src/libsyntax/parse/parser.rs | 62 ++++----- src/libsyntax/print/pprust.rs | 19 +-- src/libsyntax/visit.rs | 2 +- 23 files changed, 155 insertions(+), 136 deletions(-) diff --git a/src/librustc/middle/check_loop.rs b/src/librustc/middle/check_loop.rs index 41ef55933cda2..ea584407944ab 100644 --- a/src/librustc/middle/check_loop.rs +++ b/src/librustc/middle/check_loop.rs @@ -45,7 +45,7 @@ impl<'a, 'v> Visitor<'v> for CheckLoopVisitor<'a> { ast::ExprLoop(ref b, _) => { self.with_context(Loop, |v| v.visit_block(&**b)); } - ast::ExprClosure(_, _, _, ref b) => { + ast::ExprClosure(_, _, ref b) => { self.with_context(Closure, |v| v.visit_block(&**b)); } ast::ExprBreak(_) => self.require_loop("break", e.span), diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index e40e04bdee86a..c0fabb2a3481d 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -959,7 +959,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.propagate_through_expr(&**e, succ) } - ast::ExprClosure(_, _, _, ref blk) => { + ast::ExprClosure(_, _, ref blk) => { debug!("{} is an ExprClosure", expr_to_string(expr)); diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 1ae483be2696d..156ff43e2bab3 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -739,7 +739,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { }; match fn_expr.node { - ast::ExprClosure(_, _, _, ref body) => body.id, + ast::ExprClosure(_, _, ref body) => body.id, _ => unreachable!() } }; diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index e5271cfde5a44..b9d2b9ec263ab 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -324,7 +324,7 @@ pub fn closure_to_block(closure_id: ast::NodeId, tcx: &ty::ctxt) -> ast::NodeId { match tcx.map.get(closure_id) { ast_map::NodeExpr(expr) => match expr.node { - ast::ExprClosure(_, _, _, ref block) => { + ast::ExprClosure(_, _, ref block) => { block.id } _ => { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index d87039cbaefc1..dd739059ed0dd 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -4521,7 +4521,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { visit::walk_expr(self, expr); } - ExprClosure(_, _, ref fn_decl, ref block) => { + ExprClosure(_, ref fn_decl, ref block) => { self.resolve_function(ClosureRibKind(expr.id), Some(&**fn_decl), NoTypeParameters, &**block); diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 7758039e40a41..b0ce9641cf440 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -1394,7 +1394,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> { type, found {:?}", ty)[]), } }, - ast::ExprClosure(_, _, ref decl, ref body) => { + ast::ExprClosure(_, ref decl, ref body) => { if generated_code(body.span) { return } diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 9e561fc883bb0..6901eb25b31fe 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -1340,7 +1340,7 @@ fn build_cfg(tcx: &ty::ctxt, id: ast::NodeId) -> (ast::NodeId, Option) } Some(ast_map::NodeExpr(e)) => { match e.node { - ast::ExprClosure(_, _, _, ref blk) => { + ast::ExprClosure(_, _, ref blk) => { blk } _ => tcx.sess.bug("unexpected expr variant in has_nested_returns") diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs index 66bb299273d9f..172e105896c30 100644 --- a/src/librustc_trans/trans/debuginfo.rs +++ b/src/librustc_trans/trans/debuginfo.rs @@ -1283,7 +1283,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } ast_map::NodeExpr(ref expr) => { match expr.node { - ast::ExprClosure(_, _, ref fn_decl, ref top_level_block) => { + ast::ExprClosure(_, ref fn_decl, ref top_level_block) => { let name = format!("fn{}", token::gensym("fn")); let name = token::str_to_ident(&name[]); (name, &**fn_decl, @@ -3595,7 +3595,7 @@ fn create_scope_map(cx: &CrateContext, }) } - ast::ExprClosure(_, _, ref decl, ref block) => { + ast::ExprClosure(_, ref decl, ref block) => { with_new_scope(cx, block.span, scope_stack, diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index bed43a5c83882..df9e722569701 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -1094,7 +1094,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ast::ExprVec(..) | ast::ExprRepeat(..) => { tvec::trans_fixed_vstore(bcx, expr, dest) } - ast::ExprClosure(_, _, ref decl, ref body) => { + ast::ExprClosure(_, ref decl, ref body) => { closure::trans_closure_expr(bcx, &**decl, &**body, expr.id, dest) } ast::ExprCall(ref f, ref args) => { diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index 808dbd4b31910..b2a676e878e63 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -25,7 +25,6 @@ use util::ppaux::Repr; pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &ast::Expr, _capture: ast::CaptureClause, - opt_kind: Option, decl: &'tcx ast::FnDecl, body: &'tcx ast::Block, expected: Expectation<'tcx>) { @@ -33,38 +32,14 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr.repr(fcx.tcx()), expected.repr(fcx.tcx())); - let expected_sig_and_kind = expected.to_option(fcx).and_then(|ty| { - deduce_expectations_from_expected_type(fcx, ty) - }); - - match opt_kind { - None => { - // If users didn't specify what sort of closure they want, - // examine the expected type. For now, if we see explicit - // evidence than an unboxed closure is desired, we'll use - // that. Otherwise, we leave it unspecified, to be filled - // in by upvar inference. - match expected_sig_and_kind { - None => { // don't have information about the kind, request explicit annotation - check_closure(fcx, expr, None, decl, body, None); - }, - Some((sig, kind)) => { - check_closure(fcx, expr, Some(kind), decl, body, Some(sig)); - } - } - } - - Some(kind) => { - let kind = match kind { - ast::FnClosureKind => ty::FnClosureKind, - ast::FnMutClosureKind => ty::FnMutClosureKind, - ast::FnOnceClosureKind => ty::FnOnceClosureKind, - }; - - let expected_sig = expected_sig_and_kind.map(|t| t.0); - check_closure(fcx, expr, Some(kind), decl, body, expected_sig); - } - } + // It's always helpful for inference if we know the kind of + // closure sooner rather than later, so first examine the expected + // type, and see if can glean a closure kind from there. + let (expected_sig,expected_kind) = match expected.to_option(fcx) { + Some(ty) => deduce_expectations_from_expected_type(fcx, ty), + None => (None, None) + }; + check_closure(fcx, expr, expected_kind, decl, body, expected_sig) } fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, @@ -133,21 +108,30 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, fn deduce_expectations_from_expected_type<'a,'tcx>( fcx: &FnCtxt<'a,'tcx>, expected_ty: Ty<'tcx>) - -> Option<(ty::FnSig<'tcx>,ty::ClosureKind)> + -> (Option>,Option) { + debug!("deduce_expectations_from_expected_type(expected_ty={})", + expected_ty.repr(fcx.tcx())); + match expected_ty.sty { ty::ty_trait(ref object_type) => { let proj_bounds = object_type.projection_bounds_with_self_ty(fcx.tcx(), fcx.tcx().types.err); - proj_bounds.iter() - .filter_map(|pb| deduce_expectations_from_projection(fcx, pb)) - .next() + let expectations = + proj_bounds.iter() + .filter_map(|pb| deduce_expectations_from_projection(fcx, pb)) + .next(); + + match expectations { + Some((sig, kind)) => (Some(sig), Some(kind)), + None => (None, None) + } } ty::ty_infer(ty::TyVar(vid)) => { deduce_expectations_from_obligations(fcx, vid) } _ => { - None + (None, None) } } } @@ -155,33 +139,61 @@ fn deduce_expectations_from_expected_type<'a,'tcx>( fn deduce_expectations_from_obligations<'a,'tcx>( fcx: &FnCtxt<'a,'tcx>, expected_vid: ty::TyVid) - -> Option<(ty::FnSig<'tcx>, ty::ClosureKind)> + -> (Option>, Option) { let fulfillment_cx = fcx.inh.fulfillment_cx.borrow(); // Here `expected_ty` is known to be a type inference variable. - fulfillment_cx.pending_obligations() - .iter() - .filter_map(|obligation| { - match obligation.predicate { - ty::Predicate::Projection(ref proj_predicate) => { - let trait_ref = proj_predicate.to_poly_trait_ref(); - let self_ty = fcx.infcx().shallow_resolve(trait_ref.self_ty()); - match self_ty.sty { - ty::ty_infer(ty::TyVar(v)) if expected_vid == v => { - deduce_expectations_from_projection(fcx, proj_predicate) - } - _ => { - None - } - } - } - _ => { - None - } - } - }) - .next() + let expected_sig_and_kind = + fulfillment_cx + .pending_obligations() + .iter() + .filter_map(|obligation| { + debug!("deduce_expectations_from_obligations: obligation.predicate={}", + obligation.predicate.repr(fcx.tcx())); + + match obligation.predicate { + // Given a Projection predicate, we can potentially infer + // the complete signature. + ty::Predicate::Projection(ref proj_predicate) => { + let trait_ref = proj_predicate.to_poly_trait_ref(); + self_type_matches_expected_vid(fcx, trait_ref, expected_vid) + .and_then(|_| deduce_expectations_from_projection(fcx, proj_predicate)) + } + _ => { + None + } + } + }) + .next(); + + match expected_sig_and_kind { + Some((sig, kind)) => { return (Some(sig), Some(kind)); } + None => { } + } + + // Even if we can't infer the full signature, we may be able to + // infer the kind. This can occur if there is a trait-reference + // like `F : Fn`. + let expected_kind = + fulfillment_cx + .pending_obligations() + .iter() + .filter_map(|obligation| { + let opt_trait_ref = match obligation.predicate { + ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref()), + ty::Predicate::Trait(ref data) => Some(data.to_poly_trait_ref()), + ty::Predicate::Equate(..) => None, + ty::Predicate::RegionOutlives(..) => None, + ty::Predicate::TypeOutlives(..) => None, + }; + opt_trait_ref + .and_then(|trait_ref| self_type_matches_expected_vid(fcx, trait_ref, expected_vid)) + .and_then(|trait_ref| fcx.tcx().lang_items.fn_trait_kind(trait_ref.def_id())) + }) + .next(); + + (None, expected_kind) } /// Given a projection like "::Result == Y", we can deduce @@ -229,3 +241,20 @@ fn deduce_expectations_from_projection<'a,'tcx>( return Some((fn_sig, kind)); } +fn self_type_matches_expected_vid<'a,'tcx>( + fcx: &FnCtxt<'a,'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + expected_vid: ty::TyVid) + -> Option> +{ + let self_ty = fcx.infcx().shallow_resolve(trait_ref.self_ty()); + debug!("self_type_matches_expected_vid(trait_ref={}, self_ty={})", + trait_ref.repr(fcx.tcx()), + self_ty.repr(fcx.tcx())); + match self_ty.sty { + ty::ty_infer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref), + _ => None, + } +} + + diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 07b67d543a87e..adf15fbf28a8f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3736,8 +3736,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, ast::ExprMatch(ref discrim, ref arms, match_src) => { _match::check_match(fcx, expr, &**discrim, arms.as_slice(), expected, match_src); } - ast::ExprClosure(capture, opt_kind, ref decl, ref body) => { - closure::check_expr_closure(fcx, expr, capture, opt_kind, &**decl, &**body, expected); + ast::ExprClosure(capture, ref decl, ref body) => { + closure::check_expr_closure(fcx, expr, capture, &**decl, &**body, expected); } ast::ExprBlock(ref b) => { check_block_with_expected(fcx, &**b, expected); diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index d5baff3a0c4a3..9df0403794d7c 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -638,7 +638,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { visit::walk_expr(rcx, expr); } - ast::ExprClosure(_, _, _, ref body) => { + ast::ExprClosure(_, _, ref body) => { check_expr_fn_block(rcx, expr, &**body); } diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs index b52e01f9a7a76..f452c8488ce1c 100644 --- a/src/librustc_typeck/check/upvar.rs +++ b/src/librustc_typeck/check/upvar.rs @@ -83,7 +83,7 @@ struct SeedBorrowKind<'a,'tcx:'a> { impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> { fn visit_expr(&mut self, expr: &ast::Expr) { match expr.node { - ast::ExprClosure(cc, _, _, ref body) => { + ast::ExprClosure(cc, _, ref body) => { self.check_closure(expr, cc, &**body); } diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 52b1eb490cc25..f047a36c56095 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -118,7 +118,7 @@ impl<'cx, 'tcx, 'v> Visitor<'v> for WritebackCx<'cx, 'tcx> { MethodCall::expr(e.id)); match e.node { - ast::ExprClosure(_, _, ref decl, _) => { + ast::ExprClosure(_, ref decl, _) => { for input in &decl.inputs { let _ = self.visit_node_id(ResolvingExpr(e.span), input.id); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index d7283db25a5f2..34eeedeaa7650 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -48,7 +48,6 @@ pub use self::TraitItem::*; pub use self::Ty_::*; pub use self::TyParamBound::*; pub use self::UintTy::*; -pub use self::ClosureKind::*; pub use self::UnOp::*; pub use self::UnsafeSource::*; pub use self::VariantKind::*; @@ -736,7 +735,7 @@ pub enum Expr_ { // FIXME #6993: change to Option ... or not, if these are hygienic. ExprLoop(P, Option), ExprMatch(P, Vec, MatchSource), - ExprClosure(CaptureClause, Option, P, P), + ExprClosure(CaptureClause, P, P), ExprBlock(P), ExprAssign(P, P), @@ -1687,13 +1686,6 @@ impl ForeignItem_ { } } -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] -pub enum ClosureKind { - FnClosureKind, - FnMutClosureKind, - FnOnceClosureKind, -} - /// The data we save and restore about an inlined item or method. This is not /// part of the AST that we parse from a file, but it becomes part of the tree /// that we trans. diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs index 53787d71eef80..a85b87f47d6ee 100644 --- a/src/libsyntax/ast_map/blocks.rs +++ b/src/libsyntax/ast_map/blocks.rs @@ -218,7 +218,7 @@ impl<'a> FnLikeNode<'a> { } } ast_map::NodeExpr(e) => match e.node { - ast::ExprClosure(_, _, ref decl, ref block) => + ast::ExprClosure(_, ref decl, ref block) => closure(ClosureParts::new(&**decl, &**block, e.id, e.span)), _ => panic!("expr FnLikeNode that is not fn-like"), }, diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 2b3a72126831e..53c35ef34cd0d 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -876,14 +876,14 @@ impl<'a> AstBuilder for ExtCtxt<'a> { fn lambda_fn_decl(&self, span: Span, fn_decl: P, blk: P) -> P { - self.expr(span, ast::ExprClosure(ast::CaptureByRef, None, fn_decl, blk)) + self.expr(span, ast::ExprClosure(ast::CaptureByRef, fn_decl, blk)) } fn lambda(&self, span: Span, ids: Vec, blk: P) -> P { let fn_decl = self.fn_decl( ids.iter().map(|id| self.arg(span, *id, self.ty_infer(span))).collect(), self.ty_infer(span)); - self.expr(span, ast::ExprClosure(ast::CaptureByRef, None, fn_decl, blk)) + self.expr(span, ast::ExprClosure(ast::CaptureByRef, fn_decl, blk)) } fn lambda0(&self, span: Span, blk: P) -> P { self.lambda(span, Vec::new(), blk) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 6eacb34401894..77440914342fb 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -322,11 +322,10 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { fld.cx.expr_match(span, into_iter_expr, vec![iter_arm]) } - ast::ExprClosure(capture_clause, opt_kind, fn_decl, block) => { + ast::ExprClosure(capture_clause, fn_decl, block) => { let (rewritten_fn_decl, rewritten_block) = expand_and_rename_fn_decl_and_block(fn_decl, block, fld); let new_node = ast::ExprClosure(capture_clause, - opt_kind, rewritten_fn_decl, rewritten_block); P(ast::Expr{id:id, node: new_node, span: fld.new_span(span)}) diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 9012ec2114d07..07b6af651f610 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1325,9 +1325,8 @@ pub fn noop_fold_expr(Expr {id, node, span}: Expr, folder: &mut T) -> arms.move_map(|x| folder.fold_arm(x)), source) } - ExprClosure(capture_clause, opt_kind, decl, body) => { + ExprClosure(capture_clause, decl, body) => { ExprClosure(capture_clause, - opt_kind, folder.fold_fn_decl(decl), folder.fold_block(body)) } diff --git a/src/libsyntax/parse/obsolete.rs b/src/libsyntax/parse/obsolete.rs index a3600506057af..60de6c909b78b 100644 --- a/src/libsyntax/parse/obsolete.rs +++ b/src/libsyntax/parse/obsolete.rs @@ -27,6 +27,7 @@ pub enum ObsoleteSyntax { ProcType, ProcExpr, ClosureType, + ClosureKind, } pub trait ParserObsoleteMethods { @@ -65,6 +66,10 @@ impl<'a> ParserObsoleteMethods for parser::Parser<'a> { "`|usize| -> bool` closure type syntax", "use unboxed closures instead, no type annotation needed" ), + ObsoleteSyntax::ClosureKind => ( + "`:`, `&mut:`, or `&:` syntax", + "rely on inference instead" + ), ObsoleteSyntax::Sized => ( "`Sized? T` syntax for removing the `Sized` bound", "write `T: ?Sized` instead" diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c3182602a4b89..385c0a48f870f 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -28,8 +28,6 @@ use ast::{ExprLit, ExprLoop, ExprMac, ExprRange}; use ast::{ExprMethodCall, ExprParen, ExprPath, ExprQPath}; use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary}; use ast::{ExprVec, ExprWhile, ExprWhileLet, ExprForLoop, Field, FnDecl}; -use ast::{FnClosureKind, FnMutClosureKind}; -use ast::{FnOnceClosureKind}; use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, ForeignMod, FunctionRetTy}; use ast::{Ident, Inherited, ImplItem, Item, Item_, ItemStatic}; use ast::{ItemEnum, ItemFn, ItemForeignMod, ItemImpl, ItemConst}; @@ -57,7 +55,7 @@ use ast::{TyFixedLengthVec, TyBareFn}; use ast::{TyTypeof, TyInfer, TypeMethod}; use ast::{TyParam, TyParamBound, TyParen, TyPath, TyPolyTraitRef, TyPtr, TyQPath}; use ast::{TyRptr, TyTup, TyU32, TyVec, UnUniq}; -use ast::{TypeImplItem, TypeTraitItem, Typedef, ClosureKind}; +use ast::{TypeImplItem, TypeTraitItem, Typedef,}; use ast::{UnnamedField, UnsafeBlock}; use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple}; use ast::{Visibility, WhereClause}; @@ -1139,29 +1137,36 @@ impl<'a> Parser<'a> { TyInfer } - /// Parses an optional closure kind (`&:`, `&mut:`, or `:`). - pub fn parse_optional_closure_kind(&mut self) -> Option { - if self.check(&token::BinOp(token::And)) && - self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) && - self.look_ahead(2, |t| *t == token::Colon) { + /// Parses an obsolete closure kind (`&:`, `&mut:`, or `:`). + pub fn parse_obsolete_closure_kind(&mut self) { + // let lo = self.span.lo; + if + self.check(&token::BinOp(token::And)) && + self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) && + self.look_ahead(2, |t| *t == token::Colon) + { self.bump(); self.bump(); self.bump(); - return Some(FnMutClosureKind) - } - - if self.token == token::BinOp(token::And) && - self.look_ahead(1, |t| *t == token::Colon) { + } else if + self.token == token::BinOp(token::And) && + self.look_ahead(1, |t| *t == token::Colon) + { self.bump(); self.bump(); - return Some(FnClosureKind) - } - - if self.eat(&token::Colon) { - return Some(FnOnceClosureKind) + return; + } else if + self.eat(&token::Colon) + { + /* nothing */ + } else { + return; } - return None + // SNAP a45e117 + // Enable these obsolete errors after snapshot: + // let span = mk_sp(lo, self.span.hi); + // self.obsolete(span, ObsoleteSyntax::ClosureKind); } pub fn parse_ty_bare_fn_or_ty_closure(&mut self, lifetime_defs: Vec) -> Ty_ { @@ -3047,7 +3052,7 @@ impl<'a> Parser<'a> { -> P { let lo = self.span.lo; - let (decl, optional_closure_kind) = self.parse_fn_block_decl(); + let decl = self.parse_fn_block_decl(); let body = self.parse_expr(); let fakeblock = P(ast::Block { id: ast::DUMMY_NODE_ID, @@ -3060,7 +3065,7 @@ impl<'a> Parser<'a> { self.mk_expr( lo, fakeblock.span.hi, - ExprClosure(capture_clause, optional_closure_kind, decl, fakeblock)) + ExprClosure(capture_clause, decl, fakeblock)) } pub fn parse_else_expr(&mut self) -> P { @@ -4529,30 +4534,29 @@ impl<'a> Parser<'a> { } // parse the |arg, arg| header on a lambda - fn parse_fn_block_decl(&mut self) -> (P, Option) { - let (optional_closure_kind, inputs_captures) = { + fn parse_fn_block_decl(&mut self) -> P { + let inputs_captures = { if self.eat(&token::OrOr) { - (None, Vec::new()) + Vec::new() } else { self.expect(&token::BinOp(token::Or)); - let optional_closure_kind = - self.parse_optional_closure_kind(); + self.parse_obsolete_closure_kind(); let args = self.parse_seq_to_before_end( &token::BinOp(token::Or), seq_sep_trailing_allowed(token::Comma), |p| p.parse_fn_block_arg() ); self.bump(); - (optional_closure_kind, args) + args } }; let output = self.parse_ret_ty(); - (P(FnDecl { + P(FnDecl { inputs: inputs_captures, output: output, variadic: false - }), optional_closure_kind) + }) } /// Parses the `(arg, arg) -> return_type` header on a procedure. diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index e6d895a49fcd7..ee8e207fa6c05 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -11,11 +11,9 @@ pub use self::AnnNode::*; use abi; -use ast::{self, FnClosureKind, FnMutClosureKind}; -use ast::{FnOnceClosureKind}; +use ast; use ast::{MethodImplItem, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier}; use ast::{RequiredMethod, ProvidedMethod, TypeImplItem, TypeTraitItem}; -use ast::{ClosureKind}; use ast_util; use owned_slice::OwnedSlice; use attr::{AttrMetaMethods, AttributeMethods}; @@ -350,7 +348,7 @@ pub fn method_to_string(p: &ast::Method) -> String { } pub fn fn_block_to_string(p: &ast::FnDecl) -> String { - $to_string(|s| s.print_fn_block_args(p, None)) + $to_string(|s| s.print_fn_block_args(p)) } pub fn path_to_string(p: &ast::Path) -> String { @@ -1747,10 +1745,10 @@ impl<'a> State<'a> { } try!(self.bclose_(expr.span, indent_unit)); } - ast::ExprClosure(capture_clause, opt_kind, ref decl, ref body) => { + ast::ExprClosure(capture_clause, ref decl, ref body) => { try!(self.print_capture_clause(capture_clause)); - try!(self.print_fn_block_args(&**decl, opt_kind)); + try!(self.print_fn_block_args(&**decl)); try!(space(&mut self.s)); if !body.stmts.is_empty() || !body.expr.is_some() { @@ -2350,16 +2348,9 @@ impl<'a> State<'a> { pub fn print_fn_block_args( &mut self, - decl: &ast::FnDecl, - closure_kind: Option) + decl: &ast::FnDecl) -> IoResult<()> { try!(word(&mut self.s, "|")); - match closure_kind { - None => {} - Some(FnClosureKind) => try!(self.word_space("&:")), - Some(FnMutClosureKind) => try!(self.word_space("&mut:")), - Some(FnOnceClosureKind) => try!(self.word_space(":")), - } try!(self.print_fn_args(decl, None)); try!(word(&mut self.s, "|")); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index bd84306fe17e0..fbcfcaadf12b7 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -836,7 +836,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_arm(arm) } } - ExprClosure(_, _, ref function_declaration, ref body) => { + ExprClosure(_, ref function_declaration, ref body) => { visitor.visit_fn(FkFnBlock, &**function_declaration, &**body, From 68ad6949d4c3e2160098c94007b9c48abc94aaad Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Feb 2015 13:14:29 -0500 Subject: [PATCH 5/6] Correct one case where the inference was detecting a looser result than the explicit annotation, leading to "extra `mut` declaration" lint errors. --- src/libsyntax/ext/deriving/rand.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsyntax/ext/deriving/rand.rs b/src/libsyntax/ext/deriving/rand.rs index 9fd5091e194dc..739c73a70b02b 100644 --- a/src/libsyntax/ext/deriving/rand.rs +++ b/src/libsyntax/ext/deriving/rand.rs @@ -66,7 +66,7 @@ fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) cx.ident_of("Rand"), cx.ident_of("rand") ); - let mut rand_call = |&mut: cx: &mut ExtCtxt, span| { + let rand_call = |&: cx: &mut ExtCtxt, span| { cx.expr_call_global(span, rand_ident.clone(), vec!(rng.clone())) From 8ddcb06b1d021560bfe641c0dbc452a04e80388e Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Feb 2015 13:14:36 -0500 Subject: [PATCH 6/6] Update for new snapshot after rebasing. --- src/libsyntax/parse/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 385c0a48f870f..2cb265033c399 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1163,7 +1163,7 @@ impl<'a> Parser<'a> { return; } - // SNAP a45e117 + // SNAP 474b324 // Enable these obsolete errors after snapshot: // let span = mk_sp(lo, self.span.hi); // self.obsolete(span, ObsoleteSyntax::ClosureKind);