Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions compiler/rustc_mir_transform/src/uninhabited_enum_branching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
use crate::MirPass;
use rustc_data_structures::stable_set::FxHashSet;
use rustc_middle::mir::{
BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, SwitchTargets, TerminatorKind,
BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, SwitchTargets, Terminator,
TerminatorKind,
};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{Ty, TyCtxt};
Expand Down Expand Up @@ -71,6 +72,28 @@ 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 {
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
sess.mir_opt_level() > 0
Expand Down Expand Up @@ -99,12 +122,25 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
if let TerminatorKind::SwitchInt { targets, .. } =
&mut body.basic_blocks_mut()[bb].terminator_mut().kind
{
let new_targets = SwitchTargets::new(
let mut new_targets = SwitchTargets::new(
targets.iter().filter(|(val, _)| allowed_variants.contains(val)),
targets.otherwise(),
);

*targets = new_targets;
if new_targets.iter().count() == allowed_variants.len() {
if let Some(updated) = ensure_otherwise_unreachable(body, &new_targets) {
let new_otherwise = body.basic_blocks_mut().push(updated);
*new_targets.all_targets_mut().last_mut().unwrap() = new_otherwise;
}
}

if let TerminatorKind::SwitchInt { targets, .. } =
&mut body.basic_blocks_mut()[bb].terminator_mut().kind
{
*targets = new_targets;
} else {
unreachable!()
}
} else {
unreachable!()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch.rs:12:16: 12:17
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch.rs:12:16: 12:17
_8 = discriminant((_3.0: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:12:11: 12:17
- switchInt(move _8) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
- switchInt(move _8) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb7]; // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
+ StorageLive(_11); // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
+ _11 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
+ StorageLive(_12); // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
Expand Down Expand Up @@ -81,8 +81,10 @@
+ bb4: {
StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch.rs:17:1: 17:2
return; // scope 0 at $DIR/early_otherwise_branch.rs:17:2: 17:2
+ }
+
}

- bb7: {
- unreachable; // scope 0 at $DIR/early_otherwise_branch.rs:15:14: 15:15
+ bb5: {
+ StorageDead(_12); // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
+ switchInt(_8) -> [0_isize: bb3, 1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
- StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:23: 21:24
+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:23: 21:24
_11 = discriminant((*(_4.0: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
- switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
- switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb11]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ StorageLive(_34); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ _34 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ StorageLive(_35); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
Expand Down Expand Up @@ -217,14 +217,9 @@
- StorageDead(_13); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
- StorageDead(_12); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
- goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
+ discriminant(_0) = 0; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:27:6: 27:7
+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:1: 28:2
+ return; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
}

bb7: {
- }
-
- bb7: {
- StorageLive(_17); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
- _17 = (((*(_4.0: &ViewportPercentageLength)) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
- StorageLive(_18); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
Expand Down Expand Up @@ -289,10 +284,18 @@
-
- bb10: {
- ((_0 as Ok).0: ViewportPercentageLength) = move _3; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
- discriminant(_0) = 0; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
discriminant(_0) = 0; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
- StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch_68867.rs:27:6: 27:7
- StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:1: 28:2
- return; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:27:6: 27:7
+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:1: 28:2
return; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
}

- bb11: {
- unreachable; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
+ bb7: {
+ StorageDead(_35); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ switchInt(_11) -> [0_isize: bb2, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
StorageDead(_6); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:23: 21:24
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:23: 21:24
_11 = discriminant((*(_4.0: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
- switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
- switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb11]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ StorageLive(_34); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ _34 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ StorageLive(_35); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
Expand Down Expand Up @@ -206,8 +206,10 @@
StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch_68867.rs:27:6: 27:7
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:1: 28:2
return; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
+ }
+
}

- bb11: {
- unreachable; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
+ bb7: {
+ StorageDead(_35); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ switchInt(_11) -> [0_isize: bb2, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
- // MIR for `eliminate_fallthrough` before UninhabitedEnumBranching
+ // MIR for `eliminate_fallthrough` after UninhabitedEnumBranching

fn eliminate_fallthrough(_1: S) -> u32 {
debug s => _1; // in scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:21:26: 21:27
let mut _0: u32; // return place in scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:21:35: 21:38
let mut _2: isize; // in scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:23:9: 23:10

bb0: {
_2 = discriminant(_1); // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:11: 22:12
- switchInt(move _2) -> [1_isize: bb3, 2_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:5: 22:12
+ switchInt(move _2) -> [1_isize: bb3, 2_isize: bb2, otherwise: bb5]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:5: 22:12
}

bb1: {
_0 = const 3_u32; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:25:14: 25:15
goto -> bb4; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:25:14: 25:15
}

bb2: {
_0 = const 1_u32; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:23:14: 23:15
goto -> bb4; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:23:14: 23:15
}

bb3: {
_0 = const 2_u32; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:24:14: 24:15
goto -> bb4; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:24:14: 24:15
}

bb4: {
return; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:27:2: 27:2
+ }
+
+ bb5: {
+ unreachable; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:25:14: 25:15
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
- // MIR for `keep_fallthrough` before UninhabitedEnumBranching
+ // MIR for `keep_fallthrough` after UninhabitedEnumBranching

fn keep_fallthrough(_1: S) -> u32 {
debug s => _1; // in scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:12:21: 12:22
let mut _0: u32; // return place in scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:12:30: 12:33
let mut _2: isize; // in scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:14:9: 14:13

bb0: {
_2 = discriminant(_1); // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:13:11: 13:12
- switchInt(move _2) -> [0_isize: bb2, 1_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:13:5: 13:12
+ switchInt(move _2) -> [1_isize: bb3, otherwise: bb1]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:13:5: 13:12
}

bb1: {
_0 = const 3_u32; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:16:14: 16:15
goto -> bb4; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:16:14: 16:15
}

bb2: {
_0 = const 1_u32; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:14:17: 14:18
goto -> bb4; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:14:17: 14:18
}

bb3: {
_0 = const 2_u32; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:15:14: 15:15
goto -> bb4; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:15:14: 15:15
}

bb4: {
return; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:18:2: 18:2
}
}

32 changes: 32 additions & 0 deletions src/test/mir-opt/uninhabited_fallthrough_elimination.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
enum Empty {}

enum S {
A(Empty),
B,
C,
}

use S::*;

// EMIT_MIR uninhabited_fallthrough_elimination.keep_fallthrough.UninhabitedEnumBranching.diff
fn keep_fallthrough(s: S) -> u32 {
match s {
A(_) => 1,
B => 2,
_ => 3,
}
}

// EMIT_MIR uninhabited_fallthrough_elimination.eliminate_fallthrough.UninhabitedEnumBranching.diff
fn eliminate_fallthrough(s: S) -> u32 {
match s {
C => 1,
B => 2,
_ => 3,
}
}

fn main() {
keep_fallthrough(B);
eliminate_fallthrough(B);
}