Skip to content

Commit 74f7e06

Browse files
committed
rollup merge of rust-lang#21899: nikomatsakis/closure-unify-anyhow
This *almost* completes the job for rust-lang#16440. The idea is that even if we do not know whether some closure type `C` implements `Fn` or `FnMut` (etc), we still know its argument and return types. So if we see an obligation `C : Fn(_0)`, we can unify `_0` with those argument types while still considering the obligation ambiguous and unsatisfied. This helps to make a lot of progress with type inference even before closure kind inference is done. As part of this PR, the explicit `:` syntax is removed from the AST and completely ignored. We still infer the closure kind based on the expected type if that is available. There are several reasons for this. First, deciding the closure kind earlier is always better, as it allows us to make more progress. Second, this retains a (admittedly obscure) way for users to manually specify the closure kind, which is useful for writing tests if nothing else. Finally, there are still some cases where inference can fail, so it may be useful to have this manual override. (The expectation is that we will eventually revisit an explicit syntax for specifying the closure kind, but it will not be `:` and may be some sort of generalization of the `||` syntax to handle other traits as well.) This commit does not *quite* fix rust-lang#16640 because a snapshot is still needed to enable the obsolete syntax errors for explicit `&mut:` and friends. r? @eddyb as he reviewed the prior patch in this direction
2 parents 1d921f5 + 8ddcb06 commit 74f7e06

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+349
-249
lines changed

src/librustc/middle/check_loop.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ impl<'a, 'v> Visitor<'v> for CheckLoopVisitor<'a> {
4545
ast::ExprLoop(ref b, _) => {
4646
self.with_context(Loop, |v| v.visit_block(&**b));
4747
}
48-
ast::ExprClosure(_, _, _, ref b) => {
48+
ast::ExprClosure(_, _, ref b) => {
4949
self.with_context(Closure, |v| v.visit_block(&**b));
5050
}
5151
ast::ExprBreak(_) => self.require_loop("break", e.span),

src/librustc/middle/liveness.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
959959
self.propagate_through_expr(&**e, succ)
960960
}
961961

962-
ast::ExprClosure(_, _, _, ref blk) => {
962+
ast::ExprClosure(_, _, ref blk) => {
963963
debug!("{} is an ExprClosure",
964964
expr_to_string(expr));
965965

src/librustc/middle/mem_categorization.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -739,7 +739,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
739739
};
740740

741741
match fn_expr.node {
742-
ast::ExprClosure(_, _, _, ref body) => body.id,
742+
ast::ExprClosure(_, _, ref body) => body.id,
743743
_ => unreachable!()
744744
}
745745
};

src/librustc/middle/traits/project.rs

+54-21
Original file line numberDiff line numberDiff line change
@@ -80,37 +80,23 @@ pub fn poly_project_and_unify_type<'cx,'tcx>(
8080
obligation.repr(selcx.tcx()));
8181

8282
let infcx = selcx.infcx();
83-
let result = infcx.try(|snapshot| {
83+
infcx.try(|snapshot| {
8484
let (skol_predicate, skol_map) =
8585
infcx.skolemize_late_bound_regions(&obligation.predicate, snapshot);
8686

8787
let skol_obligation = obligation.with(skol_predicate);
8888
match project_and_unify_type(selcx, &skol_obligation) {
89-
Ok(Some(obligations)) => {
89+
Ok(result) => {
9090
match infcx.leak_check(&skol_map, snapshot) {
91-
Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, &obligations)),
92-
Err(e) => Err(Some(MismatchedProjectionTypes { err: e })),
91+
Ok(()) => Ok(infcx.plug_leaks(skol_map, snapshot, &result)),
92+
Err(e) => Err(MismatchedProjectionTypes { err: e }),
9393
}
9494
}
95-
Ok(None) => {
96-
// Signal ambiguity using Err just so that infcx.try()
97-
// rolls back the snapshot. We adapt below.
98-
Err(None)
99-
}
10095
Err(e) => {
101-
Err(Some(e))
96+
Err(e)
10297
}
10398
}
104-
});
105-
106-
// Above, we use Err(None) to signal ambiguity so that the
107-
// snapshot will be rolled back. But here, we want to translate to
108-
// Ok(None). Kind of weird.
109-
match result {
110-
Ok(obligations) => Ok(Some(obligations)),
111-
Err(None) => Ok(None),
112-
Err(Some(e)) => Err(e),
113-
}
99+
})
114100
}
115101

