Rollup merge of #69562 - ecstatic-morse:dataflow-generator-discriminant, r=oli-obk
Don't `bug` when taking discriminant of generator during dataflow The proper fix for rust-lang/rust-clippy#5239. `Rvalue::Discriminant` is used on generators as well as `enum`s. This didn't cause a test failure in `rustc` since we don't need to do any dataflow passes until after the generator transform that adds the `Rvalue::Discriminant`. This required a small refactoring. `diff -w` is beneficial. r? @oli-obk cc @JohnTitor
This commit is contained in:
commit
48ec25224b
@ -239,14 +239,24 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
SwitchInt { ref targets, ref values, ref discr, .. } => {
|
SwitchInt { ref targets, ref values, ref discr, .. } => {
|
||||||
self.propagate_bits_into_switch_int_successors(
|
// If this is a switch on an enum discriminant, a custom effect may be applied
|
||||||
in_out,
|
// along each outgoing edge.
|
||||||
(bb, bb_data),
|
if let Some(place) = discr.place() {
|
||||||
dirty_list,
|
let enum_def = switch_on_enum_discriminant(self.tcx, self.body, bb_data, place);
|
||||||
discr,
|
if let Some(enum_def) = enum_def {
|
||||||
&*values,
|
self.propagate_bits_into_enum_discriminant_switch_successors(
|
||||||
&*targets,
|
in_out, bb, enum_def, place, dirty_list, &*values, &*targets,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, it's just a normal `SwitchInt`, and every successor sees the same
|
||||||
|
// exit state.
|
||||||
|
for target in targets.iter().copied() {
|
||||||
|
self.propagate_bits_into_entry_set_for(&in_out, target, dirty_list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Call { cleanup, ref destination, ref func, ref args, .. } => {
|
Call { cleanup, ref destination, ref func, ref args, .. } => {
|
||||||
@ -293,64 +303,72 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn propagate_bits_into_switch_int_successors(
|
fn propagate_bits_into_enum_discriminant_switch_successors(
|
||||||
&mut self,
|
&mut self,
|
||||||
in_out: &mut BitSet<A::Idx>,
|
in_out: &mut BitSet<A::Idx>,
|
||||||
(bb, bb_data): (BasicBlock, &mir::BasicBlockData<'tcx>),
|
bb: BasicBlock,
|
||||||
|
enum_def: &'tcx ty::AdtDef,
|
||||||
|
enum_place: &mir::Place<'tcx>,
|
||||||
dirty_list: &mut WorkQueue<BasicBlock>,
|
dirty_list: &mut WorkQueue<BasicBlock>,
|
||||||
switch_on: &mir::Operand<'tcx>,
|
|
||||||
values: &[u128],
|
values: &[u128],
|
||||||
targets: &[BasicBlock],
|
targets: &[BasicBlock],
|
||||||
) {
|
) {
|
||||||
match bb_data.statements.last().map(|stmt| &stmt.kind) {
|
// MIR building adds discriminants to the `values` array in the same order as they
|
||||||
// Look at the last statement to see if it is an assignment of an enum discriminant to
|
// are yielded by `AdtDef::discriminants`. We rely on this to match each
|
||||||
// the local that determines the target of a `SwitchInt` like so:
|
// discriminant in `values` to its corresponding variant in linear time.
|
||||||
// _42 = discriminant(..)
|
let mut tmp = BitSet::new_empty(in_out.domain_size());
|
||||||
// SwitchInt(_42, ..)
|
let mut discriminants = enum_def.discriminants(self.tcx);
|
||||||
Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(enum_))))
|
for (value, target) in values.iter().zip(targets.iter().copied()) {
|
||||||
if Some(lhs) == switch_on.place() =>
|
let (variant_idx, _) = discriminants.find(|&(_, discr)| discr.val == *value).expect(
|
||||||
{
|
"Order of `AdtDef::discriminants` differed from that of `SwitchInt::values`",
|
||||||
let adt = match enum_.ty(self.body, self.tcx).ty.kind {
|
);
|
||||||
ty::Adt(def, _) => def,
|
|
||||||
_ => bug!("Switch on discriminant of non-ADT"),
|
|
||||||
};
|
|
||||||
|
|
||||||
// MIR building adds discriminants to the `values` array in the same order as they
|
tmp.overwrite(in_out);
|
||||||
// are yielded by `AdtDef::discriminants`. We rely on this to match each
|
self.analysis.apply_discriminant_switch_effect(
|
||||||
// discriminant in `values` to its corresponding variant in linear time.
|
&mut tmp,
|
||||||
let mut tmp = BitSet::new_empty(in_out.domain_size());
|
bb,
|
||||||
let mut discriminants = adt.discriminants(self.tcx);
|
enum_place,
|
||||||
for (value, target) in values.iter().zip(targets.iter().copied()) {
|
enum_def,
|
||||||
let (variant_idx, _) =
|
variant_idx,
|
||||||
discriminants.find(|&(_, discr)| discr.val == *value).expect(
|
);
|
||||||
"Order of `AdtDef::discriminants` differed \
|
self.propagate_bits_into_entry_set_for(&tmp, target, dirty_list);
|
||||||
from that of `SwitchInt::values`",
|
}
|
||||||
);
|
|
||||||
|
|
||||||
tmp.overwrite(in_out);
|
std::mem::drop(tmp);
|
||||||
self.analysis.apply_discriminant_switch_effect(
|
|
||||||
&mut tmp,
|
|
||||||
bb,
|
|
||||||
enum_,
|
|
||||||
adt,
|
|
||||||
variant_idx,
|
|
||||||
);
|
|
||||||
self.propagate_bits_into_entry_set_for(&tmp, target, dirty_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::mem::drop(tmp);
|
// Propagate dataflow state along the "otherwise" edge.
|
||||||
|
let otherwise = targets.last().copied().unwrap();
|
||||||
|
self.propagate_bits_into_entry_set_for(&in_out, otherwise, dirty_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Propagate dataflow state along the "otherwise" edge.
|
/// Look at the last statement of a block that ends with to see if it is an assignment of an enum
|
||||||
let otherwise = targets.last().copied().unwrap();
|
/// discriminant to the local that determines the target of a `SwitchInt` like so:
|
||||||
self.propagate_bits_into_entry_set_for(&in_out, otherwise, dirty_list);
|
/// _42 = discriminant(..)
|
||||||
}
|
/// SwitchInt(_42, ..)
|
||||||
|
fn switch_on_enum_discriminant(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
body: &mir::Body<'tcx>,
|
||||||
|
block: &mir::BasicBlockData<'tcx>,
|
||||||
|
switch_on: &mir::Place<'tcx>,
|
||||||
|
) -> Option<&'tcx ty::AdtDef> {
|
||||||
|
match block.statements.last().map(|stmt| &stmt.kind) {
|
||||||
|
Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated))))
|
||||||
|
if lhs == switch_on =>
|
||||||
|
{
|
||||||
|
match &discriminated.ty(body, tcx).ty.kind {
|
||||||
|
ty::Adt(def, _) => Some(def),
|
||||||
|
|
||||||
_ => {
|
// `Rvalue::Discriminant` is also used to get the active yield point for a
|
||||||
for target in targets.iter().copied() {
|
// generator, but we do not need edge-specific effects in that case. This may
|
||||||
self.propagate_bits_into_entry_set_for(&in_out, target, dirty_list);
|
// change in the future.
|
||||||
}
|
ty::Generator(..) => None,
|
||||||
|
|
||||||
|
t => bug!("`discriminant` called on unexpected type {:?}", t),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user