]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir_transform/src/generator.rs
Auto merge of #107406 - cjgillot:eliminate-witnesses, r=compiler-errors
[rust.git] / compiler / rustc_mir_transform / src / generator.rs
1 //! This is the implementation of the pass which transforms generators into state machines.
2 //!
3 //! MIR generation for generators creates a function which has a self argument which
4 //! passes by value. This argument is effectively a generator type which only contains upvars and
5 //! is only used for this argument inside the MIR for the generator.
6 //! It is passed by value to enable upvars to be moved out of it. Drop elaboration runs on that
7 //! MIR before this pass and creates drop flags for MIR locals.
8 //! It will also drop the generator argument (which only consists of upvars) if any of the upvars
9 //! are moved out of. This pass elaborates the drops of upvars / generator argument in the case
10 //! that none of the upvars were moved out of. This is because we cannot have any drops of this
11 //! generator in the MIR, since it is used to create the drop glue for the generator. We'd get
12 //! infinite recursion otherwise.
13 //!
14 //! This pass creates the implementation for either the `Generator::resume` or `Future::poll`
15 //! function and the drop shim for the generator based on the MIR input.
16 //! It converts the generator argument from Self to &mut Self adding derefs in the MIR as needed.
17 //! It computes the final layout of the generator struct which looks like this:
18 //!     First upvars are stored
19 //!     It is followed by the generator state field.
20 //!     Then finally the MIR locals which are live across a suspension point are stored.
21 //!     ```ignore (illustrative)
22 //!     struct Generator {
23 //!         upvars...,
24 //!         state: u32,
25 //!         mir_locals...,
26 //!     }
27 //!     ```
28 //! This pass computes the meaning of the state field and the MIR locals which are live
29 //! across a suspension point. There are however three hardcoded generator states:
30 //!     0 - Generator have not been resumed yet
31 //!     1 - Generator has returned / is completed
32 //!     2 - Generator has been poisoned
33 //!
34 //! It also rewrites `return x` and `yield y` as setting a new generator state and returning
35 //! `GeneratorState::Complete(x)` and `GeneratorState::Yielded(y)`,
36 //! or `Poll::Ready(x)` and `Poll::Pending` respectively.
37 //! MIR locals which are live across a suspension point are moved to the generator struct
38 //! with references to them being updated with references to the generator struct.
39 //!
40 //! The pass creates two functions which have a switch on the generator state giving
41 //! the action to take.
42 //!
43 //! One of them is the implementation of `Generator::resume` / `Future::poll`.
44 //! For generators with state 0 (unresumed) it starts the execution of the generator.
45 //! For generators with state 1 (returned) and state 2 (poisoned) it panics.
46 //! Otherwise it continues the execution from the last suspension point.
47 //!
48 //! The other function is the drop glue for the generator.
49 //! For generators with state 0 (unresumed) it drops the upvars of the generator.
50 //! For generators with state 1 (returned) and state 2 (poisoned) it does nothing.
51 //! Otherwise it drops all the values in scope at the last suspension point.
52
53 use crate::deref_separator::deref_finder;
54 use crate::simplify;
55 use crate::util::expand_aggregate;
56 use crate::MirPass;
57 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
58 use rustc_errors::pluralize;
59 use rustc_hir as hir;
60 use rustc_hir::lang_items::LangItem;
61 use rustc_hir::GeneratorKind;
62 use rustc_index::bit_set::{BitMatrix, BitSet, GrowableBitSet};
63 use rustc_index::vec::{Idx, IndexVec};
64 use rustc_middle::mir::dump_mir;
65 use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
66 use rustc_middle::mir::*;
67 use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
68 use rustc_middle::ty::{GeneratorSubsts, SubstsRef};
69 use rustc_mir_dataflow::impls::{
70     MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive,
71 };
72 use rustc_mir_dataflow::storage::always_storage_live_locals;
73 use rustc_mir_dataflow::{self, Analysis};
74 use rustc_span::def_id::DefId;
75 use rustc_span::symbol::sym;
76 use rustc_span::Span;
77 use rustc_target::abi::VariantIdx;
78 use rustc_target::spec::PanicStrategy;
79 use std::{iter, ops};
80
81 pub struct StateTransform;
82
83 struct RenameLocalVisitor<'tcx> {
84     from: Local,
85     to: Local,
86     tcx: TyCtxt<'tcx>,
87 }
88
89 impl<'tcx> MutVisitor<'tcx> for RenameLocalVisitor<'tcx> {
90     fn tcx(&self) -> TyCtxt<'tcx> {
91         self.tcx
92     }
93
94     fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
95         if *local == self.from {
96             *local = self.to;
97         }
98     }
99
100     fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
101         match terminator.kind {
102             TerminatorKind::Return => {
103                 // Do not replace the implicit `_0` access here, as that's not possible. The
104                 // transform already handles `return` correctly.
105             }
106             _ => self.super_terminator(terminator, location),
107         }
108     }
109 }
110
111 struct DerefArgVisitor<'tcx> {
112     tcx: TyCtxt<'tcx>,
113 }
114
115 impl<'tcx> MutVisitor<'tcx> for DerefArgVisitor<'tcx> {
116     fn tcx(&self) -> TyCtxt<'tcx> {
117         self.tcx
118     }
119
120     fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
121         assert_ne!(*local, SELF_ARG);
122     }
123
124     fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
125         if place.local == SELF_ARG {
126             replace_base(
127                 place,
128                 Place {
129                     local: SELF_ARG,
130                     projection: self.tcx().intern_place_elems(&[ProjectionElem::Deref]),
131                 },
132                 self.tcx,
133             );
134         } else {
135             self.visit_local(&mut place.local, context, location);
136
137             for elem in place.projection.iter() {
138                 if let PlaceElem::Index(local) = elem {
139                     assert_ne!(local, SELF_ARG);
140                 }
141             }
142         }
143     }
144 }
145
146 struct PinArgVisitor<'tcx> {
147     ref_gen_ty: Ty<'tcx>,
148     tcx: TyCtxt<'tcx>,
149 }
150
151 impl<'tcx> MutVisitor<'tcx> for PinArgVisitor<'tcx> {
152     fn tcx(&self) -> TyCtxt<'tcx> {
153         self.tcx
154     }
155
156     fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
157         assert_ne!(*local, SELF_ARG);
158     }
159
160     fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
161         if place.local == SELF_ARG {
162             replace_base(
163                 place,
164                 Place {
165                     local: SELF_ARG,
166                     projection: self.tcx().intern_place_elems(&[ProjectionElem::Field(
167                         Field::new(0),
168                         self.ref_gen_ty,
169                     )]),
170                 },
171                 self.tcx,
172             );
173         } else {
174             self.visit_local(&mut place.local, context, location);
175
176             for elem in place.projection.iter() {
177                 if let PlaceElem::Index(local) = elem {
178                     assert_ne!(local, SELF_ARG);
179                 }
180             }
181         }
182     }
183 }
184
185 fn replace_base<'tcx>(place: &mut Place<'tcx>, new_base: Place<'tcx>, tcx: TyCtxt<'tcx>) {
186     place.local = new_base.local;
187
188     let mut new_projection = new_base.projection.to_vec();
189     new_projection.append(&mut place.projection.to_vec());
190
191     place.projection = tcx.intern_place_elems(&new_projection);
192 }
193
194 const SELF_ARG: Local = Local::from_u32(1);
195
196 /// Generator has not been resumed yet.
197 const UNRESUMED: usize = GeneratorSubsts::UNRESUMED;
198 /// Generator has returned / is completed.
199 const RETURNED: usize = GeneratorSubsts::RETURNED;
200 /// Generator has panicked and is poisoned.
201 const POISONED: usize = GeneratorSubsts::POISONED;
202
203 /// Number of variants to reserve in generator state. Corresponds to
204 /// `UNRESUMED` (beginning of a generator) and `RETURNED`/`POISONED`
205 /// (end of a generator) states.
206 const RESERVED_VARIANTS: usize = 3;
207
208 /// A `yield` point in the generator.
209 struct SuspensionPoint<'tcx> {
210     /// State discriminant used when suspending or resuming at this point.
211     state: usize,
212     /// The block to jump to after resumption.
213     resume: BasicBlock,
214     /// Where to move the resume argument after resumption.
215     resume_arg: Place<'tcx>,
216     /// Which block to jump to if the generator is dropped in this state.
217     drop: Option<BasicBlock>,
218     /// Set of locals that have live storage while at this suspension point.
219     storage_liveness: GrowableBitSet<Local>,
220 }
221
222 struct TransformVisitor<'tcx> {
223     tcx: TyCtxt<'tcx>,
224     is_async_kind: bool,
225     state_adt_ref: AdtDef<'tcx>,
226     state_substs: SubstsRef<'tcx>,
227
228     // The type of the discriminant in the generator struct
229     discr_ty: Ty<'tcx>,
230
231     // Mapping from Local to (type of local, generator struct index)
232     // FIXME(eddyb) This should use `IndexVec<Local, Option<_>>`.
233     remap: FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
234
235     // A map from a suspension point in a block to the locals which have live storage at that point
236     storage_liveness: IndexVec<BasicBlock, Option<BitSet<Local>>>,
237
238     // A list of suspension points, generated during the transform
239     suspension_points: Vec<SuspensionPoint<'tcx>>,
240
241     // The set of locals that have no `StorageLive`/`StorageDead` annotations.
242     always_live_locals: BitSet<Local>,
243
244     // The original RETURN_PLACE local
245     new_ret_local: Local,
246 }
247
248 impl<'tcx> TransformVisitor<'tcx> {
249     // Make a `GeneratorState` or `Poll` variant assignment.
250     //
251     // `core::ops::GeneratorState` only has single element tuple variants,
252     // so we can just write to the downcasted first field and then set the
253     // discriminant to the appropriate variant.
254     fn make_state(
255         &self,
256         val: Operand<'tcx>,
257         source_info: SourceInfo,
258         is_return: bool,
259         statements: &mut Vec<Statement<'tcx>>,
260     ) {
261         let idx = VariantIdx::new(match (is_return, self.is_async_kind) {
262             (true, false) => 1,  // GeneratorState::Complete
263             (false, false) => 0, // GeneratorState::Yielded
264             (true, true) => 0,   // Poll::Ready
265             (false, true) => 1,  // Poll::Pending
266         });
267
268         let kind = AggregateKind::Adt(self.state_adt_ref.did(), idx, self.state_substs, None, None);
269
270         // `Poll::Pending`
271         if self.is_async_kind && idx == VariantIdx::new(1) {
272             assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 0);
273
274             // FIXME(swatinem): assert that `val` is indeed unit?
275             statements.extend(expand_aggregate(
276                 Place::return_place(),
277                 std::iter::empty(),
278                 kind,
279                 source_info,
280                 self.tcx,
281             ));
282             return;
283         }
284
285         // else: `Poll::Ready(x)`, `GeneratorState::Yielded(x)` or `GeneratorState::Complete(x)`
286         assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 1);
287
288         let ty = self
289             .tcx
290             .bound_type_of(self.state_adt_ref.variant(idx).fields[0].did)
291             .subst(self.tcx, self.state_substs);
292
293         statements.extend(expand_aggregate(
294             Place::return_place(),
295             std::iter::once((val, ty)),
296             kind,
297             source_info,
298             self.tcx,
299         ));
300     }
301
302     // Create a Place referencing a generator struct field
303     fn make_field(&self, variant_index: VariantIdx, idx: usize, ty: Ty<'tcx>) -> Place<'tcx> {
304         let self_place = Place::from(SELF_ARG);
305         let base = self.tcx.mk_place_downcast_unnamed(self_place, variant_index);
306         let mut projection = base.projection.to_vec();
307         projection.push(ProjectionElem::Field(Field::new(idx), ty));
308
309         Place { local: base.local, projection: self.tcx.intern_place_elems(&projection) }
310     }
311
312     // Create a statement which changes the discriminant
313     fn set_discr(&self, state_disc: VariantIdx, source_info: SourceInfo) -> Statement<'tcx> {
314         let self_place = Place::from(SELF_ARG);
315         Statement {
316             source_info,
317             kind: StatementKind::SetDiscriminant {
318                 place: Box::new(self_place),
319                 variant_index: state_disc,
320             },
321         }
322     }
323
324     // Create a statement which reads the discriminant into a temporary
325     fn get_discr(&self, body: &mut Body<'tcx>) -> (Statement<'tcx>, Place<'tcx>) {
326         let temp_decl = LocalDecl::new(self.discr_ty, body.span).internal();
327         let local_decls_len = body.local_decls.push(temp_decl);
328         let temp = Place::from(local_decls_len);
329
330         let self_place = Place::from(SELF_ARG);
331         let assign = Statement {
332             source_info: SourceInfo::outermost(body.span),
333             kind: StatementKind::Assign(Box::new((temp, Rvalue::Discriminant(self_place)))),
334         };
335         (assign, temp)
336     }
337 }
338
339 impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
340     fn tcx(&self) -> TyCtxt<'tcx> {
341         self.tcx
342     }
343
344     fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
345         assert_eq!(self.remap.get(local), None);
346     }
347
348     fn visit_place(
349         &mut self,
350         place: &mut Place<'tcx>,
351         _context: PlaceContext,
352         _location: Location,
353     ) {
354         // Replace an Local in the remap with a generator struct access
355         if let Some(&(ty, variant_index, idx)) = self.remap.get(&place.local) {
356             replace_base(place, self.make_field(variant_index, idx, ty), self.tcx);
357         }
358     }
359
360     fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
361         // Remove StorageLive and StorageDead statements for remapped locals
362         data.retain_statements(|s| match s.kind {
363             StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => {
364                 !self.remap.contains_key(&l)
365             }
366             _ => true,
367         });
368
369         let ret_val = match data.terminator().kind {
370             TerminatorKind::Return => {
371                 Some((true, None, Operand::Move(Place::from(self.new_ret_local)), None))
372             }
373             TerminatorKind::Yield { ref value, resume, resume_arg, drop } => {
374                 Some((false, Some((resume, resume_arg)), value.clone(), drop))
375             }
376             _ => None,
377         };
378
379         if let Some((is_return, resume, v, drop)) = ret_val {
380             let source_info = data.terminator().source_info;
381             // We must assign the value first in case it gets declared dead below
382             self.make_state(v, source_info, is_return, &mut data.statements);
383             let state = if let Some((resume, mut resume_arg)) = resume {
384                 // Yield
385                 let state = RESERVED_VARIANTS + self.suspension_points.len();
386
387                 // The resume arg target location might itself be remapped if its base local is
388                 // live across a yield.
389                 let resume_arg =
390                     if let Some(&(ty, variant, idx)) = self.remap.get(&resume_arg.local) {
391                         replace_base(&mut resume_arg, self.make_field(variant, idx, ty), self.tcx);
392                         resume_arg
393                     } else {
394                         resume_arg
395                     };
396
397                 self.suspension_points.push(SuspensionPoint {
398                     state,
399                     resume,
400                     resume_arg,
401                     drop,
402                     storage_liveness: self.storage_liveness[block].clone().unwrap().into(),
403                 });
404
405                 VariantIdx::new(state)
406             } else {
407                 // Return
408                 VariantIdx::new(RETURNED) // state for returned
409             };
410             data.statements.push(self.set_discr(state, source_info));
411             data.terminator_mut().kind = TerminatorKind::Return;
412         }
413
414         self.super_basic_block_data(block, data);
415     }
416 }
417
418 fn make_generator_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
419     let gen_ty = body.local_decls.raw[1].ty;
420
421     let ref_gen_ty =
422         tcx.mk_ref(tcx.lifetimes.re_erased, ty::TypeAndMut { ty: gen_ty, mutbl: Mutability::Mut });
423
424     // Replace the by value generator argument
425     body.local_decls.raw[1].ty = ref_gen_ty;
426
427     // Add a deref to accesses of the generator state
428     DerefArgVisitor { tcx }.visit_body(body);
429 }
430
431 fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
432     let ref_gen_ty = body.local_decls.raw[1].ty;
433
434     let pin_did = tcx.require_lang_item(LangItem::Pin, Some(body.span));
435     let pin_adt_ref = tcx.adt_def(pin_did);
436     let substs = tcx.intern_substs(&[ref_gen_ty.into()]);
437     let pin_ref_gen_ty = tcx.mk_adt(pin_adt_ref, substs);
438
439     // Replace the by ref generator argument
440     body.local_decls.raw[1].ty = pin_ref_gen_ty;
441
442     // Add the Pin field access to accesses of the generator state
443     PinArgVisitor { ref_gen_ty, tcx }.visit_body(body);
444 }
445
446 /// Allocates a new local and replaces all references of `local` with it. Returns the new local.
447 ///
448 /// `local` will be changed to a new local decl with type `ty`.
449 ///
450 /// Note that the new local will be uninitialized. It is the caller's responsibility to assign some
451 /// valid value to it before its first use.
452 fn replace_local<'tcx>(
453     local: Local,
454     ty: Ty<'tcx>,
455     body: &mut Body<'tcx>,
456     tcx: TyCtxt<'tcx>,
457 ) -> Local {
458     let new_decl = LocalDecl::new(ty, body.span);
459     let new_local = body.local_decls.push(new_decl);
460     body.local_decls.swap(local, new_local);
461
462     RenameLocalVisitor { from: local, to: new_local, tcx }.visit_body(body);
463
464     new_local
465 }
466
467 /// Transforms the `body` of the generator applying the following transforms:
468 ///
469 /// - Eliminates all the `get_context` calls that async lowering created.
470 /// - Replace all `Local` `ResumeTy` types with `&mut Context<'_>` (`context_mut_ref`).
471 ///
472 /// The `Local`s that have their types replaced are:
473 /// - The `resume` argument itself.
474 /// - The argument to `get_context`.
475 /// - The yielded value of a `yield`.
476 ///
477 /// The `ResumeTy` hides a `&mut Context<'_>` behind an unsafe raw pointer, and the
478 /// `get_context` function is being used to convert that back to a `&mut Context<'_>`.
479 ///
480 /// Ideally the async lowering would not use the `ResumeTy`/`get_context` indirection,
481 /// but rather directly use `&mut Context<'_>`, however that would currently
482 /// lead to higher-kinded lifetime errors.
483 /// See <https://github.com/rust-lang/rust/issues/105501>.
484 ///
485 /// The async lowering step and the type / lifetime inference / checking are
486 /// still using the `ResumeTy` indirection for the time being, and that indirection
487 /// is removed here. After this transform, the generator body only knows about `&mut Context<'_>`.
488 fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
489     let context_mut_ref = tcx.mk_task_context();
490
491     // replace the type of the `resume` argument
492     replace_resume_ty_local(tcx, body, Local::new(2), context_mut_ref);
493
494     let get_context_def_id = tcx.require_lang_item(LangItem::GetContext, None);
495
496     for bb in BasicBlock::new(0)..body.basic_blocks.next_index() {
497         let bb_data = &body[bb];
498         if bb_data.is_cleanup {
499             continue;
500         }
501
502         match &bb_data.terminator().kind {
503             TerminatorKind::Call { func, .. } => {
504                 let func_ty = func.ty(body, tcx);
505                 if let ty::FnDef(def_id, _) = *func_ty.kind() {
506                     if def_id == get_context_def_id {
507                         let local = eliminate_get_context_call(&mut body[bb]);
508                         replace_resume_ty_local(tcx, body, local, context_mut_ref);
509                     }
510                 } else {
511                     continue;
512                 }
513             }
514             TerminatorKind::Yield { resume_arg, .. } => {
515                 replace_resume_ty_local(tcx, body, resume_arg.local, context_mut_ref);
516             }
517             _ => {}
518         }
519     }
520 }
521
522 fn eliminate_get_context_call<'tcx>(bb_data: &mut BasicBlockData<'tcx>) -> Local {
523     let terminator = bb_data.terminator.take().unwrap();
524     if let TerminatorKind::Call { mut args, destination, target, .. } = terminator.kind {
525         let arg = args.pop().unwrap();
526         let local = arg.place().unwrap().local;
527
528         let arg = Rvalue::Use(arg);
529         let assign = Statement {
530             source_info: terminator.source_info,
531             kind: StatementKind::Assign(Box::new((destination, arg))),
532         };
533         bb_data.statements.push(assign);
534         bb_data.terminator = Some(Terminator {
535             source_info: terminator.source_info,
536             kind: TerminatorKind::Goto { target: target.unwrap() },
537         });
538         local
539     } else {
540         bug!();
541     }
542 }
543
544 #[cfg_attr(not(debug_assertions), allow(unused))]
545 fn replace_resume_ty_local<'tcx>(
546     tcx: TyCtxt<'tcx>,
547     body: &mut Body<'tcx>,
548     local: Local,
549     context_mut_ref: Ty<'tcx>,
550 ) {
551     let local_ty = std::mem::replace(&mut body.local_decls[local].ty, context_mut_ref);
552     // We have to replace the `ResumeTy` that is used for type and borrow checking
553     // with `&mut Context<'_>` in MIR.
554     #[cfg(debug_assertions)]
555     {
556         if let ty::Adt(resume_ty_adt, _) = local_ty.kind() {
557             let expected_adt = tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, None));
558             assert_eq!(*resume_ty_adt, expected_adt);
559         } else {
560             panic!("expected `ResumeTy`, found `{:?}`", local_ty);
561         };
562     }
563 }
564
565 struct LivenessInfo {
566     /// Which locals are live across any suspension point.
567     saved_locals: GeneratorSavedLocals,
568
569     /// The set of saved locals live at each suspension point.
570     live_locals_at_suspension_points: Vec<BitSet<GeneratorSavedLocal>>,
571
572     /// Parallel vec to the above with SourceInfo for each yield terminator.
573     source_info_at_suspension_points: Vec<SourceInfo>,
574
575     /// For every saved local, the set of other saved locals that are
576     /// storage-live at the same time as this local. We cannot overlap locals in
577     /// the layout which have conflicting storage.
578     storage_conflicts: BitMatrix<GeneratorSavedLocal, GeneratorSavedLocal>,
579
580     /// For every suspending block, the locals which are storage-live across
581     /// that suspension point.
582     storage_liveness: IndexVec<BasicBlock, Option<BitSet<Local>>>,
583 }
584
585 fn locals_live_across_suspend_points<'tcx>(
586     tcx: TyCtxt<'tcx>,
587     body: &Body<'tcx>,
588     always_live_locals: &BitSet<Local>,
589     movable: bool,
590 ) -> LivenessInfo {
591     let body_ref: &Body<'_> = &body;
592
593     // Calculate when MIR locals have live storage. This gives us an upper bound of their
594     // lifetimes.
595     let mut storage_live = MaybeStorageLive::new(std::borrow::Cow::Borrowed(always_live_locals))
596         .into_engine(tcx, body_ref)
597         .iterate_to_fixpoint()
598         .into_results_cursor(body_ref);
599
600     // Calculate the MIR locals which have been previously
601     // borrowed (even if they are still active).
602     let borrowed_locals_results =
603         MaybeBorrowedLocals.into_engine(tcx, body_ref).pass_name("generator").iterate_to_fixpoint();
604
605     let mut borrowed_locals_cursor =
606         rustc_mir_dataflow::ResultsCursor::new(body_ref, &borrowed_locals_results);
607
608     // Calculate the MIR locals that we actually need to keep storage around
609     // for.
610     let requires_storage_results = MaybeRequiresStorage::new(body, &borrowed_locals_results)
611         .into_engine(tcx, body_ref)
612         .iterate_to_fixpoint();
613     let mut requires_storage_cursor =
614         rustc_mir_dataflow::ResultsCursor::new(body_ref, &requires_storage_results);
615
616     // Calculate the liveness of MIR locals ignoring borrows.
617     let mut liveness = MaybeLiveLocals
618         .into_engine(tcx, body_ref)
619         .pass_name("generator")
620         .iterate_to_fixpoint()
621         .into_results_cursor(body_ref);
622
623     let mut storage_liveness_map = IndexVec::from_elem(None, &body.basic_blocks);
624     let mut live_locals_at_suspension_points = Vec::new();
625     let mut source_info_at_suspension_points = Vec::new();
626     let mut live_locals_at_any_suspension_point = BitSet::new_empty(body.local_decls.len());
627
628     for (block, data) in body.basic_blocks.iter_enumerated() {
629         if let TerminatorKind::Yield { .. } = data.terminator().kind {
630             let loc = Location { block, statement_index: data.statements.len() };
631
632             liveness.seek_to_block_end(block);
633             let mut live_locals: BitSet<_> = BitSet::new_empty(body.local_decls.len());
634             live_locals.union(liveness.get());
635
636             if !movable {
637                 // The `liveness` variable contains the liveness of MIR locals ignoring borrows.
638                 // This is correct for movable generators since borrows cannot live across
639                 // suspension points. However for immovable generators we need to account for
640                 // borrows, so we conservatively assume that all borrowed locals are live until
641                 // we find a StorageDead statement referencing the locals.
642                 // To do this we just union our `liveness` result with `borrowed_locals`, which
643                 // contains all the locals which has been borrowed before this suspension point.
644                 // If a borrow is converted to a raw reference, we must also assume that it lives
645                 // forever. Note that the final liveness is still bounded by the storage liveness
646                 // of the local, which happens using the `intersect` operation below.
647                 borrowed_locals_cursor.seek_before_primary_effect(loc);
648                 live_locals.union(borrowed_locals_cursor.get());
649             }
650
651             // Store the storage liveness for later use so we can restore the state
652             // after a suspension point
653             storage_live.seek_before_primary_effect(loc);
654             storage_liveness_map[block] = Some(storage_live.get().clone());
655
656             // Locals live are live at this point only if they are used across
657             // suspension points (the `liveness` variable)
658             // and their storage is required (the `storage_required` variable)
659             requires_storage_cursor.seek_before_primary_effect(loc);
660             live_locals.intersect(requires_storage_cursor.get());
661
662             // The generator argument is ignored.
663             live_locals.remove(SELF_ARG);
664
665             debug!("loc = {:?}, live_locals = {:?}", loc, live_locals);
666
667             // Add the locals live at this suspension point to the set of locals which live across
668             // any suspension points
669             live_locals_at_any_suspension_point.union(&live_locals);
670
671             live_locals_at_suspension_points.push(live_locals);
672             source_info_at_suspension_points.push(data.terminator().source_info);
673         }
674     }
675
676     debug!("live_locals_anywhere = {:?}", live_locals_at_any_suspension_point);
677     let saved_locals = GeneratorSavedLocals(live_locals_at_any_suspension_point);
678
679     // Renumber our liveness_map bitsets to include only the locals we are
680     // saving.
681     let live_locals_at_suspension_points = live_locals_at_suspension_points
682         .iter()
683         .map(|live_here| saved_locals.renumber_bitset(&live_here))
684         .collect();
685
686     let storage_conflicts = compute_storage_conflicts(
687         body_ref,
688         &saved_locals,
689         always_live_locals.clone(),
690         requires_storage_results,
691     );
692
693     LivenessInfo {
694         saved_locals,
695         live_locals_at_suspension_points,
696         source_info_at_suspension_points,
697         storage_conflicts,
698         storage_liveness: storage_liveness_map,
699     }
700 }
701
702 /// The set of `Local`s that must be saved across yield points.
703 ///
704 /// `GeneratorSavedLocal` is indexed in terms of the elements in this set;
705 /// i.e. `GeneratorSavedLocal::new(1)` corresponds to the second local
706 /// included in this set.
707 struct GeneratorSavedLocals(BitSet<Local>);
708
709 impl GeneratorSavedLocals {
710     /// Returns an iterator over each `GeneratorSavedLocal` along with the `Local` it corresponds
711     /// to.
712     fn iter_enumerated(&self) -> impl '_ + Iterator<Item = (GeneratorSavedLocal, Local)> {
713         self.iter().enumerate().map(|(i, l)| (GeneratorSavedLocal::from(i), l))
714     }
715
716     /// Transforms a `BitSet<Local>` that contains only locals saved across yield points to the
717     /// equivalent `BitSet<GeneratorSavedLocal>`.
718     fn renumber_bitset(&self, input: &BitSet<Local>) -> BitSet<GeneratorSavedLocal> {
719         assert!(self.superset(&input), "{:?} not a superset of {:?}", self.0, input);
720         let mut out = BitSet::new_empty(self.count());
721         for (saved_local, local) in self.iter_enumerated() {
722             if input.contains(local) {
723                 out.insert(saved_local);
724             }
725         }
726         out
727     }
728
729     fn get(&self, local: Local) -> Option<GeneratorSavedLocal> {
730         if !self.contains(local) {
731             return None;
732         }
733
734         let idx = self.iter().take_while(|&l| l < local).count();
735         Some(GeneratorSavedLocal::new(idx))
736     }
737 }
738
739 impl ops::Deref for GeneratorSavedLocals {
740     type Target = BitSet<Local>;
741
742     fn deref(&self) -> &Self::Target {
743         &self.0
744     }
745 }
746
747 /// For every saved local, looks for which locals are StorageLive at the same
748 /// time. Generates a bitset for every local of all the other locals that may be
749 /// StorageLive simultaneously with that local. This is used in the layout
750 /// computation; see `GeneratorLayout` for more.
751 fn compute_storage_conflicts<'mir, 'tcx>(
752     body: &'mir Body<'tcx>,
753     saved_locals: &GeneratorSavedLocals,
754     always_live_locals: BitSet<Local>,
755     requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>,
756 ) -> BitMatrix<GeneratorSavedLocal, GeneratorSavedLocal> {
757     assert_eq!(body.local_decls.len(), saved_locals.domain_size());
758
759     debug!("compute_storage_conflicts({:?})", body.span);
760     debug!("always_live = {:?}", always_live_locals);
761
762     // Locals that are always live or ones that need to be stored across
763     // suspension points are not eligible for overlap.
764     let mut ineligible_locals = always_live_locals;
765     ineligible_locals.intersect(&**saved_locals);
766
767     // Compute the storage conflicts for all eligible locals.
768     let mut visitor = StorageConflictVisitor {
769         body,
770         saved_locals: &saved_locals,
771         local_conflicts: BitMatrix::from_row_n(&ineligible_locals, body.local_decls.len()),
772     };
773
774     requires_storage.visit_reachable_with(body, &mut visitor);
775
776     let local_conflicts = visitor.local_conflicts;
777
778     // Compress the matrix using only stored locals (Local -> GeneratorSavedLocal).
779     //
780     // NOTE: Today we store a full conflict bitset for every local. Technically
781     // this is twice as many bits as we need, since the relation is symmetric.
782     // However, in practice these bitsets are not usually large. The layout code
783     // also needs to keep track of how many conflicts each local has, so it's
784     // simpler to keep it this way for now.
785     let mut storage_conflicts = BitMatrix::new(saved_locals.count(), saved_locals.count());
786     for (saved_local_a, local_a) in saved_locals.iter_enumerated() {
787         if ineligible_locals.contains(local_a) {
788             // Conflicts with everything.
789             storage_conflicts.insert_all_into_row(saved_local_a);
790         } else {
791             // Keep overlap information only for stored locals.
792             for (saved_local_b, local_b) in saved_locals.iter_enumerated() {
793                 if local_conflicts.contains(local_a, local_b) {
794                     storage_conflicts.insert(saved_local_a, saved_local_b);
795                 }
796             }
797         }
798     }
799     storage_conflicts
800 }
801
802 struct StorageConflictVisitor<'mir, 'tcx, 's> {
803     body: &'mir Body<'tcx>,
804     saved_locals: &'s GeneratorSavedLocals,
805     // FIXME(tmandry): Consider using sparse bitsets here once we have good
806     // benchmarks for generators.
807     local_conflicts: BitMatrix<Local, Local>,
808 }
809
810 impl<'mir, 'tcx> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx>
811     for StorageConflictVisitor<'mir, 'tcx, '_>
812 {
813     type FlowState = BitSet<Local>;
814
815     fn visit_statement_before_primary_effect(
816         &mut self,
817         state: &Self::FlowState,
818         _statement: &'mir Statement<'tcx>,
819         loc: Location,
820     ) {
821         self.apply_state(state, loc);
822     }
823
824     fn visit_terminator_before_primary_effect(
825         &mut self,
826         state: &Self::FlowState,
827         _terminator: &'mir Terminator<'tcx>,
828         loc: Location,
829     ) {
830         self.apply_state(state, loc);
831     }
832 }
833
834 impl StorageConflictVisitor<'_, '_, '_> {
835     fn apply_state(&mut self, flow_state: &BitSet<Local>, loc: Location) {
836         // Ignore unreachable blocks.
837         if self.body.basic_blocks[loc.block].terminator().kind == TerminatorKind::Unreachable {
838             return;
839         }
840
841         let mut eligible_storage_live = flow_state.clone();
842         eligible_storage_live.intersect(&**self.saved_locals);
843
844         for local in eligible_storage_live.iter() {
845             self.local_conflicts.union_row_with(&eligible_storage_live, local);
846         }
847
848         if eligible_storage_live.count() > 1 {
849             trace!("at {:?}, eligible_storage_live={:?}", loc, eligible_storage_live);
850         }
851     }
852 }
853
854 /// Validates the typeck view of the generator against the actual set of types saved between
855 /// yield points.
856 fn sanitize_witness<'tcx>(
857     tcx: TyCtxt<'tcx>,
858     body: &Body<'tcx>,
859     witness: Ty<'tcx>,
860     upvars: Vec<Ty<'tcx>>,
861     layout: &GeneratorLayout<'tcx>,
862 ) {
863     let did = body.source.def_id();
864     let param_env = tcx.param_env(did);
865
866     let allowed_upvars = tcx.normalize_erasing_regions(param_env, upvars);
867     let allowed = match witness.kind() {
868         &ty::GeneratorWitness(interior_tys) => {
869             tcx.normalize_erasing_late_bound_regions(param_env, interior_tys)
870         }
871         _ => {
872             tcx.sess.delay_span_bug(
873                 body.span,
874                 &format!("unexpected generator witness type {:?}", witness.kind()),
875             );
876             return;
877         }
878     };
879
880     let mut mismatches = Vec::new();
881     for fty in &layout.field_tys {
882         if fty.ignore_for_traits {
883             continue;
884         }
885         let decl_ty = tcx.normalize_erasing_regions(param_env, fty.ty);
886
887         // Sanity check that typeck knows about the type of locals which are
888         // live across a suspension point
889         if !allowed.contains(&decl_ty) && !allowed_upvars.contains(&decl_ty) {
890             mismatches.push(decl_ty);
891         }
892     }
893
894     if !mismatches.is_empty() {
895         span_bug!(
896             body.span,
897             "Broken MIR: generator contains type {:?} in MIR, \
898                        but typeck only knows about {} and {:?}",
899             mismatches,
900             allowed,
901             allowed_upvars
902         );
903     }
904 }
905
906 fn compute_layout<'tcx>(
907     tcx: TyCtxt<'tcx>,
908     liveness: LivenessInfo,
909     body: &Body<'tcx>,
910 ) -> (
911     FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
912     GeneratorLayout<'tcx>,
913     IndexVec<BasicBlock, Option<BitSet<Local>>>,
914 ) {
915     let LivenessInfo {
916         saved_locals,
917         live_locals_at_suspension_points,
918         source_info_at_suspension_points,
919         storage_conflicts,
920         storage_liveness,
921     } = liveness;
922
923     // Gather live local types and their indices.
924     let mut locals = IndexVec::<GeneratorSavedLocal, _>::new();
925     let mut tys = IndexVec::<GeneratorSavedLocal, _>::new();
926     for (saved_local, local) in saved_locals.iter_enumerated() {
927         debug!("generator saved local {:?} => {:?}", saved_local, local);
928
929         locals.push(local);
930         let decl = &body.local_decls[local];
931         debug!(?decl);
932
933         let ignore_for_traits = if tcx.sess.opts.unstable_opts.drop_tracking_mir {
934             match decl.local_info {
935                 // Do not include raw pointers created from accessing `static` items, as those could
936                 // well be re-created by another access to the same static.
937                 Some(box LocalInfo::StaticRef { is_thread_local, .. }) => !is_thread_local,
938                 // Fake borrows are only read by fake reads, so do not have any reality in
939                 // post-analysis MIR.
940                 Some(box LocalInfo::FakeBorrow) => true,
941                 _ => false,
942             }
943         } else {
944             // FIXME(#105084) HIR-based drop tracking does not account for all the temporaries that
945             // MIR building may introduce. This leads to wrongly ignored types, but this is
946             // necessary for internal consistency and to avoid ICEs.
947             decl.internal
948         };
949         let decl =
950             GeneratorSavedTy { ty: decl.ty, source_info: decl.source_info, ignore_for_traits };
951         debug!(?decl);
952
953         tys.push(decl);
954     }
955
956     // Leave empty variants for the UNRESUMED, RETURNED, and POISONED states.
957     // In debuginfo, these will correspond to the beginning (UNRESUMED) or end
958     // (RETURNED, POISONED) of the function.
959     let body_span = body.source_scopes[OUTERMOST_SOURCE_SCOPE].span;
960     let mut variant_source_info: IndexVec<VariantIdx, SourceInfo> = [
961         SourceInfo::outermost(body_span.shrink_to_lo()),
962         SourceInfo::outermost(body_span.shrink_to_hi()),
963         SourceInfo::outermost(body_span.shrink_to_hi()),
964     ]
965     .iter()
966     .copied()
967     .collect();
968
969     // Build the generator variant field list.
970     // Create a map from local indices to generator struct indices.
971     let mut variant_fields: IndexVec<VariantIdx, IndexVec<Field, GeneratorSavedLocal>> =
972         iter::repeat(IndexVec::new()).take(RESERVED_VARIANTS).collect();
973     let mut remap = FxHashMap::default();
974     for (suspension_point_idx, live_locals) in live_locals_at_suspension_points.iter().enumerate() {
975         let variant_index = VariantIdx::from(RESERVED_VARIANTS + suspension_point_idx);
976         let mut fields = IndexVec::new();
977         for (idx, saved_local) in live_locals.iter().enumerate() {
978             fields.push(saved_local);
979             // Note that if a field is included in multiple variants, we will
980             // just use the first one here. That's fine; fields do not move
981             // around inside generators, so it doesn't matter which variant
982             // index we access them by.
983             remap.entry(locals[saved_local]).or_insert((tys[saved_local].ty, variant_index, idx));
984         }
985         variant_fields.push(fields);
986         variant_source_info.push(source_info_at_suspension_points[suspension_point_idx]);
987     }
988     debug!("generator variant_fields = {:?}", variant_fields);
989     debug!("generator storage_conflicts = {:#?}", storage_conflicts);
990
991     let layout =
992         GeneratorLayout { field_tys: tys, variant_fields, variant_source_info, storage_conflicts };
993     debug!(?layout);
994
995     (remap, layout, storage_liveness)
996 }
997
998 /// Replaces the entry point of `body` with a block that switches on the generator discriminant and
999 /// dispatches to blocks according to `cases`.
1000 ///
1001 /// After this function, the former entry point of the function will be bb1.
1002 fn insert_switch<'tcx>(
1003     body: &mut Body<'tcx>,
1004     cases: Vec<(usize, BasicBlock)>,
1005     transform: &TransformVisitor<'tcx>,
1006     default: TerminatorKind<'tcx>,
1007 ) {
1008     let default_block = insert_term_block(body, default);
1009     let (assign, discr) = transform.get_discr(body);
1010     let switch_targets =
1011         SwitchTargets::new(cases.iter().map(|(i, bb)| ((*i) as u128, *bb)), default_block);
1012     let switch = TerminatorKind::SwitchInt { discr: Operand::Move(discr), targets: switch_targets };
1013
1014     let source_info = SourceInfo::outermost(body.span);
1015     body.basic_blocks_mut().raw.insert(
1016         0,
1017         BasicBlockData {
1018             statements: vec![assign],
1019             terminator: Some(Terminator { source_info, kind: switch }),
1020             is_cleanup: false,
1021         },
1022     );
1023
1024     let blocks = body.basic_blocks_mut().iter_mut();
1025
1026     for target in blocks.flat_map(|b| b.terminator_mut().successors_mut()) {
1027         *target = BasicBlock::new(target.index() + 1);
1028     }
1029 }
1030
1031 fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
1032     use crate::shim::DropShimElaborator;
1033     use rustc_middle::mir::patch::MirPatch;
1034     use rustc_mir_dataflow::elaborate_drops::{elaborate_drop, Unwind};
1035
1036     // Note that `elaborate_drops` only drops the upvars of a generator, and
1037     // this is ok because `open_drop` can only be reached within that own
1038     // generator's resume function.
1039
1040     let def_id = body.source.def_id();
1041     let param_env = tcx.param_env(def_id);
1042
1043     let mut elaborator = DropShimElaborator { body, patch: MirPatch::new(body), tcx, param_env };
1044
1045     for (block, block_data) in body.basic_blocks.iter_enumerated() {
1046         let (target, unwind, source_info) = match block_data.terminator() {
1047             Terminator { source_info, kind: TerminatorKind::Drop { place, target, unwind } } => {
1048                 if let Some(local) = place.as_local() {
1049                     if local == SELF_ARG {
1050                         (target, unwind, source_info)
1051                     } else {
1052                         continue;
1053                     }
1054                 } else {
1055                     continue;
1056                 }
1057             }
1058             _ => continue,
1059         };
1060         let unwind = if block_data.is_cleanup {
1061             Unwind::InCleanup
1062         } else {
1063             Unwind::To(unwind.unwrap_or_else(|| elaborator.patch.resume_block()))
1064         };
1065         elaborate_drop(
1066             &mut elaborator,
1067             *source_info,
1068             Place::from(SELF_ARG),
1069             (),
1070             *target,
1071             unwind,
1072             block,
1073         );
1074     }
1075     elaborator.patch.apply(body);
1076 }
1077
1078 fn create_generator_drop_shim<'tcx>(
1079     tcx: TyCtxt<'tcx>,
1080     transform: &TransformVisitor<'tcx>,
1081     gen_ty: Ty<'tcx>,
1082     body: &mut Body<'tcx>,
1083     drop_clean: BasicBlock,
1084 ) -> Body<'tcx> {
1085     let mut body = body.clone();
1086     body.arg_count = 1; // make sure the resume argument is not included here
1087
1088     let source_info = SourceInfo::outermost(body.span);
1089
1090     let mut cases = create_cases(&mut body, transform, Operation::Drop);
1091
1092     cases.insert(0, (UNRESUMED, drop_clean));
1093
1094     // The returned state and the poisoned state fall through to the default
1095     // case which is just to return
1096
1097     insert_switch(&mut body, cases, &transform, TerminatorKind::Return);
1098
1099     for block in body.basic_blocks_mut() {
1100         let kind = &mut block.terminator_mut().kind;
1101         if let TerminatorKind::GeneratorDrop = *kind {
1102             *kind = TerminatorKind::Return;
1103         }
1104     }
1105
1106     // Replace the return variable
1107     body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(tcx.mk_unit(), source_info);
1108
1109     make_generator_state_argument_indirect(tcx, &mut body);
1110
1111     // Change the generator argument from &mut to *mut
1112     body.local_decls[SELF_ARG] = LocalDecl::with_source_info(
1113         tcx.mk_ptr(ty::TypeAndMut { ty: gen_ty, mutbl: hir::Mutability::Mut }),
1114         source_info,
1115     );
1116
1117     // Make sure we remove dead blocks to remove
1118     // unrelated code from the resume part of the function
1119     simplify::remove_dead_blocks(tcx, &mut body);
1120
1121     dump_mir(tcx, false, "generator_drop", &0, &body, |_, _| Ok(()));
1122
1123     body
1124 }
1125
1126 fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) -> BasicBlock {
1127     let source_info = SourceInfo::outermost(body.span);
1128     body.basic_blocks_mut().push(BasicBlockData {
1129         statements: Vec::new(),
1130         terminator: Some(Terminator { source_info, kind }),
1131         is_cleanup: false,
1132     })
1133 }
1134
1135 fn insert_panic_block<'tcx>(
1136     tcx: TyCtxt<'tcx>,
1137     body: &mut Body<'tcx>,
1138     message: AssertMessage<'tcx>,
1139 ) -> BasicBlock {
1140     let assert_block = BasicBlock::new(body.basic_blocks.len());
1141     let term = TerminatorKind::Assert {
1142         cond: Operand::Constant(Box::new(Constant {
1143             span: body.span,
1144             user_ty: None,
1145             literal: ConstantKind::from_bool(tcx, false),
1146         })),
1147         expected: true,
1148         msg: message,
1149         target: assert_block,
1150         cleanup: None,
1151     };
1152
1153     let source_info = SourceInfo::outermost(body.span);
1154     body.basic_blocks_mut().push(BasicBlockData {
1155         statements: Vec::new(),
1156         terminator: Some(Terminator { source_info, kind: term }),
1157         is_cleanup: false,
1158     });
1159
1160     assert_block
1161 }
1162
1163 fn can_return<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
1164     // Returning from a function with an uninhabited return type is undefined behavior.
1165     if body.return_ty().is_privately_uninhabited(tcx, param_env) {
1166         return false;
1167     }
1168
1169     // If there's a return terminator the function may return.
1170     for block in body.basic_blocks.iter() {
1171         if let TerminatorKind::Return = block.terminator().kind {
1172             return true;
1173         }
1174     }
1175
1176     // Otherwise the function can't return.
1177     false
1178 }
1179
1180 fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool {
1181     // Nothing can unwind when landing pads are off.
1182     if tcx.sess.panic_strategy() == PanicStrategy::Abort {
1183         return false;
1184     }
1185
1186     // Unwinds can only start at certain terminators.
1187     for block in body.basic_blocks.iter() {
1188         match block.terminator().kind {
1189             // These never unwind.
1190             TerminatorKind::Goto { .. }
1191             | TerminatorKind::SwitchInt { .. }
1192             | TerminatorKind::Abort
1193             | TerminatorKind::Return
1194             | TerminatorKind::Unreachable
1195             | TerminatorKind::GeneratorDrop
1196             | TerminatorKind::FalseEdge { .. }
1197             | TerminatorKind::FalseUnwind { .. } => {}
1198
1199             // Resume will *continue* unwinding, but if there's no other unwinding terminator it
1200             // will never be reached.
1201             TerminatorKind::Resume => {}
1202
1203             TerminatorKind::Yield { .. } => {
1204                 unreachable!("`can_unwind` called before generator transform")
1205             }
1206
1207             // These may unwind.
1208             TerminatorKind::Drop { .. }
1209             | TerminatorKind::DropAndReplace { .. }
1210             | TerminatorKind::Call { .. }
1211             | TerminatorKind::InlineAsm { .. }
1212             | TerminatorKind::Assert { .. } => return true,
1213         }
1214     }
1215
1216     // If we didn't find an unwinding terminator, the function cannot unwind.
1217     false
1218 }
1219
1220 fn create_generator_resume_function<'tcx>(
1221     tcx: TyCtxt<'tcx>,
1222     transform: TransformVisitor<'tcx>,
1223     body: &mut Body<'tcx>,
1224     can_return: bool,
1225 ) {
1226     let can_unwind = can_unwind(tcx, body);
1227
1228     // Poison the generator when it unwinds
1229     if can_unwind {
1230         let source_info = SourceInfo::outermost(body.span);
1231         let poison_block = body.basic_blocks_mut().push(BasicBlockData {
1232             statements: vec![transform.set_discr(VariantIdx::new(POISONED), source_info)],
1233             terminator: Some(Terminator { source_info, kind: TerminatorKind::Resume }),
1234             is_cleanup: true,
1235         });
1236
1237         for (idx, block) in body.basic_blocks_mut().iter_enumerated_mut() {
1238             let source_info = block.terminator().source_info;
1239
1240             if let TerminatorKind::Resume = block.terminator().kind {
1241                 // An existing `Resume` terminator is redirected to jump to our dedicated
1242                 // "poisoning block" above.
1243                 if idx != poison_block {
1244                     *block.terminator_mut() = Terminator {
1245                         source_info,
1246                         kind: TerminatorKind::Goto { target: poison_block },
1247                     };
1248                 }
1249             } else if !block.is_cleanup {
1250                 // Any terminators that *can* unwind but don't have an unwind target set are also
1251                 // pointed at our poisoning block (unless they're part of the cleanup path).
1252                 if let Some(unwind @ None) = block.terminator_mut().unwind_mut() {
1253                     *unwind = Some(poison_block);
1254                 }
1255             }
1256         }
1257     }
1258
1259     let mut cases = create_cases(body, &transform, Operation::Resume);
1260
1261     use rustc_middle::mir::AssertKind::{ResumedAfterPanic, ResumedAfterReturn};
1262
1263     // Jump to the entry point on the unresumed
1264     cases.insert(0, (UNRESUMED, BasicBlock::new(0)));
1265
1266     // Panic when resumed on the returned or poisoned state
1267     let generator_kind = body.generator_kind().unwrap();
1268
1269     if can_unwind {
1270         cases.insert(
1271             1,
1272             (POISONED, insert_panic_block(tcx, body, ResumedAfterPanic(generator_kind))),
1273         );
1274     }
1275
1276     if can_return {
1277         cases.insert(
1278             1,
1279             (RETURNED, insert_panic_block(tcx, body, ResumedAfterReturn(generator_kind))),
1280         );
1281     }
1282
1283     insert_switch(body, cases, &transform, TerminatorKind::Unreachable);
1284
1285     make_generator_state_argument_indirect(tcx, body);
1286     make_generator_state_argument_pinned(tcx, body);
1287
1288     // Make sure we remove dead blocks to remove
1289     // unrelated code from the drop part of the function
1290     simplify::remove_dead_blocks(tcx, body);
1291
1292     dump_mir(tcx, false, "generator_resume", &0, body, |_, _| Ok(()));
1293 }
1294
1295 fn insert_clean_drop(body: &mut Body<'_>) -> BasicBlock {
1296     let return_block = insert_term_block(body, TerminatorKind::Return);
1297
1298     let term =
1299         TerminatorKind::Drop { place: Place::from(SELF_ARG), target: return_block, unwind: None };
1300     let source_info = SourceInfo::outermost(body.span);
1301
1302     // Create a block to destroy an unresumed generators. This can only destroy upvars.
1303     body.basic_blocks_mut().push(BasicBlockData {
1304         statements: Vec::new(),
1305         terminator: Some(Terminator { source_info, kind: term }),
1306         is_cleanup: false,
1307     })
1308 }
1309
1310 /// An operation that can be performed on a generator.
1311 #[derive(PartialEq, Copy, Clone)]
1312 enum Operation {
1313     Resume,
1314     Drop,
1315 }
1316
1317 impl Operation {
1318     fn target_block(self, point: &SuspensionPoint<'_>) -> Option<BasicBlock> {
1319         match self {
1320             Operation::Resume => Some(point.resume),
1321             Operation::Drop => point.drop,
1322         }
1323     }
1324 }
1325
1326 fn create_cases<'tcx>(
1327     body: &mut Body<'tcx>,
1328     transform: &TransformVisitor<'tcx>,
1329     operation: Operation,
1330 ) -> Vec<(usize, BasicBlock)> {
1331     let source_info = SourceInfo::outermost(body.span);
1332
1333     transform
1334         .suspension_points
1335         .iter()
1336         .filter_map(|point| {
1337             // Find the target for this suspension point, if applicable
1338             operation.target_block(point).map(|target| {
1339                 let mut statements = Vec::new();
1340
1341                 // Create StorageLive instructions for locals with live storage
1342                 for i in 0..(body.local_decls.len()) {
1343                     if i == 2 {
1344                         // The resume argument is live on function entry. Don't insert a
1345                         // `StorageLive`, or the following `Assign` will read from uninitialized
1346                         // memory.
1347                         continue;
1348                     }
1349
1350                     let l = Local::new(i);
1351                     let needs_storage_live = point.storage_liveness.contains(l)
1352                         && !transform.remap.contains_key(&l)
1353                         && !transform.always_live_locals.contains(l);
1354                     if needs_storage_live {
1355                         statements
1356                             .push(Statement { source_info, kind: StatementKind::StorageLive(l) });
1357                     }
1358                 }
1359
1360                 if operation == Operation::Resume {
1361                     // Move the resume argument to the destination place of the `Yield` terminator
1362                     let resume_arg = Local::new(2); // 0 = return, 1 = self
1363                     statements.push(Statement {
1364                         source_info,
1365                         kind: StatementKind::Assign(Box::new((
1366                             point.resume_arg,
1367                             Rvalue::Use(Operand::Move(resume_arg.into())),
1368                         ))),
1369                     });
1370                 }
1371
1372                 // Then jump to the real target
1373                 let block = body.basic_blocks_mut().push(BasicBlockData {
1374                     statements,
1375                     terminator: Some(Terminator {
1376                         source_info,
1377                         kind: TerminatorKind::Goto { target },
1378                     }),
1379                     is_cleanup: false,
1380                 });
1381
1382                 (point.state, block)
1383             })
1384         })
1385         .collect()
1386 }
1387
1388 #[instrument(level = "debug", skip(tcx), ret)]
1389 pub(crate) fn mir_generator_witnesses<'tcx>(
1390     tcx: TyCtxt<'tcx>,
1391     def_id: DefId,
1392 ) -> GeneratorLayout<'tcx> {
1393     assert!(tcx.sess.opts.unstable_opts.drop_tracking_mir);
1394     let def_id = def_id.expect_local();
1395
1396     let (body, _) = tcx.mir_promoted(ty::WithOptConstParam::unknown(def_id));
1397     let body = body.borrow();
1398     let body = &*body;
1399
1400     // The first argument is the generator type passed by value
1401     let gen_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty;
1402
1403     // Get the interior types and substs which typeck computed
1404     let movable = match *gen_ty.kind() {
1405         ty::Generator(_, _, movability) => movability == hir::Movability::Movable,
1406         _ => span_bug!(body.span, "unexpected generator type {}", gen_ty),
1407     };
1408
1409     // When first entering the generator, move the resume argument into its new local.
1410     let always_live_locals = always_storage_live_locals(&body);
1411
1412     let liveness_info = locals_live_across_suspend_points(tcx, body, &always_live_locals, movable);
1413
1414     // Extract locals which are live across suspension point into `layout`
1415     // `remap` gives a mapping from local indices onto generator struct indices
1416     // `storage_liveness` tells us which locals have live storage at suspension points
1417     let (_, generator_layout, _) = compute_layout(tcx, liveness_info, body);
1418
1419     check_suspend_tys(tcx, &generator_layout, &body);
1420
1421     generator_layout
1422 }
1423
1424 impl<'tcx> MirPass<'tcx> for StateTransform {
1425     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
1426         let Some(yield_ty) = body.yield_ty() else {
1427             // This only applies to generators
1428             return;
1429         };
1430
1431         assert!(body.generator_drop().is_none());
1432
1433         // The first argument is the generator type passed by value
1434         let gen_ty = body.local_decls.raw[1].ty;
1435
1436         // Get the discriminant type and substs which typeck computed
1437         let (discr_ty, upvars, interior, movable) = match *gen_ty.kind() {
1438             ty::Generator(_, substs, movability) => {
1439                 let substs = substs.as_generator();
1440                 (
1441                     substs.discr_ty(tcx),
1442                     substs.upvar_tys().collect::<Vec<_>>(),
1443                     substs.witness(),
1444                     movability == hir::Movability::Movable,
1445                 )
1446             }
1447             _ => {
1448                 tcx.sess
1449                     .delay_span_bug(body.span, &format!("unexpected generator type {}", gen_ty));
1450                 return;
1451             }
1452         };
1453
1454         let is_async_kind = matches!(body.generator_kind(), Some(GeneratorKind::Async(_)));
1455         let (state_adt_ref, state_substs) = if is_async_kind {
1456             // Compute Poll<return_ty>
1457             let poll_did = tcx.require_lang_item(LangItem::Poll, None);
1458             let poll_adt_ref = tcx.adt_def(poll_did);
1459             let poll_substs = tcx.intern_substs(&[body.return_ty().into()]);
1460             (poll_adt_ref, poll_substs)
1461         } else {
1462             // Compute GeneratorState<yield_ty, return_ty>
1463             let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
1464             let state_adt_ref = tcx.adt_def(state_did);
1465             let state_substs = tcx.intern_substs(&[yield_ty.into(), body.return_ty().into()]);
1466             (state_adt_ref, state_substs)
1467         };
1468         let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
1469
1470         // We rename RETURN_PLACE which has type mir.return_ty to new_ret_local
1471         // RETURN_PLACE then is a fresh unused local with type ret_ty.
1472         let new_ret_local = replace_local(RETURN_PLACE, ret_ty, body, tcx);
1473
1474         // Replace all occurrences of `ResumeTy` with `&mut Context<'_>` within async bodies.
1475         if is_async_kind {
1476             transform_async_context(tcx, body);
1477         }
1478
1479         // We also replace the resume argument and insert an `Assign`.
1480         // This is needed because the resume argument `_2` might be live across a `yield`, in which
1481         // case there is no `Assign` to it that the transform can turn into a store to the generator
1482         // state. After the yield the slot in the generator state would then be uninitialized.
1483         let resume_local = Local::new(2);
1484         let resume_ty =
1485             if is_async_kind { tcx.mk_task_context() } else { body.local_decls[resume_local].ty };
1486         let new_resume_local = replace_local(resume_local, resume_ty, body, tcx);
1487
1488         // When first entering the generator, move the resume argument into its new local.
1489         let source_info = SourceInfo::outermost(body.span);
1490         let stmts = &mut body.basic_blocks_mut()[BasicBlock::new(0)].statements;
1491         stmts.insert(
1492             0,
1493             Statement {
1494                 source_info,
1495                 kind: StatementKind::Assign(Box::new((
1496                     new_resume_local.into(),
1497                     Rvalue::Use(Operand::Move(resume_local.into())),
1498                 ))),
1499             },
1500         );
1501
1502         let always_live_locals = always_storage_live_locals(&body);
1503
1504         let liveness_info =
1505             locals_live_across_suspend_points(tcx, body, &always_live_locals, movable);
1506
1507         if tcx.sess.opts.unstable_opts.validate_mir {
1508             let mut vis = EnsureGeneratorFieldAssignmentsNeverAlias {
1509                 assigned_local: None,
1510                 saved_locals: &liveness_info.saved_locals,
1511                 storage_conflicts: &liveness_info.storage_conflicts,
1512             };
1513
1514             vis.visit_body(body);
1515         }
1516
1517         // Extract locals which are live across suspension point into `layout`
1518         // `remap` gives a mapping from local indices onto generator struct indices
1519         // `storage_liveness` tells us which locals have live storage at suspension points
1520         let (remap, layout, storage_liveness) = compute_layout(tcx, liveness_info, body);
1521
1522         if tcx.sess.opts.unstable_opts.validate_mir
1523             && !tcx.sess.opts.unstable_opts.drop_tracking_mir
1524         {
1525             sanitize_witness(tcx, body, interior, upvars, &layout);
1526         }
1527
1528         let can_return = can_return(tcx, body, tcx.param_env(body.source.def_id()));
1529
1530         // Run the transformation which converts Places from Local to generator struct
1531         // accesses for locals in `remap`.
1532         // It also rewrites `return x` and `yield y` as writing a new generator state and returning
1533         // either GeneratorState::Complete(x) and GeneratorState::Yielded(y),
1534         // or Poll::Ready(x) and Poll::Pending respectively depending on `is_async_kind`.
1535         let mut transform = TransformVisitor {
1536             tcx,
1537             is_async_kind,
1538             state_adt_ref,
1539             state_substs,
1540             remap,
1541             storage_liveness,
1542             always_live_locals,
1543             suspension_points: Vec::new(),
1544             new_ret_local,
1545             discr_ty,
1546         };
1547         transform.visit_body(body);
1548
1549         // Update our MIR struct to reflect the changes we've made
1550         body.arg_count = 2; // self, resume arg
1551         body.spread_arg = None;
1552
1553         body.generator.as_mut().unwrap().yield_ty = None;
1554         body.generator.as_mut().unwrap().generator_layout = Some(layout);
1555
1556         // Insert `drop(generator_struct)` which is used to drop upvars for generators in
1557         // the unresumed state.
1558         // This is expanded to a drop ladder in `elaborate_generator_drops`.
1559         let drop_clean = insert_clean_drop(body);
1560
1561         dump_mir(tcx, false, "generator_pre-elab", &0, body, |_, _| Ok(()));
1562
1563         // Expand `drop(generator_struct)` to a drop ladder which destroys upvars.
1564         // If any upvars are moved out of, drop elaboration will handle upvar destruction.
1565         // However we need to also elaborate the code generated by `insert_clean_drop`.
1566         elaborate_generator_drops(tcx, body);
1567
1568         dump_mir(tcx, false, "generator_post-transform", &0, body, |_, _| Ok(()));
1569
1570         // Create a copy of our MIR and use it to create the drop shim for the generator
1571         let drop_shim = create_generator_drop_shim(tcx, &transform, gen_ty, body, drop_clean);
1572
1573         body.generator.as_mut().unwrap().generator_drop = Some(drop_shim);
1574
1575         // Create the Generator::resume / Future::poll function
1576         create_generator_resume_function(tcx, transform, body, can_return);
1577
1578         // Run derefer to fix Derefs that are not in the first place
1579         deref_finder(tcx, body);
1580     }
1581 }
1582
1583 /// Looks for any assignments between locals (e.g., `_4 = _5`) that will both be converted to fields
1584 /// in the generator state machine but whose storage is not marked as conflicting
1585 ///
1586 /// Validation needs to happen immediately *before* `TransformVisitor` is invoked, not after.
1587 ///
1588 /// This condition would arise when the assignment is the last use of `_5` but the initial
1589 /// definition of `_4` if we weren't extra careful to mark all locals used inside a statement as
1590 /// conflicting. Non-conflicting generator saved locals may be stored at the same location within
1591 /// the generator state machine, which would result in ill-formed MIR: the left-hand and right-hand
1592 /// sides of an assignment may not alias. This caused a miscompilation in [#73137].
1593 ///
1594 /// [#73137]: https://github.com/rust-lang/rust/issues/73137
1595 struct EnsureGeneratorFieldAssignmentsNeverAlias<'a> {
1596     saved_locals: &'a GeneratorSavedLocals,
1597     storage_conflicts: &'a BitMatrix<GeneratorSavedLocal, GeneratorSavedLocal>,
1598     assigned_local: Option<GeneratorSavedLocal>,
1599 }
1600
1601 impl EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
1602     fn saved_local_for_direct_place(&self, place: Place<'_>) -> Option<GeneratorSavedLocal> {
1603         if place.is_indirect() {
1604             return None;
1605         }
1606
1607         self.saved_locals.get(place.local)
1608     }
1609
1610     fn check_assigned_place(&mut self, place: Place<'_>, f: impl FnOnce(&mut Self)) {
1611         if let Some(assigned_local) = self.saved_local_for_direct_place(place) {
1612             assert!(self.assigned_local.is_none(), "`check_assigned_place` must not recurse");
1613
1614             self.assigned_local = Some(assigned_local);
1615             f(self);
1616             self.assigned_local = None;
1617         }
1618     }
1619 }
1620
1621 impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
1622     fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
1623         let Some(lhs) = self.assigned_local else {
1624             // This visitor only invokes `visit_place` for the right-hand side of an assignment
1625             // and only after setting `self.assigned_local`. However, the default impl of
1626             // `Visitor::super_body` may call `visit_place` with a `NonUseContext` for places
1627             // with debuginfo. Ignore them here.
1628             assert!(!context.is_use());
1629             return;
1630         };
1631
1632         let Some(rhs) = self.saved_local_for_direct_place(*place) else { return };
1633
1634         if !self.storage_conflicts.contains(lhs, rhs) {
1635             bug!(
1636                 "Assignment between generator saved locals whose storage is not \
1637                     marked as conflicting: {:?}: {:?} = {:?}",
1638                 location,
1639                 lhs,
1640                 rhs,
1641             );
1642         }
1643     }
1644
1645     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
1646         match &statement.kind {
1647             StatementKind::Assign(box (lhs, rhs)) => {
1648                 self.check_assigned_place(*lhs, |this| this.visit_rvalue(rhs, location));
1649             }
1650
1651             StatementKind::FakeRead(..)
1652             | StatementKind::SetDiscriminant { .. }
1653             | StatementKind::Deinit(..)
1654             | StatementKind::StorageLive(_)
1655             | StatementKind::StorageDead(_)
1656             | StatementKind::Retag(..)
1657             | StatementKind::AscribeUserType(..)
1658             | StatementKind::Coverage(..)
1659             | StatementKind::Intrinsic(..)
1660             | StatementKind::Nop => {}
1661         }
1662     }
1663
1664     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
1665         // Checking for aliasing in terminators is probably overkill, but until we have actual
1666         // semantics, we should be conservative here.
1667         match &terminator.kind {
1668             TerminatorKind::Call {
1669                 func,
1670                 args,
1671                 destination,
1672                 target: Some(_),
1673                 cleanup: _,
1674                 from_hir_call: _,
1675                 fn_span: _,
1676             } => {
1677                 self.check_assigned_place(*destination, |this| {
1678                     this.visit_operand(func, location);
1679                     for arg in args {
1680                         this.visit_operand(arg, location);
1681                     }
1682                 });
1683             }
1684
1685             TerminatorKind::Yield { value, resume: _, resume_arg, drop: _ } => {
1686                 self.check_assigned_place(*resume_arg, |this| this.visit_operand(value, location));
1687             }
1688
1689             // FIXME: Does `asm!` have any aliasing requirements?
1690             TerminatorKind::InlineAsm { .. } => {}
1691
1692             TerminatorKind::Call { .. }
1693             | TerminatorKind::Goto { .. }
1694             | TerminatorKind::SwitchInt { .. }
1695             | TerminatorKind::Resume
1696             | TerminatorKind::Abort
1697             | TerminatorKind::Return
1698             | TerminatorKind::Unreachable
1699             | TerminatorKind::Drop { .. }
1700             | TerminatorKind::DropAndReplace { .. }
1701             | TerminatorKind::Assert { .. }
1702             | TerminatorKind::GeneratorDrop
1703             | TerminatorKind::FalseEdge { .. }
1704             | TerminatorKind::FalseUnwind { .. } => {}
1705         }
1706     }
1707 }
1708
1709 fn check_suspend_tys<'tcx>(tcx: TyCtxt<'tcx>, layout: &GeneratorLayout<'tcx>, body: &Body<'tcx>) {
1710     let mut linted_tys = FxHashSet::default();
1711
1712     // We want a user-facing param-env.
1713     let param_env = tcx.param_env(body.source.def_id());
1714
1715     for (variant, yield_source_info) in
1716         layout.variant_fields.iter().zip(&layout.variant_source_info)
1717     {
1718         debug!(?variant);
1719         for &local in variant {
1720             let decl = &layout.field_tys[local];
1721             debug!(?decl);
1722
1723             if !decl.ignore_for_traits && linted_tys.insert(decl.ty) {
1724                 let Some(hir_id) = decl.source_info.scope.lint_root(&body.source_scopes) else { continue };
1725
1726                 check_must_not_suspend_ty(
1727                     tcx,
1728                     decl.ty,
1729                     hir_id,
1730                     param_env,
1731                     SuspendCheckData {
1732                         source_span: decl.source_info.span,
1733                         yield_span: yield_source_info.span,
1734                         plural_len: 1,
1735                         ..Default::default()
1736                     },
1737                 );
1738             }
1739         }
1740     }
1741 }
1742
1743 #[derive(Default)]
1744 struct SuspendCheckData<'a> {
1745     source_span: Span,
1746     yield_span: Span,
1747     descr_pre: &'a str,
1748     descr_post: &'a str,
1749     plural_len: usize,
1750 }
1751
1752 // Returns whether it emitted a diagnostic or not
1753 // Note that this fn and the proceeding one are based on the code
1754 // for creating must_use diagnostics
1755 //
1756 // Note that this technique was chosen over things like a `Suspend` marker trait
1757 // as it is simpler and has precedent in the compiler
1758 fn check_must_not_suspend_ty<'tcx>(
1759     tcx: TyCtxt<'tcx>,
1760     ty: Ty<'tcx>,
1761     hir_id: hir::HirId,
1762     param_env: ty::ParamEnv<'tcx>,
1763     data: SuspendCheckData<'_>,
1764 ) -> bool {
1765     if ty.is_unit() {
1766         return false;
1767     }
1768
1769     let plural_suffix = pluralize!(data.plural_len);
1770
1771     debug!("Checking must_not_suspend for {}", ty);
1772
1773     match *ty.kind() {
1774         ty::Adt(..) if ty.is_box() => {
1775             let boxed_ty = ty.boxed_ty();
1776             let descr_pre = &format!("{}boxed ", data.descr_pre);
1777             check_must_not_suspend_ty(
1778                 tcx,
1779                 boxed_ty,
1780                 hir_id,
1781                 param_env,
1782                 SuspendCheckData { descr_pre, ..data },
1783             )
1784         }
1785         ty::Adt(def, _) => check_must_not_suspend_def(tcx, def.did(), hir_id, data),
1786         // FIXME: support adding the attribute to TAITs
1787         ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
1788             let mut has_emitted = false;
1789             for &(predicate, _) in tcx.explicit_item_bounds(def) {
1790                 // We only look at the `DefId`, so it is safe to skip the binder here.
1791                 if let ty::PredicateKind::Clause(ty::Clause::Trait(ref poly_trait_predicate)) =
1792                     predicate.kind().skip_binder()
1793                 {
1794                     let def_id = poly_trait_predicate.trait_ref.def_id;
1795                     let descr_pre = &format!("{}implementer{} of ", data.descr_pre, plural_suffix);
1796                     if check_must_not_suspend_def(
1797                         tcx,
1798                         def_id,
1799                         hir_id,
1800                         SuspendCheckData { descr_pre, ..data },
1801                     ) {
1802                         has_emitted = true;
1803                         break;
1804                     }
1805                 }
1806             }
1807             has_emitted
1808         }
1809         ty::Dynamic(binder, _, _) => {
1810             let mut has_emitted = false;
1811             for predicate in binder.iter() {
1812                 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
1813                     let def_id = trait_ref.def_id;
1814                     let descr_post = &format!(" trait object{}{}", plural_suffix, data.descr_post);
1815                     if check_must_not_suspend_def(
1816                         tcx,
1817                         def_id,
1818                         hir_id,
1819                         SuspendCheckData { descr_post, ..data },
1820                     ) {
1821                         has_emitted = true;
1822                         break;
1823                     }
1824                 }
1825             }
1826             has_emitted
1827         }
1828         ty::Tuple(fields) => {
1829             let mut has_emitted = false;
1830             for (i, ty) in fields.iter().enumerate() {
1831                 let descr_post = &format!(" in tuple element {i}");
1832                 if check_must_not_suspend_ty(
1833                     tcx,
1834                     ty,
1835                     hir_id,
1836                     param_env,
1837                     SuspendCheckData { descr_post, ..data },
1838                 ) {
1839                     has_emitted = true;
1840                 }
1841             }
1842             has_emitted
1843         }
1844         ty::Array(ty, len) => {
1845             let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix);
1846             check_must_not_suspend_ty(
1847                 tcx,
1848                 ty,
1849                 hir_id,
1850                 param_env,
1851                 SuspendCheckData {
1852                     descr_pre,
1853                     plural_len: len.try_eval_usize(tcx, param_env).unwrap_or(0) as usize + 1,
1854                     ..data
1855                 },
1856             )
1857         }
1858         // If drop tracking is enabled, we want to look through references, since the referrent
1859         // may not be considered live across the await point.
1860         ty::Ref(_region, ty, _mutability) => {
1861             let descr_pre = &format!("{}reference{} to ", data.descr_pre, plural_suffix);
1862             check_must_not_suspend_ty(
1863                 tcx,
1864                 ty,
1865                 hir_id,
1866                 param_env,
1867                 SuspendCheckData { descr_pre, ..data },
1868             )
1869         }
1870         _ => false,
1871     }
1872 }
1873
1874 fn check_must_not_suspend_def(
1875     tcx: TyCtxt<'_>,
1876     def_id: DefId,
1877     hir_id: hir::HirId,
1878     data: SuspendCheckData<'_>,
1879 ) -> bool {
1880     if let Some(attr) = tcx.get_attr(def_id, sym::must_not_suspend) {
1881         let msg = format!(
1882             "{}`{}`{} held across a suspend point, but should not be",
1883             data.descr_pre,
1884             tcx.def_path_str(def_id),
1885             data.descr_post,
1886         );
1887         tcx.struct_span_lint_hir(
1888             rustc_session::lint::builtin::MUST_NOT_SUSPEND,
1889             hir_id,
1890             data.source_span,
1891             msg,
1892             |lint| {
1893                 // add span pointing to the offending yield/await
1894                 lint.span_label(data.yield_span, "the value is held across this suspend point");
1895
1896                 // Add optional reason note
1897                 if let Some(note) = attr.value_str() {
1898                     // FIXME(guswynn): consider formatting this better
1899                     lint.span_note(data.source_span, note.as_str());
1900                 }
1901
1902                 // Add some quick suggestions on what to do
1903                 // FIXME: can `drop` work as a suggestion here as well?
1904                 lint.span_help(
1905                     data.source_span,
1906                     "consider using a block (`{ ... }`) \
1907                     to shrink the value's scope, ending before the suspend point",
1908                 )
1909             },
1910         );
1911
1912         true
1913     } else {
1914         false
1915     }
1916 }