Skip to content

Commit 79ce057

Browse files
committed
Auto merge of rust-lang#133328 - nnethercote:simplify-SwitchInt-handling, r=<try>
Simplify `SwitchInt` handling Dataflow handling of `SwitchInt` is currently complicated. This PR simplifies it. r? `@cjgillot`
2 parents 706141b + 8e59df9 commit 79ce057

File tree

5 files changed

+178
-244
lines changed

5 files changed

+178
-244
lines changed

compiler/rustc_borrowck/src/dataflow.rs

+1-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_middle::mir::{
99
use rustc_middle::ty::{RegionVid, TyCtxt};
1010
use rustc_mir_dataflow::fmt::DebugWithContext;
1111
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
12-
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice, SwitchIntEdgeEffects};
12+
use rustc_mir_dataflow::{Analysis, GenKill, JoinSemiLattice};
1313
use tracing::debug;
1414

1515
use crate::{BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext, places_conflict};
@@ -98,16 +98,6 @@ impl<'a, 'tcx> Analysis<'tcx> for Borrowck<'a, 'tcx> {
9898
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
9999
unreachable!();
100100
}
101-
102-
fn apply_switch_int_edge_effects(
103-
&mut self,
104-
_block: BasicBlock,
105-
_discr: &mir::Operand<'tcx>,
106-
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
107-
) {
108-
// This is only reachable from `iterate_to_fixpoint`, which this analysis doesn't use.
109-
unreachable!();
110-
}
111101
}
112102

113103
impl JoinSemiLattice for BorrowckDomain<'_, '_> {

compiler/rustc_mir_dataflow/src/framework/direction.rs

+36-115
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, SwitchTargets, TerminatorEdges,
5-
};
3+
use rustc_middle::mir::{self, BasicBlock, CallReturnPlaces, Location, TerminatorEdges};
64

75
use super::visitor::ResultsVisitor;
86
use super::{Analysis, Effect, EffectIndex, Results, SwitchIntTarget};
@@ -115,18 +113,18 @@ impl Direction for Backward {
115113
}
116114

117115
mir::TerminatorKind::SwitchInt { targets: _, ref discr } => {
118-
let mut applier = BackwardSwitchIntEdgeEffectsApplier {
119-
body,
120-
pred,
121-
exit_state,
122-
block,
123-
propagate: &mut propagate,
124-
effects_applied: false,
125-
};
126-
127-
analysis.apply_switch_int_edge_effects(pred, discr, &mut applier);
128-
129-
if !applier.effects_applied {
116+
if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
117+
let values = &body.basic_blocks.switch_sources()[&(block, pred)];
118+
let targets =
119+
values.iter().map(|&value| SwitchIntTarget { value, target: block });
120+
121+
let mut tmp = analysis.bottom_value(body);
122+
for target in targets {
123+
tmp.clone_from(&exit_state);
124+
analysis.apply_switch_int_edge_effect(&mut data, &mut tmp, target);
125+
propagate(pred, &tmp);
126+
}
127+
} else {
130128
propagate(pred, exit_state)
131129
}
132130
}
@@ -245,37 +243,6 @@ impl Direction for Backward {
245243
}
246244
}
247245

248-
struct BackwardSwitchIntEdgeEffectsApplier<'mir, 'tcx, D, F> {
249-
body: &'mir mir::Body<'tcx>,
250-
pred: BasicBlock,
251-
exit_state: &'mir mut D,
252-
block: BasicBlock,
253-
propagate: &'mir mut F,
254-
effects_applied: bool,
255-
}
256-
257-
impl<D, F> super::SwitchIntEdgeEffects<D> for BackwardSwitchIntEdgeEffectsApplier<'_, '_, D, F>
258-
where
259-
D: Clone,
260-
F: FnMut(BasicBlock, &D),
261-
{
262-
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
263-
assert!(!self.effects_applied);
264-
265-
let values = &self.body.basic_blocks.switch_sources()[&(self.block, self.pred)];
266-
let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.block });
267-
268-
let mut tmp = None;
269-
for target in targets {
270-
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
271-
apply_edge_effect(tmp, target);
272-
(self.propagate)(self.pred, tmp);
273-
}
274-
275-
self.effects_applied = true;
276-
}
277-
}
278-
279246
/// Dataflow that runs from the entry of a block (the first statement), to its exit (terminator).
280247
pub struct Forward;
281248

