Skip to content

Commit d9dd6ee

Browse files
committed
Apply effects to otherwise edge in dataflow analysis
1 parent 255aa22 commit d9dd6ee

File tree

9 files changed

+212
-44
lines changed

9 files changed

+212
-44
lines changed

compiler/rustc_mir_dataflow/src/drop_flag_effects.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use rustc_abi::VariantIdx;
22
use rustc_middle::mir::{self, Body, Location, Terminator, TerminatorKind};
3+
use smallvec::SmallVec;
34
use tracing::debug;
45

56
use super::move_paths::{InitKind, LookupResult, MoveData, MovePathIndex};
@@ -155,15 +156,28 @@ where
155156
}
156157
}
157158

158-
/// Calls `handle_inactive_variant` for each descendant move path of `enum_place` that contains a
159-
/// `Downcast` to a variant besides the `active_variant`.
160-
///
161-
/// NOTE: If there are no move paths corresponding to an inactive variant,
162-
/// `handle_inactive_variant` will not be called for that variant.
159+
/// Indicates which variants are inactive at a `SwitchInt` edge by listing their `VariantIdx`s or
160+
/// specifying the single active variant's `VariantIdx`.
161+
pub(crate) enum InactiveVariants {
162+
Inactives(SmallVec<[VariantIdx; 4]>),
163+
Active(VariantIdx),
164+
}
165+
166+
impl InactiveVariants {
167+
fn contains(&self, variant_idx: VariantIdx) -> bool {
168+
match self {
169+
InactiveVariants::Inactives(inactives) => inactives.contains(&variant_idx),
170+
InactiveVariants::Active(active) => variant_idx != *active,
171+
}
172+
}
173+
}
174+
175+
/// Calls `handle_inactive_variant` for each child move path of `enum_place` corresponding to an
176+
/// inactive variant at a particular `SwitchInt` edge.
163177
pub(crate) fn on_all_inactive_variants<'tcx>(
164178
move_data: &MoveData<'tcx>,
165179
enum_place: mir::Place<'tcx>,
166-
active_variant: VariantIdx,
180+
inactive_variants: &InactiveVariants,
167181
mut handle_inactive_variant: impl FnMut(MovePathIndex),
168182
) {
169183
let LookupResult::Exact(enum_mpi) = move_data.rev_lookup.find(enum_place.as_ref()) else {
@@ -182,7 +196,7 @@ pub(crate) fn on_all_inactive_variants<'tcx>(
182196
unreachable!();
183197
};
184198

185-
if variant_idx != active_variant {
199+
if inactive_variants.contains(variant_idx) {
186200
on_all_children_bits(move_data, variant_mpi, |mpi| handle_inactive_variant(mpi));
187201
}
188202
}

compiler/rustc_mir_dataflow/src/framework/direction.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,13 @@ impl Direction for Backward {
112112
propagate(pred, &tmp);
113113
}
114114

115-
mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
115+
mir::TerminatorKind::SwitchInt { ref targets, ref discr } => {
116116
if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
117117
let mut tmp = analysis.bottom_value(body);
118118
for &value in &body.basic_blocks.switch_sources()[&(block, pred)] {
119119
tmp.clone_from(exit_state);
120-
analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, value);
120+
analysis
121+
.apply_switch_int_edge_effect(&mut data, &mut tmp, value, targets);
121122
propagate(pred, &tmp);
122123
}
123124
} else {
@@ -290,20 +291,20 @@ impl Direction for Forward {
290291
for (value, target) in targets.iter() {
291292
tmp.clone_from(exit_state);
292293
let value = SwitchTargetValue::Normal(value);
293-
analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, value);
294+
analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, value, targets);
294295
propagate(target, &tmp);
295296
}
296297

