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.
This commit is contained in:
Niko Matsakis 2015-02-02 11:52:08 -05:00
parent 3d072a193b
commit c9e1c445db
2 changed files with 63 additions and 22 deletions

View file

@ -233,9 +233,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// is `Vec<Foo>:Iterable<Bar>`, but the impl specifies
// `impl<T> Iterable<T> for Vec<T>`, 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<A>
///
/// 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<ty::ClosureKind>
{
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>,

View file

@ -14,6 +14,6 @@ fn foo(i: int) -> int { i + 1 }
fn apply<A, F>(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);
}