@@ -284,7 +251,7 @@ impl Direction for Forward {
284251

285252
fn apply_effects_in_block<'mir, 'tcx, A>(
286253
analysis: &mut A,
287-
_body: &mir::Body<'tcx>,
254+
body: &mir::Body<'tcx>,
288255
state: &mut A::Domain,
289256
block: BasicBlock,
290257
block_data: &'mir mir::BasicBlockData<'tcx>,
@@ -324,23 +291,28 @@ impl Direction for Forward {
324291
}
325292
}
326293
TerminatorEdges::SwitchInt { targets, discr } => {
327-
let mut applier = ForwardSwitchIntEdgeEffectsApplier {
328-
exit_state,
329-
targets,
330-
propagate,
331-
effects_applied: false,
332-
};
333-
334-
analysis.apply_switch_int_edge_effects(block, discr, &mut applier);
335-
336-
let ForwardSwitchIntEdgeEffectsApplier {
337-
exit_state,
338-
mut propagate,
339-
effects_applied,
340-
..
341-
} = applier;
342-
343-
if !effects_applied {
294+
if let Some(mut data) = analysis.get_switch_int_data(block, discr) {
295+
let mut tmp = analysis.bottom_value(body);
296+
for (value, target) in targets.iter() {
297+
tmp.clone_from(&exit_state);
298+
analysis.apply_switch_int_edge_effect(
299+
&mut data,
300+
&mut tmp,
301+
SwitchIntTarget { value: Some(value), target },
302+
);
303+
propagate(target, &tmp);
304+
}
305+
306+
// Once we get to the final, "otherwise" branch, there is no need to preserve
307+
// `exit_state`, so pass it directly to `apply_switch_int_edge_effect` to save
308+
// a clone of the dataflow state.
309+
let otherwise = targets.otherwise();
310+
analysis.apply_switch_int_edge_effect(&mut data, exit_state, SwitchIntTarget {
311+
value: None,
312+
target: otherwise,
313+
});
314+
propagate(otherwise, exit_state);
315+
} else {
344316
for target in targets.all_targets() {
345317
propagate(*target, exit_state);
346318
}
@@ -454,54 +426,3 @@ impl Direction for Forward {
454426
vis.visit_block_end(state);
455427
}
456428
}
457-
458-
struct ForwardSwitchIntEdgeEffectsApplier<'mir, D, F> {
459-
exit_state: &'mir mut D,
460-
targets: &'mir SwitchTargets,
461-
propagate: F,
462-
463-
effects_applied: bool,
464-
}
465-
466-
impl<D, F> super::SwitchIntEdgeEffects<D> for ForwardSwitchIntEdgeEffectsApplier<'_, D, F>
467-
where
468-
D: Clone,
469-
F: FnMut(BasicBlock, &D),
470-
{
471-
fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) {
472-
assert!(!self.effects_applied);
473-
474-
let mut tmp = None;
475-
for (value, target) in self.targets.iter() {
476-
let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state);
477-
apply_edge_effect(tmp, SwitchIntTarget { value: Some(value), target });
478-
(self.propagate)(target, tmp);
479-
}
480-
481-
// Once we get to the final, "otherwise" branch, there is no need to preserve `exit_state`,
482-
// so pass it directly to `apply_edge_effect` to save a clone of the dataflow state.
483-
let otherwise = self.targets.otherwise();
484-
apply_edge_effect(self.exit_state, SwitchIntTarget { value: None, target: otherwise });
485-
(self.propagate)(otherwise, self.exit_state);
486-
487-
self.effects_applied = true;
488-
}
489-
}
490-
491-
/// An analogue of `Option::get_or_insert_with` that stores a clone of `val` into `opt`, but uses
492-
/// the more efficient `clone_from` if `opt` was `Some`.
493-
///
494-
/// Returns a mutable reference to the new clone that resides in `opt`.
495-
//
496-
// FIXME: Figure out how to express this using `Option::clone_from`, or maybe lift it into the
497-
// standard library?
498-
fn opt_clone_from_or_clone<'a, T: Clone>(opt: &'a mut Option<T>, val: &T) -> &'a mut T {
499-
if opt.is_some() {
500-
let ret = opt.as_mut().unwrap();
501-
ret.clone_from(val);
502-
ret
503-
} else {
504-
*opt = Some(val.clone());
505-
opt.as_mut().unwrap()
506-
}
507-
}