297298
// Once we get to the final, "otherwise" branch, there is no need to preserve
298299
// `exit_state`, so pass it directly to `apply_switch_int_edge_effect` to save
299300
// a clone of the dataflow state.
300-
let otherwise = targets.otherwise();
301301
analysis.apply_switch_int_edge_effect(
302302
&mut data,
303303
exit_state,
304304
SwitchTargetValue::Otherwise,
305+
targets,
305306
);
306-
propagate(otherwise, exit_state);
307+
propagate(targets.otherwise(), exit_state);
307308
} else {
308309
for target in targets.all_targets() {
309310
propagate(*target, exit_state);

compiler/rustc_mir_dataflow/src/framework/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ pub trait Analysis<'tcx> {
224224
_data: &mut Self::SwitchIntData,
225225
_state: &mut Self::Domain,
226226
_value: SwitchTargetValue,
227+
_targets: &mir::SwitchTargets,
227228
) {
228229
unreachable!();
229230
}

compiler/rustc_mir_dataflow/src/impls/initialized.rs

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ use rustc_middle::mir::{
99
};
1010
use rustc_middle::ty::util::Discr;
1111
use rustc_middle::ty::{self, TyCtxt};
12+
use smallvec::SmallVec;
1213
use tracing::{debug, instrument};
1314

14-
use crate::drop_flag_effects::DropFlagState;
15+
use crate::drop_flag_effects::{DropFlagState, InactiveVariants};
1516
use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
1617
use crate::{
1718
Analysis, GenKill, MaybeReachable, drop_flag_effects, drop_flag_effects_for_function_entry,
@@ -26,6 +27,12 @@ pub struct MaybePlacesSwitchIntData<'tcx> {
2627
}
2728

2829
impl<'tcx> MaybePlacesSwitchIntData<'tcx> {
30+
/// Creates a `SmallVec` mapping each target in `targets` to its `VariantIdx`.
31+
fn variants(&mut self, targets: &mir::SwitchTargets) -> SmallVec<[VariantIdx; 4]> {
32+
self.index = 0;
33+
targets.all_values().iter().map(|value| self.next_discr(value.get())).collect()
34+
}
35+
2936
// The discriminant order in the `SwitchInt` targets should match the order yielded by
3037
// `AdtDef::discriminants`. We rely on this to match each discriminant in the targets to its
3138
// corresponding variant in linear time.
@@ -131,12 +138,26 @@ pub struct MaybeInitializedPlaces<'a, 'tcx> {
131138
tcx: TyCtxt<'tcx>,
132139
body: &'a Body<'tcx>,
133140
move_data: &'a MoveData<'tcx>,
141+
exclude_inactive_in_otherwise: bool,
134142
skip_unreachable_unwind: bool,
135143
}
136144

137145
impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
138146
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, move_data: &'a MoveData<'tcx>) -> Self {
139-
MaybeInitializedPlaces { tcx, body, move_data, skip_unreachable_unwind: false }
147+
MaybeInitializedPlaces {
148+
tcx,
149+
body,
150+
move_data,
151+
exclude_inactive_in_otherwise: false,
152+
skip_unreachable_unwind: false,
153+
}
154+
}
155+
156+
/// Ensures definitely inactive variants are excluded from the set of initialized places for
157+
/// blocks reached through an `otherwise` edge.
158+
pub fn exclude_inactive_in_otherwise(mut self) -> Self {
159+
self.exclude_inactive_in_otherwise = true;
160+
self
140161
}
141162

142163
pub fn skipping_unreachable_unwind(mut self) -> Self {
@@ -208,6 +229,7 @@ pub struct MaybeUninitializedPlaces<'a, 'tcx> {
208229
move_data: &'a MoveData<'tcx>,
209230

210231
mark_inactive_variants_as_uninit: bool,
232+
include_inactive_in_otherwise: bool,
211233
skip_unreachable_unwind: DenseBitSet<mir::BasicBlock>,
212234
}
213235

@@ -218,6 +240,7 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
218240
body,
219241
move_data,
220242
mark_inactive_variants_as_uninit: false,
243+
include_inactive_in_otherwise: false,
221244
skip_unreachable_unwind: DenseBitSet::new_empty(body.basic_blocks.len()),
222245
}
223246
}
@@ -232,6 +255,13 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
232255
self
233256
}
234257

258+
/// Ensures definitely inactive variants are included in the set of uninitialized places for
259+
/// blocks reached through an `otherwise` edge.
260+
pub fn include_inactive_in_otherwise(mut self) -> Self {
261+
self.include_inactive_in_otherwise = true;
262+
self
263+
}
264+
235265
pub fn skipping_unreachable_unwind(
236266
mut self,
237267
unreachable_unwind: DenseBitSet<mir::BasicBlock>,
@@ -431,17 +461,24 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
431461
data: &mut Self::SwitchIntData,
432462
state: &mut Self::Domain,
433463
value: SwitchTargetValue,
464+
targets: &mir::SwitchTargets,
434465
) {
435-
if let SwitchTargetValue::Normal(value) = value {
436-
// Kill all move paths that correspond to variants we know to be inactive along this
437-
// particular outgoing edge of a `SwitchInt`.
438-
drop_flag_effects::on_all_inactive_variants(
439-
self.move_data,
440-
data.enum_place,
441-
data.next_discr(value),
442-
|mpi| state.kill(mpi),
443-
);
444-
}
466+
let inactive_variants = match value {
467+
SwitchTargetValue::Normal(value) => InactiveVariants::Active(data.next_discr(value)),
468+
SwitchTargetValue::Otherwise if self.exclude_inactive_in_otherwise => {
469+
InactiveVariants::Inactives(data.variants(targets))
470+
}
471+
_ => return,
472+
};
473+
474+
// Kill all move paths that correspond to variants we know to be inactive along this
475+
// particular outgoing edge of a `SwitchInt`.
476+
drop_flag_effects::on_all_inactive_variants(
477+
self.move_data,
478+
data.enum_place,
479+
&inactive_variants,
480+
|mpi| state.kill(mpi),
481+
);
445482
}
446483
}
447484

@@ -544,17 +581,24 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
544581
data: &mut Self::SwitchIntData,
545582
state: &mut Self::Domain,
546583
value: SwitchTargetValue,
584+
targets: &mir::SwitchTargets,
547585
) {
548-
if let SwitchTargetValue::Normal(value) = value {
549-
// Mark all move paths that correspond to variants other than this one as maybe
550-
// uninitialized (in reality, they are *definitely* uninitialized).
551-
drop_flag_effects::on_all_inactive_variants(
552-
self.move_data,
553-
data.enum_place,
554-
data.next_discr(value),
555-
|mpi| state.gen_(mpi),
556-
);
557-
}
586+
let inactive_variants = match value {
587+
SwitchTargetValue::Normal(value) => InactiveVariants::Active(data.next_discr(value)),
588+
SwitchTargetValue::Otherwise if self.include_inactive_in_otherwise => {
589+
InactiveVariants::Inactives(data.variants(targets))
590+
}
591+
_ => return,
592+
};
593+
594+
// Mark all move paths that correspond to variants other than this one as maybe
595+
// uninitialized (in reality, they are *definitely* uninitialized).
596+
drop_flag_effects::on_all_inactive_variants(
597+
self.move_data,
598+
data.enum_place,
599+
&inactive_variants,
600+
|mpi| state.gen_(mpi),
601+
);
558602
}
559603
}
560604

