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:
parent
3d072a193b
commit
c9e1c445db
@ -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>,
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user