Skip to content

Commit c9e1c44

Browse files
committed
Allow closure arguments types to unify even if we can't fully resolve
a trait obligation. Partial fix for rust-lang#16440 -- closure return types are not handled yet.
1 parent 3d072a1 commit c9e1c44

File tree

2 files changed

+63
-22
lines changed

2 files changed

+63
-22
lines changed

src/librustc/middle/traits/select.rs

+62-21
Original file line numberDiff line numberDiff line change
@@ -233,21 +233,78 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
233233
// is `Vec<Foo>:Iterable<Bar>`, but the impl specifies
234234
// `impl<T> Iterable<T> for Vec<T>`, than an error would result.
235235

236-
/// Evaluates whether the obligation can be satisfied. Returns an indication of whether the
237-
/// obligation can be satisfied and, if so, by what means. Never affects surrounding typing
238-
/// environment.
236+
/// Attempts to satisfy the obligation. If successful, this will affect the surrounding
237+
/// type environment by performing unification.
239238
pub fn select(&mut self, obligation: &TraitObligation<'tcx>)
240239
-> SelectionResult<'tcx, Selection<'tcx>> {
241240
debug!("select({})", obligation.repr(self.tcx()));
242241
assert!(!obligation.predicate.has_escaping_regions());
243242

244243
let stack = self.push_stack(None, obligation);
245244
match try!(self.candidate_from_obligation(&stack)) {
246-
None => Ok(None),
245+
None => {
246+
self.consider_unification_despite_ambiguity(obligation);
247+
Ok(None)
248+
}
247249
Some(candidate) => Ok(Some(try!(self.confirm_candidate(obligation, candidate)))),
248250
}
249251
}
250252

253+
/// In the particular case of unboxed closure obligations, we can
254+
/// sometimes do some amount of unification for the
255+
/// argument/return types even though we can't yet fully match obligation.
256+
/// The particular case we are interesting in is an obligation of the form:
257+
///
258+
/// C : FnFoo<A>
259+
///
260+
/// where `C` is an unboxed closure type and `FnFoo` is one of the
261+
/// `Fn` traits. Because we know that users cannot write impls for closure types
262+
/// themselves, the only way that `C : FnFoo` can fail to match is under two
263+
/// conditions:
264+
///
265+
/// 1. The closure kind for `C` is not yet known, because inference isn't complete.
266+
/// 2. The closure kind for `C` *is* known, but doesn't match what is needed.
267+
/// For example, `C` may be a `FnOnce` closure, but a `Fn` closure is needed.
268+
///
269+
/// In either case, we always know what argument types are
270+
/// expected by `C`, no matter what kind of `Fn` trait it
271+
/// eventually matches. So we can go ahead and unify the argument
272+
/// types, even though the end result is ambiguous.
273+
///
274+
/// Note that this is safe *even if* the trait would never be
275+
/// matched (case 2 above). After all, in that case, an error will
276+
/// result, so it kind of doesn't matter what we do --- unifying
277+
/// the argument types can only be helpful to the user, because
278+
/// once they patch up the kind of closure that is expected, the
279+
/// argment types won't really change.
280+
fn consider_unification_despite_ambiguity(&mut self, obligation: &TraitObligation<'tcx>)
281+
{
282+
// Is this a `C : FnFoo(...)` trait reference for some trait binding `FnFoo`?
283+
match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) {
284+
Some(_) => { }
285+
None => { return; }
286+
}
287+
288+
// Is the self-type a closure type? We ignore bindings here
289+
// because if it is a closure type, it must be a closure type from
290+
// within this current fn, and hence none of the higher-ranked
291+
// lifetimes can appear inside the self-type.
292+
let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
293+
let (closure_def_id, substs) = match self_ty.sty {
294+
ty::ty_closure(id, _, ref substs) => (id, substs.clone()),
295+
_ => { return; }
296+
};
297+
assert!(!substs.has_escaping_regions());
298+
299+
let closure_trait_ref = self.closure_trait_ref(obligation, closure_def_id, substs);
300+
match self.confirm_poly_trait_refs(obligation.cause.clone(),
301+
obligation.predicate.to_poly_trait_ref(),
302+
closure_trait_ref) {
303+
Ok(()) => { }
304+
Err(_) => { /* Silently ignore errors. */ }
305+
}
306+
}
307+
251308
///////////////////////////////////////////////////////////////////////////
252309
// EVALUATION
253310
//
@@ -1003,7 +1060,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
10031060
candidates: &mut SelectionCandidateSet<'tcx>)
10041061
-> Result<(),SelectionError<'tcx>>
10051062
{
1006-
let kind = match self.fn_family_trait_kind(obligation.predicate.0.def_id()) {
1063+
let kind = match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) {
10071064
Some(k) => k,
10081065
None => { return Ok(()); }
10091066
};
@@ -2303,22 +2360,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
23032360
impl_obligations
23042361
}
23052362

2306-
fn fn_family_trait_kind(&self,
2307-
trait_def_id: ast::DefId)
2308-
-> Option<ty::ClosureKind>
2309-
{
2310-
let tcx = self.tcx();
2311-
if Some(trait_def_id) == tcx.lang_items.fn_trait() {
2312-
Some(ty::FnClosureKind)
2313-
} else if Some(trait_def_id) == tcx.lang_items.fn_mut_trait() {
2314-
Some(ty::FnMutClosureKind)
2315-
} else if Some(trait_def_id) == tcx.lang_items.fn_once_trait() {
2316-
Some(ty::FnOnceClosureKind)
2317-
} else {
2318-
None
2319-
}
2320-
}
2321-
23222363
#[allow(unused_comparisons)]
23232364
fn derived_cause(&self,
23242365
obligation: &TraitObligation<'tcx>,

src/test/run-pass/closure-inference.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ fn foo(i: int) -> int { i + 1 }
1414
fn apply<A, F>(f: F, v: A) -> A where F: FnOnce(A) -> A { f(v) }
1515

1616
pub fn main() {
17-
let f = {|: i| foo(i)};
17+
let f = {|i| foo(i)};
1818
assert_eq!(apply(f, 2), 3);
1919
}

0 commit comments

Comments
 (0)