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