diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 000572cdd40a..7a59909e1316 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 893003dd9972..51996ddfbe80 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); }