compiler/rustc_mir_dataflow/src/framework/mod.rs

+23-16
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ pub trait Analysis<'tcx> {
103103
/// The direction of this analysis. Either `Forward` or `Backward`.
104104
type Direction: Direction = Forward;
105105

106+
/// Auxiliary data used for analyzing `SwitchInt` terminators, if necessary.
107+
type SwitchIntData = !;
108+
106109
/// A descriptive name for this analysis. Used only for debugging.
107110
///
108111
/// This name should be brief and contain no spaces, periods or other characters that are not
@@ -188,25 +191,36 @@ pub trait Analysis<'tcx> {
188191
) {
189192
}
190193

191-
/// Updates the current dataflow state with the effect of taking a particular branch in a
192-
/// `SwitchInt` terminator.
194+
/// Used to update the current dataflow state with the effect of taking a particular branch in
195+
/// a `SwitchInt` terminator.
193196
///
194197
/// Unlike the other edge-specific effects, which are allowed to mutate `Self::Domain`
195-
/// directly, overriders of this method must pass a callback to
196-
/// `SwitchIntEdgeEffects::apply`. The callback will be run once for each outgoing edge and
197-
/// will have access to the dataflow state that will be propagated along that edge.
198+
/// directly, overriders of this method must return a `Self::SwitchIntData` value (wrapped in
199+
/// `Some`). The `apply_switch_int_edge_effect` method will then be called once for each
200+
/// outgoing edge and will have access to the dataflow state that will be propagated along that
201+
/// edge, and also the `Self::SwitchIntData` value.
198202
///
199203
/// This interface is somewhat more complex than the other visitor-like "effect" methods.
200204
/// However, it is both more ergonomic—callers don't need to recompute or cache information
201205
/// about a given `SwitchInt` terminator for each one of its edges—and more efficient—the
202206
/// engine doesn't need to clone the exit state for a block unless
203-
/// `SwitchIntEdgeEffects::apply` is actually called.
204-
fn apply_switch_int_edge_effects(
207+
/// `get_switch_int_data` is actually called.
208+
fn get_switch_int_data(
205209
&mut self,
206-
_block: BasicBlock,
210+
_block: mir::BasicBlock,
207211
_discr: &mir::Operand<'tcx>,
208-
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
212+
) -> Option<Self::SwitchIntData> {
213+
None
214+
}
215+
216+
/// See comments on `get_switch_int_data`.
217+
fn apply_switch_int_edge_effect(
218+
&mut self,
219+
_data: &mut Self::SwitchIntData,
220+
_state: &mut Self::Domain,
221+
_edge: SwitchIntTarget,
209222
) {
223+
unreachable!();
210224
}
211225

212226
/* Extension methods */
@@ -419,12 +433,5 @@ pub struct SwitchIntTarget {
419433
pub target: BasicBlock,
420434
}
421435

422-
/// A type that records the edge-specific effects for a `SwitchInt` terminator.
423-
pub trait SwitchIntEdgeEffects<D> {
424-
/// Calls `apply_edge_effect` for each outgoing edge from a `SwitchInt` terminator and
425-
/// records the results.
426-
fn apply(&mut self, apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget));
427-
}
428-
429436
#[cfg(test)]
430437
mod tests;

0 commit comments

Comments
 (0)