use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG};
use crate::thir::{Expr, ExprRef, LintLevel};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
use rustc_index::vec::IndexVec;
use rustc_middle::middle::region;
use rustc_middle::mir::*;
use rustc_span::{Span, DUMMY_SP};
#[derive(Debug)]
pub struct Scopes<'tcx> {
scopes: Vec<Scope>,
breakable_scopes: Vec<BreakableScope<'tcx>>,
unwind_drops: DropTree,
generator_drops: DropTree,
}
#[derive(Debug)]
struct Scope {
source_scope: SourceScope,
region_scope: region::Scope,
region_scope_span: Span,
drops: Vec<DropData>,
moved_locals: Vec<Local>,
cached_unwind_block: Option<DropIdx>,
cached_generator_drop_block: Option<DropIdx>,
}
#[derive(Clone, Copy, Debug)]
struct DropData {
source_info: SourceInfo,
local: Local,
kind: DropKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum DropKind {
Value,
Storage,
}
#[derive(Debug)]
struct BreakableScope<'tcx> {
region_scope: region::Scope,
break_destination: Place<'tcx>,
break_drops: DropTree,
continue_drops: Option<DropTree>,
}
#[derive(Clone, Copy, Debug)]
crate enum BreakableTarget {
Continue(region::Scope),
Break(region::Scope),
Return,
}
rustc_index::newtype_index! {
struct DropIdx { .. }
}
const ROOT_NODE: DropIdx = DropIdx::from_u32(0);
#[derive(Debug)]
struct DropTree {
drops: IndexVec<DropIdx, (DropData, DropIdx)>,
previous_drops: FxHashMap<(DropIdx, Local, DropKind), DropIdx>,
entry_points: Vec<(DropIdx, BasicBlock)>,
}
impl Scope {
fn needs_cleanup(&self) -> bool {
self.drops.iter().any(|drop| match drop.kind {
DropKind::Value => true,
DropKind::Storage => false,
})
}
fn invalidate_cache(&mut self) {
self.cached_unwind_block = None;
self.cached_generator_drop_block = None;
}
}
trait DropTreeBuilder<'tcx> {
fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock;
fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock);
}
impl DropTree {
fn new() -> Self {
let fake_source_info = SourceInfo::outermost(DUMMY_SP);
let fake_data =
DropData { source_info: fake_source_info, local: Local::MAX, kind: DropKind::Storage };
let drop_idx = DropIdx::MAX;
let drops = IndexVec::from_elem_n((fake_data, drop_idx), 1);
Self { drops, entry_points: Vec::new(), previous_drops: FxHashMap::default() }
}
fn add_drop(&mut self, drop: DropData, next: DropIdx) -> DropIdx {
let drops = &mut self.drops;
*self
.previous_drops
.entry((next, drop.local, drop.kind))
.or_insert_with(|| drops.push((drop, next)))
}
fn add_entry(&mut self, from: BasicBlock, to: DropIdx) {
debug_assert!(to < self.drops.next_index());
self.entry_points.push((to, from));
}
fn build_mir<'tcx, T: DropTreeBuilder<'tcx>>(
&mut self,
cfg: &mut CFG<'tcx>,
blocks: &mut IndexVec<DropIdx, Option<BasicBlock>>,
) {
debug!("DropTree::build_mir(drops = {:#?})", self);
assert_eq!(blocks.len(), self.drops.len());
self.assign_blocks::<T>(cfg, blocks);
self.link_blocks(cfg, blocks)
}
fn assign_blocks<'tcx, T: DropTreeBuilder<'tcx>>(
&mut self,
cfg: &mut CFG<'tcx>,
blocks: &mut IndexVec<DropIdx, Option<BasicBlock>>,
) {
#[derive(Clone, Copy)]
enum Block {
None,
Shares(DropIdx),
Own,
}
let mut needs_block = IndexVec::from_elem(Block::None, &self.drops);
if blocks[ROOT_NODE].is_some() {
needs_block[ROOT_NODE] = Block::Own;
}
let entry_points = &mut self.entry_points;
entry_points.sort();
for (drop_idx, drop_data) in self.drops.iter_enumerated().rev() {
if entry_points.last().map_or(false, |entry_point| entry_point.0 == drop_idx) {
let block = *blocks[drop_idx].get_or_insert_with(|| T::make_block(cfg));
needs_block[drop_idx] = Block::Own;
while entry_points.last().map_or(false, |entry_point| entry_point.0 == drop_idx) {
let entry_block = entry_points.pop().unwrap().1;
T::add_entry(cfg, entry_block, block);
}
}
match needs_block[drop_idx] {
Block::None => continue,
Block::Own => {
blocks[drop_idx].get_or_insert_with(|| T::make_block(cfg));
}
Block::Shares(pred) => {
blocks[drop_idx] = blocks[pred];
}
}
if let DropKind::Value = drop_data.0.kind {
needs_block[drop_data.1] = Block::Own;
} else if drop_idx != ROOT_NODE {
match &mut needs_block[drop_data.1] {
pred @ Block::None => *pred = Block::Shares(drop_idx),
pred @ Block::Shares(_) => *pred = Block::Own,
Block::Own => (),
}
}
}
debug!("assign_blocks: blocks = {:#?}", blocks);
assert!(entry_points.is_empty());
}
fn link_blocks<'tcx>(
&self,
cfg: &mut CFG<'tcx>,
blocks: &IndexVec<DropIdx, Option<BasicBlock>>,
) {
for (drop_idx, drop_data) in self.drops.iter_enumerated().rev() {
let block = if let Some(block) = blocks[drop_idx] {
block
} else {
continue;
};
match drop_data.0.kind {
DropKind::Value => {
let terminator = TerminatorKind::Drop {
target: blocks[drop_data.1].unwrap(),
unwind: None,
place: drop_data.0.local.into(),
};
cfg.terminate(block, drop_data.0.source_info, terminator);
}
DropKind::Storage if drop_idx == ROOT_NODE => {}
DropKind::Storage => {
let stmt = Statement {
source_info: drop_data.0.source_info,
kind: StatementKind::StorageDead(drop_data.0.local),
};
cfg.push(block, stmt);
let target = blocks[drop_data.1].unwrap();
if target != block {
let source_info = SourceInfo { span: DUMMY_SP, ..drop_data.0.source_info };
let terminator = TerminatorKind::Goto { target };
cfg.terminate(block, source_info, terminator);
}
}
}
}
}
}
impl<'tcx> Scopes<'tcx> {
pub(crate) fn new() -> Self {
Self {
scopes: Vec::new(),
breakable_scopes: Vec::new(),
unwind_drops: DropTree::new(),
generator_drops: DropTree::new(),
}
}
fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo), vis_scope: SourceScope) {
debug!("push_scope({:?})", region_scope);
self.scopes.push(Scope {
source_scope: vis_scope,
region_scope: region_scope.0,
region_scope_span: region_scope.1.span,
drops: vec![],
moved_locals: vec![],
cached_unwind_block: None,
cached_generator_drop_block: None,
});
}
fn pop_scope(&mut self, region_scope: (region::Scope, SourceInfo)) -> Scope {
let scope = self.scopes.pop().unwrap();
assert_eq!(scope.region_scope, region_scope.0);
scope
}
fn scope_index(&self, region_scope: region::Scope, span: Span) -> usize {
self.scopes
.iter()
.rposition(|scope| scope.region_scope == region_scope)
.unwrap_or_else(|| span_bug!(span, "region_scope {:?} does not enclose", region_scope))
}
fn topmost(&self) -> region::Scope {
self.scopes.last().expect("topmost_scope: no scopes present").region_scope
}
}
impl<'a, 'tcx> Builder<'a, 'tcx> {
crate fn in_breakable_scope<F>(
&mut self,
loop_block: Option<BasicBlock>,
break_destination: Place<'tcx>,
span: Span,
f: F,
) -> BlockAnd<()>
where
F: FnOnce(&mut Builder<'a, 'tcx>) -> Option<BlockAnd<()>>,
{
let region_scope = self.scopes.topmost();
let scope = BreakableScope {
region_scope,
break_destination,
break_drops: DropTree::new(),
continue_drops: loop_block.map(|_| DropTree::new()),
};
self.scopes.breakable_scopes.push(scope);
let normal_exit_block = f(self);
let breakable_scope = self.scopes.breakable_scopes.pop().unwrap();
assert!(breakable_scope.region_scope == region_scope);
let break_block = self.build_exit_tree(breakable_scope.break_drops, None);
if let Some(drops) = breakable_scope.continue_drops { self.build_exit_tree(drops, loop_block); }
match (normal_exit_block, break_block) {
(Some(block), None) | (None, Some(block)) => block,
(None, None) => self.cfg.start_new_block().unit(),
(Some(normal_block), Some(exit_block)) => {
let target = self.cfg.start_new_block();
let source_info = self.source_info(span);
self.cfg.terminate(
unpack!(normal_block),
source_info,
TerminatorKind::Goto { target },
);
self.cfg.terminate(
unpack!(exit_block),
source_info,
TerminatorKind::Goto { target },
);
target.unit()
}
}
}
crate fn in_opt_scope<F, R>(
&mut self,
opt_scope: Option<(region::Scope, SourceInfo)>,
f: F,
) -> BlockAnd<R>
where
F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
{
debug!("in_opt_scope(opt_scope={:?})", opt_scope);
if let Some(region_scope) = opt_scope {
self.push_scope(region_scope);
}
let mut block;
let rv = unpack!(block = f(self));
if let Some(region_scope) = opt_scope {
unpack!(block = self.pop_scope(region_scope, block));
}
debug!("in_scope: exiting opt_scope={:?} block={:?}", opt_scope, block);
block.and(rv)
}
crate fn in_scope<F, R>(
&mut self,
region_scope: (region::Scope, SourceInfo),
lint_level: LintLevel,
f: F,
) -> BlockAnd<R>
where
F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd<R>,
{
debug!("in_scope(region_scope={:?})", region_scope);
let source_scope = self.source_scope;
let tcx = self.hir.tcx();
if let LintLevel::Explicit(current_hir_id) = lint_level {
let parent_root = tcx.maybe_lint_level_root_bounded(
self.source_scopes[source_scope].local_data.as_ref().assert_crate_local().lint_root,
self.hir.root_lint_level,
);
let current_root =
tcx.maybe_lint_level_root_bounded(current_hir_id, self.hir.root_lint_level);
if parent_root != current_root {
self.source_scope = self.new_source_scope(
region_scope.1.span,
LintLevel::Explicit(current_root),
None,
);
}
}
self.push_scope(region_scope);
let mut block;
let rv = unpack!(block = f(self));
unpack!(block = self.pop_scope(region_scope, block));
self.source_scope = source_scope;
debug!("in_scope: exiting region_scope={:?} block={:?}", region_scope, block);
block.and(rv)
}
crate fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo)) {
self.scopes.push_scope(region_scope, self.source_scope);
}
crate fn pop_scope(
&mut self,
region_scope: (region::Scope, SourceInfo),
mut block: BasicBlock,
) -> BlockAnd<()> {
debug!("pop_scope({:?}, {:?})", region_scope, block);
block = self.leave_top_scope(block);
self.scopes.pop_scope(region_scope);
block.unit()
}
crate fn break_scope(
&mut self,
mut block: BasicBlock,
value: Option<ExprRef<'tcx>>,
target: BreakableTarget,
source_info: SourceInfo,
) -> BlockAnd<()> {
let span = source_info.span;
let get_scope_index = |scope: region::Scope| {
self.scopes
.breakable_scopes
.iter()
.rposition(|breakable_scope| breakable_scope.region_scope == scope)
.unwrap_or_else(|| span_bug!(span, "no enclosing breakable scope found"))
};
let (break_index, destination) = match target {
BreakableTarget::Return => {
let scope = &self.scopes.breakable_scopes[0];
if scope.break_destination != Place::return_place() {
span_bug!(span, "`return` in item with no return scope");
}
(0, Some(scope.break_destination))
}
BreakableTarget::Break(scope) => {
let break_index = get_scope_index(scope);
let scope = &self.scopes.breakable_scopes[break_index];
(break_index, Some(scope.break_destination))
}
BreakableTarget::Continue(scope) => {
let break_index = get_scope_index(scope);
(break_index, None)
}
};
if let Some(destination) = destination {
if let Some(value) = value {
debug!("stmt_expr Break val block_context.push(SubExpr)");
self.block_context.push(BlockFrame::SubExpr);
unpack!(block = self.into(destination, block, value));
self.block_context.pop();
} else {
self.cfg.push_assign_unit(block, source_info, destination, self.hir.tcx())
}
} else {
assert!(value.is_none(), "`return` and `break` should have a destination");
}
let region_scope = self.scopes.breakable_scopes[break_index].region_scope;
let scope_index = self.scopes.scope_index(region_scope, span);
let drops = if destination.is_some() {
&mut self.scopes.breakable_scopes[break_index].break_drops
} else {
self.scopes.breakable_scopes[break_index].continue_drops.as_mut().unwrap()
};
let mut drop_idx = ROOT_NODE;
for scope in &self.scopes.scopes[scope_index + 1..] {
for drop in &scope.drops {
drop_idx = drops.add_drop(*drop, drop_idx);
}
}
drops.add_entry(block, drop_idx);
self.cfg.terminate(block, source_info, TerminatorKind::Resume);
self.cfg.start_new_block().unit()
}
crate fn exit_top_scope(
&mut self,
mut block: BasicBlock,
target: BasicBlock,
source_info: SourceInfo,
) {
block = self.leave_top_scope(block);
self.cfg.terminate(block, source_info, TerminatorKind::Goto { target });
}
fn leave_top_scope(&mut self, block: BasicBlock) -> BasicBlock {
let needs_cleanup = self.scopes.scopes.last().map_or(false, |scope| scope.needs_cleanup());
let is_generator = self.generator_kind.is_some();
let unwind_to = if needs_cleanup { self.diverge_cleanup() } else { DropIdx::MAX };
let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes");
unpack!(build_scope_drops(
&mut self.cfg,
&mut self.scopes.unwind_drops,
scope,
block,
unwind_to,
is_generator && needs_cleanup,
self.arg_count,
))
}
crate fn new_source_scope(
&mut self,
span: Span,
lint_level: LintLevel,
safety: Option<Safety>,
) -> SourceScope {
let parent = self.source_scope;
debug!(
"new_source_scope({:?}, {:?}, {:?}) - parent({:?})={:?}",
span,
lint_level,
safety,
parent,
self.source_scopes.get(parent)
);
let scope_local_data = SourceScopeLocalData {
lint_root: if let LintLevel::Explicit(lint_root) = lint_level {
lint_root
} else {
self.source_scopes[parent].local_data.as_ref().assert_crate_local().lint_root
},
safety: safety.unwrap_or_else(|| {
self.source_scopes[parent].local_data.as_ref().assert_crate_local().safety
}),
};
self.source_scopes.push(SourceScopeData {
span,
parent_scope: Some(parent),
inlined: None,
inlined_parent_scope: None,
local_data: ClearCrossCrate::Set(scope_local_data),
})
}
crate fn source_info(&self, span: Span) -> SourceInfo {
SourceInfo { span, scope: self.source_scope }
}
crate fn local_scope(&self) -> Option<region::Scope> {
match self.hir.body_owner_kind {
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) =>
{
None
}
hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => Some(self.scopes.topmost()),
}
}
crate fn schedule_drop_storage_and_value(
&mut self,
span: Span,
region_scope: region::Scope,
local: Local,
) {
self.schedule_drop(span, region_scope, local, DropKind::Storage);
self.schedule_drop(span, region_scope, local, DropKind::Value);
}
crate fn schedule_drop(
&mut self,
span: Span,
region_scope: region::Scope,
local: Local,
drop_kind: DropKind,
) {
let needs_drop = match drop_kind {
DropKind::Value => {
if !self.hir.needs_drop(self.local_decls[local].ty) {
return;
}
true
}
DropKind::Storage => {
if local.index() <= self.arg_count {
span_bug!(
span,
"`schedule_drop` called with local {:?} and arg_count {}",
local,
self.arg_count,
)
}
false
}
};
let invalidate_caches = needs_drop || self.generator_kind.is_some();
for scope in self.scopes.scopes.iter_mut().rev() {
if invalidate_caches {
scope.invalidate_cache();
}
if scope.region_scope == region_scope {
let region_scope_span =
region_scope.span(self.hir.tcx(), &self.hir.region_scope_tree);
let scope_end = self.hir.tcx().sess.source_map().end_point(region_scope_span);
scope.drops.push(DropData {
source_info: SourceInfo { span: scope_end, scope: scope.source_scope },
local,
kind: drop_kind,
});
return;
}
}
span_bug!(span, "region scope {:?} not in scope to drop {:?}", region_scope, local);
}
crate fn record_operands_moved(&mut self, operands: &[Operand<'tcx>]) {
let scope = match self.local_scope() {
None => {
return;
}
Some(local_scope) => self
.scopes
.scopes
.iter_mut()
.rfind(|scope| scope.region_scope == local_scope)
.unwrap_or_else(|| bug!("scope {:?} not found in scope list!", local_scope)),
};
let locals_moved = operands.iter().flat_map(|operand| match operand {
Operand::Copy(_) | Operand::Constant(_) => None,
Operand::Move(place) => place.as_local(),
});
for local in locals_moved {
if scope.drops.iter().any(|drop| drop.local == local && drop.kind == DropKind::Value) {
scope.moved_locals.push(local);
}
}
}
crate fn test_bool(
&mut self,
mut block: BasicBlock,
condition: Expr<'tcx>,
source_info: SourceInfo,
) -> (BasicBlock, BasicBlock) {
let cond = unpack!(block = self.as_local_operand(block, condition));
let true_block = self.cfg.start_new_block();
let false_block = self.cfg.start_new_block();
let term = TerminatorKind::if_(self.hir.tcx(), cond.clone(), true_block, false_block);
self.cfg.terminate(block, source_info, term);
match cond {
Operand::Constant(_) => (),
_ if self.local_scope().is_none() => (),
Operand::Copy(place) | Operand::Move(place) => {
if let Some(cond_temp) = place.as_local() {
let top_scope = self.scopes.scopes.last_mut().unwrap();
let top_drop_data = top_scope.drops.pop().unwrap();
if self.generator_kind.is_some() {
top_scope.invalidate_cache();
}
match top_drop_data.kind {
DropKind::Value { .. } => {
bug!("Drop scheduled on top of condition variable")
}
DropKind::Storage => {
let source_info = top_drop_data.source_info;
let local = top_drop_data.local;
assert_eq!(local, cond_temp, "Drop scheduled on top of condition");
self.cfg.push(
true_block,
Statement { source_info, kind: StatementKind::StorageDead(local) },
);
self.cfg.push(
false_block,
Statement { source_info, kind: StatementKind::StorageDead(local) },
);
}
}
} else {
bug!("Expected as_local_operand to produce a temporary");
}
}
}
(true_block, false_block)
}
fn diverge_cleanup(&mut self) -> DropIdx {
let is_generator = self.generator_kind.is_some();
let (uncached_scope, mut cached_drop) = self
.scopes
.scopes
.iter()
.enumerate()
.rev()
.find_map(|(scope_idx, scope)| {
scope.cached_unwind_block.map(|cached_block| (scope_idx + 1, cached_block))
})
.unwrap_or((0, ROOT_NODE));
for scope in &mut self.scopes.scopes[uncached_scope..] {
for drop in &scope.drops {
if is_generator || drop.kind == DropKind::Value {
cached_drop = self.scopes.unwind_drops.add_drop(*drop, cached_drop);
}
}
scope.cached_unwind_block = Some(cached_drop);
}
cached_drop
}
crate fn diverge_from(&mut self, start: BasicBlock) {
debug_assert!(
matches!(
self.cfg.block_data(start).terminator().kind,
TerminatorKind::Assert { .. }
| TerminatorKind::Call {..}
| TerminatorKind::DropAndReplace { .. }
| TerminatorKind::FalseUnwind { .. }
),
"diverge_from called on block with terminator that cannot unwind."
);
let next_drop = self.diverge_cleanup();
self.scopes.unwind_drops.add_entry(start, next_drop);
}
crate fn generator_drop_cleanup(&mut self, yield_block: BasicBlock) {
debug_assert!(
matches!(
self.cfg.block_data(yield_block).terminator().kind,
TerminatorKind::Yield { .. }
),
"generator_drop_cleanup called on block with non-yield terminator."
);
let (uncached_scope, mut cached_drop) = self
.scopes
.scopes
.iter()
.enumerate()
.rev()
.find_map(|(scope_idx, scope)| {
scope.cached_generator_drop_block.map(|cached_block| (scope_idx + 1, cached_block))
})
.unwrap_or((0, ROOT_NODE));
for scope in &mut self.scopes.scopes[uncached_scope..] {
for drop in &scope.drops {
cached_drop = self.scopes.generator_drops.add_drop(*drop, cached_drop);
}
scope.cached_generator_drop_block = Some(cached_drop);
}
self.scopes.generator_drops.add_entry(yield_block, cached_drop);
}
crate fn build_drop_and_replace(
&mut self,
block: BasicBlock,
span: Span,
place: Place<'tcx>,
value: Operand<'tcx>,
) -> BlockAnd<()> {
let source_info = self.source_info(span);
let next_target = self.cfg.start_new_block();
self.cfg.terminate(
block,
source_info,
TerminatorKind::DropAndReplace { place, value, target: next_target, unwind: None },
);
self.diverge_from(block);
next_target.unit()
}
crate fn assert(
&mut self,
block: BasicBlock,
cond: Operand<'tcx>,
expected: bool,
msg: AssertMessage<'tcx>,
span: Span,
) -> BasicBlock {
let source_info = self.source_info(span);
let success_block = self.cfg.start_new_block();
self.cfg.terminate(
block,
source_info,
TerminatorKind::Assert { cond, expected, msg, target: success_block, cleanup: None },
);
self.diverge_from(block);
success_block
}
crate fn clear_top_scope(&mut self, region_scope: region::Scope) {
let top_scope = self.scopes.scopes.last_mut().unwrap();
assert_eq!(top_scope.region_scope, region_scope);
top_scope.drops.clear();
top_scope.invalidate_cache();
}
}
fn build_scope_drops<'tcx>(
cfg: &mut CFG<'tcx>,
unwind_drops: &mut DropTree,
scope: &Scope,
mut block: BasicBlock,
mut unwind_to: DropIdx,
storage_dead_on_unwind: bool,
arg_count: usize,
) -> BlockAnd<()> {
debug!("build_scope_drops({:?} -> {:?})", block, scope);
for drop_data in scope.drops.iter().rev() {
let source_info = drop_data.source_info;
let local = drop_data.local;
match drop_data.kind {
DropKind::Value => {
debug_assert_eq!(unwind_drops.drops[unwind_to].0.local, drop_data.local);
debug_assert_eq!(unwind_drops.drops[unwind_to].0.kind, drop_data.kind);
unwind_to = unwind_drops.drops[unwind_to].1;
if scope.moved_locals.iter().any(|&o| o == local) {
continue;
}
unwind_drops.add_entry(block, unwind_to);
let next = cfg.start_new_block();
cfg.terminate(
block,
source_info,
TerminatorKind::Drop { place: local.into(), target: next, unwind: None },
);
block = next;
}
DropKind::Storage => {
if storage_dead_on_unwind {
debug_assert_eq!(unwind_drops.drops[unwind_to].0.local, drop_data.local);
debug_assert_eq!(unwind_drops.drops[unwind_to].0.kind, drop_data.kind);
unwind_to = unwind_drops.drops[unwind_to].1;
}
assert!(local.index() > arg_count);
cfg.push(block, Statement { source_info, kind: StatementKind::StorageDead(local) });
}
}
}
block.unit()
}
impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
fn build_exit_tree(
&mut self,
mut drops: DropTree,
continue_block: Option<BasicBlock>,
) -> Option<BlockAnd<()>> {
let mut blocks = IndexVec::from_elem(None, &drops.drops);
blocks[ROOT_NODE] = continue_block;
drops.build_mir::<ExitScopes>(&mut self.cfg, &mut blocks);
if drops.drops.iter().any(|(drop, _)| drop.kind == DropKind::Value) {
let unwind_target = self.diverge_cleanup();
let mut unwind_indices = IndexVec::from_elem_n(unwind_target, 1);
for (drop_idx, drop_data) in drops.drops.iter_enumerated().skip(1) {
match drop_data.0.kind {
DropKind::Storage => {
if self.generator_kind.is_some() {
let unwind_drop = self
.scopes
.unwind_drops
.add_drop(drop_data.0, unwind_indices[drop_data.1]);
unwind_indices.push(unwind_drop);
} else {
unwind_indices.push(unwind_indices[drop_data.1]);
}
}
DropKind::Value => {
let unwind_drop = self
.scopes
.unwind_drops
.add_drop(drop_data.0, unwind_indices[drop_data.1]);
self.scopes
.unwind_drops
.add_entry(blocks[drop_idx].unwrap(), unwind_indices[drop_data.1]);
unwind_indices.push(unwind_drop);
}
}
}
}
blocks[ROOT_NODE].map(BasicBlock::unit)
}
crate fn build_drop_trees(&mut self, should_abort: bool) {
if self.generator_kind.is_some() {
self.build_generator_drop_trees(should_abort);
} else {
Self::build_unwind_tree(
&mut self.cfg,
&mut self.scopes.unwind_drops,
self.fn_span,
should_abort,
&mut None,
);
}
}
fn build_generator_drop_trees(&mut self, should_abort: bool) {
let drops = &mut self.scopes.generator_drops;
let cfg = &mut self.cfg;
let fn_span = self.fn_span;
let mut blocks = IndexVec::from_elem(None, &drops.drops);
drops.build_mir::<GeneratorDrop>(cfg, &mut blocks);
if let Some(root_block) = blocks[ROOT_NODE] {
cfg.terminate(
root_block,
SourceInfo::outermost(fn_span),
TerminatorKind::GeneratorDrop,
);
}
let resume_block = &mut None;
let unwind_drops = &mut self.scopes.unwind_drops;
Self::build_unwind_tree(cfg, unwind_drops, fn_span, should_abort, resume_block);
for (drop_idx, drop_data) in drops.drops.iter_enumerated() {
if let DropKind::Value = drop_data.0.kind {
debug_assert!(drop_data.1 < drops.drops.next_index());
drops.entry_points.push((drop_data.1, blocks[drop_idx].unwrap()));
}
}
Self::build_unwind_tree(cfg, drops, fn_span, should_abort, resume_block);
}
fn build_unwind_tree(
cfg: &mut CFG<'tcx>,
drops: &mut DropTree,
fn_span: Span,
should_abort: bool,
resume_block: &mut Option<BasicBlock>,
) {
let mut blocks = IndexVec::from_elem(None, &drops.drops);
blocks[ROOT_NODE] = *resume_block;
drops.build_mir::<Unwind>(cfg, &mut blocks);
if let (None, Some(resume)) = (*resume_block, blocks[ROOT_NODE]) {
let terminator =
if should_abort { TerminatorKind::Abort } else { TerminatorKind::Resume };
cfg.terminate(resume, SourceInfo::outermost(fn_span), terminator);
*resume_block = blocks[ROOT_NODE];
}
}
}
struct ExitScopes;
impl<'tcx> DropTreeBuilder<'tcx> for ExitScopes {
fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock {
cfg.start_new_block()
}
fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock) {
cfg.block_data_mut(from).terminator_mut().kind = TerminatorKind::Goto { target: to };
}
}
struct GeneratorDrop;
impl<'tcx> DropTreeBuilder<'tcx> for GeneratorDrop {
fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock {
cfg.start_new_block()
}
fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock) {
let term = cfg.block_data_mut(from).terminator_mut();
if let TerminatorKind::Yield { ref mut drop, .. } = term.kind {
*drop = Some(to);
} else {
span_bug!(
term.source_info.span,
"cannot enter generator drop tree from {:?}",
term.kind
)
}
}
}
struct Unwind;
impl<'tcx> DropTreeBuilder<'tcx> for Unwind {
fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock {
cfg.start_new_cleanup_block()
}
fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock) {
let term = &mut cfg.block_data_mut(from).terminator_mut();
match &mut term.kind {
TerminatorKind::Drop { unwind, .. }
| TerminatorKind::DropAndReplace { unwind, .. }
| TerminatorKind::FalseUnwind { unwind, .. }
| TerminatorKind::Call { cleanup: unwind, .. }
| TerminatorKind::Assert { cleanup: unwind, .. } => {
*unwind = Some(to);
}
TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::Resume
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::Yield { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::InlineAsm {.. } => {
span_bug!(term.source_info.span, "cannot unwind from {:?}", term.kind)
}
}
}
}