116102
/// Evaluates constraints of the form:
@@ -132,7 +118,10 @@ fn project_and_unify_type<'cx,'tcx>(
132118
obligation.cause.clone(),
133119
obligation.recursion_depth) {
134120
Some(n) => n,
135-
None => { return Ok(None); }
121+
None => {
122+
consider_unification_despite_ambiguity(selcx, obligation);
123+
return Ok(None);
124+
}
136125
};
137126

138127
debug!("project_and_unify_type: normalized_ty={} obligations={}",
@@ -147,6 +136,50 @@ fn project_and_unify_type<'cx,'tcx>(
147136
}
148137
}
149138

139+
fn consider_unification_despite_ambiguity<'cx,'tcx>(selcx: &mut SelectionContext<'cx,'tcx>,
140+
obligation: &ProjectionObligation<'tcx>) {
141+
debug!("consider_unification_despite_ambiguity(obligation={})",
142+
obligation.repr(selcx.tcx()));
143+
144+
let def_id = obligation.predicate.projection_ty.trait_ref.def_id;
145+
match selcx.tcx().lang_items.fn_trait_kind(def_id) {
146+
Some(_) => { }
147+
None => { return; }
148+
}
149+
150+
let infcx = selcx.infcx();
151+
let self_ty = obligation.predicate.projection_ty.trait_ref.self_ty();
152+
let self_ty = infcx.shallow_resolve(self_ty);
153+
debug!("consider_unification_despite_ambiguity: self_ty.sty={:?}",
154+
self_ty.sty);
155+
match self_ty.sty {
156+
ty::ty_closure(closure_def_id, _, substs) => {
157+
let closure_typer = selcx.closure_typer();
158+
let closure_type = closure_typer.closure_type(closure_def_id, substs);
159+
let ty::Binder((_, ret_type)) =
160+
util::closure_trait_ref_and_return_type(infcx.tcx,
161+
def_id,
162+
self_ty,
163+
&closure_type.sig,
164+
util::TupleArgumentsFlag::No);
165+
let (ret_type, _) =
166+
infcx.replace_late_bound_regions_with_fresh_var(
167+
obligation.cause.span,
168+
infer::AssocTypeProjection(obligation.predicate.projection_ty.item_name),
169+
&ty::Binder(ret_type));
170+
debug!("consider_unification_despite_ambiguity: ret_type={:?}",
171+
ret_type.repr(selcx.tcx()));
172+
let origin = infer::RelateOutputImplTypes(obligation.cause.span);
173+
let obligation_ty = obligation.predicate.ty;
174+
match infer::mk_eqty(infcx, true, origin, obligation_ty, ret_type) {
175+
Ok(()) => { }
176+
Err(_) => { /* ignore errors */ }
177+
}
178+
}
179+
_ => { }
180+
}
181+
}
182+
150183
/// Normalizes any associated type projections in `value`, replacing
151184
/// them with a fully resolved type where possible. The return value
152185
/// combines the normalized result and any additional obligations that

src/librustc/middle/traits/select.rs

