]>
Commit | Line | Data |
---|---|---|
ea8adc8c XL |
1 | //! This calculates the types which has storage which lives across a suspension point in a |
2 | //! generator from the perspective of typeck. The actual types used at runtime | |
3 | //! is calculated in `rustc_mir::transform::generator` and may be a subset of the | |
4 | //! types computed here. | |
5 | ||
dfeec247 | 6 | use super::FnCtxt; |
74b04a01 | 7 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
dfeec247 XL |
8 | use rustc_hir as hir; |
9 | use rustc_hir::def::{CtorKind, DefKind, Res}; | |
10 | use rustc_hir::def_id::DefId; | |
11 | use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; | |
12 | use rustc_hir::{Expr, ExprKind, Pat, PatKind}; | |
ba9703b0 XL |
13 | use rustc_middle::middle::region::{self, YieldData}; |
14 | use rustc_middle::ty::{self, Ty}; | |
dfeec247 | 15 | use rustc_span::Span; |
ea8adc8c | 16 | |
dc9dc135 XL |
17 | struct InteriorVisitor<'a, 'tcx> { |
18 | fcx: &'a FnCtxt<'a, 'tcx>, | |
e1599b0c | 19 | types: FxHashMap<ty::GeneratorInteriorTypeCause<'tcx>, usize>, |
dc9dc135 | 20 | region_scope_tree: &'tcx region::ScopeTree, |
ea8adc8c | 21 | expr_count: usize, |
dc9dc135 | 22 | kind: hir::GeneratorKind, |
dfeec247 | 23 | prev_unresolved_span: Option<Span>, |
ea8adc8c XL |
24 | } |
25 | ||
dc9dc135 | 26 | impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { |
dfeec247 XL |
27 | fn record( |
28 | &mut self, | |
29 | ty: Ty<'tcx>, | |
30 | scope: Option<region::Scope>, | |
31 | expr: Option<&'tcx Expr<'tcx>>, | |
32 | source_span: Span, | |
33 | ) { | |
34 | use rustc_span::DUMMY_SP; | |
35 | ||
36 | debug!( | |
37 | "generator_interior: attempting to record type {:?} {:?} {:?} {:?}", | |
38 | ty, scope, expr, source_span | |
39 | ); | |
40 | ||
41 | let live_across_yield = scope | |
42 | .map(|s| { | |
43 | self.region_scope_tree.yield_in_scope(s).and_then(|yield_data| { | |
44 | // If we are recording an expression that is the last yield | |
45 | // in the scope, or that has a postorder CFG index larger | |
46 | // than the one of all of the yields, then its value can't | |
47 | // be storage-live (and therefore live) at any of the yields. | |
48 | // | |
49 | // See the mega-comment at `yield_in_scope` for a proof. | |
50 | ||
51 | debug!( | |
52 | "comparing counts yield: {} self: {}, source_span = {:?}", | |
53 | yield_data.expr_and_pat_count, self.expr_count, source_span | |
54 | ); | |
55 | ||
56 | if yield_data.expr_and_pat_count >= self.expr_count { | |
57 | Some(yield_data) | |
58 | } else { | |
59 | None | |
60 | } | |
61 | }) | |
ea8adc8c | 62 | }) |
dfeec247 XL |
63 | .unwrap_or_else(|| { |
64 | Some(YieldData { span: DUMMY_SP, expr_and_pat_count: 0, source: self.kind.into() }) | |
65 | }); | |
dc9dc135 XL |
66 | |
67 | if let Some(yield_data) = live_across_yield { | |
68 | let ty = self.fcx.resolve_vars_if_possible(&ty); | |
dfeec247 XL |
69 | debug!( |
70 | "type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}", | |
71 | expr, scope, ty, self.expr_count, yield_data.span | |
72 | ); | |
2c00a5a8 | 73 | |
48663c56 XL |
74 | if let Some((unresolved_type, unresolved_type_span)) = |
75 | self.fcx.unresolved_type_vars(&ty) | |
76 | { | |
dfeec247 XL |
77 | let note = format!( |
78 | "the type is part of the {} because of this {}", | |
79 | self.kind, yield_data.source | |
80 | ); | |
dc9dc135 | 81 | |
48663c56 | 82 | // If unresolved type isn't a ty_var then unresolved_type_span is None |
dfeec247 XL |
83 | let span = self |
84 | .prev_unresolved_span | |
85 | .unwrap_or_else(|| unresolved_type_span.unwrap_or(source_span)); | |
86 | self.fcx | |
87 | .need_type_info_err_in_generator(self.kind, span, unresolved_type) | |
dc9dc135 | 88 | .span_note(yield_data.span, &*note) |
48663c56 | 89 | .emit(); |
2c00a5a8 XL |
90 | } else { |
91 | // Map the type to the number of types added before it | |
92 | let entries = self.types.len(); | |
e1599b0c | 93 | let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree)); |
dfeec247 XL |
94 | self.types |
95 | .entry(ty::GeneratorInteriorTypeCause { | |
96 | span: source_span, | |
97 | ty: &ty, | |
98 | scope_span, | |
99 | expr: expr.map(|e| e.hir_id), | |
100 | }) | |
101 | .or_insert(entries); | |
2c00a5a8 | 102 | } |
ea8adc8c | 103 | } else { |
dfeec247 XL |
104 | debug!( |
105 | "no type in expr = {:?}, count = {:?}, span = {:?}", | |
106 | expr, | |
107 | self.expr_count, | |
108 | expr.map(|e| e.span) | |
109 | ); | |
110 | let ty = self.fcx.resolve_vars_if_possible(&ty); | |
111 | if let Some((unresolved_type, unresolved_type_span)) = | |
112 | self.fcx.unresolved_type_vars(&ty) | |
113 | { | |
114 | debug!( | |
115 | "remained unresolved_type = {:?}, unresolved_type_span: {:?}", | |
116 | unresolved_type, unresolved_type_span | |
117 | ); | |
118 | self.prev_unresolved_span = unresolved_type_span; | |
119 | } | |
ea8adc8c XL |
120 | } |
121 | } | |
122 | } | |
123 | ||
dc9dc135 XL |
124 | pub fn resolve_interior<'a, 'tcx>( |
125 | fcx: &'a FnCtxt<'a, 'tcx>, | |
126 | def_id: DefId, | |
127 | body_id: hir::BodyId, | |
128 | interior: Ty<'tcx>, | |
129 | kind: hir::GeneratorKind, | |
130 | ) { | |
0731742a | 131 | let body = fcx.tcx.hir().body(body_id); |
ea8adc8c XL |
132 | let mut visitor = InteriorVisitor { |
133 | fcx, | |
0bf4aa26 | 134 | types: FxHashMap::default(), |
ea8adc8c XL |
135 | region_scope_tree: fcx.tcx.region_scope_tree(def_id), |
136 | expr_count: 0, | |
dc9dc135 | 137 | kind, |
dfeec247 | 138 | prev_unresolved_span: None, |
ea8adc8c XL |
139 | }; |
140 | intravisit::walk_body(&mut visitor, body); | |
141 | ||
142 | // Check that we visited the same amount of expressions and the RegionResolutionVisitor | |
143 | let region_expr_count = visitor.region_scope_tree.body_expr_count(body_id).unwrap(); | |
144 | assert_eq!(region_expr_count, visitor.expr_count); | |
145 | ||
146 | let mut types: Vec<_> = visitor.types.drain().collect(); | |
147 | ||
148 | // Sort types by insertion order | |
149 | types.sort_by_key(|t| t.1); | |
150 | ||
2c00a5a8 XL |
151 | // The types in the generator interior contain lifetimes local to the generator itself, |
152 | // which should not be exposed outside of the generator. Therefore, we replace these | |
153 | // lifetimes with existentially-bound lifetimes, which reflect the exact value of the | |
154 | // lifetimes not being known by users. | |
155 | // | |
156 | // These lifetimes are used in auto trait impl checking (for example, | |
157 | // if a Sync generator contains an &'α T, we need to check whether &'α T: Sync), | |
158 | // so knowledge of the exact relationships between them isn't particularly important. | |
159 | ||
e1599b0c | 160 | debug!("types in generator {:?}, span = {:?}", types, body.value.span); |
2c00a5a8 | 161 | |
2c00a5a8 | 162 | let mut counter = 0; |
74b04a01 XL |
163 | let mut captured_tys = FxHashSet::default(); |
164 | let type_causes: Vec<_> = types | |
dfeec247 | 165 | .into_iter() |
74b04a01 XL |
166 | .filter_map(|(mut cause, _)| { |
167 | // Erase regions and canonicalize late-bound regions to deduplicate as many types as we | |
168 | // can. | |
169 | let erased = fcx.tcx.erase_regions(&cause.ty); | |
170 | if captured_tys.insert(erased) { | |
171 | // Replace all regions inside the generator interior with late bound regions. | |
172 | // Note that each region slot in the types gets a new fresh late bound region, | |
173 | // which means that none of the regions inside relate to any other, even if | |
174 | // typeck had previously found constraints that would cause them to be related. | |
175 | let folded = fcx.tcx.fold_regions(&erased, &mut false, |_, current_depth| { | |
176 | counter += 1; | |
177 | fcx.tcx.mk_region(ty::ReLateBound(current_depth, ty::BrAnon(counter))) | |
178 | }); | |
179 | ||
180 | cause.ty = folded; | |
181 | Some(cause) | |
182 | } else { | |
183 | None | |
184 | } | |
dfeec247 XL |
185 | }) |
186 | .collect(); | |
e1599b0c | 187 | |
74b04a01 XL |
188 | // Extract type components to build the witness type. |
189 | let type_list = fcx.tcx.mk_type_list(type_causes.iter().map(|cause| cause.ty)); | |
83c7162d | 190 | let witness = fcx.tcx.mk_generator_witness(ty::Binder::bind(type_list)); |
2c00a5a8 | 191 | |
74b04a01 XL |
192 | // Store the generator types and spans into the tables for this generator. |
193 | visitor.fcx.inh.tables.borrow_mut().generator_interior_types = type_causes; | |
194 | ||
dfeec247 XL |
195 | debug!( |
196 | "types in generator after region replacement {:?}, span = {:?}", | |
197 | witness, body.value.span | |
198 | ); | |
2c00a5a8 XL |
199 | |
200 | // Unify the type variable inside the generator with the new witness | |
94b46f34 | 201 | match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(interior, witness) { |
ea8adc8c XL |
202 | Ok(ok) => fcx.register_infer_ok_obligations(ok), |
203 | _ => bug!(), | |
2c00a5a8 | 204 | } |
ea8adc8c XL |
205 | } |
206 | ||
207 | // This visitor has to have the same visit_expr calls as RegionResolutionVisitor in | |
ba9703b0 | 208 | // librustc_middle/middle/region.rs since `expr_count` is compared against the results |
ea8adc8c | 209 | // there. |
dc9dc135 | 210 | impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { |
ba9703b0 | 211 | type Map = intravisit::ErasedMap<'tcx>; |
dfeec247 | 212 | |
ba9703b0 | 213 | fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { |
ea8adc8c XL |
214 | NestedVisitorMap::None |
215 | } | |
216 | ||
dfeec247 | 217 | fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { |
2c00a5a8 XL |
218 | intravisit::walk_pat(self, pat); |
219 | ||
220 | self.expr_count += 1; | |
221 | ||
e74abb32 | 222 | if let PatKind::Binding(..) = pat.kind { |
ea8adc8c XL |
223 | let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id); |
224 | let ty = self.fcx.tables.borrow().pat_ty(pat); | |
2c00a5a8 | 225 | self.record(ty, Some(scope), None, pat.span); |
ea8adc8c | 226 | } |
ea8adc8c XL |
227 | } |
228 | ||
dfeec247 | 229 | fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { |
e74abb32 XL |
230 | match &expr.kind { |
231 | ExprKind::Call(callee, args) => match &callee.kind { | |
232 | ExprKind::Path(qpath) => { | |
233 | let res = self.fcx.tables.borrow().qpath_res(qpath, callee.hir_id); | |
234 | match res { | |
235 | // Direct calls never need to keep the callee `ty::FnDef` | |
236 | // ZST in a temporary, so skip its type, just in case it | |
237 | // can significantly complicate the generator type. | |
ba9703b0 XL |
238 | Res::Def( |
239 | DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn), | |
240 | _, | |
241 | ) => { | |
e74abb32 XL |
242 | // NOTE(eddyb) this assumes a path expression has |
243 | // no nested expressions to keep track of. | |
244 | self.expr_count += 1; | |
245 | ||
246 | // Record the rest of the call expression normally. | |
dfeec247 | 247 | for arg in *args { |
e74abb32 XL |
248 | self.visit_expr(arg); |
249 | } | |
250 | } | |
251 | _ => intravisit::walk_expr(self, expr), | |
252 | } | |
253 | } | |
254 | _ => intravisit::walk_expr(self, expr), | |
dfeec247 | 255 | }, |
e74abb32 XL |
256 | _ => intravisit::walk_expr(self, expr), |
257 | } | |
ea8adc8c XL |
258 | |
259 | self.expr_count += 1; | |
260 | ||
261 | let scope = self.region_scope_tree.temporary_scope(expr.hir_id.local_id); | |
262 | ||
e1599b0c XL |
263 | // If there are adjustments, then record the final type -- |
264 | // this is the actual value that is being produced. | |
265 | if let Some(adjusted_ty) = self.fcx.tables.borrow().expr_ty_adjusted_opt(expr) { | |
266 | self.record(adjusted_ty, scope, Some(expr), expr.span); | |
267 | } | |
268 | ||
269 | // Also record the unadjusted type (which is the only type if | |
270 | // there are no adjustments). The reason for this is that the | |
271 | // unadjusted value is sometimes a "temporary" that would wind | |
272 | // up in a MIR temporary. | |
273 | // | |
274 | // As an example, consider an expression like `vec![].push()`. | |
275 | // Here, the `vec![]` would wind up MIR stored into a | |
276 | // temporary variable `t` which we can borrow to invoke | |
277 | // `<Vec<_>>::push(&mut t)`. | |
278 | // | |
279 | // Note that an expression can have many adjustments, and we | |
280 | // are just ignoring those intermediate types. This is because | |
281 | // those intermediate values are always linearly "consumed" by | |
282 | // the other adjustments, and hence would never be directly | |
283 | // captured in the MIR. | |
284 | // | |
285 | // (Note that this partly relies on the fact that the `Deref` | |
286 | // traits always return references, which means their content | |
287 | // can be reborrowed without needing to spill to a temporary. | |
288 | // If this were not the case, then we could conceivably have | |
289 | // to create intermediate temporaries.) | |
e74abb32 XL |
290 | // |
291 | // The type table might not have information for this expression | |
292 | // if it is in a malformed scope. (#66387) | |
293 | if let Some(ty) = self.fcx.tables.borrow().expr_ty_opt(expr) { | |
294 | self.record(ty, scope, Some(expr), expr.span); | |
295 | } else { | |
296 | self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node"); | |
297 | } | |
ea8adc8c XL |
298 | } |
299 | } |