]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_hir_typeck/src/generator_interior/mod.rs
New upstream version 1.71.1+dfsg1
[rustc.git] / compiler / rustc_hir_typeck / src / generator_interior / mod.rs
CommitLineData
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 6use self::drop_ranges::DropRanges;
dfeec247 7use super::FnCtxt;
3dfed10e 8use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
2b03887a 9use rustc_errors::{pluralize, DelayDm};
dfeec247
XL
10use rustc_hir as hir;
11use rustc_hir::def::{CtorKind, DefKind, Res};
12use rustc_hir::def_id::DefId;
29967ef6 13use rustc_hir::hir_id::HirIdSet;
5099ac24 14use rustc_hir::intravisit::{self, Visitor};
29967ef6 15use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind};
353b0b11 16use rustc_infer::infer::{DefineOpaqueTypes, RegionVariableOrigin};
5e7ed085 17use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData};
487cf647 18use rustc_middle::ty::fold::FnMutDelegate;
9ffffee4 19use rustc_middle::ty::{self, BoundVariableKind, RvalueScopes, Ty, TyCtxt, TypeVisitableExt};
c295e0f8 20use rustc_span::symbol::sym;
dfeec247 21use rustc_span::Span;
487cf647 22use smallvec::{smallvec, SmallVec};
ea8adc8c 23
5099ac24
FG
24mod drop_ranges;
25
dc9dc135
XL
26struct 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 38impl<'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
188pub 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 337impl<'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 530struct 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 545fn 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
663fn 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}