+61-21
Original file line numberDiff line numberDiff line change
@@ -233,21 +233,77 @@ 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+
// Is this a `C : FnFoo(...)` trait reference for some trait binding `FnFoo`?
282+
match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) {
283+
Some(_) => { }
284+
None => { return; }
285+
}
286+
287+
// Is the self-type a closure type? We ignore bindings here
288+
// because if it is a closure type, it must be a closure type from
289+
// within this current fn, and hence none of the higher-ranked
290+
// lifetimes can appear inside the self-type.
291+
let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
292+
let (closure_def_id, substs) = match self_ty.sty {
293+
ty::ty_closure(id, _, ref substs) => (id, substs.clone()),
294+
_ => { return; }
295+
};
296+
assert!(!substs.has_escaping_regions());
297+
298+
let closure_trait_ref = self.closure_trait_ref(obligation, closure_def_id, substs);
299+
match self.confirm_poly_trait_refs(obligation.cause.clone(),
300+
obligation.predicate.to_poly_trait_ref(),
301+
closure_trait_ref) {
302+
Ok(()) => { }
303+
Err(_) => { /* Silently ignore errors. */ }
304+
}
305+
}
306+
251307
///////////////////////////////////////////////////////////////////////////
252308
// EVALUATION
253309
//
@@ -1003,7 +1059,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
10031059
candidates: &mut SelectionCandidateSet<'tcx>)
10041060
-> Result<(),SelectionError<'tcx>>
10051061
{
1006-
let kind = match self.fn_family_trait_kind(obligation.predicate.0.def_id()) {
1062+
let kind = match self.tcx().lang_items.fn_trait_kind(obligation.predicate.0.def_id()) {
10071063
Some(k) => k,
10081064
None => { return Ok(()); }
10091065
};
@@ -2303,22 +2359,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
23032359
impl_obligations
23042360
}
23052361

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-
23222362
#[allow(unused_comparisons)]
23232363
fn derived_cause(&self,
23242364
obligation: &TraitObligation<'tcx>,

src/librustc_borrowck/borrowck/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ pub fn closure_to_block(closure_id: ast::NodeId,
324324
tcx: &ty::ctxt) -> ast::NodeId {
325325
match tcx.map.get(closure_id) {
326326
ast_map::NodeExpr(expr) => match expr.node {
327-
ast::ExprClosure(_, _, _, ref block) => {
327+
ast::ExprClosure(_, _, ref block) => {
328328
block.id
329329
}
330330
_ => {

src/librustc_resolve/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4521,7 +4521,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
45214521
visit::walk_expr(self, expr);
45224522
}
45234523

4524-
ExprClosure(_, _, ref fn_decl, ref block) => {
4524+
ExprClosure(_, ref fn_decl, ref block) => {
45254525
self.resolve_function(ClosureRibKind(expr.id),
45264526
Some(&**fn_decl), NoTypeParameters,
45274527
&**block);

src/librustc_trans/save/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1394,7 +1394,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
13941394
type, found {:?}", ty)[]),
13951395
}
13961396
},
1397-
ast::ExprClosure(_, _, ref decl, ref body) => {
1397+
ast::ExprClosure(_, ref decl, ref body) => {
13981398
if generated_code(body.span) {
13991399
return
14001400
}

src/librustc_trans/trans/base.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1340,7 +1340,7 @@ fn build_cfg(tcx: &ty::ctxt, id: ast::NodeId) -> (ast::NodeId, Option<cfg::CFG>)
13401340
}
13411341
Some(ast_map::NodeExpr(e)) => {
13421342
match e.node {
1343-
ast::ExprClosure(_, _, _, ref blk) => {
1343+
ast::ExprClosure(_, _, ref blk) => {
13441344
blk
13451345
}
13461346
_ => tcx.sess.bug("unexpected expr variant in has_nested_returns")

src/librustc_trans/trans/debuginfo.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1283,7 +1283,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
12831283
}
12841284
ast_map::NodeExpr(ref expr) => {
12851285
match expr.node {
1286-
ast::ExprClosure(_, _, ref fn_decl, ref top_level_block) => {
1286+
ast::ExprClosure(_, ref fn_decl, ref top_level_block) => {
12871287
let name = format!("fn{}", token::gensym("fn"));
12881288
let name = token::str_to_ident(&name[]);
12891289
(name, &**fn_decl,
@@ -3595,7 +3595,7 @@ fn create_scope_map(cx: &CrateContext,
35953595
})
35963596
}
35973597

3598-
ast::ExprClosure(_, _, ref decl, ref block) => {
3598+
ast::ExprClosure(_, ref decl, ref block) => {
35993599
with_new_scope(cx,
36003600
block.span,
36013601
scope_stack,

src/librustc_trans/trans/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1095,7 +1095,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
10951095
ast::ExprVec(..) | ast::ExprRepeat(..) => {
10961096
tvec::trans_fixed_vstore(bcx, expr, dest)
10971097
}
1098-
ast::ExprClosure(_, _, ref decl, ref body) => {
1098+
ast::ExprClosure(_, ref decl, ref body) => {
10991099
closure::trans_closure_expr(bcx, &**decl, &**body, expr.id, dest)
11001100
}
11011101
ast::ExprCall(ref f, ref args) => {

0 commit comments

Comments
 (0)