From b789017403beb28c7f73cdd994e7fbd982a311f0 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Tue, 14 May 2019 17:43:37 -0700 Subject: [PATCH 1/4] Emit StorageDead for all locals in generators --- src/librustc_mir/build/mod.rs | 11 +- src/librustc_mir/build/scope.rs | 114 ++++++++++++++------- src/test/mir-opt/generator-drop-cleanup.rs | 1 + 3 files changed, 85 insertions(+), 41 deletions(-) diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 16ab233bd2e36..e46c515355521 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -343,6 +343,7 @@ struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { fn_span: Span, arg_count: usize, + is_generator: bool, /// The current set of scopes, updated as we traverse; /// see the `scope` module for more details. @@ -689,7 +690,8 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>, return_ty, return_ty_span, upvar_debuginfo, - upvar_mutbls); + upvar_mutbls, + body.is_generator); let call_site_scope = region::Scope { id: body.value.hir_id.local_id, @@ -759,6 +761,7 @@ fn construct_const<'a, 'gcx, 'tcx>( const_ty_span, vec![], vec![], + false, ); let mut block = START_BLOCK; @@ -788,7 +791,7 @@ fn construct_error<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>, let owner_id = hir.tcx().hir().body_owner(body_id); let span = hir.tcx().hir().span(owner_id); let ty = hir.tcx().types.err; - let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty, span, vec![], vec![]); + let mut builder = Builder::new(hir, span, 0, Safety::Safe, ty, span, vec![], vec![], false); let source_info = builder.source_info(span); builder.cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable); builder.finish(None) @@ -802,7 +805,8 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { return_ty: Ty<'tcx>, return_span: Span, __upvar_debuginfo_codegen_only_do_not_use: Vec, - upvar_mutbls: Vec) + upvar_mutbls: Vec, + is_generator: bool) -> Builder<'a, 'gcx, 'tcx> { let lint_level = LintLevel::Explicit(hir.root_lint_level); let mut builder = Builder { @@ -810,6 +814,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { cfg: CFG { basic_blocks: IndexVec::new() }, fn_span: span, arg_count, + is_generator, scopes: vec![], block_context: BlockContext::new(), source_scopes: IndexVec::new(), diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 4aa463b37ab77..3b11e335fb87f 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -83,9 +83,10 @@ use rustc::middle::region; use rustc::ty::Ty; use rustc::hir; use rustc::mir::*; -use syntax_pos::{Span}; +use syntax_pos::{Span, DUMMY_SP}; use rustc_data_structures::fx::FxHashMap; use std::collections::hash_map::Entry; +use std::mem; #[derive(Debug)] pub struct Scope<'tcx> { @@ -107,6 +108,8 @@ pub struct Scope<'tcx> { /// * polluting the cleanup MIR with StorageDead creates /// landing pads even though there's no actual destructors /// * freeing up stack space has no effect during unwinding + /// Note that for generators we do emit StorageDeads, for the + /// use of optimizations in the MIR generator transform. needs_cleanup: bool, /// set of places to drop when exiting this scope. This starts @@ -466,10 +469,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// This path terminates in GeneratorDrop. Returns the start of the path. /// None indicates there’s no cleanup to do at this point. pub fn generator_drop_cleanup(&mut self) -> Option { - if !self.scopes.iter().any(|scope| scope.needs_cleanup) { - return None; - } - // Fill in the cache for unwinds self.diverge_cleanup_gen(true); @@ -480,7 +479,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let result = block; while let Some(scope) = scopes.next() { - if !scope.needs_cleanup { + if !scope.needs_cleanup && !self.is_generator { continue; } @@ -802,7 +801,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { for scope in self.scopes[first_uncached..].iter_mut() { target = build_diverge_scope(&mut self.cfg, scope.region_scope_span, - scope, target, generator_drop); + scope, target, generator_drop, self.is_generator); } target @@ -900,12 +899,6 @@ fn build_scope_drops<'tcx>( // drops panic (panicking while unwinding will abort, so there's no need for // another set of arrows). The drops for the unwind path should have already // been generated by `diverge_cleanup_gen`. - // - // The code in this function reads from right to left. - // Storage dead drops have to be done left to right (since we can only push - // to the end of a Vec). So, we find the next drop and then call - // push_storage_deads which will iterate backwards through them so that - // they are added in the correct order. let mut unwind_blocks = scope.drops.iter().rev().filter_map(|drop_data| { if let DropKind::Value { cached_block } = drop_data.kind { @@ -936,11 +929,6 @@ fn build_scope_drops<'tcx>( block = next; } DropKind::Storage => { - // We do not need to emit StorageDead for generator drops - if generator_drop { - continue - } - // Drop the storage for both value and storage drops. // Only temps and vars need their storage dead. match drop_data.location { @@ -962,7 +950,8 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>, span: Span, scope: &mut Scope<'tcx>, mut target: BasicBlock, - generator_drop: bool) + generator_drop: bool, + is_generator: bool) -> BasicBlock { // Build up the drops in **reverse** order. The end result will @@ -981,41 +970,90 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>, scope: source_scope }; - // Next, build up the drops. Here we iterate the vector in + // We keep track of StorageDead statements to prepend to our current block + // and store them here, in reverse order. + let mut storage_deads = vec![]; + + let mut target_built_by_us = false; + + // Build up the drops. Here we iterate the vector in // *forward* order, so that we generate drops[0] first (right to // left in diagram above). for (j, drop_data) in scope.drops.iter_mut().enumerate() { debug!("build_diverge_scope drop_data[{}]: {:?}", j, drop_data); // Only full value drops are emitted in the diverging path, - // not StorageDead. + // not StorageDead, except in the case of generators. // // Note: This may not actually be what we desire (are we // "freeing" stack storage as we unwind, or merely observing a // frozen stack)? In particular, the intent may have been to // match the behavior of clang, but on inspection eddyb says // this is not what clang does. - let cached_block = match drop_data.kind { - DropKind::Value { ref mut cached_block } => cached_block.ref_mut(generator_drop), - DropKind::Storage => continue - }; - target = if let Some(cached_block) = *cached_block { - cached_block - } else { - let block = cfg.start_new_cleanup_block(); - cfg.terminate(block, source_info(drop_data.span), - TerminatorKind::Drop { - location: drop_data.location.clone(), - target, - unwind: None - }); - *cached_block = Some(block); - block + match drop_data.kind { + DropKind::Storage if is_generator => { + // Only temps and vars need their storage dead. + match drop_data.location { + Place::Base(PlaceBase::Local(index)) => { + storage_deads.push(Statement { + source_info: source_info(drop_data.span), + kind: StatementKind::StorageDead(index) + }); + } + _ => unreachable!(), + }; + } + DropKind::Storage => {} + DropKind::Value { ref mut cached_block } => { + let cached_block = cached_block.ref_mut(generator_drop); + target = if let Some(cached_block) = *cached_block { + storage_deads.clear(); + target_built_by_us = false; + cached_block + } else { + push_storage_deads( + cfg, &mut target, &mut storage_deads, target_built_by_us, source_scope); + let block = cfg.start_new_cleanup_block(); + cfg.terminate(block, source_info(drop_data.span), + TerminatorKind::Drop { + location: drop_data.location.clone(), + target, + unwind: None + }); + *cached_block = Some(block); + target_built_by_us = true; + block + }; + } }; } - + push_storage_deads(cfg, &mut target, &mut storage_deads, target_built_by_us, source_scope); *scope.cached_unwind.ref_mut(generator_drop) = Some(target); + assert!(storage_deads.is_empty()); debug!("build_diverge_scope({:?}, {:?}) = {:?}", scope, span, target); target } + +fn push_storage_deads(cfg: &mut CFG<'tcx>, + target: &mut BasicBlock, + storage_deads: &mut Vec>, + target_built_by_us: bool, + source_scope: SourceScope) { + if storage_deads.is_empty() { return; } + if !target_built_by_us { + // We cannot add statements to an existing block, so we create a new + // block for our StorageDead statements. + let block = cfg.start_new_cleanup_block(); + let source_info = SourceInfo { span: DUMMY_SP, scope: source_scope }; + cfg.terminate(block, source_info, TerminatorKind::Goto { target: *target }); + *target = block; + } + let statements = &mut cfg.block_data_mut(*target).statements; + storage_deads.reverse(); + debug!("push_storage_deads({:?}), storage_deads={:?}, statements={:?}", + *target, storage_deads, statements); + storage_deads.append(statements); + mem::swap(statements, storage_deads); + assert!(storage_deads.is_empty()); +} diff --git a/src/test/mir-opt/generator-drop-cleanup.rs b/src/test/mir-opt/generator-drop-cleanup.rs index 9cc4272fafabf..30f6d0deb9187 100644 --- a/src/test/mir-opt/generator-drop-cleanup.rs +++ b/src/test/mir-opt/generator-drop-cleanup.rs @@ -17,6 +17,7 @@ fn main() { // switchInt(move _5) -> [0u32: bb4, 3u32: bb7, otherwise: bb8]; // } // bb1: { +// StorageDead(_3); // goto -> bb5; // } // bb2: { From dd2eabc49d415dd30cea0953df5d7659d4d9440f Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Tue, 14 May 2019 14:08:31 -0700 Subject: [PATCH 2/4] Make MaybeStorageLive drop-aware --- src/librustc_mir/dataflow/impls/storage_liveness.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/librustc_mir/dataflow/impls/storage_liveness.rs b/src/librustc_mir/dataflow/impls/storage_liveness.rs index 6b8eb6f17f6c1..3bf11c57379c2 100644 --- a/src/librustc_mir/dataflow/impls/storage_liveness.rs +++ b/src/librustc_mir/dataflow/impls/storage_liveness.rs @@ -43,9 +43,14 @@ impl<'a, 'tcx> BitDenotation<'tcx> for MaybeStorageLive<'a, 'tcx> { } fn terminator_effect(&self, - _sets: &mut BlockSets<'_, Local>, - _loc: Location) { - // Terminators have no effect + sets: &mut BlockSets<'_, Local>, + loc: Location) { + match &self.mir[loc.block].terminator().kind { + TerminatorKind::Drop { location, .. } => if let Some(l) = location.local() { + sets.kill(l); + } + _ => (), + } } fn propagate_call_return( From e2cde9119bf59a15973065a9b594c860be1520a5 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Tue, 14 May 2019 17:44:46 -0700 Subject: [PATCH 3/4] Remove separation between generator_drop and unwind paths --- src/librustc_mir/build/scope.rs | 81 ++++++++++++--------------------- 1 file changed, 28 insertions(+), 53 deletions(-) diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 3b11e335fb87f..e6cebd72f19db 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -124,7 +124,10 @@ pub struct Scope<'tcx> { /// The cache for drop chain on "generator drop" exit. cached_generator_drop: Option, - /// The cache for drop chain on "unwind" exit. + /// The cache for drop chain on "unwind" exit. This block + /// contains code to run the current drop and all the preceding + /// drops (i.e., those having lower index in Drop’s Scope drop + /// array) cached_unwind: CachedBlock, } @@ -141,21 +144,7 @@ struct DropData<'tcx> { } #[derive(Debug, Default, Clone, Copy)] -pub(crate) struct CachedBlock { - /// The cached block for the cleanups-on-diverge path. This block - /// contains code to run the current drop and all the preceding - /// drops (i.e., those having lower index in Drop’s Scope drop - /// array) - unwind: Option, - - /// The cached block for unwinds during cleanups-on-generator-drop path - /// - /// This is split from the standard unwind path here to prevent drop - /// elaboration from creating drop flags that would have to be captured - /// by the generator. I'm not sure how important this optimization is, - /// but it is here. - generator_drop: Option, -} +pub(crate) struct CachedBlock(Option); #[derive(Debug)] pub(crate) enum DropKind { @@ -181,24 +170,15 @@ pub struct BreakableScope<'tcx> { impl CachedBlock { fn invalidate(&mut self) { - self.generator_drop = None; - self.unwind = None; + self.0 = None; } - fn get(&self, generator_drop: bool) -> Option { - if generator_drop { - self.generator_drop - } else { - self.unwind - } + fn get(&self) -> Option { + self.0 } - fn ref_mut(&mut self, generator_drop: bool) -> &mut Option { - if generator_drop { - &mut self.generator_drop - } else { - &mut self.unwind - } + fn ref_mut(&mut self) -> &mut Option { + &mut self.0 } } @@ -378,7 +358,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { assert_eq!(scope.region_scope, region_scope.0); let unwind_to = self.scopes.last().and_then(|next_scope| { - next_scope.cached_unwind.get(false) + next_scope.cached_unwind.get() }).unwrap_or_else(|| self.resume_block()); unpack!(block = build_scope_drops( @@ -387,7 +367,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { block, unwind_to, self.arg_count, - false, )); block.unit() @@ -442,7 +421,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } }; - let unwind_to = next_scope.cached_unwind.get(false).unwrap_or_else(|| { + let unwind_to = next_scope.cached_unwind.get().unwrap_or_else(|| { debug_assert!(!may_panic, "cached block not present?"); START_BLOCK }); @@ -453,7 +432,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { block, unwind_to, self.arg_count, - false, )); scope = next_scope; @@ -470,7 +448,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// None indicates there’s no cleanup to do at this point. pub fn generator_drop_cleanup(&mut self) -> Option { // Fill in the cache for unwinds - self.diverge_cleanup_gen(true); + self.diverge_cleanup_gen(); let src_info = self.scopes[0].source_info(self.fn_span); let resume_block = self.resume_block(); @@ -496,7 +474,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }; let unwind_to = scopes.peek().as_ref().map(|scope| { - scope.cached_unwind.get(true).unwrap_or_else(|| { + scope.cached_unwind.get().unwrap_or_else(|| { span_bug!(src_info.span, "cached block not present?") }) }).unwrap_or(resume_block); @@ -507,7 +485,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { block, unwind_to, self.arg_count, - true, )); } @@ -760,7 +737,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// This path terminates in Resume. Returns the start of the path. /// See module comment for more details. pub fn diverge_cleanup(&mut self) -> BasicBlock { - self.diverge_cleanup_gen(false) + self.diverge_cleanup_gen() } fn resume_block(&mut self) -> BasicBlock { @@ -779,7 +756,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } - fn diverge_cleanup_gen(&mut self, generator_drop: bool) -> BasicBlock { + fn diverge_cleanup_gen(&mut self) -> BasicBlock { // Build up the drops in **reverse** order. The end result will // look like: // @@ -793,15 +770,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Find the last cached block let (mut target, first_uncached) = if let Some(cached_index) = self.scopes.iter() - .rposition(|scope| scope.cached_unwind.get(generator_drop).is_some()) { - (self.scopes[cached_index].cached_unwind.get(generator_drop).unwrap(), cached_index + 1) + .rposition(|scope| scope.cached_unwind.get().is_some()) { + (self.scopes[cached_index].cached_unwind.get().unwrap(), cached_index + 1) } else { (self.resume_block(), 0) }; for scope in self.scopes[first_uncached..].iter_mut() { target = build_diverge_scope(&mut self.cfg, scope.region_scope_span, - scope, target, generator_drop, self.is_generator); + scope, target, self.is_generator); } target @@ -881,7 +858,6 @@ fn build_scope_drops<'tcx>( mut block: BasicBlock, last_unwind_to: BasicBlock, arg_count: usize, - generator_drop: bool, ) -> BlockAnd<()> { debug!("build_scope_drops({:?} -> {:?}", block, scope); @@ -902,7 +878,7 @@ fn build_scope_drops<'tcx>( let mut unwind_blocks = scope.drops.iter().rev().filter_map(|drop_data| { if let DropKind::Value { cached_block } = drop_data.kind { - Some(cached_block.get(generator_drop).unwrap_or_else(|| { + Some(cached_block.get().unwrap_or_else(|| { span_bug!(drop_data.span, "cached block not present?") })) } else { @@ -946,13 +922,12 @@ fn build_scope_drops<'tcx>( block.unit() } -fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>, - span: Span, - scope: &mut Scope<'tcx>, - mut target: BasicBlock, - generator_drop: bool, - is_generator: bool) - -> BasicBlock +fn build_diverge_scope(cfg: &mut CFG<'tcx>, + span: Span, + scope: &mut Scope<'tcx>, + mut target: BasicBlock, + is_generator: bool) + -> BasicBlock { // Build up the drops in **reverse** order. The end result will // look like: @@ -1004,7 +979,7 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>, } DropKind::Storage => {} DropKind::Value { ref mut cached_block } => { - let cached_block = cached_block.ref_mut(generator_drop); + let cached_block = cached_block.ref_mut(); target = if let Some(cached_block) = *cached_block { storage_deads.clear(); target_built_by_us = false; @@ -1027,7 +1002,7 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>, }; } push_storage_deads(cfg, &mut target, &mut storage_deads, target_built_by_us, source_scope); - *scope.cached_unwind.ref_mut(generator_drop) = Some(target); + *scope.cached_unwind.ref_mut() = Some(target); assert!(storage_deads.is_empty()); debug!("build_diverge_scope({:?}, {:?}) = {:?}", scope, span, target); From 26c37d7b168781d8be0236fdcdcb502b6c2b8ee2 Mon Sep 17 00:00:00 2001 From: Tyler Mandry Date: Tue, 14 May 2019 18:33:04 -0700 Subject: [PATCH 4/4] Revert "Remove separation between generator_drop and unwind paths" This reverts commit 26a7228f0fd5929f2134ac36180eb1e8ff5e16e3. --- src/librustc_mir/build/scope.rs | 81 +++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index e6cebd72f19db..3b11e335fb87f 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -124,10 +124,7 @@ pub struct Scope<'tcx> { /// The cache for drop chain on "generator drop" exit. cached_generator_drop: Option, - /// The cache for drop chain on "unwind" exit. This block - /// contains code to run the current drop and all the preceding - /// drops (i.e., those having lower index in Drop’s Scope drop - /// array) + /// The cache for drop chain on "unwind" exit. cached_unwind: CachedBlock, } @@ -144,7 +141,21 @@ struct DropData<'tcx> { } #[derive(Debug, Default, Clone, Copy)] -pub(crate) struct CachedBlock(Option); +pub(crate) struct CachedBlock { + /// The cached block for the cleanups-on-diverge path. This block + /// contains code to run the current drop and all the preceding + /// drops (i.e., those having lower index in Drop’s Scope drop + /// array) + unwind: Option, + + /// The cached block for unwinds during cleanups-on-generator-drop path + /// + /// This is split from the standard unwind path here to prevent drop + /// elaboration from creating drop flags that would have to be captured + /// by the generator. I'm not sure how important this optimization is, + /// but it is here. + generator_drop: Option, +} #[derive(Debug)] pub(crate) enum DropKind { @@ -170,15 +181,24 @@ pub struct BreakableScope<'tcx> { impl CachedBlock { fn invalidate(&mut self) { - self.0 = None; + self.generator_drop = None; + self.unwind = None; } - fn get(&self) -> Option { - self.0 + fn get(&self, generator_drop: bool) -> Option { + if generator_drop { + self.generator_drop + } else { + self.unwind + } } - fn ref_mut(&mut self) -> &mut Option { - &mut self.0 + fn ref_mut(&mut self, generator_drop: bool) -> &mut Option { + if generator_drop { + &mut self.generator_drop + } else { + &mut self.unwind + } } } @@ -358,7 +378,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { assert_eq!(scope.region_scope, region_scope.0); let unwind_to = self.scopes.last().and_then(|next_scope| { - next_scope.cached_unwind.get() + next_scope.cached_unwind.get(false) }).unwrap_or_else(|| self.resume_block()); unpack!(block = build_scope_drops( @@ -367,6 +387,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { block, unwind_to, self.arg_count, + false, )); block.unit() @@ -421,7 +442,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } }; - let unwind_to = next_scope.cached_unwind.get().unwrap_or_else(|| { + let unwind_to = next_scope.cached_unwind.get(false).unwrap_or_else(|| { debug_assert!(!may_panic, "cached block not present?"); START_BLOCK }); @@ -432,6 +453,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { block, unwind_to, self.arg_count, + false, )); scope = next_scope; @@ -448,7 +470,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// None indicates there’s no cleanup to do at this point. pub fn generator_drop_cleanup(&mut self) -> Option { // Fill in the cache for unwinds - self.diverge_cleanup_gen(); + self.diverge_cleanup_gen(true); let src_info = self.scopes[0].source_info(self.fn_span); let resume_block = self.resume_block(); @@ -474,7 +496,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { }; let unwind_to = scopes.peek().as_ref().map(|scope| { - scope.cached_unwind.get().unwrap_or_else(|| { + scope.cached_unwind.get(true).unwrap_or_else(|| { span_bug!(src_info.span, "cached block not present?") }) }).unwrap_or(resume_block); @@ -485,6 +507,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { block, unwind_to, self.arg_count, + true, )); } @@ -737,7 +760,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /// This path terminates in Resume. Returns the start of the path. /// See module comment for more details. pub fn diverge_cleanup(&mut self) -> BasicBlock { - self.diverge_cleanup_gen() + self.diverge_cleanup_gen(false) } fn resume_block(&mut self) -> BasicBlock { @@ -756,7 +779,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } - fn diverge_cleanup_gen(&mut self) -> BasicBlock { + fn diverge_cleanup_gen(&mut self, generator_drop: bool) -> BasicBlock { // Build up the drops in **reverse** order. The end result will // look like: // @@ -770,15 +793,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Find the last cached block let (mut target, first_uncached) = if let Some(cached_index) = self.scopes.iter() - .rposition(|scope| scope.cached_unwind.get().is_some()) { - (self.scopes[cached_index].cached_unwind.get().unwrap(), cached_index + 1) + .rposition(|scope| scope.cached_unwind.get(generator_drop).is_some()) { + (self.scopes[cached_index].cached_unwind.get(generator_drop).unwrap(), cached_index + 1) } else { (self.resume_block(), 0) }; for scope in self.scopes[first_uncached..].iter_mut() { target = build_diverge_scope(&mut self.cfg, scope.region_scope_span, - scope, target, self.is_generator); + scope, target, generator_drop, self.is_generator); } target @@ -858,6 +881,7 @@ fn build_scope_drops<'tcx>( mut block: BasicBlock, last_unwind_to: BasicBlock, arg_count: usize, + generator_drop: bool, ) -> BlockAnd<()> { debug!("build_scope_drops({:?} -> {:?}", block, scope); @@ -878,7 +902,7 @@ fn build_scope_drops<'tcx>( let mut unwind_blocks = scope.drops.iter().rev().filter_map(|drop_data| { if let DropKind::Value { cached_block } = drop_data.kind { - Some(cached_block.get().unwrap_or_else(|| { + Some(cached_block.get(generator_drop).unwrap_or_else(|| { span_bug!(drop_data.span, "cached block not present?") })) } else { @@ -922,12 +946,13 @@ fn build_scope_drops<'tcx>( block.unit() } -fn build_diverge_scope(cfg: &mut CFG<'tcx>, - span: Span, - scope: &mut Scope<'tcx>, - mut target: BasicBlock, - is_generator: bool) - -> BasicBlock +fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>, + span: Span, + scope: &mut Scope<'tcx>, + mut target: BasicBlock, + generator_drop: bool, + is_generator: bool) + -> BasicBlock { // Build up the drops in **reverse** order. The end result will // look like: @@ -979,7 +1004,7 @@ fn build_diverge_scope(cfg: &mut CFG<'tcx>, } DropKind::Storage => {} DropKind::Value { ref mut cached_block } => { - let cached_block = cached_block.ref_mut(); + let cached_block = cached_block.ref_mut(generator_drop); target = if let Some(cached_block) = *cached_block { storage_deads.clear(); target_built_by_us = false; @@ -1002,7 +1027,7 @@ fn build_diverge_scope(cfg: &mut CFG<'tcx>, }; } push_storage_deads(cfg, &mut target, &mut storage_deads, target_built_by_us, source_scope); - *scope.cached_unwind.ref_mut() = Some(target); + *scope.cached_unwind.ref_mut(generator_drop) = Some(target); assert!(storage_deads.is_empty()); debug!("build_diverge_scope({:?}, {:?}) = {:?}", scope, span, target);