]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/transform/generator.rs
b5edcbe457b636f02f27c079785d45fe45688f02
[rust.git] / src / librustc_mir / transform / 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 the Generator::resume function and the drop shim
15 //! for the generator based on the MIR input. It converts the generator argument from Self to
16 //! &mut Self adding derefs in the MIR as needed. It computes the final layout of the generator
17 //! 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 //!
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) respectively.
36 //! MIR locals which are live across a suspension point are moved to the generator struct
37 //! with references to them being updated with references to the generator struct.
38 //!
39 //! The pass creates two functions which have a switch on the generator state giving
40 //! the action to take.
41 //!
42 //! One of them is the implementation of Generator::resume.
43 //! For generators with state 0 (unresumed) it starts the execution of the generator.
44 //! For generators with state 1 (returned) and state 2 (poisoned) it panics.
45 //! Otherwise it continues the execution from the last suspension point.
46 //!
47 //! The other function is the drop glue for the generator.
48 //! For generators with state 0 (unresumed) it drops the upvars of the generator.
49 //! For generators with state 1 (returned) and state 2 (poisoned) it does nothing.
50 //! Otherwise it drops all the values in scope at the last suspension point.
51
52 use crate::dataflow::{do_dataflow, DataflowResultsCursor, DebugFormatted};
53 use crate::dataflow::{DataflowResults, DataflowResultsConsumer, FlowAtLocation};
54 use crate::dataflow::{HaveBeenBorrowedLocals, MaybeStorageLive, RequiresStorage};
55 use crate::transform::no_landing_pads::no_landing_pads;
56 use crate::transform::simplify;
57 use crate::transform::{MirPass, MirSource};
58 use crate::util::dump_mir;
59 use crate::util::liveness;
60 use rustc::mir::visit::{MutVisitor, PlaceContext, Visitor};
61 use rustc::mir::*;
62 use rustc::ty::layout::VariantIdx;
63 use rustc::ty::subst::SubstsRef;
64 use rustc::ty::GeneratorSubsts;
65 use rustc::ty::{self, AdtDef, Ty, TyCtxt};
66 use rustc_data_structures::fx::FxHashMap;
67 use rustc_hir as hir;
68 use rustc_hir::def_id::DefId;
69 use rustc_index::bit_set::{BitMatrix, BitSet};
70 use rustc_index::vec::{Idx, IndexVec};
71 use std::borrow::Cow;
72 use std::iter;
73
74 pub struct StateTransform;
75
76 struct RenameLocalVisitor<'tcx> {
77     from: Local,
78     to: Local,
79     tcx: TyCtxt<'tcx>,
80 }
81
82 impl<'tcx> MutVisitor<'tcx> for RenameLocalVisitor<'tcx> {
83     fn tcx(&self) -> TyCtxt<'tcx> {
84         self.tcx
85     }
86
87     fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
88         if *local == self.from {
89             *local = self.to;
90         }
91     }
92
93     fn process_projection_elem(&mut self, elem: &PlaceElem<'tcx>) -> Option<PlaceElem<'tcx>> {
94         match elem {
95             PlaceElem::Index(local) if *local == self.from => Some(PlaceElem::Index(self.to)),
96             _ => None,
97         }
98     }
99 }
100
101 struct DerefArgVisitor<'tcx> {
102     tcx: TyCtxt<'tcx>,
103 }
104
105 impl<'tcx> MutVisitor<'tcx> for DerefArgVisitor<'tcx> {
106     fn tcx(&self) -> TyCtxt<'tcx> {
107         self.tcx
108     }
109
110     fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
111         assert_ne!(*local, self_arg());
112     }
113
114     fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
115         if place.local == self_arg() {
116             replace_base(
117                 place,
118                 Place {
119                     local: self_arg(),
120                     projection: self.tcx().intern_place_elems(&[ProjectionElem::Deref]),
121                 },
122                 self.tcx,
123             );
124         } else {
125             self.visit_place_base(&mut place.local, context, location);
126
127             for elem in place.projection.iter() {
128                 if let PlaceElem::Index(local) = elem {
129                     assert_ne!(*local, self_arg());
130                 }
131             }
132         }
133     }
134 }
135
136 struct PinArgVisitor<'tcx> {
137     ref_gen_ty: Ty<'tcx>,
138     tcx: TyCtxt<'tcx>,
139 }
140
141 impl<'tcx> MutVisitor<'tcx> for PinArgVisitor<'tcx> {
142     fn tcx(&self) -> TyCtxt<'tcx> {
143         self.tcx
144     }
145
146     fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
147         assert_ne!(*local, self_arg());
148     }
149
150     fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
151         if place.local == self_arg() {
152             replace_base(
153                 place,
154                 Place {
155                     local: self_arg(),
156                     projection: self.tcx().intern_place_elems(&[ProjectionElem::Field(
157                         Field::new(0),
158                         self.ref_gen_ty,
159                     )]),
160                 },
161                 self.tcx,
162             );
163         } else {
164             self.visit_place_base(&mut place.local, context, location);
165
166             for elem in place.projection.iter() {
167                 if let PlaceElem::Index(local) = elem {
168                     assert_ne!(*local, self_arg());
169                 }
170             }
171         }
172     }
173 }
174
175 fn replace_base<'tcx>(place: &mut Place<'tcx>, new_base: Place<'tcx>, tcx: TyCtxt<'tcx>) {
176     place.local = new_base.local;
177
178     let mut new_projection = new_base.projection.to_vec();
179     new_projection.append(&mut place.projection.to_vec());
180
181     place.projection = tcx.intern_place_elems(&new_projection);
182 }
183
184 fn self_arg() -> Local {
185     Local::new(1)
186 }
187
188 /// Generator have not been resumed yet
189 const UNRESUMED: usize = GeneratorSubsts::UNRESUMED;
190 /// Generator has returned / is completed
191 const RETURNED: usize = GeneratorSubsts::RETURNED;
192 /// Generator has been poisoned
193 const POISONED: usize = GeneratorSubsts::POISONED;
194
195 struct SuspensionPoint<'tcx> {
196     state: usize,
197     resume: BasicBlock,
198     resume_arg: Place<'tcx>,
199     drop: Option<BasicBlock>,
200     storage_liveness: liveness::LiveVarSet,
201 }
202
203 struct TransformVisitor<'tcx> {
204     tcx: TyCtxt<'tcx>,
205     state_adt_ref: &'tcx AdtDef,
206     state_substs: SubstsRef<'tcx>,
207
208     // The type of the discriminant in the generator struct
209     discr_ty: Ty<'tcx>,
210
211     // Mapping from Local to (type of local, generator struct index)
212     // FIXME(eddyb) This should use `IndexVec<Local, Option<_>>`.
213     remap: FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
214
215     // A map from a suspension point in a block to the locals which have live storage at that point
216     // FIXME(eddyb) This should use `IndexVec<BasicBlock, Option<_>>`.
217     storage_liveness: FxHashMap<BasicBlock, liveness::LiveVarSet>,
218
219     // A list of suspension points, generated during the transform
220     suspension_points: Vec<SuspensionPoint<'tcx>>,
221
222     // The original RETURN_PLACE local
223     new_ret_local: Local,
224 }
225
226 impl TransformVisitor<'tcx> {
227     // Make a GeneratorState rvalue
228     fn make_state(&self, idx: VariantIdx, val: Operand<'tcx>) -> Rvalue<'tcx> {
229         let adt = AggregateKind::Adt(self.state_adt_ref, idx, self.state_substs, None, None);
230         Rvalue::Aggregate(box adt, vec![val])
231     }
232
233     // Create a Place referencing a generator struct field
234     fn make_field(&self, variant_index: VariantIdx, idx: usize, ty: Ty<'tcx>) -> Place<'tcx> {
235         let self_place = Place::from(self_arg());
236         let base = self.tcx.mk_place_downcast_unnamed(self_place, variant_index);
237         let mut projection = base.projection.to_vec();
238         projection.push(ProjectionElem::Field(Field::new(idx), ty));
239
240         Place { local: base.local, projection: self.tcx.intern_place_elems(&projection) }
241     }
242
243     // Create a statement which changes the discriminant
244     fn set_discr(&self, state_disc: VariantIdx, source_info: SourceInfo) -> Statement<'tcx> {
245         let self_place = Place::from(self_arg());
246         Statement {
247             source_info,
248             kind: StatementKind::SetDiscriminant {
249                 place: box self_place,
250                 variant_index: state_disc,
251             },
252         }
253     }
254
255     // Create a statement which reads the discriminant into a temporary
256     fn get_discr(&self, body: &mut Body<'tcx>) -> (Statement<'tcx>, Place<'tcx>) {
257         let temp_decl = LocalDecl::new_internal(self.tcx.types.isize, body.span);
258         let local_decls_len = body.local_decls.push(temp_decl);
259         let temp = Place::from(local_decls_len);
260
261         let self_place = Place::from(self_arg());
262         let assign = Statement {
263             source_info: source_info(body),
264             kind: StatementKind::Assign(box (temp, Rvalue::Discriminant(self_place))),
265         };
266         (assign, temp)
267     }
268 }
269
270 impl MutVisitor<'tcx> for TransformVisitor<'tcx> {
271     fn tcx(&self) -> TyCtxt<'tcx> {
272         self.tcx
273     }
274
275     fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
276         assert_eq!(self.remap.get(local), None);
277     }
278
279     fn visit_place(
280         &mut self,
281         place: &mut Place<'tcx>,
282         _context: PlaceContext,
283         _location: Location,
284     ) {
285         // Replace an Local in the remap with a generator struct access
286         if let Some(&(ty, variant_index, idx)) = self.remap.get(&place.local) {
287             replace_base(place, self.make_field(variant_index, idx, ty), self.tcx);
288         }
289     }
290
291     fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
292         // Remove StorageLive and StorageDead statements for remapped locals
293         data.retain_statements(|s| match s.kind {
294             StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => {
295                 !self.remap.contains_key(&l)
296             }
297             _ => true,
298         });
299
300         let ret_val = match data.terminator().kind {
301             TerminatorKind::Return => Some((
302                 VariantIdx::new(1),
303                 None,
304                 Operand::Move(Place::from(self.new_ret_local)),
305                 None,
306             )),
307             TerminatorKind::Yield { ref value, resume, resume_arg, drop } => {
308                 Some((VariantIdx::new(0), Some((resume, resume_arg)), value.clone(), drop))
309             }
310             _ => None,
311         };
312
313         if let Some((state_idx, resume, v, drop)) = ret_val {
314             let source_info = data.terminator().source_info;
315             // We must assign the value first in case it gets declared dead below
316             data.statements.push(Statement {
317                 source_info,
318                 kind: StatementKind::Assign(box (
319                     Place::return_place(),
320                     self.make_state(state_idx, v),
321                 )),
322             });
323             let state = if let Some((resume, resume_arg)) = resume {
324                 // Yield
325                 let state = 3 + self.suspension_points.len();
326
327                 self.suspension_points.push(SuspensionPoint {
328                     state,
329                     resume,
330                     resume_arg,
331                     drop,
332                     storage_liveness: self.storage_liveness.get(&block).unwrap().clone(),
333                 });
334
335                 VariantIdx::new(state)
336             } else {
337                 // Return
338                 VariantIdx::new(RETURNED) // state for returned
339             };
340             data.statements.push(self.set_discr(state, source_info));
341             data.terminator_mut().kind = TerminatorKind::Return;
342         }
343
344         self.super_basic_block_data(block, data);
345     }
346 }
347
348 fn make_generator_state_argument_indirect<'tcx>(
349     tcx: TyCtxt<'tcx>,
350     def_id: DefId,
351     body: &mut BodyAndCache<'tcx>,
352 ) {
353     let gen_ty = body.local_decls.raw[1].ty;
354
355     let region = ty::ReFree(ty::FreeRegion { scope: def_id, bound_region: ty::BoundRegion::BrEnv });
356
357     let region = tcx.mk_region(region);
358
359     let ref_gen_ty = tcx.mk_ref(region, ty::TypeAndMut { ty: gen_ty, mutbl: hir::Mutability::Mut });
360
361     // Replace the by value generator argument
362     body.local_decls.raw[1].ty = ref_gen_ty;
363
364     // Add a deref to accesses of the generator state
365     DerefArgVisitor { tcx }.visit_body(body);
366 }
367
368 fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut BodyAndCache<'tcx>) {
369     let ref_gen_ty = body.local_decls.raw[1].ty;
370
371     let pin_did = tcx.lang_items().pin_type().unwrap();
372     let pin_adt_ref = tcx.adt_def(pin_did);
373     let substs = tcx.intern_substs(&[ref_gen_ty.into()]);
374     let pin_ref_gen_ty = tcx.mk_adt(pin_adt_ref, substs);
375
376     // Replace the by ref generator argument
377     body.local_decls.raw[1].ty = pin_ref_gen_ty;
378
379     // Add the Pin field access to accesses of the generator state
380     PinArgVisitor { ref_gen_ty, tcx }.visit_body(body);
381 }
382
383 fn replace_result_variable<'tcx>(
384     ret_ty: Ty<'tcx>,
385     body: &mut BodyAndCache<'tcx>,
386     tcx: TyCtxt<'tcx>,
387 ) -> Local {
388     let source_info = source_info(body);
389     let new_ret = LocalDecl {
390         mutability: Mutability::Mut,
391         ty: ret_ty,
392         user_ty: UserTypeProjections::none(),
393         source_info,
394         internal: false,
395         is_block_tail: None,
396         local_info: LocalInfo::Other,
397     };
398     let new_ret_local = Local::new(body.local_decls.len());
399     body.local_decls.push(new_ret);
400     body.local_decls.swap(RETURN_PLACE, new_ret_local);
401
402     RenameLocalVisitor { from: RETURN_PLACE, to: new_ret_local, tcx }.visit_body(body);
403
404     new_ret_local
405 }
406
407 struct StorageIgnored(liveness::LiveVarSet);
408
409 impl<'tcx> Visitor<'tcx> for StorageIgnored {
410     fn visit_statement(&mut self, statement: &Statement<'tcx>, _location: Location) {
411         match statement.kind {
412             StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => {
413                 self.0.remove(l);
414             }
415             _ => (),
416         }
417     }
418 }
419
420 struct LivenessInfo {
421     /// Which locals are live across any suspension point.
422     ///
423     /// GeneratorSavedLocal is indexed in terms of the elements in this set;
424     /// i.e. GeneratorSavedLocal::new(1) corresponds to the second local
425     /// included in this set.
426     live_locals: liveness::LiveVarSet,
427
428     /// The set of saved locals live at each suspension point.
429     live_locals_at_suspension_points: Vec<BitSet<GeneratorSavedLocal>>,
430
431     /// For every saved local, the set of other saved locals that are
432     /// storage-live at the same time as this local. We cannot overlap locals in
433     /// the layout which have conflicting storage.
434     storage_conflicts: BitMatrix<GeneratorSavedLocal, GeneratorSavedLocal>,
435
436     /// For every suspending block, the locals which are storage-live across
437     /// that suspension point.
438     storage_liveness: FxHashMap<BasicBlock, liveness::LiveVarSet>,
439 }
440
441 fn locals_live_across_suspend_points(
442     tcx: TyCtxt<'tcx>,
443     body: ReadOnlyBodyAndCache<'_, 'tcx>,
444     source: MirSource<'tcx>,
445     movable: bool,
446 ) -> LivenessInfo {
447     let dead_unwinds = BitSet::new_empty(body.basic_blocks().len());
448     let def_id = source.def_id();
449     let body_ref: &Body<'_> = &body;
450
451     // Calculate when MIR locals have live storage. This gives us an upper bound of their
452     // lifetimes.
453     let storage_live_analysis = MaybeStorageLive::new(body_ref);
454     let storage_live_results =
455         do_dataflow(tcx, body_ref, def_id, &[], &dead_unwinds, storage_live_analysis, |bd, p| {
456             DebugFormatted::new(&bd.body().local_decls[p])
457         });
458     let mut storage_live_cursor = DataflowResultsCursor::new(&storage_live_results, body_ref);
459
460     // Find the MIR locals which do not use StorageLive/StorageDead statements.
461     // The storage of these locals are always live.
462     let mut ignored = StorageIgnored(BitSet::new_filled(body.local_decls.len()));
463     ignored.visit_body(body);
464
465     // Calculate the MIR locals which have been previously
466     // borrowed (even if they are still active).
467     let borrowed_locals_analysis = HaveBeenBorrowedLocals::new(body_ref);
468     let borrowed_locals_results = do_dataflow(
469         tcx,
470         body_ref,
471         def_id,
472         &[],
473         &dead_unwinds,
474         borrowed_locals_analysis,
475         |bd, p| DebugFormatted::new(&bd.body().local_decls[p]),
476     );
477     let mut borrowed_locals_cursor = DataflowResultsCursor::new(&borrowed_locals_results, body_ref);
478
479     // Calculate the MIR locals that we actually need to keep storage around
480     // for.
481     let requires_storage_analysis = RequiresStorage::new(body, &borrowed_locals_results);
482     let requires_storage_results = do_dataflow(
483         tcx,
484         body_ref,
485         def_id,
486         &[],
487         &dead_unwinds,
488         requires_storage_analysis,
489         |bd, p| DebugFormatted::new(&bd.body().local_decls[p]),
490     );
491     let mut requires_storage_cursor =
492         DataflowResultsCursor::new(&requires_storage_results, body_ref);
493
494     // Calculate the liveness of MIR locals ignoring borrows.
495     let mut live_locals = liveness::LiveVarSet::new_empty(body.local_decls.len());
496     let mut liveness = liveness::liveness_of_locals(body);
497     liveness::dump_mir(tcx, "generator_liveness", source, body_ref, &liveness);
498
499     let mut storage_liveness_map = FxHashMap::default();
500     let mut live_locals_at_suspension_points = Vec::new();
501
502     for (block, data) in body.basic_blocks().iter_enumerated() {
503         if let TerminatorKind::Yield { .. } = data.terminator().kind {
504             let loc = Location { block: block, statement_index: data.statements.len() };
505
506             if !movable {
507                 // The `liveness` variable contains the liveness of MIR locals ignoring borrows.
508                 // This is correct for movable generators since borrows cannot live across
509                 // suspension points. However for immovable generators we need to account for
510                 // borrows, so we conseratively assume that all borrowed locals are live until
511                 // we find a StorageDead statement referencing the locals.
512                 // To do this we just union our `liveness` result with `borrowed_locals`, which
513                 // contains all the locals which has been borrowed before this suspension point.
514                 // If a borrow is converted to a raw reference, we must also assume that it lives
515                 // forever. Note that the final liveness is still bounded by the storage liveness
516                 // of the local, which happens using the `intersect` operation below.
517                 borrowed_locals_cursor.seek(loc);
518                 liveness.outs[block].union(borrowed_locals_cursor.get());
519             }
520
521             storage_live_cursor.seek(loc);
522             let storage_liveness = storage_live_cursor.get();
523
524             // Store the storage liveness for later use so we can restore the state
525             // after a suspension point
526             storage_liveness_map.insert(block, storage_liveness.clone());
527
528             requires_storage_cursor.seek(loc);
529             let storage_required = requires_storage_cursor.get().clone();
530
531             // Locals live are live at this point only if they are used across
532             // suspension points (the `liveness` variable)
533             // and their storage is required (the `storage_required` variable)
534             let mut live_locals_here = storage_required;
535             live_locals_here.intersect(&liveness.outs[block]);
536
537             // The generator argument is ignored
538             live_locals_here.remove(self_arg());
539
540             debug!("loc = {:?}, live_locals_here = {:?}", loc, live_locals_here);
541
542             // Add the locals live at this suspension point to the set of locals which live across
543             // any suspension points
544             live_locals.union(&live_locals_here);
545
546             live_locals_at_suspension_points.push(live_locals_here);
547         }
548     }
549     debug!("live_locals = {:?}", live_locals);
550
551     // Renumber our liveness_map bitsets to include only the locals we are
552     // saving.
553     let live_locals_at_suspension_points = live_locals_at_suspension_points
554         .iter()
555         .map(|live_here| renumber_bitset(&live_here, &live_locals))
556         .collect();
557
558     let storage_conflicts =
559         compute_storage_conflicts(body_ref, &live_locals, &ignored, requires_storage_results);
560
561     LivenessInfo {
562         live_locals,
563         live_locals_at_suspension_points,
564         storage_conflicts,
565         storage_liveness: storage_liveness_map,
566     }
567 }
568
569 /// Renumbers the items present in `stored_locals` and applies the renumbering
570 /// to 'input`.
571 ///
572 /// For example, if `stored_locals = [1, 3, 5]`, this would be renumbered to
573 /// `[0, 1, 2]`. Thus, if `input = [3, 5]` we would return `[1, 2]`.
574 fn renumber_bitset(
575     input: &BitSet<Local>,
576     stored_locals: &liveness::LiveVarSet,
577 ) -> BitSet<GeneratorSavedLocal> {
578     assert!(stored_locals.superset(&input), "{:?} not a superset of {:?}", stored_locals, input);
579     let mut out = BitSet::new_empty(stored_locals.count());
580     for (idx, local) in stored_locals.iter().enumerate() {
581         let saved_local = GeneratorSavedLocal::from(idx);
582         if input.contains(local) {
583             out.insert(saved_local);
584         }
585     }
586     debug!("renumber_bitset({:?}, {:?}) => {:?}", input, stored_locals, out);
587     out
588 }
589
590 /// For every saved local, looks for which locals are StorageLive at the same
591 /// time. Generates a bitset for every local of all the other locals that may be
592 /// StorageLive simultaneously with that local. This is used in the layout
593 /// computation; see `GeneratorLayout` for more.
594 fn compute_storage_conflicts(
595     body: &'mir Body<'tcx>,
596     stored_locals: &liveness::LiveVarSet,
597     ignored: &StorageIgnored,
598     requires_storage: DataflowResults<'tcx, RequiresStorage<'mir, 'tcx>>,
599 ) -> BitMatrix<GeneratorSavedLocal, GeneratorSavedLocal> {
600     assert_eq!(body.local_decls.len(), ignored.0.domain_size());
601     assert_eq!(body.local_decls.len(), stored_locals.domain_size());
602     debug!("compute_storage_conflicts({:?})", body.span);
603     debug!("ignored = {:?}", ignored.0);
604
605     // Storage ignored locals are not eligible for overlap, since their storage
606     // is always live.
607     let mut ineligible_locals = ignored.0.clone();
608     ineligible_locals.intersect(&stored_locals);
609
610     // Compute the storage conflicts for all eligible locals.
611     let mut visitor = StorageConflictVisitor {
612         body,
613         stored_locals: &stored_locals,
614         local_conflicts: BitMatrix::from_row_n(&ineligible_locals, body.local_decls.len()),
615     };
616     let mut state = FlowAtLocation::new(requires_storage);
617     visitor.analyze_results(&mut state);
618     let local_conflicts = visitor.local_conflicts;
619
620     // Compress the matrix using only stored locals (Local -> GeneratorSavedLocal).
621     //
622     // NOTE: Today we store a full conflict bitset for every local. Technically
623     // this is twice as many bits as we need, since the relation is symmetric.
624     // However, in practice these bitsets are not usually large. The layout code
625     // also needs to keep track of how many conflicts each local has, so it's
626     // simpler to keep it this way for now.
627     let mut storage_conflicts = BitMatrix::new(stored_locals.count(), stored_locals.count());
628     for (idx_a, local_a) in stored_locals.iter().enumerate() {
629         let saved_local_a = GeneratorSavedLocal::new(idx_a);
630         if ineligible_locals.contains(local_a) {
631             // Conflicts with everything.
632             storage_conflicts.insert_all_into_row(saved_local_a);
633         } else {
634             // Keep overlap information only for stored locals.
635             for (idx_b, local_b) in stored_locals.iter().enumerate() {
636                 let saved_local_b = GeneratorSavedLocal::new(idx_b);
637                 if local_conflicts.contains(local_a, local_b) {
638                     storage_conflicts.insert(saved_local_a, saved_local_b);
639                 }
640             }
641         }
642     }
643     storage_conflicts
644 }
645
646 struct StorageConflictVisitor<'body, 'tcx, 's> {
647     body: &'body Body<'tcx>,
648     stored_locals: &'s liveness::LiveVarSet,
649     // FIXME(tmandry): Consider using sparse bitsets here once we have good
650     // benchmarks for generators.
651     local_conflicts: BitMatrix<Local, Local>,
652 }
653
654 impl<'body, 'tcx, 's> DataflowResultsConsumer<'body, 'tcx>
655     for StorageConflictVisitor<'body, 'tcx, 's>
656 {
657     type FlowState = FlowAtLocation<'tcx, RequiresStorage<'body, 'tcx>>;
658
659     fn body(&self) -> &'body Body<'tcx> {
660         self.body
661     }
662
663     fn visit_block_entry(&mut self, block: BasicBlock, flow_state: &Self::FlowState) {
664         // statement_index is only used for logging, so this is fine.
665         self.apply_state(flow_state, Location { block, statement_index: 0 });
666     }
667
668     fn visit_statement_entry(
669         &mut self,
670         loc: Location,
671         _stmt: &Statement<'tcx>,
672         flow_state: &Self::FlowState,
673     ) {
674         self.apply_state(flow_state, loc);
675     }
676
677     fn visit_terminator_entry(
678         &mut self,
679         loc: Location,
680         _term: &Terminator<'tcx>,
681         flow_state: &Self::FlowState,
682     ) {
683         self.apply_state(flow_state, loc);
684     }
685 }
686
687 impl<'body, 'tcx, 's> StorageConflictVisitor<'body, 'tcx, 's> {
688     fn apply_state(
689         &mut self,
690         flow_state: &FlowAtLocation<'tcx, RequiresStorage<'body, 'tcx>>,
691         loc: Location,
692     ) {
693         // Ignore unreachable blocks.
694         match self.body.basic_blocks()[loc.block].terminator().kind {
695             TerminatorKind::Unreachable => return,
696             _ => (),
697         };
698
699         let mut eligible_storage_live = flow_state.as_dense().clone();
700         eligible_storage_live.intersect(&self.stored_locals);
701
702         for local in eligible_storage_live.iter() {
703             self.local_conflicts.union_row_with(&eligible_storage_live, local);
704         }
705
706         if eligible_storage_live.count() > 1 {
707             trace!("at {:?}, eligible_storage_live={:?}", loc, eligible_storage_live);
708         }
709     }
710 }
711
712 fn compute_layout<'tcx>(
713     tcx: TyCtxt<'tcx>,
714     source: MirSource<'tcx>,
715     upvars: &Vec<Ty<'tcx>>,
716     interior: Ty<'tcx>,
717     movable: bool,
718     body: &mut BodyAndCache<'tcx>,
719 ) -> (
720     FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
721     GeneratorLayout<'tcx>,
722     FxHashMap<BasicBlock, liveness::LiveVarSet>,
723 ) {
724     // Use a liveness analysis to compute locals which are live across a suspension point
725     let LivenessInfo {
726         live_locals,
727         live_locals_at_suspension_points,
728         storage_conflicts,
729         storage_liveness,
730     } = locals_live_across_suspend_points(tcx, read_only!(body), source, movable);
731
732     // Erase regions from the types passed in from typeck so we can compare them with
733     // MIR types
734     let allowed_upvars = tcx.erase_regions(upvars);
735     let allowed = match interior.kind {
736         ty::GeneratorWitness(s) => tcx.erase_late_bound_regions(&s),
737         _ => bug!(),
738     };
739
740     for (local, decl) in body.local_decls.iter_enumerated() {
741         // Ignore locals which are internal or not live
742         if !live_locals.contains(local) || decl.internal {
743             continue;
744         }
745
746         // Sanity check that typeck knows about the type of locals which are
747         // live across a suspension point
748         if !allowed.contains(&decl.ty) && !allowed_upvars.contains(&decl.ty) {
749             span_bug!(
750                 body.span,
751                 "Broken MIR: generator contains type {} in MIR, \
752                        but typeck only knows about {}",
753                 decl.ty,
754                 interior
755             );
756         }
757     }
758
759     // Gather live local types and their indices.
760     let mut locals = IndexVec::<GeneratorSavedLocal, _>::new();
761     let mut tys = IndexVec::<GeneratorSavedLocal, _>::new();
762     for (idx, local) in live_locals.iter().enumerate() {
763         locals.push(local);
764         tys.push(body.local_decls[local].ty);
765         debug!("generator saved local {:?} => {:?}", GeneratorSavedLocal::from(idx), local);
766     }
767
768     // Leave empty variants for the UNRESUMED, RETURNED, and POISONED states.
769     const RESERVED_VARIANTS: usize = 3;
770
771     // Build the generator variant field list.
772     // Create a map from local indices to generator struct indices.
773     let mut variant_fields: IndexVec<VariantIdx, IndexVec<Field, GeneratorSavedLocal>> =
774         iter::repeat(IndexVec::new()).take(RESERVED_VARIANTS).collect();
775     let mut remap = FxHashMap::default();
776     for (suspension_point_idx, live_locals) in live_locals_at_suspension_points.iter().enumerate() {
777         let variant_index = VariantIdx::from(RESERVED_VARIANTS + suspension_point_idx);
778         let mut fields = IndexVec::new();
779         for (idx, saved_local) in live_locals.iter().enumerate() {
780             fields.push(saved_local);
781             // Note that if a field is included in multiple variants, we will
782             // just use the first one here. That's fine; fields do not move
783             // around inside generators, so it doesn't matter which variant
784             // index we access them by.
785             remap.entry(locals[saved_local]).or_insert((tys[saved_local], variant_index, idx));
786         }
787         variant_fields.push(fields);
788     }
789     debug!("generator variant_fields = {:?}", variant_fields);
790     debug!("generator storage_conflicts = {:#?}", storage_conflicts);
791
792     let layout = GeneratorLayout { field_tys: tys, variant_fields, storage_conflicts };
793
794     (remap, layout, storage_liveness)
795 }
796
797 fn insert_switch<'tcx>(
798     body: &mut BodyAndCache<'tcx>,
799     cases: Vec<(usize, BasicBlock)>,
800     transform: &TransformVisitor<'tcx>,
801     default: TerminatorKind<'tcx>,
802 ) {
803     let default_block = insert_term_block(body, default);
804     let (assign, discr) = transform.get_discr(body);
805     let switch = TerminatorKind::SwitchInt {
806         discr: Operand::Move(discr),
807         switch_ty: transform.discr_ty,
808         values: Cow::from(cases.iter().map(|&(i, _)| i as u128).collect::<Vec<_>>()),
809         targets: cases.iter().map(|&(_, d)| d).chain(iter::once(default_block)).collect(),
810     };
811
812     let source_info = source_info(body);
813     body.basic_blocks_mut().raw.insert(
814         0,
815         BasicBlockData {
816             statements: vec![assign],
817             terminator: Some(Terminator { source_info, kind: switch }),
818             is_cleanup: false,
819         },
820     );
821
822     let blocks = body.basic_blocks_mut().iter_mut();
823
824     for target in blocks.flat_map(|b| b.terminator_mut().successors_mut()) {
825         *target = BasicBlock::new(target.index() + 1);
826     }
827 }
828
829 fn elaborate_generator_drops<'tcx>(
830     tcx: TyCtxt<'tcx>,
831     def_id: DefId,
832     body: &mut BodyAndCache<'tcx>,
833 ) {
834     use crate::shim::DropShimElaborator;
835     use crate::util::elaborate_drops::{elaborate_drop, Unwind};
836     use crate::util::patch::MirPatch;
837
838     // Note that `elaborate_drops` only drops the upvars of a generator, and
839     // this is ok because `open_drop` can only be reached within that own
840     // generator's resume function.
841
842     let param_env = tcx.param_env(def_id);
843     let gen = self_arg();
844
845     let mut elaborator = DropShimElaborator { body, patch: MirPatch::new(body), tcx, param_env };
846
847     for (block, block_data) in body.basic_blocks().iter_enumerated() {
848         let (target, unwind, source_info) = match block_data.terminator() {
849             Terminator { source_info, kind: TerminatorKind::Drop { location, target, unwind } } => {
850                 if let Some(local) = location.as_local() {
851                     if local == gen {
852                         (target, unwind, source_info)
853                     } else {
854                         continue;
855                     }
856                 } else {
857                     continue;
858                 }
859             }
860             _ => continue,
861         };
862         let unwind = if block_data.is_cleanup {
863             Unwind::InCleanup
864         } else {
865             Unwind::To(unwind.unwrap_or_else(|| elaborator.patch.resume_block()))
866         };
867         elaborate_drop(
868             &mut elaborator,
869             *source_info,
870             &Place::from(gen),
871             (),
872             *target,
873             unwind,
874             block,
875         );
876     }
877     elaborator.patch.apply(body);
878 }
879
880 fn create_generator_drop_shim<'tcx>(
881     tcx: TyCtxt<'tcx>,
882     transform: &TransformVisitor<'tcx>,
883     def_id: DefId,
884     source: MirSource<'tcx>,
885     gen_ty: Ty<'tcx>,
886     body: &mut BodyAndCache<'tcx>,
887     drop_clean: BasicBlock,
888 ) -> BodyAndCache<'tcx> {
889     let mut body = body.clone();
890     body.arg_count = 1; // make sure the resume argument is not included here
891
892     let source_info = source_info(&body);
893
894     let mut cases = create_cases(&mut body, transform, Operation::Drop);
895
896     cases.insert(0, (UNRESUMED, drop_clean));
897
898     // The returned state and the poisoned state fall through to the default
899     // case which is just to return
900
901     insert_switch(&mut body, cases, &transform, TerminatorKind::Return);
902
903     for block in body.basic_blocks_mut() {
904         let kind = &mut block.terminator_mut().kind;
905         if let TerminatorKind::GeneratorDrop = *kind {
906             *kind = TerminatorKind::Return;
907         }
908     }
909
910     // Replace the return variable
911     body.local_decls[RETURN_PLACE] = LocalDecl {
912         mutability: Mutability::Mut,
913         ty: tcx.mk_unit(),
914         user_ty: UserTypeProjections::none(),
915         source_info,
916         internal: false,
917         is_block_tail: None,
918         local_info: LocalInfo::Other,
919     };
920
921     make_generator_state_argument_indirect(tcx, def_id, &mut body);
922
923     // Change the generator argument from &mut to *mut
924     body.local_decls[self_arg()] = LocalDecl {
925         mutability: Mutability::Mut,
926         ty: tcx.mk_ptr(ty::TypeAndMut { ty: gen_ty, mutbl: hir::Mutability::Mut }),
927         user_ty: UserTypeProjections::none(),
928         source_info,
929         internal: false,
930         is_block_tail: None,
931         local_info: LocalInfo::Other,
932     };
933     if tcx.sess.opts.debugging_opts.mir_emit_retag {
934         // Alias tracking must know we changed the type
935         body.basic_blocks_mut()[START_BLOCK].statements.insert(
936             0,
937             Statement {
938                 source_info,
939                 kind: StatementKind::Retag(RetagKind::Raw, box Place::from(self_arg())),
940             },
941         )
942     }
943
944     no_landing_pads(tcx, &mut body);
945
946     // Make sure we remove dead blocks to remove
947     // unrelated code from the resume part of the function
948     simplify::remove_dead_blocks(&mut body);
949
950     dump_mir(tcx, None, "generator_drop", &0, source, &mut body, |_, _| Ok(()));
951
952     body
953 }
954
955 fn insert_term_block<'tcx>(
956     body: &mut BodyAndCache<'tcx>,
957     kind: TerminatorKind<'tcx>,
958 ) -> BasicBlock {
959     let term_block = BasicBlock::new(body.basic_blocks().len());
960     let source_info = source_info(body);
961     body.basic_blocks_mut().push(BasicBlockData {
962         statements: Vec::new(),
963         terminator: Some(Terminator { source_info, kind }),
964         is_cleanup: false,
965     });
966     term_block
967 }
968
969 fn insert_panic_block<'tcx>(
970     tcx: TyCtxt<'tcx>,
971     body: &mut BodyAndCache<'tcx>,
972     message: AssertMessage<'tcx>,
973 ) -> BasicBlock {
974     let assert_block = BasicBlock::new(body.basic_blocks().len());
975     let term = TerminatorKind::Assert {
976         cond: Operand::Constant(box Constant {
977             span: body.span,
978             user_ty: None,
979             literal: ty::Const::from_bool(tcx, false),
980         }),
981         expected: true,
982         msg: message,
983         target: assert_block,
984         cleanup: None,
985     };
986
987     let source_info = source_info(body);
988     body.basic_blocks_mut().push(BasicBlockData {
989         statements: Vec::new(),
990         terminator: Some(Terminator { source_info, kind: term }),
991         is_cleanup: false,
992     });
993
994     assert_block
995 }
996
997 fn create_generator_resume_function<'tcx>(
998     tcx: TyCtxt<'tcx>,
999     transform: TransformVisitor<'tcx>,
1000     def_id: DefId,
1001     source: MirSource<'tcx>,
1002     body: &mut BodyAndCache<'tcx>,
1003 ) {
1004     // Poison the generator when it unwinds
1005     for block in body.basic_blocks_mut() {
1006         let source_info = block.terminator().source_info;
1007         if let &TerminatorKind::Resume = &block.terminator().kind {
1008             block.statements.push(transform.set_discr(VariantIdx::new(POISONED), source_info));
1009         }
1010     }
1011
1012     let mut cases = create_cases(body, &transform, Operation::Resume);
1013
1014     use rustc::mir::interpret::PanicInfo::{ResumedAfterPanic, ResumedAfterReturn};
1015
1016     // Jump to the entry point on the unresumed
1017     cases.insert(0, (UNRESUMED, BasicBlock::new(0)));
1018
1019     // Panic when resumed on the returned or poisoned state
1020     let generator_kind = body.generator_kind.unwrap();
1021     cases.insert(1, (RETURNED, insert_panic_block(tcx, body, ResumedAfterReturn(generator_kind))));
1022     cases.insert(2, (POISONED, insert_panic_block(tcx, body, ResumedAfterPanic(generator_kind))));
1023
1024     insert_switch(body, cases, &transform, TerminatorKind::Unreachable);
1025
1026     make_generator_state_argument_indirect(tcx, def_id, body);
1027     make_generator_state_argument_pinned(tcx, body);
1028
1029     no_landing_pads(tcx, body);
1030
1031     // Make sure we remove dead blocks to remove
1032     // unrelated code from the drop part of the function
1033     simplify::remove_dead_blocks(body);
1034
1035     dump_mir(tcx, None, "generator_resume", &0, source, body, |_, _| Ok(()));
1036 }
1037
1038 fn source_info(body: &Body<'_>) -> SourceInfo {
1039     SourceInfo { span: body.span, scope: OUTERMOST_SOURCE_SCOPE }
1040 }
1041
1042 fn insert_clean_drop(body: &mut BodyAndCache<'_>) -> BasicBlock {
1043     let return_block = insert_term_block(body, TerminatorKind::Return);
1044
1045     // Create a block to destroy an unresumed generators. This can only destroy upvars.
1046     let drop_clean = BasicBlock::new(body.basic_blocks().len());
1047     let term = TerminatorKind::Drop {
1048         location: Place::from(self_arg()),
1049         target: return_block,
1050         unwind: None,
1051     };
1052     let source_info = source_info(body);
1053     body.basic_blocks_mut().push(BasicBlockData {
1054         statements: Vec::new(),
1055         terminator: Some(Terminator { source_info, kind: term }),
1056         is_cleanup: false,
1057     });
1058
1059     drop_clean
1060 }
1061
1062 /// An operation that can be performed on a generator.
1063 #[derive(PartialEq, Copy, Clone)]
1064 enum Operation {
1065     Resume,
1066     Drop,
1067 }
1068
1069 impl Operation {
1070     fn target_block(self, point: &SuspensionPoint<'_>) -> Option<BasicBlock> {
1071         match self {
1072             Operation::Resume => Some(point.resume),
1073             Operation::Drop => point.drop,
1074         }
1075     }
1076 }
1077
1078 fn create_cases<'tcx>(
1079     body: &mut BodyAndCache<'tcx>,
1080     transform: &TransformVisitor<'tcx>,
1081     operation: Operation,
1082 ) -> Vec<(usize, BasicBlock)> {
1083     let source_info = source_info(body);
1084
1085     transform
1086         .suspension_points
1087         .iter()
1088         .filter_map(|point| {
1089             // Find the target for this suspension point, if applicable
1090             operation.target_block(point).map(|target| {
1091                 let block = BasicBlock::new(body.basic_blocks().len());
1092                 let mut statements = Vec::new();
1093
1094                 // Create StorageLive instructions for locals with live storage
1095                 for i in 0..(body.local_decls.len()) {
1096                     let l = Local::new(i);
1097                     if point.storage_liveness.contains(l) && !transform.remap.contains_key(&l) {
1098                         statements
1099                             .push(Statement { source_info, kind: StatementKind::StorageLive(l) });
1100                     }
1101                 }
1102
1103                 if operation == Operation::Resume {
1104                     // Move the resume argument to the destination place of the `Yield` terminator
1105                     let resume_arg = Local::new(2); // 0 = return, 1 = self
1106                     statements.push(Statement {
1107                         source_info,
1108                         kind: StatementKind::Assign(box (
1109                             point.resume_arg,
1110                             Rvalue::Use(Operand::Move(resume_arg.into())),
1111                         )),
1112                     });
1113                 }
1114
1115                 // Then jump to the real target
1116                 body.basic_blocks_mut().push(BasicBlockData {
1117                     statements,
1118                     terminator: Some(Terminator {
1119                         source_info,
1120                         kind: TerminatorKind::Goto { target },
1121                     }),
1122                     is_cleanup: false,
1123                 });
1124
1125                 (point.state, block)
1126             })
1127         })
1128         .collect()
1129 }
1130
1131 impl<'tcx> MirPass<'tcx> for StateTransform {
1132     fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyAndCache<'tcx>) {
1133         let yield_ty = if let Some(yield_ty) = body.yield_ty {
1134             yield_ty
1135         } else {
1136             // This only applies to generators
1137             return;
1138         };
1139
1140         assert!(body.generator_drop.is_none());
1141
1142         let def_id = source.def_id();
1143
1144         // The first argument is the generator type passed by value
1145         let gen_ty = body.local_decls.raw[1].ty;
1146
1147         // Get the interior types and substs which typeck computed
1148         let (upvars, interior, discr_ty, movable) = match gen_ty.kind {
1149             ty::Generator(_, substs, movability) => {
1150                 let substs = substs.as_generator();
1151                 (
1152                     substs.upvar_tys(def_id, tcx).collect(),
1153                     substs.witness(def_id, tcx),
1154                     substs.discr_ty(tcx),
1155                     movability == hir::Movability::Movable,
1156                 )
1157             }
1158             _ => bug!(),
1159         };
1160
1161         // Compute GeneratorState<yield_ty, return_ty>
1162         let state_did = tcx.lang_items().gen_state().unwrap();
1163         let state_adt_ref = tcx.adt_def(state_did);
1164         let state_substs = tcx.intern_substs(&[yield_ty.into(), body.return_ty().into()]);
1165         let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
1166
1167         // We rename RETURN_PLACE which has type mir.return_ty to new_ret_local
1168         // RETURN_PLACE then is a fresh unused local with type ret_ty.
1169         let new_ret_local = replace_result_variable(ret_ty, body, tcx);
1170
1171         // Extract locals which are live across suspension point into `layout`
1172         // `remap` gives a mapping from local indices onto generator struct indices
1173         // `storage_liveness` tells us which locals have live storage at suspension points
1174         let (remap, layout, storage_liveness) =
1175             compute_layout(tcx, source, &upvars, interior, movable, body);
1176
1177         // Run the transformation which converts Places from Local to generator struct
1178         // accesses for locals in `remap`.
1179         // It also rewrites `return x` and `yield y` as writing a new generator state and returning
1180         // GeneratorState::Complete(x) and GeneratorState::Yielded(y) respectively.
1181         let mut transform = TransformVisitor {
1182             tcx,
1183             state_adt_ref,
1184             state_substs,
1185             remap,
1186             storage_liveness,
1187             suspension_points: Vec::new(),
1188             new_ret_local,
1189             discr_ty,
1190         };
1191         transform.visit_body(body);
1192
1193         // Update our MIR struct to reflect the changes we've made
1194         body.yield_ty = None;
1195         body.arg_count = 2; // self, resume arg
1196         body.spread_arg = None;
1197         body.generator_layout = Some(layout);
1198
1199         // Insert `drop(generator_struct)` which is used to drop upvars for generators in
1200         // the unresumed state.
1201         // This is expanded to a drop ladder in `elaborate_generator_drops`.
1202         let drop_clean = insert_clean_drop(body);
1203
1204         dump_mir(tcx, None, "generator_pre-elab", &0, source, body, |_, _| Ok(()));
1205
1206         // Expand `drop(generator_struct)` to a drop ladder which destroys upvars.
1207         // If any upvars are moved out of, drop elaboration will handle upvar destruction.
1208         // However we need to also elaborate the code generated by `insert_clean_drop`.
1209         elaborate_generator_drops(tcx, def_id, body);
1210
1211         dump_mir(tcx, None, "generator_post-transform", &0, source, body, |_, _| Ok(()));
1212
1213         // Create a copy of our MIR and use it to create the drop shim for the generator
1214         let drop_shim =
1215             create_generator_drop_shim(tcx, &transform, def_id, source, gen_ty, body, drop_clean);
1216
1217         body.generator_drop = Some(box drop_shim);
1218
1219         // Create the Generator::resume function
1220         create_generator_resume_function(tcx, transform, def_id, source, body);
1221     }
1222 }