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