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