/
closure.rs
246 lines (211 loc) · 9.77 KB
/
closure.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Code for type-checking closure expressions.
use super::{check_fn, Expectation, FnCtxt};
use astconv::AstConv;
use rustc::ty::{self, ToPolyTraitRef, Ty};
use std::cmp;
use syntax::abi::Abi;
use rustc::hir;
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn check_expr_closure(&self,
expr: &hir::Expr,
_capture: hir::CaptureClause,
decl: &'gcx hir::FnDecl,
body: &'gcx hir::Block,
expected: Expectation<'tcx>) {
debug!("check_expr_closure(expr={:?},expected={:?})",
expr,
expected);
// It's always helpful for inference if we know the kind of
// closure sooner rather than later, so first examine the expected
// type, and see if can glean a closure kind from there.
let (expected_sig,expected_kind) = match expected.to_option(self) {
Some(ty) => self.deduce_expectations_from_expected_type(ty),
None => (None, None)
};
self.check_closure(expr, expected_kind, decl, body, expected_sig)
}
fn check_closure(&self,
expr: &hir::Expr,
opt_kind: Option<ty::ClosureKind>,
decl: &'gcx hir::FnDecl,
body: &'gcx hir::Block,
expected_sig: Option<ty::FnSig<'tcx>>) {
let expr_def_id = self.tcx.map.local_def_id(expr.id);
debug!("check_closure opt_kind={:?} expected_sig={:?}",
opt_kind,
expected_sig);
let mut fn_ty = AstConv::ty_of_closure(self,
hir::Unsafety::Normal,
decl,
Abi::RustCall,
expected_sig);
// Create type variables (for now) to represent the transformed
// types of upvars. These will be unified during the upvar
// inference phase (`upvar.rs`).
let num_upvars = self.tcx.with_freevars(expr.id, |fv| fv.len());
let upvar_tys = self.next_ty_vars(num_upvars);
debug!("check_closure: expr.id={:?} upvar_tys={:?}",
expr.id, upvar_tys);
let closure_type = self.tcx.mk_closure(expr_def_id,
self.parameter_environment.free_substs,
upvar_tys);
self.write_ty(expr.id, closure_type);
let fn_sig = self.tcx.liberate_late_bound_regions(
self.tcx.region_maps.call_site_extent(expr.id, body.id), &fn_ty.sig);
let fn_sig =
(**self).normalize_associated_types_in(body.span, body.id, &fn_sig);
check_fn(self, hir::Unsafety::Normal, expr.id, &fn_sig, decl, expr.id, &body);
// Tuple up the arguments and insert the resulting function type into
// the `closures` table.
fn_ty.sig.0.inputs = vec![self.tcx.mk_tup(fn_ty.sig.0.inputs)];
debug!("closure for {:?} --> sig={:?} opt_kind={:?}",
expr_def_id,
fn_ty.sig,
opt_kind);
self.tables.borrow_mut().closure_tys.insert(expr_def_id, fn_ty);
match opt_kind {
Some(kind) => { self.tables.borrow_mut().closure_kinds.insert(expr_def_id, kind); }
None => { }
}
}
fn deduce_expectations_from_expected_type(&self, expected_ty: Ty<'tcx>)
-> (Option<ty::FnSig<'tcx>>,Option<ty::ClosureKind>)
{
debug!("deduce_expectations_from_expected_type(expected_ty={:?})",
expected_ty);
match expected_ty.sty {
ty::TyTrait(ref object_type) => {
let sig = object_type.projection_bounds.iter().filter_map(|pb| {
let pb = pb.with_self_ty(self.tcx, self.tcx.types.err);
self.deduce_sig_from_projection(&pb)
}).next();
let kind = self.tcx.lang_items.fn_trait_kind(object_type.principal.def_id());
(sig, kind)
}
ty::TyInfer(ty::TyVar(vid)) => {
self.deduce_expectations_from_obligations(vid)
}
_ => {
(None, None)
}
}
}
fn deduce_expectations_from_obligations(&self, expected_vid: ty::TyVid)
-> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>)
{
let fulfillment_cx = self.fulfillment_cx.borrow();
// Here `expected_ty` is known to be a type inference variable.
let expected_sig =
fulfillment_cx
.pending_obligations()
.iter()
.map(|obligation| &obligation.obligation)
.filter_map(|obligation| {
debug!("deduce_expectations_from_obligations: obligation.predicate={:?}",
obligation.predicate);
match obligation.predicate {
// Given a Projection predicate, we can potentially infer
// the complete signature.
ty::Predicate::Projection(ref proj_predicate) => {
let trait_ref = proj_predicate.to_poly_trait_ref();
self.self_type_matches_expected_vid(trait_ref, expected_vid)
.and_then(|_| self.deduce_sig_from_projection(proj_predicate))
}
_ => {
None
}
}
})
.next();
// Even if we can't infer the full signature, we may be able to
// infer the kind. This can occur if there is a trait-reference
// like `F : Fn<A>`. Note that due to subtyping we could encounter
// many viable options, so pick the most restrictive.
let expected_kind =
fulfillment_cx
.pending_obligations()
.iter()
.map(|obligation| &obligation.obligation)
.filter_map(|obligation| {
let opt_trait_ref = match obligation.predicate {
ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref()),
ty::Predicate::Trait(ref data) => Some(data.to_poly_trait_ref()),
ty::Predicate::Equate(..) => None,
ty::Predicate::RegionOutlives(..) => None,
ty::Predicate::TypeOutlives(..) => None,
ty::Predicate::WellFormed(..) => None,
ty::Predicate::ObjectSafe(..) => None,
ty::Predicate::Rfc1592(..) => None,
// NB: This predicate is created by breaking down a
// `ClosureType: FnFoo()` predicate, where
// `ClosureType` represents some `TyClosure`. It can't
// possibly be referring to the current closure,
// because we haven't produced the `TyClosure` for
// this closure yet; this is exactly why the other
// code is looking for a self type of a unresolved
// inference variable.
ty::Predicate::ClosureKind(..) => None,
};
opt_trait_ref
.and_then(|tr| self.self_type_matches_expected_vid(tr, expected_vid))
.and_then(|tr| self.tcx.lang_items.fn_trait_kind(tr.def_id()))
})
.fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur))));
(expected_sig, expected_kind)
}
/// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce
/// everything we need to know about a closure.
fn deduce_sig_from_projection(&self,
projection: &ty::PolyProjectionPredicate<'tcx>)
-> Option<ty::FnSig<'tcx>>
{
let tcx = self.tcx;
debug!("deduce_sig_from_projection({:?})",
projection);
let trait_ref = projection.to_poly_trait_ref();
if tcx.lang_items.fn_trait_kind(trait_ref.def_id()).is_none() {
return None;
}
let arg_param_ty = trait_ref.substs().type_at(1);
let arg_param_ty = self.resolve_type_vars_if_possible(&arg_param_ty);
debug!("deduce_sig_from_projection: arg_param_ty {:?}", arg_param_ty);
let input_tys = match arg_param_ty.sty {
ty::TyTuple(tys) => tys.to_vec(),
_ => { return None; }
};
debug!("deduce_sig_from_projection: input_tys {:?}", input_tys);
let ret_param_ty = projection.0.ty;
let ret_param_ty = self.resolve_type_vars_if_possible(&ret_param_ty);
debug!("deduce_sig_from_projection: ret_param_ty {:?}", ret_param_ty);
let fn_sig = ty::FnSig {
inputs: input_tys,
output: ret_param_ty,
variadic: false
};
debug!("deduce_sig_from_projection: fn_sig {:?}", fn_sig);
Some(fn_sig)
}
fn self_type_matches_expected_vid(&self,
trait_ref: ty::PolyTraitRef<'tcx>,
expected_vid: ty::TyVid)
-> Option<ty::PolyTraitRef<'tcx>>
{
let self_ty = self.shallow_resolve(trait_ref.self_ty());
debug!("self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?})",
trait_ref,
self_ty);
match self_ty.sty {
ty::TyInfer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref),
_ => None,
}
}
}