Auto merge of #113970 - cjgillot:assume-all-the-things, r=nikic
Replace switch to unreachable by assume statements `UnreachablePropagation` currently keeps some switch terminators alive in order to ensure codegen can infer the inequalities on the discriminants. This PR proposes to encode those inequalities as `Assume` statements. This allows to simplify MIR further by removing some useless terminators.
This commit is contained in:
commit
98f5ebbe2e
26 changed files with 573 additions and 470 deletions
|
@ -568,10 +568,11 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
&[
|
&[
|
||||||
&check_alignment::CheckAlignment,
|
&check_alignment::CheckAlignment,
|
||||||
&lower_slice_len::LowerSliceLenCalls, // has to be done before inlining, otherwise actual call will be almost always inlined. Also simple, so can just do first
|
&lower_slice_len::LowerSliceLenCalls, // has to be done before inlining, otherwise actual call will be almost always inlined. Also simple, so can just do first
|
||||||
&unreachable_prop::UnreachablePropagation,
|
|
||||||
&uninhabited_enum_branching::UninhabitedEnumBranching,
|
|
||||||
&o1(simplify::SimplifyCfg::AfterUninhabitedEnumBranching),
|
|
||||||
&inline::Inline,
|
&inline::Inline,
|
||||||
|
// Substitutions during inlining may introduce switch on enums with uninhabited branches.
|
||||||
|
&uninhabited_enum_branching::UninhabitedEnumBranching,
|
||||||
|
&unreachable_prop::UnreachablePropagation,
|
||||||
|
&o1(simplify::SimplifyCfg::AfterUninhabitedEnumBranching),
|
||||||
&remove_storage_markers::RemoveStorageMarkers,
|
&remove_storage_markers::RemoveStorageMarkers,
|
||||||
&remove_zsts::RemoveZsts,
|
&remove_zsts::RemoveZsts,
|
||||||
&normalize_array_len::NormalizeArrayLen, // has to run after `slice::len` lowering
|
&normalize_array_len::NormalizeArrayLen, // has to run after `slice::len` lowering
|
||||||
|
|
|
@ -16,8 +16,25 @@ impl<'tcx> MirPass<'tcx> for SimplifyConstCondition {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
|
trace!("Running SimplifyConstCondition on {:?}", body.source);
|
||||||
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
|
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
|
||||||
for block in body.basic_blocks_mut() {
|
'blocks: for block in body.basic_blocks_mut() {
|
||||||
|
for stmt in block.statements.iter_mut() {
|
||||||
|
if let StatementKind::Intrinsic(box ref intrinsic) = stmt.kind
|
||||||
|
&& let NonDivergingIntrinsic::Assume(discr) = intrinsic
|
||||||
|
&& let Operand::Constant(ref c) = discr
|
||||||
|
&& let Some(constant) = c.const_.try_eval_bool(tcx, param_env)
|
||||||
|
{
|
||||||
|
if constant {
|
||||||
|
stmt.make_nop();
|
||||||
|
} else {
|
||||||
|
block.statements.clear();
|
||||||
|
block.terminator_mut().kind = TerminatorKind::Unreachable;
|
||||||
|
continue 'blocks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let terminator = block.terminator_mut();
|
let terminator = block.terminator_mut();
|
||||||
terminator.kind = match terminator.kind {
|
terminator.kind = match terminator.kind {
|
||||||
TerminatorKind::SwitchInt {
|
TerminatorKind::SwitchInt {
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
use crate::MirPass;
|
use crate::MirPass;
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_middle::mir::{
|
use rustc_middle::mir::{
|
||||||
BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, SwitchTargets, Terminator,
|
BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, Terminator, TerminatorKind,
|
||||||
TerminatorKind,
|
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::layout::TyAndLayout;
|
use rustc_middle::ty::layout::TyAndLayout;
|
||||||
use rustc_middle::ty::{Ty, TyCtxt};
|
use rustc_middle::ty::{Ty, TyCtxt};
|
||||||
|
@ -30,17 +29,20 @@ fn get_switched_on_type<'tcx>(
|
||||||
let terminator = block_data.terminator();
|
let terminator = block_data.terminator();
|
||||||
|
|
||||||
// Only bother checking blocks which terminate by switching on a local.
|
// Only bother checking blocks which terminate by switching on a local.
|
||||||
if let Some(local) = get_discriminant_local(&terminator.kind)
|
let local = get_discriminant_local(&terminator.kind)?;
|
||||||
&& let [.., stmt_before_term] = &block_data.statements[..]
|
|
||||||
&& let StatementKind::Assign(box (l, Rvalue::Discriminant(place))) = stmt_before_term.kind
|
let stmt_before_term = block_data.statements.last()?;
|
||||||
|
|
||||||
|
if let StatementKind::Assign(box (l, Rvalue::Discriminant(place))) = stmt_before_term.kind
|
||||||
&& l.as_local() == Some(local)
|
&& l.as_local() == Some(local)
|
||||||
&& let ty = place.ty(body, tcx).ty
|
|
||||||
&& ty.is_enum()
|
|
||||||
{
|
{
|
||||||
Some(ty)
|
let ty = place.ty(body, tcx).ty;
|
||||||
} else {
|
if ty.is_enum() {
|
||||||
None
|
return Some(ty);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variant_discriminants<'tcx>(
|
fn variant_discriminants<'tcx>(
|
||||||
|
@ -67,28 +69,6 @@ fn variant_discriminants<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensures that the `otherwise` branch leads to an unreachable bb, returning `None` if so and a new
|
|
||||||
/// bb to use as the new target if not.
|
|
||||||
fn ensure_otherwise_unreachable<'tcx>(
|
|
||||||
body: &Body<'tcx>,
|
|
||||||
targets: &SwitchTargets,
|
|
||||||
) -> Option<BasicBlockData<'tcx>> {
|
|
||||||
let otherwise = targets.otherwise();
|
|
||||||
let bb = &body.basic_blocks[otherwise];
|
|
||||||
if bb.terminator().kind == TerminatorKind::Unreachable
|
|
||||||
&& bb.statements.iter().all(|s| matches!(&s.kind, StatementKind::StorageDead(_)))
|
|
||||||
{
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut new_block = BasicBlockData::new(Some(Terminator {
|
|
||||||
source_info: bb.terminator().source_info,
|
|
||||||
kind: TerminatorKind::Unreachable,
|
|
||||||
}));
|
|
||||||
new_block.is_cleanup = bb.is_cleanup;
|
|
||||||
Some(new_block)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
|
impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
|
||||||
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
|
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
|
||||||
sess.mir_opt_level() > 0
|
sess.mir_opt_level() > 0
|
||||||
|
@ -97,13 +77,16 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
|
||||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
trace!("UninhabitedEnumBranching starting for {:?}", body.source);
|
trace!("UninhabitedEnumBranching starting for {:?}", body.source);
|
||||||
|
|
||||||
for bb in body.basic_blocks.indices() {
|
let mut removable_switchs = Vec::new();
|
||||||
|
|
||||||
|
for (bb, bb_data) in body.basic_blocks.iter_enumerated() {
|
||||||
trace!("processing block {:?}", bb);
|
trace!("processing block {:?}", bb);
|
||||||
|
|
||||||
let Some(discriminant_ty) = get_switched_on_type(&body.basic_blocks[bb], tcx, body)
|
if bb_data.is_cleanup {
|
||||||
else {
|
|
||||||
continue;
|
continue;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
let Some(discriminant_ty) = get_switched_on_type(&bb_data, tcx, body) else { continue };
|
||||||
|
|
||||||
let layout = tcx.layout_of(
|
let layout = tcx.layout_of(
|
||||||
tcx.param_env_reveal_all_normalized(body.source.def_id()).and(discriminant_ty),
|
tcx.param_env_reveal_all_normalized(body.source.def_id()).and(discriminant_ty),
|
||||||
|
@ -117,31 +100,38 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
|
||||||
|
|
||||||
trace!("allowed_variants = {:?}", allowed_variants);
|
trace!("allowed_variants = {:?}", allowed_variants);
|
||||||
|
|
||||||
if let TerminatorKind::SwitchInt { targets, .. } =
|
let terminator = bb_data.terminator();
|
||||||
&mut body.basic_blocks_mut()[bb].terminator_mut().kind
|
let TerminatorKind::SwitchInt { targets, .. } = &terminator.kind else { bug!() };
|
||||||
{
|
|
||||||
let mut new_targets = SwitchTargets::new(
|
|
||||||
targets.iter().filter(|(val, _)| allowed_variants.contains(val)),
|
|
||||||
targets.otherwise(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if new_targets.iter().count() == allowed_variants.len() {
|
let mut reachable_count = 0;
|
||||||
if let Some(updated) = ensure_otherwise_unreachable(body, &new_targets) {
|
for (index, (val, _)) in targets.iter().enumerate() {
|
||||||
let new_otherwise = body.basic_blocks_mut().push(updated);
|
if allowed_variants.contains(&val) {
|
||||||
*new_targets.all_targets_mut().last_mut().unwrap() = new_otherwise;
|
reachable_count += 1;
|
||||||
|
} else {
|
||||||
|
removable_switchs.push((bb, index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let TerminatorKind::SwitchInt { targets, .. } =
|
if reachable_count == allowed_variants.len() {
|
||||||
&mut body.basic_blocks_mut()[bb].terminator_mut().kind
|
removable_switchs.push((bb, targets.iter().count()));
|
||||||
{
|
|
||||||
*targets = new_targets;
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if removable_switchs.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_block = BasicBlockData::new(Some(Terminator {
|
||||||
|
source_info: body.basic_blocks[removable_switchs[0].0].terminator().source_info,
|
||||||
|
kind: TerminatorKind::Unreachable,
|
||||||
|
}));
|
||||||
|
let unreachable_block = body.basic_blocks.as_mut().push(new_block);
|
||||||
|
|
||||||
|
for (bb, index) in removable_switchs {
|
||||||
|
let bb = &mut body.basic_blocks.as_mut()[bb];
|
||||||
|
let terminator = bb.terminator_mut();
|
||||||
|
let TerminatorKind::SwitchInt { targets, .. } = &mut terminator.kind else { bug!() };
|
||||||
|
targets.all_targets_mut()[index] = unreachable_block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
//! when all of their successors are unreachable. This is achieved through a
|
//! when all of their successors are unreachable. This is achieved through a
|
||||||
//! post-order traversal of the blocks.
|
//! post-order traversal of the blocks.
|
||||||
|
|
||||||
use crate::simplify;
|
|
||||||
use crate::MirPass;
|
use crate::MirPass;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
|
use rustc_middle::mir::interpret::Scalar;
|
||||||
|
use rustc_middle::mir::patch::MirPatch;
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::{self, TyCtxt};
|
||||||
|
use rustc_target::abi::Size;
|
||||||
|
|
||||||
pub struct UnreachablePropagation;
|
pub struct UnreachablePropagation;
|
||||||
|
|
||||||
|
@ -21,76 +23,60 @@ impl MirPass<'_> for UnreachablePropagation {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||||
|
let mut patch = MirPatch::new(body);
|
||||||
let mut unreachable_blocks = FxHashSet::default();
|
let mut unreachable_blocks = FxHashSet::default();
|
||||||
let mut replacements = FxHashMap::default();
|
|
||||||
|
|
||||||
for (bb, bb_data) in traversal::postorder(body) {
|
for (bb, bb_data) in traversal::postorder(body) {
|
||||||
let terminator = bb_data.terminator();
|
let terminator = bb_data.terminator();
|
||||||
if terminator.kind == TerminatorKind::Unreachable {
|
let is_unreachable = match &terminator.kind {
|
||||||
|
TerminatorKind::Unreachable => true,
|
||||||
|
// This will unconditionally run into an unreachable and is therefore unreachable as well.
|
||||||
|
TerminatorKind::Goto { target } if unreachable_blocks.contains(target) => {
|
||||||
|
patch.patch_terminator(bb, TerminatorKind::Unreachable);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
// Try to remove unreachable targets from the switch.
|
||||||
|
TerminatorKind::SwitchInt { .. } => {
|
||||||
|
remove_successors_from_switch(tcx, bb, &unreachable_blocks, body, &mut patch)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if is_unreachable {
|
||||||
unreachable_blocks.insert(bb);
|
unreachable_blocks.insert(bb);
|
||||||
} else {
|
}
|
||||||
let is_unreachable = |succ: BasicBlock| unreachable_blocks.contains(&succ);
|
}
|
||||||
let terminator_kind_opt = remove_successors(&terminator.kind, is_unreachable);
|
|
||||||
|
|
||||||
if let Some(terminator_kind) = terminator_kind_opt {
|
if !tcx
|
||||||
if terminator_kind == TerminatorKind::Unreachable {
|
.consider_optimizing(|| format!("UnreachablePropagation {:?} ", body.source.def_id()))
|
||||||
unreachable_blocks.insert(bb);
|
{
|
||||||
}
|
return;
|
||||||
replacements.insert(bb, terminator_kind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
patch.apply(body);
|
||||||
|
|
||||||
// We do want do keep some unreachable blocks, but make them empty.
|
// We do want do keep some unreachable blocks, but make them empty.
|
||||||
for bb in unreachable_blocks {
|
for bb in unreachable_blocks {
|
||||||
if !tcx.consider_optimizing(|| {
|
|
||||||
format!("UnreachablePropagation {:?} ", body.source.def_id())
|
|
||||||
}) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.basic_blocks_mut()[bb].statements.clear();
|
body.basic_blocks_mut()[bb].statements.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
let replaced = !replacements.is_empty();
|
|
||||||
|
|
||||||
for (bb, terminator_kind) in replacements {
|
|
||||||
if !tcx.consider_optimizing(|| {
|
|
||||||
format!("UnreachablePropagation {:?} ", body.source.def_id())
|
|
||||||
}) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.basic_blocks_mut()[bb].terminator_mut().kind = terminator_kind;
|
|
||||||
}
|
|
||||||
|
|
||||||
if replaced {
|
|
||||||
simplify::remove_dead_blocks(body);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_successors<'tcx, F>(
|
/// Return whether the current terminator is fully unreachable.
|
||||||
terminator_kind: &TerminatorKind<'tcx>,
|
fn remove_successors_from_switch<'tcx>(
|
||||||
is_unreachable: F,
|
tcx: TyCtxt<'tcx>,
|
||||||
) -> Option<TerminatorKind<'tcx>>
|
bb: BasicBlock,
|
||||||
where
|
unreachable_blocks: &FxHashSet<BasicBlock>,
|
||||||
F: Fn(BasicBlock) -> bool,
|
body: &Body<'tcx>,
|
||||||
{
|
patch: &mut MirPatch<'tcx>,
|
||||||
let terminator = match terminator_kind {
|
) -> bool {
|
||||||
// This will unconditionally run into an unreachable and is therefore unreachable as well.
|
let terminator = body.basic_blocks[bb].terminator();
|
||||||
TerminatorKind::Goto { target } if is_unreachable(*target) => TerminatorKind::Unreachable,
|
let TerminatorKind::SwitchInt { discr, targets } = &terminator.kind else { bug!() };
|
||||||
TerminatorKind::SwitchInt { targets, discr } => {
|
let source_info = terminator.source_info;
|
||||||
let otherwise = targets.otherwise();
|
let location = body.terminator_loc(bb);
|
||||||
|
|
||||||
// If all targets are unreachable, we can be unreachable as well.
|
let is_unreachable = |bb| unreachable_blocks.contains(&bb);
|
||||||
if targets.all_targets().iter().all(|bb| is_unreachable(*bb)) {
|
|
||||||
TerminatorKind::Unreachable
|
// If there are multiple targets, we want to keep information about reachability for codegen.
|
||||||
} else if is_unreachable(otherwise) {
|
|
||||||
// If there are multiple targets, don't delete unreachable branches (like an unreachable otherwise)
|
|
||||||
// unless otherwise is unreachable, in which case deleting a normal branch causes it to be merged with
|
|
||||||
// the otherwise, keeping its unreachable.
|
|
||||||
// This looses information about reachability causing worse codegen.
|
|
||||||
// For example (see tests/codegen/match-optimizes-away.rs)
|
// For example (see tests/codegen/match-optimizes-away.rs)
|
||||||
//
|
//
|
||||||
// pub enum Two { A, B }
|
// pub enum Two { A, B }
|
||||||
|
@ -103,24 +89,67 @@ where
|
||||||
//
|
//
|
||||||
// This generates a `switchInt() -> [0: 0, 1: 1, otherwise: unreachable]`, which allows us or LLVM to
|
// This generates a `switchInt() -> [0: 0, 1: 1, otherwise: unreachable]`, which allows us or LLVM to
|
||||||
// turn it into just `x` later. Without the unreachable, such a transformation would be illegal.
|
// turn it into just `x` later. Without the unreachable, such a transformation would be illegal.
|
||||||
// If the otherwise branch is unreachable, we can delete all other unreachable targets, as they will
|
//
|
||||||
// still point to the unreachable and therefore not lose reachability information.
|
// In order to preserve this information, we record reachable and unreachable targets as
|
||||||
let reachable_iter = targets.iter().filter(|(_, bb)| !is_unreachable(*bb));
|
// `Assume` statements in MIR.
|
||||||
|
|
||||||
|
let discr_ty = discr.ty(body, tcx);
|
||||||
|
let discr_size = Size::from_bits(match discr_ty.kind() {
|
||||||
|
ty::Uint(uint) => uint.normalize(tcx.sess.target.pointer_width).bit_width().unwrap(),
|
||||||
|
ty::Int(int) => int.normalize(tcx.sess.target.pointer_width).bit_width().unwrap(),
|
||||||
|
ty::Char => 32,
|
||||||
|
ty::Bool => 1,
|
||||||
|
other => bug!("unhandled type: {:?}", other),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut add_assumption = |binop, value| {
|
||||||
|
let local = patch.new_temp(tcx.types.bool, source_info.span);
|
||||||
|
let value = Operand::Constant(Box::new(ConstOperand {
|
||||||
|
span: source_info.span,
|
||||||
|
user_ty: None,
|
||||||
|
const_: Const::from_scalar(tcx, Scalar::from_uint(value, discr_size), discr_ty),
|
||||||
|
}));
|
||||||
|
let cmp = Rvalue::BinaryOp(binop, Box::new((discr.to_copy(), value)));
|
||||||
|
patch.add_assign(location, local.into(), cmp);
|
||||||
|
|
||||||
|
let assume = NonDivergingIntrinsic::Assume(Operand::Move(local.into()));
|
||||||
|
patch.add_statement(location, StatementKind::Intrinsic(Box::new(assume)));
|
||||||
|
};
|
||||||
|
|
||||||
|
let otherwise = targets.otherwise();
|
||||||
|
let otherwise_unreachable = is_unreachable(otherwise);
|
||||||
|
|
||||||
|
let reachable_iter = targets.iter().filter(|&(value, bb)| {
|
||||||
|
let is_unreachable = is_unreachable(bb);
|
||||||
|
// We remove this target from the switch, so record the inequality using `Assume`.
|
||||||
|
if is_unreachable && !otherwise_unreachable {
|
||||||
|
add_assumption(BinOp::Ne, value);
|
||||||
|
}
|
||||||
|
!is_unreachable
|
||||||
|
});
|
||||||
|
|
||||||
let new_targets = SwitchTargets::new(reachable_iter, otherwise);
|
let new_targets = SwitchTargets::new(reachable_iter, otherwise);
|
||||||
|
|
||||||
// No unreachable branches were removed.
|
let num_targets = new_targets.all_targets().len();
|
||||||
if new_targets.all_targets().len() == targets.all_targets().len() {
|
let fully_unreachable = num_targets == 1 && otherwise_unreachable;
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
TerminatorKind::SwitchInt { discr: discr.clone(), targets: new_targets }
|
let terminator = match (num_targets, otherwise_unreachable) {
|
||||||
} else {
|
// If all targets are unreachable, we can be unreachable as well.
|
||||||
// If the otherwise branch is reachable, we don't want to delete any unreachable branches.
|
(1, true) => TerminatorKind::Unreachable,
|
||||||
return None;
|
(1, false) => TerminatorKind::Goto { target: otherwise },
|
||||||
|
(2, true) => {
|
||||||
|
// All targets are unreachable except one. Record the equality, and make it a goto.
|
||||||
|
let (value, target) = new_targets.iter().next().unwrap();
|
||||||
|
add_assumption(BinOp::Eq, value);
|
||||||
|
TerminatorKind::Goto { target }
|
||||||
}
|
}
|
||||||
|
_ if num_targets == targets.all_targets().len() => {
|
||||||
|
// Nothing has changed.
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
_ => return None,
|
_ => TerminatorKind::SwitchInt { discr: discr.clone(), targets: new_targets },
|
||||||
};
|
};
|
||||||
Some(terminator)
|
|
||||||
|
patch.patch_terminator(bb, terminator);
|
||||||
|
fully_unreachable
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
+ debug self => _2;
|
+ debug self => _2;
|
||||||
+ let mut _3: &std::option::Option<T>;
|
+ let mut _3: &std::option::Option<T>;
|
||||||
+ let mut _4: isize;
|
+ let mut _4: isize;
|
||||||
|
+ let mut _5: bool;
|
||||||
+ scope 2 {
|
+ scope 2 {
|
||||||
+ debug val => _0;
|
+ debug val => _0;
|
||||||
+ }
|
+ }
|
||||||
|
@ -29,18 +30,17 @@
|
||||||
StorageLive(_2);
|
StorageLive(_2);
|
||||||
_2 = move _1;
|
_2 = move _1;
|
||||||
- _0 = Option::<T>::unwrap_unchecked(move _2) -> [return: bb1, unwind unreachable];
|
- _0 = Option::<T>::unwrap_unchecked(move _2) -> [return: bb1, unwind unreachable];
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bb1: {
|
||||||
+ StorageLive(_3);
|
+ StorageLive(_3);
|
||||||
+ StorageLive(_4);
|
+ StorageLive(_4);
|
||||||
|
+ StorageLive(_5);
|
||||||
+ _4 = discriminant(_2);
|
+ _4 = discriminant(_2);
|
||||||
+ switchInt(move _4) -> [1: bb2, otherwise: bb1];
|
+ _5 = Eq(_4, const 1_isize);
|
||||||
}
|
+ assume(move _5);
|
||||||
|
|
||||||
bb1: {
|
|
||||||
+ unreachable;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ bb2: {
|
|
||||||
+ _0 = move ((_2 as Some).0: T);
|
+ _0 = move ((_2 as Some).0: T);
|
||||||
|
+ StorageDead(_5);
|
||||||
+ StorageDead(_4);
|
+ StorageDead(_4);
|
||||||
+ StorageDead(_3);
|
+ StorageDead(_3);
|
||||||
StorageDead(_2);
|
StorageDead(_2);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
+ debug self => _2;
|
+ debug self => _2;
|
||||||
+ let mut _3: &std::option::Option<T>;
|
+ let mut _3: &std::option::Option<T>;
|
||||||
+ let mut _4: isize;
|
+ let mut _4: isize;
|
||||||
|
+ let mut _5: bool;
|
||||||
+ scope 2 {
|
+ scope 2 {
|
||||||
+ debug val => _0;
|
+ debug val => _0;
|
||||||
+ }
|
+ }
|
||||||
|
@ -29,26 +30,25 @@
|
||||||
StorageLive(_2);
|
StorageLive(_2);
|
||||||
_2 = move _1;
|
_2 = move _1;
|
||||||
- _0 = Option::<T>::unwrap_unchecked(move _2) -> [return: bb1, unwind: bb2];
|
- _0 = Option::<T>::unwrap_unchecked(move _2) -> [return: bb1, unwind: bb2];
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bb1: {
|
||||||
+ StorageLive(_3);
|
+ StorageLive(_3);
|
||||||
+ StorageLive(_4);
|
+ StorageLive(_4);
|
||||||
|
+ StorageLive(_5);
|
||||||
+ _4 = discriminant(_2);
|
+ _4 = discriminant(_2);
|
||||||
+ switchInt(move _4) -> [1: bb2, otherwise: bb1];
|
+ _5 = Eq(_4, const 1_isize);
|
||||||
}
|
+ assume(move _5);
|
||||||
|
|
||||||
bb1: {
|
|
||||||
- StorageDead(_2);
|
|
||||||
- return;
|
|
||||||
+ unreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
- bb2 (cleanup): {
|
|
||||||
- resume;
|
|
||||||
+ bb2: {
|
|
||||||
+ _0 = move ((_2 as Some).0: T);
|
+ _0 = move ((_2 as Some).0: T);
|
||||||
|
+ StorageDead(_5);
|
||||||
+ StorageDead(_4);
|
+ StorageDead(_4);
|
||||||
+ StorageDead(_3);
|
+ StorageDead(_3);
|
||||||
+ StorageDead(_2);
|
StorageDead(_2);
|
||||||
+ return;
|
return;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- bb2 (cleanup): {
|
||||||
|
- resume;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,8 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
|
||||||
scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) {
|
scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) {
|
||||||
debug self => _1;
|
debug self => _1;
|
||||||
let mut _2: isize;
|
let mut _2: isize;
|
||||||
let mut _3: &std::option::Option<T>;
|
let mut _3: bool;
|
||||||
|
let mut _4: &std::option::Option<T>;
|
||||||
scope 2 {
|
scope 2 {
|
||||||
debug val => _0;
|
debug val => _0;
|
||||||
}
|
}
|
||||||
|
@ -19,25 +20,21 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scope 4 (inlined Option::<T>::is_some) {
|
scope 4 (inlined Option::<T>::is_some) {
|
||||||
debug self => _3;
|
debug self => _4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bb0: {
|
bb0: {
|
||||||
StorageLive(_3);
|
StorageLive(_4);
|
||||||
StorageLive(_2);
|
StorageLive(_2);
|
||||||
|
StorageLive(_3);
|
||||||
_2 = discriminant(_1);
|
_2 = discriminant(_1);
|
||||||
switchInt(move _2) -> [1: bb1, otherwise: bb2];
|
_3 = Eq(_2, const 1_isize);
|
||||||
}
|
assume(move _3);
|
||||||
|
|
||||||
bb1: {
|
|
||||||
_0 = move ((_1 as Some).0: T);
|
_0 = move ((_1 as Some).0: T);
|
||||||
StorageDead(_2);
|
|
||||||
StorageDead(_3);
|
StorageDead(_3);
|
||||||
|
StorageDead(_2);
|
||||||
|
StorageDead(_4);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb2: {
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,8 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
|
||||||
scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) {
|
scope 1 (inlined #[track_caller] Option::<T>::unwrap_unchecked) {
|
||||||
debug self => _1;
|
debug self => _1;
|
||||||
let mut _2: isize;
|
let mut _2: isize;
|
||||||
let mut _3: &std::option::Option<T>;
|
let mut _3: bool;
|
||||||
|
let mut _4: &std::option::Option<T>;
|
||||||
scope 2 {
|
scope 2 {
|
||||||
debug val => _0;
|
debug val => _0;
|
||||||
}
|
}
|
||||||
|
@ -19,25 +20,21 @@ fn unwrap_unchecked(_1: Option<T>) -> T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scope 4 (inlined Option::<T>::is_some) {
|
scope 4 (inlined Option::<T>::is_some) {
|
||||||
debug self => _3;
|
debug self => _4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bb0: {
|
bb0: {
|
||||||
StorageLive(_3);
|
StorageLive(_4);
|
||||||
StorageLive(_2);
|
StorageLive(_2);
|
||||||
|
StorageLive(_3);
|
||||||
_2 = discriminant(_1);
|
_2 = discriminant(_1);
|
||||||
switchInt(move _2) -> [1: bb1, otherwise: bb2];
|
_3 = Eq(_2, const 1_isize);
|
||||||
}
|
assume(move _3);
|
||||||
|
|
||||||
bb1: {
|
|
||||||
_0 = move ((_1 as Some).0: T);
|
_0 = move ((_1 as Some).0: T);
|
||||||
StorageDead(_2);
|
|
||||||
StorageDead(_3);
|
StorageDead(_3);
|
||||||
|
StorageDead(_2);
|
||||||
|
StorageDead(_4);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb2: {
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ fn ub_if_b(_1: Thing) -> Thing {
|
||||||
debug t => _1;
|
debug t => _1;
|
||||||
let mut _0: Thing;
|
let mut _0: Thing;
|
||||||
let mut _2: isize;
|
let mut _2: isize;
|
||||||
|
let mut _3: bool;
|
||||||
scope 1 (inlined unreachable_unchecked) {
|
scope 1 (inlined unreachable_unchecked) {
|
||||||
scope 2 {
|
scope 2 {
|
||||||
scope 3 (inlined unreachable_unchecked::runtime) {
|
scope 3 (inlined unreachable_unchecked::runtime) {
|
||||||
|
@ -13,15 +14,9 @@ fn ub_if_b(_1: Thing) -> Thing {
|
||||||
|
|
||||||
bb0: {
|
bb0: {
|
||||||
_2 = discriminant(_1);
|
_2 = discriminant(_1);
|
||||||
switchInt(move _2) -> [0: bb1, otherwise: bb2];
|
_3 = Eq(_2, const 0_isize);
|
||||||
}
|
assume(move _3);
|
||||||
|
|
||||||
bb1: {
|
|
||||||
_0 = move _1;
|
_0 = move _1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb2: {
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
StorageLive(_10);
|
StorageLive(_10);
|
||||||
StorageLive(_11);
|
StorageLive(_11);
|
||||||
_9 = discriminant(_1);
|
_9 = discriminant(_1);
|
||||||
switchInt(move _9) -> [0: bb7, 1: bb5, otherwise: bb6];
|
switchInt(move _9) -> [0: bb6, 1: bb5, otherwise: bb3];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb1: {
|
bb1: {
|
||||||
|
@ -92,10 +92,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
bb6: {
|
bb6: {
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb7: {
|
|
||||||
_10 = ((_1 as Ok).0: i32);
|
_10 = ((_1 as Ok).0: i32);
|
||||||
_3 = ControlFlow::<Result<Infallible, i32>, i32>::Continue(move _10);
|
_3 = ControlFlow::<Result<Infallible, i32>, i32>::Continue(move _10);
|
||||||
goto -> bb1;
|
goto -> bb1;
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
- // MIR for `main` before UninhabitedEnumBranching
|
- // MIR for `byref` before UninhabitedEnumBranching
|
||||||
+ // MIR for `main` after UninhabitedEnumBranching
|
+ // MIR for `byref` after UninhabitedEnumBranching
|
||||||
|
|
||||||
fn main() -> () {
|
fn byref() -> () {
|
||||||
let mut _0: ();
|
let mut _0: ();
|
||||||
let _1: Plop;
|
let _1: Plop;
|
||||||
let mut _2: Test1;
|
let mut _2: Test3;
|
||||||
let _3: &str;
|
let _3: &str;
|
||||||
let mut _4: &Test1;
|
let mut _4: &Test3;
|
||||||
let mut _5: isize;
|
let mut _5: isize;
|
||||||
let _6: &str;
|
let _6: &str;
|
||||||
let _7: &str;
|
let _7: &str;
|
||||||
|
@ -23,15 +23,15 @@
|
||||||
bb0: {
|
bb0: {
|
||||||
StorageLive(_1);
|
StorageLive(_1);
|
||||||
StorageLive(_2);
|
StorageLive(_2);
|
||||||
_2 = Test1::C;
|
_2 = Test3::C;
|
||||||
_1 = Plop { xx: const 51_u32, test1: move _2 };
|
_1 = Plop { xx: const 51_u32, test3: move _2 };
|
||||||
StorageDead(_2);
|
StorageDead(_2);
|
||||||
StorageLive(_3);
|
StorageLive(_3);
|
||||||
StorageLive(_4);
|
StorageLive(_4);
|
||||||
_4 = &(_1.1: Test1);
|
_4 = &(_1.1: Test3);
|
||||||
_5 = discriminant((*_4));
|
_5 = discriminant((*_4));
|
||||||
- switchInt(move _5) -> [0: bb3, 1: bb4, 2: bb5, 3: bb1, otherwise: bb2];
|
- switchInt(move _5) -> [0: bb3, 1: bb4, 2: bb5, 3: bb1, otherwise: bb2];
|
||||||
+ switchInt(move _5) -> [2: bb5, 3: bb1, otherwise: bb2];
|
+ switchInt(move _5) -> [0: bb12, 1: bb12, 2: bb5, 3: bb1, otherwise: bb12];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb1: {
|
bb1: {
|
||||||
|
@ -71,9 +71,9 @@
|
||||||
StorageDead(_4);
|
StorageDead(_4);
|
||||||
StorageDead(_3);
|
StorageDead(_3);
|
||||||
StorageLive(_9);
|
StorageLive(_9);
|
||||||
_10 = discriminant((_1.1: Test1));
|
_10 = discriminant((_1.1: Test3));
|
||||||
- switchInt(move _10) -> [0: bb8, 1: bb9, 2: bb10, 3: bb7, otherwise: bb2];
|
- switchInt(move _10) -> [0: bb8, 1: bb9, 2: bb10, 3: bb7, otherwise: bb2];
|
||||||
+ switchInt(move _10) -> [2: bb10, 3: bb7, otherwise: bb2];
|
+ switchInt(move _10) -> [0: bb12, 1: bb12, 2: bb10, 3: bb7, otherwise: bb12];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb7: {
|
bb7: {
|
||||||
|
@ -110,6 +110,10 @@
|
||||||
_0 = const ();
|
_0 = const ();
|
||||||
StorageDead(_1);
|
StorageDead(_1);
|
||||||
return;
|
return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ bb12: {
|
||||||
|
+ unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
- // MIR for `custom_discriminant` before UninhabitedEnumBranching
|
||||||
|
+ // MIR for `custom_discriminant` after UninhabitedEnumBranching
|
||||||
|
|
||||||
|
fn custom_discriminant() -> () {
|
||||||
|
let mut _0: ();
|
||||||
|
let _1: &str;
|
||||||
|
let mut _2: Test2;
|
||||||
|
let mut _3: isize;
|
||||||
|
let _4: &str;
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
StorageLive(_1);
|
||||||
|
StorageLive(_2);
|
||||||
|
_2 = Test2::D;
|
||||||
|
_3 = discriminant(_2);
|
||||||
|
- switchInt(move _3) -> [4: bb3, 5: bb1, otherwise: bb2];
|
||||||
|
+ switchInt(move _3) -> [4: bb3, 5: bb1, otherwise: bb5];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb1: {
|
||||||
|
StorageLive(_4);
|
||||||
|
_4 = const "E";
|
||||||
|
_1 = &(*_4);
|
||||||
|
StorageDead(_4);
|
||||||
|
goto -> bb4;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb2: {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb3: {
|
||||||
|
_1 = const "D";
|
||||||
|
goto -> bb4;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb4: {
|
||||||
|
StorageDead(_2);
|
||||||
|
StorageDead(_1);
|
||||||
|
_0 = const ();
|
||||||
|
return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ bb5: {
|
||||||
|
+ unreachable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
// MIR for `main` after SimplifyCfg-after-uninhabited-enum-branching
|
|
||||||
|
|
||||||
fn main() -> () {
|
|
||||||
let mut _0: ();
|
|
||||||
let _1: &str;
|
|
||||||
let mut _2: Test1;
|
|
||||||
let mut _3: isize;
|
|
||||||
let _4: &str;
|
|
||||||
let _5: &str;
|
|
||||||
let _6: &str;
|
|
||||||
let mut _7: Test2;
|
|
||||||
let mut _8: isize;
|
|
||||||
let _9: &str;
|
|
||||||
|
|
||||||
bb0: {
|
|
||||||
StorageLive(_1);
|
|
||||||
StorageLive(_2);
|
|
||||||
_2 = Test1::C;
|
|
||||||
_3 = discriminant(_2);
|
|
||||||
switchInt(move _3) -> [2: bb1, otherwise: bb2];
|
|
||||||
}
|
|
||||||
|
|
||||||
bb1: {
|
|
||||||
StorageLive(_5);
|
|
||||||
_5 = const "C";
|
|
||||||
_1 = &(*_5);
|
|
||||||
StorageDead(_5);
|
|
||||||
StorageDead(_2);
|
|
||||||
StorageDead(_1);
|
|
||||||
StorageLive(_6);
|
|
||||||
StorageLive(_7);
|
|
||||||
_7 = Test2::D;
|
|
||||||
_8 = discriminant(_7);
|
|
||||||
switchInt(move _8) -> [4: bb4, 5: bb3, otherwise: bb2];
|
|
||||||
}
|
|
||||||
|
|
||||||
bb2: {
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb3: {
|
|
||||||
StorageLive(_9);
|
|
||||||
_9 = const "E";
|
|
||||||
_6 = &(*_9);
|
|
||||||
StorageDead(_9);
|
|
||||||
goto -> bb5;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb4: {
|
|
||||||
_6 = const "D";
|
|
||||||
goto -> bb5;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb5: {
|
|
||||||
StorageDead(_7);
|
|
||||||
StorageDead(_6);
|
|
||||||
_0 = const ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +1,11 @@
|
||||||
// skip-filecheck
|
// unit-test: UninhabitedEnumBranching
|
||||||
enum Empty { }
|
enum Empty {}
|
||||||
|
|
||||||
// test matching an enum with uninhabited variants
|
// test matching an enum with uninhabited variants
|
||||||
enum Test1 {
|
enum Test1 {
|
||||||
A(Empty),
|
A(Empty),
|
||||||
B(Empty),
|
B(Empty),
|
||||||
C
|
C,
|
||||||
}
|
}
|
||||||
|
|
||||||
// test an enum where the discriminants don't match the variant indexes
|
// test an enum where the discriminants don't match the variant indexes
|
||||||
|
@ -15,17 +15,75 @@ enum Test2 {
|
||||||
E = 5,
|
E = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
// EMIT_MIR uninhabited_enum_branching.main.UninhabitedEnumBranching.diff
|
// test matching an enum with uninhabited variants and multiple inhabited
|
||||||
// EMIT_MIR uninhabited_enum_branching.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir
|
enum Test3 {
|
||||||
fn main() {
|
A(Empty),
|
||||||
|
B(Empty),
|
||||||
|
C,
|
||||||
|
D,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Plop {
|
||||||
|
xx: u32,
|
||||||
|
test3: Test3,
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMIT_MIR uninhabited_enum_branching.simple.UninhabitedEnumBranching.diff
|
||||||
|
fn simple() {
|
||||||
|
// CHECK-LABEL: fn simple(
|
||||||
|
// CHECK: [[discr:_.*]] = discriminant(
|
||||||
|
// CHECK: switchInt(move [[discr]]) -> [0: [[unreachable:bb.*]], 1: [[unreachable]], 2: bb1, otherwise: [[unreachable]]];
|
||||||
|
// CHECK: [[unreachable]]: {
|
||||||
|
// CHECK-NEXT: unreachable;
|
||||||
match Test1::C {
|
match Test1::C {
|
||||||
Test1::A(_) => "A(Empty)",
|
Test1::A(_) => "A(Empty)",
|
||||||
Test1::B(_) => "B(Empty)",
|
Test1::B(_) => "B(Empty)",
|
||||||
Test1::C => "C",
|
Test1::C => "C",
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMIT_MIR uninhabited_enum_branching.custom_discriminant.UninhabitedEnumBranching.diff
|
||||||
|
fn custom_discriminant() {
|
||||||
|
// CHECK-LABEL: fn custom_discriminant(
|
||||||
|
// CHECK: [[discr:_.*]] = discriminant(
|
||||||
|
// CHECK: switchInt(move [[discr]]) -> [4: bb3, 5: bb1, otherwise: bb5];
|
||||||
|
// CHECK: bb5: {
|
||||||
|
// CHECK-NEXT: unreachable;
|
||||||
match Test2::D {
|
match Test2::D {
|
||||||
Test2::D => "D",
|
Test2::D => "D",
|
||||||
Test2::E => "E",
|
Test2::E => "E",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EMIT_MIR uninhabited_enum_branching.byref.UninhabitedEnumBranching.diff
|
||||||
|
fn byref() {
|
||||||
|
// CHECK-LABEL: fn byref(
|
||||||
|
let plop = Plop { xx: 51, test3: Test3::C };
|
||||||
|
|
||||||
|
// CHECK: [[ref_discr:_.*]] = discriminant((*
|
||||||
|
// CHECK: switchInt(move [[ref_discr]]) -> [0: [[unreachable:bb.*]], 1: [[unreachable]], 2: bb5, 3: bb1, otherwise: [[unreachable]]];
|
||||||
|
match &plop.test3 {
|
||||||
|
Test3::A(_) => "A(Empty)",
|
||||||
|
Test3::B(_) => "B(Empty)",
|
||||||
|
Test3::C => "C",
|
||||||
|
Test3::D => "D",
|
||||||
|
};
|
||||||
|
|
||||||
|
// CHECK: [[discr:_.*]] = discriminant(
|
||||||
|
// CHECK: switchInt(move [[discr]]) -> [0: [[unreachable]], 1: [[unreachable]], 2: bb10, 3: bb7, otherwise: [[unreachable]]];
|
||||||
|
match plop.test3 {
|
||||||
|
Test3::A(_) => "A(Empty)",
|
||||||
|
Test3::B(_) => "B(Empty)",
|
||||||
|
Test3::C => "C",
|
||||||
|
Test3::D => "D",
|
||||||
|
};
|
||||||
|
|
||||||
|
// CHECK: [[unreachable]]: {
|
||||||
|
// CHECK-NEXT: unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
simple();
|
||||||
|
custom_discriminant();
|
||||||
|
byref();
|
||||||
|
}
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
- // MIR for `main` before UninhabitedEnumBranching
|
- // MIR for `simple` before UninhabitedEnumBranching
|
||||||
+ // MIR for `main` after UninhabitedEnumBranching
|
+ // MIR for `simple` after UninhabitedEnumBranching
|
||||||
|
|
||||||
fn main() -> () {
|
fn simple() -> () {
|
||||||
let mut _0: ();
|
let mut _0: ();
|
||||||
let _1: &str;
|
let _1: &str;
|
||||||
let mut _2: Test1;
|
let mut _2: Test1;
|
||||||
let mut _3: isize;
|
let mut _3: isize;
|
||||||
let _4: &str;
|
let _4: &str;
|
||||||
let _5: &str;
|
let _5: &str;
|
||||||
let _6: &str;
|
|
||||||
let mut _7: Test2;
|
|
||||||
let mut _8: isize;
|
|
||||||
let _9: &str;
|
|
||||||
|
|
||||||
bb0: {
|
bb0: {
|
||||||
StorageLive(_1);
|
StorageLive(_1);
|
||||||
|
@ -19,7 +15,7 @@
|
||||||
_2 = Test1::C;
|
_2 = Test1::C;
|
||||||
_3 = discriminant(_2);
|
_3 = discriminant(_2);
|
||||||
- switchInt(move _3) -> [0: bb3, 1: bb4, 2: bb1, otherwise: bb2];
|
- switchInt(move _3) -> [0: bb3, 1: bb4, 2: bb1, otherwise: bb2];
|
||||||
+ switchInt(move _3) -> [2: bb1, otherwise: bb2];
|
+ switchInt(move _3) -> [0: bb6, 1: bb6, 2: bb1, otherwise: bb6];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb1: {
|
bb1: {
|
||||||
|
@ -50,31 +46,12 @@
|
||||||
bb5: {
|
bb5: {
|
||||||
StorageDead(_2);
|
StorageDead(_2);
|
||||||
StorageDead(_1);
|
StorageDead(_1);
|
||||||
StorageLive(_6);
|
|
||||||
StorageLive(_7);
|
|
||||||
_7 = Test2::D;
|
|
||||||
_8 = discriminant(_7);
|
|
||||||
switchInt(move _8) -> [4: bb7, 5: bb6, otherwise: bb2];
|
|
||||||
}
|
|
||||||
|
|
||||||
bb6: {
|
|
||||||
StorageLive(_9);
|
|
||||||
_9 = const "E";
|
|
||||||
_6 = &(*_9);
|
|
||||||
StorageDead(_9);
|
|
||||||
goto -> bb8;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb7: {
|
|
||||||
_6 = const "D";
|
|
||||||
goto -> bb8;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb8: {
|
|
||||||
StorageDead(_7);
|
|
||||||
StorageDead(_6);
|
|
||||||
_0 = const ();
|
_0 = const ();
|
||||||
return;
|
return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ bb6: {
|
||||||
|
+ unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
// MIR for `main` after SimplifyCfg-after-uninhabited-enum-branching
|
|
||||||
|
|
||||||
fn main() -> () {
|
|
||||||
let mut _0: ();
|
|
||||||
let _1: Plop;
|
|
||||||
let mut _2: Test1;
|
|
||||||
let _3: &str;
|
|
||||||
let mut _4: &Test1;
|
|
||||||
let mut _5: isize;
|
|
||||||
let _6: &str;
|
|
||||||
let _7: &str;
|
|
||||||
let _8: &str;
|
|
||||||
let _9: &str;
|
|
||||||
let mut _10: isize;
|
|
||||||
let _11: &str;
|
|
||||||
let _12: &str;
|
|
||||||
let _13: &str;
|
|
||||||
scope 1 {
|
|
||||||
debug plop => _1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb0: {
|
|
||||||
StorageLive(_1);
|
|
||||||
StorageLive(_2);
|
|
||||||
_2 = Test1::C;
|
|
||||||
_1 = Plop { xx: const 51_u32, test1: move _2 };
|
|
||||||
StorageDead(_2);
|
|
||||||
StorageLive(_3);
|
|
||||||
StorageLive(_4);
|
|
||||||
_4 = &(_1.1: Test1);
|
|
||||||
_5 = discriminant((*_4));
|
|
||||||
switchInt(move _5) -> [2: bb3, 3: bb1, otherwise: bb2];
|
|
||||||
}
|
|
||||||
|
|
||||||
bb1: {
|
|
||||||
StorageLive(_8);
|
|
||||||
_8 = const "D";
|
|
||||||
_3 = &(*_8);
|
|
||||||
StorageDead(_8);
|
|
||||||
goto -> bb4;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb2: {
|
|
||||||
unreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb3: {
|
|
||||||
StorageLive(_7);
|
|
||||||
_7 = const "C";
|
|
||||||
_3 = &(*_7);
|
|
||||||
StorageDead(_7);
|
|
||||||
goto -> bb4;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb4: {
|
|
||||||
StorageDead(_4);
|
|
||||||
StorageDead(_3);
|
|
||||||
StorageLive(_9);
|
|
||||||
_10 = discriminant((_1.1: Test1));
|
|
||||||
switchInt(move _10) -> [2: bb6, 3: bb5, otherwise: bb2];
|
|
||||||
}
|
|
||||||
|
|
||||||
bb5: {
|
|
||||||
StorageLive(_13);
|
|
||||||
_13 = const "D";
|
|
||||||
_9 = &(*_13);
|
|
||||||
StorageDead(_13);
|
|
||||||
goto -> bb7;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb6: {
|
|
||||||
StorageLive(_12);
|
|
||||||
_12 = const "C";
|
|
||||||
_9 = &(*_12);
|
|
||||||
StorageDead(_12);
|
|
||||||
goto -> bb7;
|
|
||||||
}
|
|
||||||
|
|
||||||
bb7: {
|
|
||||||
StorageDead(_9);
|
|
||||||
_0 = const ();
|
|
||||||
StorageDead(_1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
// skip-filecheck
|
|
||||||
enum Empty { }
|
|
||||||
|
|
||||||
// test matching an enum with uninhabited variants
|
|
||||||
enum Test1 {
|
|
||||||
A(Empty),
|
|
||||||
B(Empty),
|
|
||||||
C,
|
|
||||||
D,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Plop {
|
|
||||||
xx: u32,
|
|
||||||
test1: Test1,
|
|
||||||
}
|
|
||||||
|
|
||||||
// EMIT_MIR uninhabited_enum_branching2.main.UninhabitedEnumBranching.diff
|
|
||||||
// EMIT_MIR uninhabited_enum_branching2.main.SimplifyCfg-after-uninhabited-enum-branching.after.mir
|
|
||||||
fn main() {
|
|
||||||
let plop = Plop { xx: 51, test1: Test1::C };
|
|
||||||
|
|
||||||
match &plop.test1 {
|
|
||||||
Test1::A(_) => "A(Empty)",
|
|
||||||
Test1::B(_) => "B(Empty)",
|
|
||||||
Test1::C => "C",
|
|
||||||
Test1::D => "D",
|
|
||||||
};
|
|
||||||
|
|
||||||
match plop.test1 {
|
|
||||||
Test1::A(_) => "A(Empty)",
|
|
||||||
Test1::B(_) => "B(Empty)",
|
|
||||||
Test1::C => "C",
|
|
||||||
Test1::D => "D",
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -9,7 +9,7 @@
|
||||||
bb0: {
|
bb0: {
|
||||||
_2 = discriminant(_1);
|
_2 = discriminant(_1);
|
||||||
- switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1];
|
- switchInt(move _2) -> [0: bb2, 1: bb3, otherwise: bb1];
|
||||||
+ switchInt(move _2) -> [1: bb3, otherwise: bb1];
|
+ switchInt(move _2) -> [0: bb5, 1: bb3, otherwise: bb1];
|
||||||
}
|
}
|
||||||
|
|
||||||
bb1: {
|
bb1: {
|
||||||
|
@ -29,6 +29,10 @@
|
||||||
|
|
||||||
bb4: {
|
bb4: {
|
||||||
return;
|
return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ bb5: {
|
||||||
|
+ unreachable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
- // MIR for `as_match` before UnreachablePropagation
|
||||||
|
+ // MIR for `as_match` after UnreachablePropagation
|
||||||
|
|
||||||
|
fn as_match() -> () {
|
||||||
|
let mut _0: ();
|
||||||
|
let mut _1: std::option::Option<Empty>;
|
||||||
|
let mut _2: isize;
|
||||||
|
let _3: Empty;
|
||||||
|
let mut _4: !;
|
||||||
|
+ let mut _5: bool;
|
||||||
|
scope 1 {
|
||||||
|
debug _x => _3;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
StorageLive(_1);
|
||||||
|
_1 = empty() -> [return: bb1, unwind unreachable];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb1: {
|
||||||
|
_2 = discriminant(_1);
|
||||||
|
- switchInt(move _2) -> [0: bb4, 1: bb2, otherwise: bb3];
|
||||||
|
+ _5 = Eq(_2, const 0_isize);
|
||||||
|
+ assume(move _5);
|
||||||
|
+ goto -> bb4;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb2: {
|
||||||
|
- StorageLive(_3);
|
||||||
|
- _3 = move ((_1 as Some).0: Empty);
|
||||||
|
- StorageLive(_4);
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb3: {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb4: {
|
||||||
|
_0 = const ();
|
||||||
|
StorageDead(_1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
- // MIR for `as_match` before UnreachablePropagation
|
||||||
|
+ // MIR for `as_match` after UnreachablePropagation
|
||||||
|
|
||||||
|
fn as_match() -> () {
|
||||||
|
let mut _0: ();
|
||||||
|
let mut _1: std::option::Option<Empty>;
|
||||||
|
let mut _2: isize;
|
||||||
|
let _3: Empty;
|
||||||
|
let mut _4: !;
|
||||||
|
+ let mut _5: bool;
|
||||||
|
scope 1 {
|
||||||
|
debug _x => _3;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
StorageLive(_1);
|
||||||
|
_1 = empty() -> [return: bb1, unwind continue];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb1: {
|
||||||
|
_2 = discriminant(_1);
|
||||||
|
- switchInt(move _2) -> [0: bb4, 1: bb2, otherwise: bb3];
|
||||||
|
+ _5 = Eq(_2, const 0_isize);
|
||||||
|
+ assume(move _5);
|
||||||
|
+ goto -> bb4;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb2: {
|
||||||
|
- StorageLive(_3);
|
||||||
|
- _3 = move ((_1 as Some).0: Empty);
|
||||||
|
- StorageLive(_4);
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb3: {
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb4: {
|
||||||
|
_0 = const ();
|
||||||
|
StorageDead(_1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
- // MIR for `main` before UnreachablePropagation
|
- // MIR for `if_let` before UnreachablePropagation
|
||||||
+ // MIR for `main` after UnreachablePropagation
|
+ // MIR for `if_let` after UnreachablePropagation
|
||||||
|
|
||||||
fn main() -> () {
|
fn if_let() -> () {
|
||||||
let mut _0: ();
|
let mut _0: ();
|
||||||
let mut _1: std::option::Option<Empty>;
|
let mut _1: std::option::Option<Empty>;
|
||||||
let mut _2: isize;
|
let mut _2: isize;
|
||||||
let _5: ();
|
let _5: ();
|
||||||
let mut _6: bool;
|
let mut _6: bool;
|
||||||
let mut _7: !;
|
let mut _7: !;
|
||||||
|
+ let mut _8: bool;
|
||||||
scope 1 {
|
scope 1 {
|
||||||
debug _x => _3;
|
debug _x => _3;
|
||||||
let _3: Empty;
|
let _3: Empty;
|
||||||
|
@ -25,7 +26,9 @@
|
||||||
bb1: {
|
bb1: {
|
||||||
_2 = discriminant(_1);
|
_2 = discriminant(_1);
|
||||||
- switchInt(move _2) -> [1: bb2, otherwise: bb6];
|
- switchInt(move _2) -> [1: bb2, otherwise: bb6];
|
||||||
+ switchInt(move _2) -> [1: bb2, otherwise: bb3];
|
+ _8 = Ne(_2, const 1_isize);
|
||||||
|
+ assume(move _8);
|
||||||
|
+ goto -> bb6;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb2: {
|
bb2: {
|
||||||
|
@ -36,29 +39,31 @@
|
||||||
- StorageLive(_6);
|
- StorageLive(_6);
|
||||||
- _6 = const true;
|
- _6 = const true;
|
||||||
- switchInt(move _6) -> [0: bb4, otherwise: bb3];
|
- switchInt(move _6) -> [0: bb4, otherwise: bb3];
|
||||||
- }
|
+ unreachable;
|
||||||
-
|
}
|
||||||
- bb3: {
|
|
||||||
|
bb3: {
|
||||||
- _4 = const 21_i32;
|
- _4 = const 21_i32;
|
||||||
- _5 = const ();
|
- _5 = const ();
|
||||||
- goto -> bb5;
|
- goto -> bb5;
|
||||||
- }
|
+ unreachable;
|
||||||
-
|
}
|
||||||
- bb4: {
|
|
||||||
|
bb4: {
|
||||||
- _4 = const 42_i32;
|
- _4 = const 42_i32;
|
||||||
- _5 = const ();
|
- _5 = const ();
|
||||||
- goto -> bb5;
|
- goto -> bb5;
|
||||||
- }
|
+ unreachable;
|
||||||
-
|
}
|
||||||
- bb5: {
|
|
||||||
|
bb5: {
|
||||||
- StorageDead(_6);
|
- StorageDead(_6);
|
||||||
- StorageDead(_5);
|
- StorageDead(_5);
|
||||||
- StorageLive(_7);
|
- StorageLive(_7);
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
- bb6: {
|
bb6: {
|
||||||
+ bb3: {
|
|
||||||
_0 = const ();
|
_0 = const ();
|
||||||
StorageDead(_1);
|
StorageDead(_1);
|
||||||
return;
|
return;
|
|
@ -1,13 +1,14 @@
|
||||||
- // MIR for `main` before UnreachablePropagation
|
- // MIR for `if_let` before UnreachablePropagation
|
||||||
+ // MIR for `main` after UnreachablePropagation
|
+ // MIR for `if_let` after UnreachablePropagation
|
||||||
|
|
||||||
fn main() -> () {
|
fn if_let() -> () {
|
||||||
let mut _0: ();
|
let mut _0: ();
|
||||||
let mut _1: std::option::Option<Empty>;
|
let mut _1: std::option::Option<Empty>;
|
||||||
let mut _2: isize;
|
let mut _2: isize;
|
||||||
let _5: ();
|
let _5: ();
|
||||||
let mut _6: bool;
|
let mut _6: bool;
|
||||||
let mut _7: !;
|
let mut _7: !;
|
||||||
|
+ let mut _8: bool;
|
||||||
scope 1 {
|
scope 1 {
|
||||||
debug _x => _3;
|
debug _x => _3;
|
||||||
let _3: Empty;
|
let _3: Empty;
|
||||||
|
@ -25,7 +26,9 @@
|
||||||
bb1: {
|
bb1: {
|
||||||
_2 = discriminant(_1);
|
_2 = discriminant(_1);
|
||||||
- switchInt(move _2) -> [1: bb2, otherwise: bb6];
|
- switchInt(move _2) -> [1: bb2, otherwise: bb6];
|
||||||
+ switchInt(move _2) -> [1: bb2, otherwise: bb3];
|
+ _8 = Ne(_2, const 1_isize);
|
||||||
|
+ assume(move _8);
|
||||||
|
+ goto -> bb6;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb2: {
|
bb2: {
|
||||||
|
@ -36,29 +39,31 @@
|
||||||
- StorageLive(_6);
|
- StorageLive(_6);
|
||||||
- _6 = const true;
|
- _6 = const true;
|
||||||
- switchInt(move _6) -> [0: bb4, otherwise: bb3];
|
- switchInt(move _6) -> [0: bb4, otherwise: bb3];
|
||||||
- }
|
+ unreachable;
|
||||||
-
|
}
|
||||||
- bb3: {
|
|
||||||
|
bb3: {
|
||||||
- _4 = const 21_i32;
|
- _4 = const 21_i32;
|
||||||
- _5 = const ();
|
- _5 = const ();
|
||||||
- goto -> bb5;
|
- goto -> bb5;
|
||||||
- }
|
+ unreachable;
|
||||||
-
|
}
|
||||||
- bb4: {
|
|
||||||
|
bb4: {
|
||||||
- _4 = const 42_i32;
|
- _4 = const 42_i32;
|
||||||
- _5 = const ();
|
- _5 = const ();
|
||||||
- goto -> bb5;
|
- goto -> bb5;
|
||||||
- }
|
+ unreachable;
|
||||||
-
|
}
|
||||||
- bb5: {
|
|
||||||
|
bb5: {
|
||||||
- StorageDead(_6);
|
- StorageDead(_6);
|
||||||
- StorageDead(_5);
|
- StorageDead(_5);
|
||||||
- StorageLive(_7);
|
- StorageLive(_7);
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
- bb6: {
|
bb6: {
|
||||||
+ bb3: {
|
|
||||||
_0 = const ();
|
_0 = const ();
|
||||||
StorageDead(_1);
|
StorageDead(_1);
|
||||||
return;
|
return;
|
|
@ -1,13 +1,31 @@
|
||||||
// skip-filecheck
|
// unit-test: UnreachablePropagation
|
||||||
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
||||||
|
|
||||||
enum Empty {}
|
enum Empty {}
|
||||||
|
|
||||||
fn empty() -> Option<Empty> {
|
fn empty() -> Option<Empty> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
// EMIT_MIR unreachable.main.UnreachablePropagation.diff
|
// EMIT_MIR unreachable.if_let.UnreachablePropagation.diff
|
||||||
fn main() {
|
fn if_let() {
|
||||||
|
// CHECK-LABEL: fn if_let(
|
||||||
|
// CHECK: bb0: {
|
||||||
|
// CHECK: {{_.*}} = empty()
|
||||||
|
// CHECK: bb1: {
|
||||||
|
// CHECK: [[ne:_.*]] = Ne({{.*}}, const 1_isize);
|
||||||
|
// CHECK-NEXT: assume(move [[ne]]);
|
||||||
|
// CHECK-NEXT: goto -> bb6;
|
||||||
|
// CHECK: bb2: {
|
||||||
|
// CHECK-NEXT: unreachable;
|
||||||
|
// CHECK: bb3: {
|
||||||
|
// CHECK-NEXT: unreachable;
|
||||||
|
// CHECK: bb4: {
|
||||||
|
// CHECK-NEXT: unreachable;
|
||||||
|
// CHECK: bb5: {
|
||||||
|
// CHECK-NEXT: unreachable;
|
||||||
|
// CHECK: bb6: {
|
||||||
|
// CHECK: return;
|
||||||
if let Some(_x) = empty() {
|
if let Some(_x) = empty() {
|
||||||
let mut _y;
|
let mut _y;
|
||||||
|
|
||||||
|
@ -20,3 +38,29 @@ fn main() {
|
||||||
match _x { }
|
match _x { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EMIT_MIR unreachable.as_match.UnreachablePropagation.diff
|
||||||
|
fn as_match() {
|
||||||
|
// CHECK-LABEL: fn as_match(
|
||||||
|
// CHECK: bb0: {
|
||||||
|
// CHECK: {{_.*}} = empty()
|
||||||
|
// CHECK: bb1: {
|
||||||
|
// CHECK: [[eq:_.*]] = Eq({{.*}}, const 0_isize);
|
||||||
|
// CHECK-NEXT: assume(move [[eq]]);
|
||||||
|
// CHECK-NEXT: goto -> bb4;
|
||||||
|
// CHECK: bb2: {
|
||||||
|
// CHECK-NEXT: unreachable;
|
||||||
|
// CHECK: bb3: {
|
||||||
|
// CHECK-NEXT: unreachable;
|
||||||
|
// CHECK: bb4: {
|
||||||
|
// CHECK: return;
|
||||||
|
match empty() {
|
||||||
|
None => {}
|
||||||
|
Some(_x) => match _x {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if_let();
|
||||||
|
as_match();
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
let _5: ();
|
let _5: ();
|
||||||
let mut _6: bool;
|
let mut _6: bool;
|
||||||
let mut _7: !;
|
let mut _7: !;
|
||||||
|
+ let mut _8: bool;
|
||||||
scope 1 {
|
scope 1 {
|
||||||
debug x => _1;
|
debug x => _1;
|
||||||
scope 2 {
|
scope 2 {
|
||||||
|
@ -35,7 +36,10 @@
|
||||||
StorageLive(_5);
|
StorageLive(_5);
|
||||||
StorageLive(_6);
|
StorageLive(_6);
|
||||||
_6 = _1;
|
_6 = _1;
|
||||||
switchInt(move _6) -> [0: bb4, otherwise: bb3];
|
- switchInt(move _6) -> [0: bb4, otherwise: bb3];
|
||||||
|
+ _8 = Ne(_6, const false);
|
||||||
|
+ assume(move _8);
|
||||||
|
+ goto -> bb3;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb3: {
|
bb3: {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
let _5: ();
|
let _5: ();
|
||||||
let mut _6: bool;
|
let mut _6: bool;
|
||||||
let mut _7: !;
|
let mut _7: !;
|
||||||
|
+ let mut _8: bool;
|
||||||
scope 1 {
|
scope 1 {
|
||||||
debug x => _1;
|
debug x => _1;
|
||||||
scope 2 {
|
scope 2 {
|
||||||
|
@ -35,7 +36,10 @@
|
||||||
StorageLive(_5);
|
StorageLive(_5);
|
||||||
StorageLive(_6);
|
StorageLive(_6);
|
||||||
_6 = _1;
|
_6 = _1;
|
||||||
switchInt(move _6) -> [0: bb4, otherwise: bb3];
|
- switchInt(move _6) -> [0: bb4, otherwise: bb3];
|
||||||
|
+ _8 = Ne(_6, const false);
|
||||||
|
+ assume(move _8);
|
||||||
|
+ goto -> bb3;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb3: {
|
bb3: {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// skip-filecheck
|
// unit-test: UnreachablePropagation
|
||||||
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
|
||||||
|
|
||||||
pub enum Empty {}
|
pub enum Empty {}
|
||||||
|
|
||||||
fn empty() -> Option<Empty> {
|
fn empty() -> Option<Empty> {
|
||||||
|
@ -12,6 +13,23 @@ fn loop_forever() {
|
||||||
|
|
||||||
// EMIT_MIR unreachable_diverging.main.UnreachablePropagation.diff
|
// EMIT_MIR unreachable_diverging.main.UnreachablePropagation.diff
|
||||||
fn main() {
|
fn main() {
|
||||||
|
// CHECK-LABEL: fn main(
|
||||||
|
// CHECK: bb0: {
|
||||||
|
// CHECK: {{_.*}} = empty()
|
||||||
|
// CHECK: bb1: {
|
||||||
|
// CHECK: switchInt({{.*}}) -> [1: bb2, otherwise: bb6];
|
||||||
|
// CHECK: bb2: {
|
||||||
|
// CHECK: [[ne:_.*]] = Ne({{.*}}, const false);
|
||||||
|
// CHECK: assume(move [[ne]]);
|
||||||
|
// CHECK: goto -> bb3;
|
||||||
|
// CHECK: bb3: {
|
||||||
|
// CHECK: {{_.*}} = loop_forever()
|
||||||
|
// CHECK: bb4: {
|
||||||
|
// CHECK: unreachable;
|
||||||
|
// CHECK: bb5: {
|
||||||
|
// CHECK: unreachable;
|
||||||
|
// CHECK: bb6: {
|
||||||
|
// CHECK: return;
|
||||||
let x = true;
|
let x = true;
|
||||||
if let Some(bomb) = empty() {
|
if let Some(bomb) = empty() {
|
||||||
if x {
|
if x {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue