Skip to content

Commit d2769dd

Browse files
Auto merge of #142707 - ashivaram23:drop_wildcard, r=<try>
Update `MaybeUninitializedPlaces` and `MaybeInitializedPlaces` for `otherwise` <!-- homu-ignore:start --> <!-- If this PR is related to an unstable feature or an otherwise tracked effort, please link to the relevant tracking issue here. If you don't know of a related tracking issue or there are none, feel free to ignore this. This PR will get automatically assigned to a reviewer. In case you would like a specific user to review your work, you can assign it to them by using r? <reviewer name> --> <!-- homu-ignore:end --> This allows `ElaborateDrops` to remove drops when a `match` wildcard arm covers multiple no-Drop enum variants. It modifies dataflow analysis to update the `MaybeUninitializedPlaces` and `MaybeInitializedPlaces` data for a block reached through an `otherwise` edge. This appears to fix #142705, but I don't know for sure if it's actually correct (are there cases where this would be wrong and break things?). I also haven't tested that it actually improves compile times, or machine code output outside of the examples in the issue.
2 parents bc4376f + 819cf8c commit d2769dd

File tree

7 files changed

+184
-82
lines changed

7 files changed

+184
-82
lines changed

compiler/rustc_mir_dataflow/src/drop_flag_effects.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -155,16 +155,15 @@ where
155155
}
156156
}
157157

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.
163-
pub(crate) fn on_all_inactive_variants<'tcx>(
158+
/// Handles each variant that corresponds to one of the child move paths of `enum_place`. If the
159+
/// variant is `active_variant`, `handle_active_variant` will be called. Otherwise,
160+
/// `handle_inactive_variant` will be called.
161+
pub(crate) fn on_all_variants<'tcx>(
164162
move_data: &MoveData<'tcx>,
165163
enum_place: mir::Place<'tcx>,
166164
active_variant: VariantIdx,
167165
mut handle_inactive_variant: impl FnMut(MovePathIndex),
166+
mut handle_active_variant: impl FnMut(MovePathIndex),
168167
) {
169168
let LookupResult::Exact(enum_mpi) = move_data.rev_lookup.find(enum_place.as_ref()) else {
170169
return;
@@ -184,6 +183,8 @@ pub(crate) fn on_all_inactive_variants<'tcx>(
184183

185184
if variant_idx != active_variant {
186185
on_all_children_bits(move_data, variant_mpi, |mpi| handle_inactive_variant(mpi));
186+
} else {
187+
on_all_children_bits(move_data, variant_mpi, |mpi| handle_active_variant(mpi));
187188
}
188189
}
189190
}

compiler/rustc_mir_dataflow/src/framework/direction.rs

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use std::ops::RangeInclusive;
22

3-
use rustc_middle::mir::{
4-
self, BasicBlock, CallReturnPlaces, Location, SwitchTargetValue, TerminatorEdges,
5-
};
3+
use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges};
64