compiler/rustc_mir_transform/src/elaborate_drops.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,14 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops {
6262
let env = MoveDataTypingEnv { move_data, typing_env };
6363

6464
let mut inits = MaybeInitializedPlaces::new(tcx, body, &env.move_data)
65+
.exclude_inactive_in_otherwise()
6566
.skipping_unreachable_unwind()
6667
.iterate_to_fixpoint(tcx, body, Some("elaborate_drops"))
6768
.into_results_cursor(body);
6869
let dead_unwinds = compute_dead_unwinds(body, &mut inits);
6970

7071
let uninits = MaybeUninitializedPlaces::new(tcx, body, &env.move_data)
72+
.include_inactive_in_otherwise()
7173
.mark_inactive_variants_as_uninit()
7274
.skipping_unreachable_unwind(dead_unwinds)
7375
.iterate_to_fixpoint(tcx, body, Some("elaborate_drops"))

compiler/rustc_mir_transform/src/remove_uninit_drops.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUninitDrops {
2222
let move_data = MoveData::gather_moves(body, tcx, |ty| ty.needs_drop(tcx, typing_env));
2323

2424
let mut maybe_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
25+
.exclude_inactive_in_otherwise()
2526
.iterate_to_fixpoint(tcx, body, Some("remove_uninit_drops"))
2627
.into_results_cursor(body);
2728

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// MIR for `result_ok` after ElaborateDrops
2+
3+
fn result_ok(_1: Result<String, ()>) -> Option<String> {
4+
debug result => _1;
5+
let mut _0: std::option::Option<std::string::String>;
6+
let mut _2: isize;
7+
let _3: std::string::String;
8+
let mut _4: std::string::String;
9+
let mut _5: bool;
10+
let mut _6: isize;
11+
let mut _7: isize;
12+
scope 1 {
13+
debug s => _3;
14+
}
15+
16+
bb0: {
17+
_5 = const false;
18+
_5 = const true;
19+
PlaceMention(_1);
20+
_2 = discriminant(_1);
21+
switchInt(move _2) -> [0: bb2, otherwise: bb1];
22+
}
23+
24+
bb1: {
25+
_0 = Option::<String>::None;
26+
goto -> bb5;
27+
}
28+
29+
bb2: {
30+
StorageLive(_3);
31+
_3 = move ((_1 as Ok).0: std::string::String);
32+
StorageLive(_4);
33+
_4 = move _3;
34+
_0 = Option::<String>::Some(move _4);
35+
goto -> bb3;
36+
}
37+
38+
bb3: {
39+
StorageDead(_4);
40+
goto -> bb4;
41+
}
42+
43+
bb4: {
44+
StorageDead(_3);
45+
goto -> bb5;
46+
}
47+
48+
bb5: {
49+
goto -> bb16;
50+
}
51+
52+
bb6: {
53+
return;
54+
}
55+
56+
bb7 (cleanup): {
57+
goto -> bb8;
58+
}
59+
60+
bb8 (cleanup): {
61+
goto -> bb9;
62+
}
63+
64+
bb9 (cleanup): {
65+
resume;
66+
}
67+
68+
bb10: {
69+
goto -> bb6;
70+
}
71+
72+
bb11 (cleanup): {
73+
goto -> bb9;
74+
}
75+
76+
bb12 (cleanup): {
77+
goto -> bb9;
78+
}
79+
80+
bb13: {
81+
goto -> bb10;
82+
}
83+
84+
bb14: {
85+
goto -> bb10;
86+
}
87+
88+
bb15 (cleanup): {
89+
goto -> bb9;
90+
}
91+
92+
bb16: {
93+
_6 = discriminant(_1);
94+
switchInt(move _6) -> [0: bb13, otherwise: bb14];
95+
}
96+
97+
bb17 (cleanup): {
98+
_7 = discriminant(_1);
99+
switchInt(move _7) -> [0: bb11, otherwise: bb15];
100+
}
101+
}

tests/mir-opt/otherwise_drops.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//@ compile-flags: -C panic=abort
2+
//@ test-mir-pass: ElaborateDrops
3+
4+
// Ensures there are no drops for the wildcard match arm.
5+
6+
// EMIT_MIR otherwise_drops.result_ok.ElaborateDrops.after.mir
7+
fn result_ok(result: Result<String, ()>) -> Option<String> {
8+
// CHECK-NOT: drop
9+
match result {
10+
Ok(s) => Some(s),
11+
_ => None,
12+
}
13+
}

0 commit comments

Comments
 (0)