]>
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 | |
5e7ed085 | 3 | //! is calculated in `rustc_mir_transform::generator` and may be a subset of the |
ea8adc8c XL |
4 | //! types computed here. |
5 | ||
5099ac24 | 6 | use self::drop_ranges::DropRanges; |
dfeec247 | 7 | use super::FnCtxt; |
3dfed10e | 8 | use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; |
2b03887a | 9 | use rustc_errors::{pluralize, DelayDm}; |
dfeec247 XL |
10 | use rustc_hir as hir; |
11 | use rustc_hir::def::{CtorKind, DefKind, Res}; | |
12 | use rustc_hir::def_id::DefId; | |
29967ef6 | 13 | use rustc_hir::hir_id::HirIdSet; |
5099ac24 | 14 | use rustc_hir::intravisit::{self, Visitor}; |
29967ef6 | 15 | use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind}; |
487cf647 | 16 | use rustc_infer::infer::RegionVariableOrigin; |
5e7ed085 | 17 | use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData}; |
487cf647 FG |
18 | use rustc_middle::ty::fold::FnMutDelegate; |
19 | use rustc_middle::ty::{self, BoundVariableKind, RvalueScopes, Ty, TyCtxt, TypeVisitable}; | |
c295e0f8 | 20 | use rustc_span::symbol::sym; |
dfeec247 | 21 | use rustc_span::Span; |
487cf647 | 22 | use smallvec::{smallvec, SmallVec}; |
ea8adc8c | 23 | |
5099ac24 FG |
24 | mod drop_ranges; |
25 | ||
dc9dc135 XL |
26 | struct InteriorVisitor<'a, 'tcx> { |
27 | fcx: &'a FnCtxt<'a, 'tcx>, | |
923072b8 | 28 | region_scope_tree: &'a region::ScopeTree, |
3dfed10e | 29 | types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>, |
923072b8 | 30 | rvalue_scopes: &'a RvalueScopes, |
ea8adc8c | 31 | expr_count: usize, |
dc9dc135 | 32 | kind: hir::GeneratorKind, |
dfeec247 | 33 | prev_unresolved_span: Option<Span>, |
c295e0f8 | 34 | linted_values: HirIdSet, |
5099ac24 | 35 | drop_ranges: DropRanges, |
ea8adc8c XL |
36 | } |
37 | ||
dc9dc135 | 38 | impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { |
dfeec247 XL |
39 | fn record( |
40 | &mut self, | |
41 | ty: Ty<'tcx>, | |
c295e0f8 | 42 | hir_id: HirId, |
dfeec247 XL |
43 | scope: Option<region::Scope>, |
44 | expr: Option<&'tcx Expr<'tcx>>, | |
45 | source_span: Span, | |
46 | ) { | |
47 | use rustc_span::DUMMY_SP; | |
48 | ||
5099ac24 FG |
49 | let ty = self.fcx.resolve_vars_if_possible(ty); |
50 | ||
dfeec247 | 51 | debug!( |
5099ac24 FG |
52 | "attempting to record type ty={:?}; hir_id={:?}; scope={:?}; expr={:?}; source_span={:?}; expr_count={:?}", |
53 | ty, hir_id, scope, expr, source_span, self.expr_count, | |
dfeec247 XL |
54 | ); |
55 | ||
56 | let live_across_yield = scope | |
57 | .map(|s| { | |
58 | self.region_scope_tree.yield_in_scope(s).and_then(|yield_data| { | |
59 | // If we are recording an expression that is the last yield | |
60 | // in the scope, or that has a postorder CFG index larger | |
61 | // than the one of all of the yields, then its value can't | |
62 | // be storage-live (and therefore live) at any of the yields. | |
63 | // | |
64 | // See the mega-comment at `yield_in_scope` for a proof. | |
65 | ||
5099ac24 FG |
66 | yield_data |
67 | .iter() | |
68 | .find(|yield_data| { | |
69 | debug!( | |
70 | "comparing counts yield: {} self: {}, source_span = {:?}", | |
71 | yield_data.expr_and_pat_count, self.expr_count, source_span | |
72 | ); | |
73 | ||
064997fb | 74 | if self.fcx.sess().opts.unstable_opts.drop_tracking |
5099ac24 FG |
75 | && self |
76 | .drop_ranges | |
77 | .is_dropped_at(hir_id, yield_data.expr_and_pat_count) | |
78 | { | |
79 | debug!("value is dropped at yield point; not recording"); | |
80 | return false; | |
81 | } | |
dfeec247 | 82 | |
5099ac24 FG |
83 | // If it is a borrowing happening in the guard, |
84 | // it needs to be recorded regardless because they | |
85 | // do live across this yield point. | |
923072b8 | 86 | yield_data.expr_and_pat_count >= self.expr_count |
5099ac24 FG |
87 | }) |
88 | .cloned() | |
dfeec247 | 89 | }) |
ea8adc8c | 90 | }) |
dfeec247 XL |
91 | .unwrap_or_else(|| { |
92 | Some(YieldData { span: DUMMY_SP, expr_and_pat_count: 0, source: self.kind.into() }) | |
93 | }); | |
dc9dc135 XL |
94 | |
95 | if let Some(yield_data) = live_across_yield { | |
dfeec247 XL |
96 | debug!( |
97 | "type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}", | |
98 | expr, scope, ty, self.expr_count, yield_data.span | |
99 | ); | |
2c00a5a8 | 100 | |
487cf647 FG |
101 | if let Some((unresolved_term, unresolved_type_span)) = |
102 | self.fcx.first_unresolved_const_or_ty_var(&ty) | |
48663c56 XL |
103 | { |
104 | // If unresolved type isn't a ty_var then unresolved_type_span is None | |
dfeec247 XL |
105 | let span = self |
106 | .prev_unresolved_span | |
107 | .unwrap_or_else(|| unresolved_type_span.unwrap_or(source_span)); | |
17df50a5 XL |
108 | |
109 | // If we encounter an int/float variable, then inference fallback didn't | |
110 | // finish due to some other error. Don't emit spurious additional errors. | |
487cf647 FG |
111 | if let Some(unresolved_ty) = unresolved_term.ty() |
112 | && let ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) = unresolved_ty.kind() | |
17df50a5 XL |
113 | { |
114 | self.fcx | |
115 | .tcx | |
116 | .sess | |
487cf647 | 117 | .delay_span_bug(span, &format!("Encountered var {:?}", unresolved_term)); |
17df50a5 XL |
118 | } else { |
119 | let note = format!( | |
120 | "the type is part of the {} because of this {}", | |
487cf647 FG |
121 | self.kind.descr(), |
122 | yield_data.source | |
17df50a5 XL |
123 | ); |
124 | ||
125 | self.fcx | |
487cf647 | 126 | .need_type_info_err_in_generator(self.kind, span, unresolved_term) |
17df50a5 XL |
127 | .span_note(yield_data.span, &*note) |
128 | .emit(); | |
129 | } | |
2c00a5a8 | 130 | } else { |
3dfed10e | 131 | // Insert the type into the ordered set. |
e1599b0c | 132 | let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree)); |
c295e0f8 XL |
133 | |
134 | if !self.linted_values.contains(&hir_id) { | |
135 | check_must_not_suspend_ty( | |
136 | self.fcx, | |
137 | ty, | |
138 | hir_id, | |
139 | SuspendCheckData { | |
140 | expr, | |
141 | source_span, | |
142 | yield_span: yield_data.span, | |
143 | plural_len: 1, | |
144 | ..Default::default() | |
145 | }, | |
146 | ); | |
147 | self.linted_values.insert(hir_id); | |
148 | } | |
149 | ||
3dfed10e XL |
150 | self.types.insert(ty::GeneratorInteriorTypeCause { |
151 | span: source_span, | |
5099ac24 | 152 | ty, |
3dfed10e XL |
153 | scope_span, |
154 | yield_span: yield_data.span, | |
155 | expr: expr.map(|e| e.hir_id), | |
156 | }); | |
2c00a5a8 | 157 | } |
ea8adc8c | 158 | } else { |
dfeec247 XL |
159 | debug!( |
160 | "no type in expr = {:?}, count = {:?}, span = {:?}", | |
161 | expr, | |
162 | self.expr_count, | |
163 | expr.map(|e| e.span) | |
164 | ); | |
dfeec247 | 165 | if let Some((unresolved_type, unresolved_type_span)) = |
487cf647 | 166 | self.fcx.first_unresolved_const_or_ty_var(&ty) |
dfeec247 XL |
167 | { |
168 | debug!( | |
169 | "remained unresolved_type = {:?}, unresolved_type_span: {:?}", | |
170 | unresolved_type, unresolved_type_span | |
171 | ); | |
172 | self.prev_unresolved_span = unresolved_type_span; | |
173 | } | |
ea8adc8c XL |
174 | } |
175 | } | |
176 | } | |
177 | ||
dc9dc135 XL |
178 | pub fn resolve_interior<'a, 'tcx>( |
179 | fcx: &'a FnCtxt<'a, 'tcx>, | |
180 | def_id: DefId, | |
181 | body_id: hir::BodyId, | |
182 | interior: Ty<'tcx>, | |
183 | kind: hir::GeneratorKind, | |
184 | ) { | |
0731742a | 185 | let body = fcx.tcx.hir().body(body_id); |
923072b8 | 186 | let typeck_results = fcx.inh.typeck_results.borrow(); |
ea8adc8c XL |
187 | let mut visitor = InteriorVisitor { |
188 | fcx, | |
3dfed10e | 189 | types: FxIndexSet::default(), |
ea8adc8c | 190 | region_scope_tree: fcx.tcx.region_scope_tree(def_id), |
923072b8 | 191 | rvalue_scopes: &typeck_results.rvalue_scopes, |
ea8adc8c | 192 | expr_count: 0, |
dc9dc135 | 193 | kind, |
dfeec247 | 194 | prev_unresolved_span: None, |
c295e0f8 | 195 | linted_values: <_>::default(), |
5099ac24 | 196 | drop_ranges: drop_ranges::compute_drop_ranges(fcx, def_id, body), |
ea8adc8c XL |
197 | }; |
198 | intravisit::walk_body(&mut visitor, body); | |
199 | ||
5099ac24 | 200 | // Check that we visited the same amount of expressions as the RegionResolutionVisitor |
923072b8 | 201 | let region_expr_count = fcx.tcx.region_scope_tree(def_id).body_expr_count(body_id).unwrap(); |
ea8adc8c XL |
202 | assert_eq!(region_expr_count, visitor.expr_count); |
203 | ||
3dfed10e XL |
204 | // The types are already kept in insertion order. |
205 | let types = visitor.types; | |
ea8adc8c | 206 | |
2c00a5a8 XL |
207 | // The types in the generator interior contain lifetimes local to the generator itself, |
208 | // which should not be exposed outside of the generator. Therefore, we replace these | |
209 | // lifetimes with existentially-bound lifetimes, which reflect the exact value of the | |
210 | // lifetimes not being known by users. | |
211 | // | |
212 | // These lifetimes are used in auto trait impl checking (for example, | |
213 | // if a Sync generator contains an &'α T, we need to check whether &'α T: Sync), | |
214 | // so knowledge of the exact relationships between them isn't particularly important. | |
215 | ||
e1599b0c | 216 | debug!("types in generator {:?}, span = {:?}", types, body.value.span); |
2c00a5a8 | 217 | |
487cf647 FG |
218 | // We want to deduplicate if the lifetimes are the same modulo some non-informative counter. |
219 | // So, we need to actually do two passes: first by type to anonymize (preserving information | |
220 | // required for diagnostics), then a second pass over all captured types to reassign disjoint | |
221 | // region indices. | |
74b04a01 XL |
222 | let mut captured_tys = FxHashSet::default(); |
223 | let type_causes: Vec<_> = types | |
dfeec247 | 224 | .into_iter() |
3dfed10e | 225 | .filter_map(|mut cause| { |
487cf647 FG |
226 | // Replace all regions inside the generator interior with late bound regions. |
227 | // Note that each region slot in the types gets a new fresh late bound region, | |
228 | // which means that none of the regions inside relate to any other, even if | |
229 | // typeck had previously found constraints that would cause them to be related. | |
230 | ||
231 | let mut counter = 0; | |
232 | let mut mk_bound_region = |span| { | |
233 | let kind = ty::BrAnon(counter, span); | |
234 | let var = ty::BoundVar::from_u32(counter); | |
235 | counter += 1; | |
236 | ty::BoundRegion { var, kind } | |
237 | }; | |
238 | let ty = fcx.normalize(cause.span, cause.ty); | |
239 | let ty = fcx.tcx.fold_regions(ty, |region, current_depth| { | |
240 | let br = match region.kind() { | |
241 | ty::ReVar(vid) => { | |
242 | let origin = fcx.region_var_origin(vid); | |
243 | match origin { | |
244 | RegionVariableOrigin::EarlyBoundRegion(span, _) => { | |
245 | mk_bound_region(Some(span)) | |
246 | } | |
247 | _ => mk_bound_region(None), | |
248 | } | |
249 | } | |
250 | // FIXME: these should use `BrNamed` | |
251 | ty::ReEarlyBound(region) => { | |
252 | mk_bound_region(Some(fcx.tcx.def_span(region.def_id))) | |
253 | } | |
254 | ty::ReLateBound(_, ty::BoundRegion { kind, .. }) | |
255 | | ty::ReFree(ty::FreeRegion { bound_region: kind, .. }) => match kind { | |
256 | ty::BoundRegionKind::BrAnon(_, span) => mk_bound_region(span), | |
257 | ty::BoundRegionKind::BrNamed(def_id, _) => { | |
258 | mk_bound_region(Some(fcx.tcx.def_span(def_id))) | |
259 | } | |
260 | ty::BoundRegionKind::BrEnv => mk_bound_region(None), | |
261 | }, | |
262 | _ => mk_bound_region(None), | |
263 | }; | |
264 | let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, br)); | |
265 | r | |
266 | }); | |
267 | if captured_tys.insert(ty) { | |
268 | cause.ty = ty; | |
74b04a01 XL |
269 | Some(cause) |
270 | } else { | |
271 | None | |
272 | } | |
dfeec247 XL |
273 | }) |
274 | .collect(); | |
e1599b0c | 275 | |
487cf647 FG |
276 | let mut bound_vars: SmallVec<[BoundVariableKind; 4]> = smallvec![]; |
277 | let mut counter = 0; | |
278 | // Optimization: If there is only one captured type, then we don't actually | |
279 | // need to fold and reindex (since the first type doesn't change). | |
280 | let type_causes = if captured_tys.len() > 0 { | |
281 | // Optimization: Use `replace_escaping_bound_vars_uncached` instead of | |
282 | // `fold_regions`, since we only have late bound regions, and it skips | |
283 | // types without bound regions. | |
284 | fcx.tcx.replace_escaping_bound_vars_uncached( | |
285 | type_causes, | |
286 | FnMutDelegate { | |
287 | regions: &mut |br| { | |
288 | let kind = match br.kind { | |
289 | ty::BrAnon(_, span) => ty::BrAnon(counter, span), | |
290 | _ => br.kind, | |
291 | }; | |
292 | let var = ty::BoundVar::from_usize(bound_vars.len()); | |
293 | bound_vars.push(ty::BoundVariableKind::Region(kind)); | |
294 | counter += 1; | |
295 | fcx.tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BoundRegion { var, kind })) | |
296 | }, | |
297 | types: &mut |b| bug!("unexpected bound ty in binder: {b:?}"), | |
298 | consts: &mut |b, ty| bug!("unexpected bound ct in binder: {b:?} {ty}"), | |
299 | }, | |
300 | ) | |
301 | } else { | |
302 | type_causes | |
303 | }; | |
304 | ||
74b04a01 XL |
305 | // Extract type components to build the witness type. |
306 | let type_list = fcx.tcx.mk_type_list(type_causes.iter().map(|cause| cause.ty)); | |
487cf647 | 307 | let bound_vars = fcx.tcx.mk_bound_variable_kinds(bound_vars.iter()); |
cdc7bbd5 XL |
308 | let witness = |
309 | fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone())); | |
2c00a5a8 | 310 | |
923072b8 | 311 | drop(typeck_results); |
3dfed10e | 312 | // Store the generator types and spans into the typeck results for this generator. |
923072b8 | 313 | fcx.inh.typeck_results.borrow_mut().generator_interior_types = |
cdc7bbd5 | 314 | ty::Binder::bind_with_vars(type_causes, bound_vars); |
74b04a01 | 315 | |
dfeec247 XL |
316 | debug!( |
317 | "types in generator after region replacement {:?}, span = {:?}", | |
318 | witness, body.value.span | |
319 | ); | |
2c00a5a8 XL |
320 | |
321 | // Unify the type variable inside the generator with the new witness | |
94b46f34 | 322 | match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(interior, witness) { |
ea8adc8c | 323 | Ok(ok) => fcx.register_infer_ok_obligations(ok), |
2b03887a | 324 | _ => bug!("failed to relate {interior} and {witness}"), |
2c00a5a8 | 325 | } |
ea8adc8c XL |
326 | } |
327 | ||
328 | // This visitor has to have the same visit_expr calls as RegionResolutionVisitor in | |
ba9703b0 | 329 | // librustc_middle/middle/region.rs since `expr_count` is compared against the results |
ea8adc8c | 330 | // there. |
dc9dc135 | 331 | impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { |
29967ef6 XL |
332 | fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) { |
333 | let Arm { guard, pat, body, .. } = arm; | |
334 | self.visit_pat(pat); | |
335 | if let Some(ref g) = guard { | |
923072b8 FG |
336 | { |
337 | // If there is a guard, we need to count all variables bound in the pattern as | |
338 | // borrowed for the entire guard body, regardless of whether they are accessed. | |
339 | // We do this by walking the pattern bindings and recording `&T` for any `x: T` | |
340 | // that is bound. | |
341 | ||
342 | struct ArmPatCollector<'a, 'b, 'tcx> { | |
343 | interior_visitor: &'a mut InteriorVisitor<'b, 'tcx>, | |
344 | scope: Scope, | |
345 | } | |
346 | ||
347 | impl<'a, 'b, 'tcx> Visitor<'tcx> for ArmPatCollector<'a, 'b, 'tcx> { | |
348 | fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { | |
349 | intravisit::walk_pat(self, pat); | |
350 | if let PatKind::Binding(_, id, ident, ..) = pat.kind { | |
351 | let ty = | |
352 | self.interior_visitor.fcx.typeck_results.borrow().node_type(id); | |
353 | let tcx = self.interior_visitor.fcx.tcx; | |
354 | let ty = tcx.mk_ref( | |
355 | // Use `ReErased` as `resolve_interior` is going to replace all the | |
356 | // regions anyway. | |
357 | tcx.mk_region(ty::ReErased), | |
358 | ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }, | |
359 | ); | |
360 | self.interior_visitor.record( | |
361 | ty, | |
362 | id, | |
363 | Some(self.scope), | |
364 | None, | |
365 | ident.span, | |
366 | ); | |
367 | } | |
368 | } | |
369 | } | |
370 | ||
371 | ArmPatCollector { | |
372 | interior_visitor: self, | |
373 | scope: Scope { id: g.body().hir_id.local_id, data: ScopeData::Node }, | |
374 | } | |
375 | .visit_pat(pat); | |
29967ef6 | 376 | } |
29967ef6 XL |
377 | |
378 | match g { | |
379 | Guard::If(ref e) => { | |
380 | self.visit_expr(e); | |
381 | } | |
923072b8 FG |
382 | Guard::IfLet(ref l) => { |
383 | self.visit_let_expr(l); | |
fc512014 | 384 | } |
29967ef6 | 385 | } |
29967ef6 XL |
386 | } |
387 | self.visit_expr(body); | |
388 | } | |
389 | ||
dfeec247 | 390 | fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { |
2c00a5a8 XL |
391 | intravisit::walk_pat(self, pat); |
392 | ||
393 | self.expr_count += 1; | |
394 | ||
e74abb32 | 395 | if let PatKind::Binding(..) = pat.kind { |
04454e1e | 396 | let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); |
3dfed10e | 397 | let ty = self.fcx.typeck_results.borrow().pat_ty(pat); |
923072b8 | 398 | self.record(ty, pat.hir_id, Some(scope), None, pat.span); |
ea8adc8c | 399 | } |
ea8adc8c XL |
400 | } |
401 | ||
dfeec247 | 402 | fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { |
e74abb32 XL |
403 | match &expr.kind { |
404 | ExprKind::Call(callee, args) => match &callee.kind { | |
405 | ExprKind::Path(qpath) => { | |
3dfed10e | 406 | let res = self.fcx.typeck_results.borrow().qpath_res(qpath, callee.hir_id); |
e74abb32 XL |
407 | match res { |
408 | // Direct calls never need to keep the callee `ty::FnDef` | |
409 | // ZST in a temporary, so skip its type, just in case it | |
410 | // can significantly complicate the generator type. | |
ba9703b0 XL |
411 | Res::Def( |
412 | DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn), | |
413 | _, | |
414 | ) => { | |
e74abb32 XL |
415 | // NOTE(eddyb) this assumes a path expression has |
416 | // no nested expressions to keep track of. | |
417 | self.expr_count += 1; | |
418 | ||
419 | // Record the rest of the call expression normally. | |
dfeec247 | 420 | for arg in *args { |
e74abb32 XL |
421 | self.visit_expr(arg); |
422 | } | |
423 | } | |
424 | _ => intravisit::walk_expr(self, expr), | |
425 | } | |
426 | } | |
427 | _ => intravisit::walk_expr(self, expr), | |
dfeec247 | 428 | }, |
e74abb32 XL |
429 | _ => intravisit::walk_expr(self, expr), |
430 | } | |
ea8adc8c XL |
431 | |
432 | self.expr_count += 1; | |
433 | ||
5e7ed085 FG |
434 | debug!("is_borrowed_temporary: {:?}", self.drop_ranges.is_borrowed_temporary(expr)); |
435 | ||
064997fb | 436 | let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr); |
064997fb | 437 | |
5e7ed085 FG |
438 | // Typically, the value produced by an expression is consumed by its parent in some way, |
439 | // so we only have to check if the parent contains a yield (note that the parent may, for | |
440 | // example, store the value into a local variable, but then we already consider local | |
441 | // variables to be live across their scope). | |
442 | // | |
443 | // However, in the case of temporary values, we are going to store the value into a | |
444 | // temporary on the stack that is live for the current temporary scope and then return a | |
445 | // reference to it. That value may be live across the entire temporary scope. | |
064997fb FG |
446 | // |
447 | // There's another subtlety: if the type has an observable drop, it must be dropped after | |
448 | // the yield, even if it's not borrowed or referenced after the yield. Ideally this would | |
449 | // *only* happen for types with observable drop, not all types which wrap them, but that | |
450 | // doesn't match the behavior of MIR borrowck and causes ICEs. See the FIXME comment in | |
451 | // src/test/ui/generator/drop-tracking-parent-expression.rs. | |
452 | let scope = if self.drop_ranges.is_borrowed_temporary(expr) | |
453 | || ty.map_or(true, |ty| { | |
2b03887a FG |
454 | // Avoid ICEs in needs_drop. |
455 | let ty = self.fcx.resolve_vars_if_possible(ty); | |
456 | let ty = self.fcx.tcx.erase_regions(ty); | |
457 | if ty.needs_infer() { | |
458 | self.fcx | |
459 | .tcx | |
460 | .sess | |
461 | .delay_span_bug(expr.span, &format!("inference variables in {ty}")); | |
462 | true | |
463 | } else { | |
464 | ty.needs_drop(self.fcx.tcx, self.fcx.param_env) | |
465 | } | |
064997fb | 466 | }) { |
923072b8 | 467 | self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id) |
5e7ed085 | 468 | } else { |
f2b60f7d FG |
469 | let parent_expr = self |
470 | .fcx | |
471 | .tcx | |
472 | .hir() | |
473 | .parent_iter(expr.hir_id) | |
474 | .find(|(_, node)| matches!(node, hir::Node::Expr(_))) | |
475 | .map(|(id, _)| id); | |
476 | debug!("parent_expr: {:?}", parent_expr); | |
477 | match parent_expr { | |
5e7ed085 | 478 | Some(parent) => Some(Scope { id: parent.local_id, data: ScopeData::Node }), |
923072b8 FG |
479 | None => { |
480 | self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id) | |
481 | } | |
5e7ed085 FG |
482 | } |
483 | }; | |
ea8adc8c | 484 | |
e1599b0c XL |
485 | // If there are adjustments, then record the final type -- |
486 | // this is the actual value that is being produced. | |
064997fb | 487 | if let Some(adjusted_ty) = ty { |
923072b8 | 488 | self.record(adjusted_ty, expr.hir_id, scope, Some(expr), expr.span); |
e1599b0c XL |
489 | } |
490 | ||
491 | // Also record the unadjusted type (which is the only type if | |
492 | // there are no adjustments). The reason for this is that the | |
493 | // unadjusted value is sometimes a "temporary" that would wind | |
494 | // up in a MIR temporary. | |
495 | // | |
29967ef6 | 496 | // As an example, consider an expression like `vec![].push(x)`. |
e1599b0c XL |
497 | // Here, the `vec![]` would wind up MIR stored into a |
498 | // temporary variable `t` which we can borrow to invoke | |
29967ef6 | 499 | // `<Vec<_>>::push(&mut t, x)`. |
e1599b0c XL |
500 | // |
501 | // Note that an expression can have many adjustments, and we | |
502 | // are just ignoring those intermediate types. This is because | |
503 | // those intermediate values are always linearly "consumed" by | |
504 | // the other adjustments, and hence would never be directly | |
505 | // captured in the MIR. | |
506 | // | |
507 | // (Note that this partly relies on the fact that the `Deref` | |
508 | // traits always return references, which means their content | |
509 | // can be reborrowed without needing to spill to a temporary. | |
510 | // If this were not the case, then we could conceivably have | |
511 | // to create intermediate temporaries.) | |
e74abb32 XL |
512 | // |
513 | // The type table might not have information for this expression | |
514 | // if it is in a malformed scope. (#66387) | |
3dfed10e | 515 | if let Some(ty) = self.fcx.typeck_results.borrow().expr_ty_opt(expr) { |
923072b8 | 516 | self.record(ty, expr.hir_id, scope, Some(expr), expr.span); |
e74abb32 XL |
517 | } else { |
518 | self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node"); | |
519 | } | |
ea8adc8c XL |
520 | } |
521 | } | |
29967ef6 | 522 | |
c295e0f8 | 523 | #[derive(Default)] |
f2b60f7d | 524 | struct SuspendCheckData<'a, 'tcx> { |
c295e0f8 XL |
525 | expr: Option<&'tcx Expr<'tcx>>, |
526 | source_span: Span, | |
527 | yield_span: Span, | |
528 | descr_pre: &'a str, | |
529 | descr_post: &'a str, | |
530 | plural_len: usize, | |
531 | } | |
532 | ||
533 | // Returns whether it emitted a diagnostic or not | |
5e7ed085 | 534 | // Note that this fn and the proceeding one are based on the code |
c295e0f8 XL |
535 | // for creating must_use diagnostics |
536 | // | |
537 | // Note that this technique was chosen over things like a `Suspend` marker trait | |
5e7ed085 | 538 | // as it is simpler and has precedent in the compiler |
f2b60f7d | 539 | fn check_must_not_suspend_ty<'tcx>( |
c295e0f8 XL |
540 | fcx: &FnCtxt<'_, 'tcx>, |
541 | ty: Ty<'tcx>, | |
542 | hir_id: HirId, | |
543 | data: SuspendCheckData<'_, 'tcx>, | |
544 | ) -> bool { | |
545 | if ty.is_unit() | |
487cf647 | 546 | // FIXME: should this check `Ty::is_inhabited_from`. This query is not available in this stage |
c295e0f8 XL |
547 | // of typeck (before ReVar and RePlaceholder are removed), but may remove noise, like in |
548 | // `must_use` | |
487cf647 | 549 | // || !ty.is_inhabited_from(fcx.tcx, fcx.tcx.parent_module(hir_id).to_def_id(), fcx.param_env) |
c295e0f8 XL |
550 | { |
551 | return false; | |
552 | } | |
553 | ||
554 | let plural_suffix = pluralize!(data.plural_len); | |
555 | ||
f2b60f7d FG |
556 | debug!("Checking must_not_suspend for {}", ty); |
557 | ||
c295e0f8 XL |
558 | match *ty.kind() { |
559 | ty::Adt(..) if ty.is_box() => { | |
560 | let boxed_ty = ty.boxed_ty(); | |
561 | let descr_pre = &format!("{}boxed ", data.descr_pre); | |
562 | check_must_not_suspend_ty(fcx, boxed_ty, hir_id, SuspendCheckData { descr_pre, ..data }) | |
563 | } | |
5e7ed085 | 564 | ty::Adt(def, _) => check_must_not_suspend_def(fcx.tcx, def.did(), hir_id, data), |
c295e0f8 XL |
565 | // FIXME: support adding the attribute to TAITs |
566 | ty::Opaque(def, _) => { | |
567 | let mut has_emitted = false; | |
568 | for &(predicate, _) in fcx.tcx.explicit_item_bounds(def) { | |
569 | // We only look at the `DefId`, so it is safe to skip the binder here. | |
487cf647 | 570 | if let ty::PredicateKind::Clause(ty::Clause::Trait(ref poly_trait_predicate)) = |
c295e0f8 XL |
571 | predicate.kind().skip_binder() |
572 | { | |
573 | let def_id = poly_trait_predicate.trait_ref.def_id; | |
574 | let descr_pre = &format!("{}implementer{} of ", data.descr_pre, plural_suffix); | |
575 | if check_must_not_suspend_def( | |
576 | fcx.tcx, | |
577 | def_id, | |
578 | hir_id, | |
579 | SuspendCheckData { descr_pre, ..data }, | |
580 | ) { | |
581 | has_emitted = true; | |
582 | break; | |
583 | } | |
584 | } | |
585 | } | |
586 | has_emitted | |
587 | } | |
f2b60f7d | 588 | ty::Dynamic(binder, _, _) => { |
c295e0f8 XL |
589 | let mut has_emitted = false; |
590 | for predicate in binder.iter() { | |
591 | if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { | |
592 | let def_id = trait_ref.def_id; | |
593 | let descr_post = &format!(" trait object{}{}", plural_suffix, data.descr_post); | |
594 | if check_must_not_suspend_def( | |
595 | fcx.tcx, | |
596 | def_id, | |
597 | hir_id, | |
598 | SuspendCheckData { descr_post, ..data }, | |
599 | ) { | |
600 | has_emitted = true; | |
601 | break; | |
602 | } | |
603 | } | |
604 | } | |
605 | has_emitted | |
606 | } | |
5e7ed085 | 607 | ty::Tuple(fields) => { |
c295e0f8 | 608 | let mut has_emitted = false; |
a2a8927a XL |
609 | let comps = match data.expr.map(|e| &e.kind) { |
610 | Some(hir::ExprKind::Tup(comps)) => { | |
5e7ed085 | 611 | debug_assert_eq!(comps.len(), fields.len()); |
a2a8927a XL |
612 | Some(comps) |
613 | } | |
614 | _ => None, | |
c295e0f8 | 615 | }; |
5e7ed085 | 616 | for (i, ty) in fields.iter().enumerate() { |
04454e1e | 617 | let descr_post = &format!(" in tuple element {i}"); |
a2a8927a | 618 | let span = comps.and_then(|c| c.get(i)).map(|e| e.span).unwrap_or(data.source_span); |
c295e0f8 XL |
619 | if check_must_not_suspend_ty( |
620 | fcx, | |
621 | ty, | |
622 | hir_id, | |
a2a8927a XL |
623 | SuspendCheckData { |
624 | descr_post, | |
625 | expr: comps.and_then(|comps| comps.get(i)), | |
626 | source_span: span, | |
627 | ..data | |
628 | }, | |
c295e0f8 XL |
629 | ) { |
630 | has_emitted = true; | |
631 | } | |
632 | } | |
633 | has_emitted | |
634 | } | |
635 | ty::Array(ty, len) => { | |
636 | let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix); | |
637 | check_must_not_suspend_ty( | |
638 | fcx, | |
639 | ty, | |
640 | hir_id, | |
641 | SuspendCheckData { | |
642 | descr_pre, | |
643 | plural_len: len.try_eval_usize(fcx.tcx, fcx.param_env).unwrap_or(0) as usize | |
644 | + 1, | |
645 | ..data | |
646 | }, | |
647 | ) | |
648 | } | |
f2b60f7d FG |
649 | // If drop tracking is enabled, we want to look through references, since the referrent |
650 | // may not be considered live across the await point. | |
651 | ty::Ref(_region, ty, _mutability) if fcx.sess().opts.unstable_opts.drop_tracking => { | |
652 | let descr_pre = &format!("{}reference{} to ", data.descr_pre, plural_suffix); | |
653 | check_must_not_suspend_ty(fcx, ty, hir_id, SuspendCheckData { descr_pre, ..data }) | |
654 | } | |
c295e0f8 XL |
655 | _ => false, |
656 | } | |
657 | } | |
658 | ||
659 | fn check_must_not_suspend_def( | |
660 | tcx: TyCtxt<'_>, | |
661 | def_id: DefId, | |
662 | hir_id: HirId, | |
663 | data: SuspendCheckData<'_, '_>, | |
664 | ) -> bool { | |
04454e1e FG |
665 | if let Some(attr) = tcx.get_attr(def_id, sym::must_not_suspend) { |
666 | tcx.struct_span_lint_hir( | |
667 | rustc_session::lint::builtin::MUST_NOT_SUSPEND, | |
668 | hir_id, | |
669 | data.source_span, | |
2b03887a FG |
670 | DelayDm(|| { |
671 | format!( | |
04454e1e FG |
672 | "{}`{}`{} held across a suspend point, but should not be", |
673 | data.descr_pre, | |
674 | tcx.def_path_str(def_id), | |
675 | data.descr_post, | |
2b03887a FG |
676 | ) |
677 | }), | |
678 | |lint| { | |
04454e1e | 679 | // add span pointing to the offending yield/await |
2b03887a | 680 | lint.span_label(data.yield_span, "the value is held across this suspend point"); |
c295e0f8 | 681 | |
04454e1e FG |
682 | // Add optional reason note |
683 | if let Some(note) = attr.value_str() { | |
684 | // FIXME(guswynn): consider formatting this better | |
2b03887a | 685 | lint.span_note(data.source_span, note.as_str()); |
04454e1e | 686 | } |
c295e0f8 | 687 | |
04454e1e FG |
688 | // Add some quick suggestions on what to do |
689 | // FIXME: can `drop` work as a suggestion here as well? | |
2b03887a | 690 | lint.span_help( |
04454e1e FG |
691 | data.source_span, |
692 | "consider using a block (`{ ... }`) \ | |
693 | to shrink the value's scope, ending before the suspend point", | |
694 | ); | |
c295e0f8 | 695 | |
2b03887a | 696 | lint |
04454e1e FG |
697 | }, |
698 | ); | |
c295e0f8 | 699 | |
04454e1e FG |
700 | true |
701 | } else { | |
702 | false | |
c295e0f8 | 703 | } |
c295e0f8 | 704 | } |