75
use super::visitor::ResultsVisitor;
86
use super::{Analysis, Effect, EffectIndex};
@@ -117,7 +115,7 @@ impl Direction for Backward {
117115
let mut tmp = analysis.bottom_value(body);
118116
for &value in &body.basic_blocks.switch_sources()[&(block, pred)] {
119117
tmp.clone_from(exit_state);
120-
analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, value);
118+
analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, value, None);
121119
propagate(pred, &tmp);
122120
}
123121
} else {
@@ -245,7 +243,7 @@ impl Direction for Forward {
245243

246244
fn apply_effects_in_block<'mir, 'tcx, A>(
247245
analysis: &mut A,
248-
body: &mir::Body<'tcx>,
246+
_body: &mir::Body<'tcx>,
249247
state: &mut A::Domain,
250248
block: BasicBlock,
251249
block_data: &'mir mir::BasicBlockData<'tcx>,
@@ -285,25 +283,10 @@ impl Direction for Forward {
285283
}
286284
}
287285
TerminatorEdges::SwitchInt { targets, discr } => {
288-
if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
289-
let mut tmp = analysis.bottom_value(body);
290-
for (value, target) in targets.iter() {
291-
tmp.clone_from(exit_state);
292-
let value = SwitchTargetValue::Normal(value);
293-
analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, value);
294-
propagate(target, &tmp);
295-
}
296-
297-
// Once we get to the final, "otherwise" branch, there is no need to preserve
298-
// `exit_state`, so pass it directly to `apply_switch_int_edge_effect` to save
299-
// a clone of the dataflow state.
300-
let otherwise = targets.otherwise();
301-
analysis.apply_switch_int_edge_effect(
302-
&mut data,
303-
exit_state,
304-
SwitchTargetValue::Otherwise,
286+
if let Some(data) = analysis.get_switch_int_data(block, discr) {
287+
analysis.apply_switch_int_edge_effect_for_targets(
288+
targets, data, exit_state, propagate,
305289
);
306-
propagate(otherwise, exit_state);
307290
} else {
308291
for target in targets.all_targets() {
309292
propagate(*target, exit_state);

compiler/rustc_mir_dataflow/src/framework/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,20 @@ pub trait Analysis<'tcx> {
224224
_data: &mut Self::SwitchIntData,
225225
_state: &mut Self::Domain,
226226
_value: SwitchTargetValue,
227+
_otherwise_state: Option<&mut Self::Domain>,
228+
) {
229+
unreachable!();
230+
}
231+
232+
/// Calls `apply_switch_int_edge_effect` for each target in `targets` and calls `propagate` with
233+
/// the new state. This is used in forward analysis for `MaybeUninitializedPlaces` and
234+
/// `MaybeInitializedPlaces`.
235+
fn apply_switch_int_edge_effect_for_targets(
236+
&mut self,
237+
_targets: &mir::SwitchTargets,
238+
mut _data: Self::SwitchIntData,
239+
_state: &mut Self::Domain,
240+
mut _propagate: impl FnMut(mir::BasicBlock, &Self::Domain),
227241
) {
228242
unreachable!();
229243
}

compiler/rustc_mir_dataflow/src/impls/initialized.rs

Lines changed: 132 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,26 @@ pub struct MaybeInitializedPlaces<'a, 'tcx> {
131131
tcx: TyCtxt<'tcx>,
132132
body: &'a Body<'tcx>,
133133
move_data: &'a MoveData<'tcx>,
134+
exclude_inactive_in_otherwise: bool,
134135
skip_unreachable_unwind: bool,
135136
}
136137

137138
impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
138139
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 }
140+
MaybeInitializedPlaces {
141+
tcx,
142+
body,
143+
move_data,
144+
exclude_inactive_in_otherwise: false,
145+
skip_unreachable_unwind: false,
146+
}
147+
}
148+
149+
/// Ensures definitely inactive variants are excluded from the set of initialized places for
150+
/// blocks reached through an `otherwise` edge.
151+
pub fn exclude_inactive_in_otherwise(mut self) -> Self {
152+
self.exclude_inactive_in_otherwise = true;
153+
self
140154
}
141155

142156
pub fn skipping_unreachable_unwind(mut self) -> Self {
@@ -208,6 +222,7 @@ pub struct MaybeUninitializedPlaces<'a, 'tcx> {
208222
move_data: &'a MoveData<'tcx>,
209223

210224
mark_inactive_variants_as_uninit: bool,
225+
include_inactive_in_otherwise: bool,
211226
skip_unreachable_unwind: DenseBitSet<mir::BasicBlock>,
212227
}
213228

@@ -218,6 +233,7 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
218233
body,
219234
move_data,
220235
mark_inactive_variants_as_uninit: false,
236+
include_inactive_in_otherwise: false,
221237
skip_unreachable_unwind: DenseBitSet::new_empty(body.basic_blocks.len()),
222238
}
223239
}
@@ -232,6 +248,13 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
232248
self
233249
}
234250

251+
/// Ensures definitely inactive variants are included in the set of uninitialized places for
252+
/// blocks reached through an `otherwise` edge.
253+
pub fn include_inactive_in_otherwise(mut self) -> Self {
254+
self.include_inactive_in_otherwise = true;
255+
self
256+
}
257+
235258
pub fn skipping_unreachable_unwind(
236259
mut self,
237260
unreachable_unwind: DenseBitSet<mir::BasicBlock>,
@@ -431,17 +454,63 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
431454
data: &mut Self::SwitchIntData,
432455
state: &mut Self::Domain,
433456
value: SwitchTargetValue,
457+
otherwise_state: Option<&mut Self::Domain>,
458+
) {
459+
let SwitchTargetValue::Normal(value) = value else {
460+
return;
461+
};
462+
463+
let handle_inactive_variant = |mpi| state.kill(mpi);
464+
465+
// Kill all move paths that correspond to variants we know to be inactive along this
466+
// particular outgoing edge of a `SwitchInt`.
467+
match otherwise_state {
468+
Some(otherwise_state) => {
469+
drop_flag_effects::on_all_variants(
470+
self.move_data,
471+
data.enum_place,
472+
data.next_discr(value),
473+
handle_inactive_variant,
474+
|mpi| otherwise_state.kill(mpi),
475+
);
476+
}
477+
None => {
478+
drop_flag_effects::on_all_variants(
479+
self.move_data,
480+
data.enum_place,
481+
data.next_discr(value),
482+
handle_inactive_variant,
483+
|_mpi| {},
484+
);
485+
}
486+
}
487+
}
488+
489+
fn apply_switch_int_edge_effect_for_targets(
490+
&mut self,
491+
targets: &mir::SwitchTargets,
492+
mut data: Self::SwitchIntData,
493+
state: &mut Self::Domain,
494+
mut propagate: impl FnMut(mir::BasicBlock, &Self::Domain),
434495
) {
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),
496+
let analyze_otherwise = self.exclude_inactive_in_otherwise
497+
&& (1..data.discriminants.len()).contains(&targets.all_values().len());
498+
499+
let mut otherwise_state = if analyze_otherwise { Some(state.clone()) } else { None };
500+
let mut target_state = MaybeReachable::Unreachable;
501+
502+
for (value, target) in targets.iter() {
503+
target_state.clone_from(&state);
504+
self.apply_switch_int_edge_effect(
505+
&mut data,
506+
&mut target_state,
507+
SwitchTargetValue::Normal(value),
508+
otherwise_state.as_mut(),
443509
);
510+
propagate(target, &target_state);
444511
}
512+
513+
propagate(targets.otherwise(), otherwise_state.as_ref().unwrap_or(state));
445514
}
446515
}
447516

@@ -544,17 +613,63 @@ impl<'tcx> Analysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
544613
data: &mut Self::SwitchIntData,
545614
state: &mut Self::Domain,
546615
value: SwitchTargetValue,
616+
otherwise_state: Option<&mut Self::Domain>,
617+
) {
618+
let SwitchTargetValue::Normal(value) = value else {
619+
return;
620+
};
621+
622+
let handle_inactive_variant = |mpi| state.gen_(mpi);
623+
624+
// Mark all move paths that correspond to variants other than this one as maybe
625+
// uninitialized (in reality, they are *definitely* uninitialized).
626+
match otherwise_state {
627+
Some(otherwise_state) => {
628+
drop_flag_effects::on_all_variants(
629+
self.move_data,
630+
data.enum_place,
631+
data.next_discr(value),
632+
handle_inactive_variant,
633+
|mpi| otherwise_state.gen_(mpi),
634+
);
635+
}
636+
None => {
637+
drop_flag_effects::on_all_variants(
638+
self.move_data,
639+
data.enum_place,
640+
data.next_discr(value),
641+
handle_inactive_variant,
642+
|_mpi| {},
643+
);
644+
}
645+
}
646+
}
647+
648+
fn apply_switch_int_edge_effect_for_targets(
649+
&mut self,
650+
targets: &mir::SwitchTargets,
651+
mut data: Self::SwitchIntData,
652+
state: &mut Self::Domain,
653+
mut propagate: impl FnMut(mir::BasicBlock, &Self::Domain),
547654
) {
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),
655+
let analyze_otherwise = self.include_inactive_in_otherwise
656+
&& (1..data.discriminants.len()).contains(&targets.all_values().len());
657+
658+
let mut otherwise_state = if analyze_otherwise { Some(state.clone()) } else { None };
659+
let mut target_state = MixedBitSet::new_empty(self.move_data().move_paths.len());
660+
661+
for (value, target) in targets.iter() {
662+
target_state.clone_from(&state);
663+
self.apply_switch_int_edge_effect(
664+
&mut data,
665+
&mut target_state,
666+
SwitchTargetValue::Normal(value),
667+
otherwise_state.as_mut(),
556668
);
669+
propagate(target, &target_state);
557670
}
671+
672+
propagate(targets.otherwise(), otherwise_state.as_ref().unwrap_or(state));
558673
}
559674
}
560675

compiler/rustc_mir_transform/src/elaborate_drops.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ 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);

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

0 commit comments

Comments
 (0)