Skip to content

Commit 52ad1e7

Browse files
Don't bug when taking discriminant of generator
1 parent 49c68bd commit 52ad1e7

File tree

1 file changed

+72
-54
lines changed

1 file changed

+72
-54
lines changed

src/librustc_mir/dataflow/generic/engine.rs

+72-54
Original file line numberDiff line numberDiff line change
@@ -239,14 +239,24 @@ where
239239
}
240240

241241
SwitchInt { ref targets, ref values, ref discr, .. } => {
242-
self.propagate_bits_into_switch_int_successors(
243-
in_out,
244-
(bb, bb_data),
245-
dirty_list,
246-
discr,
247-
&*values,
248-
&*targets,
249-
);
242+
// If this is a switch on an enum discriminant, a custom effect may be applied
243+
// along each outgoing edge.
244+
if let Some(place) = discr.place() {
245+
let enum_def = switch_on_enum_discriminant(self.tcx, self.body, bb_data, place);
246+
if let Some(enum_def) = enum_def {
247+
self.propagate_bits_into_enum_discriminant_switch_successors(
248+
in_out, bb, enum_def, place, dirty_list, &*values, &*targets,
249+
);
250+
251+
return;
252+
}
253+
}
254+
255+
// Otherwise, it's just a normal `SwitchInt`, and every successor sees the same
256+
// exit state.
257+
for target in targets.iter().copied() {
258+
self.propagate_bits_into_entry_set_for(&in_out, target, dirty_list);
259+
}
250260
}
251261

252262
Call { cleanup, ref destination, ref func, ref args, .. } => {
@@ -293,64 +303,72 @@ where
293303
}
294304
}
295305

296-
fn propagate_bits_into_switch_int_successors(
306+
fn propagate_bits_into_enum_discriminant_switch_successors(
297307
&mut self,
298308
in_out: &mut BitSet<A::Idx>,
299-
(bb, bb_data): (BasicBlock, &mir::BasicBlockData<'tcx>),
309+
bb: BasicBlock,
310+
enum_def: &'tcx ty::AdtDef,
311+
enum_place: &mir::Place<'tcx>,
300312
dirty_list: &mut WorkQueue<BasicBlock>,
301-
switch_on: &mir::Operand<'tcx>,
302313
values: &[u128],
303314
targets: &[BasicBlock],
304315
) {
305-
match bb_data.statements.last().map(|stmt| &stmt.kind) {
306-
// Look at the last statement to see if it is an assignment of an enum discriminant to
307-
// the local that determines the target of a `SwitchInt` like so:
308-
// _42 = discriminant(..)
309-
// SwitchInt(_42, ..)
310-
Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(enum_))))
311-
if Some(lhs) == switch_on.place() =>
312-
{
313-
let adt = match enum_.ty(self.body, self.tcx).ty.kind {
314-
ty::Adt(def, _) => def,
315-
_ => bug!("Switch on discriminant of non-ADT"),
316-
};
317-
318-
// MIR building adds discriminants to the `values` array in the same order as they
319-
// are yielded by `AdtDef::discriminants`. We rely on this to match each
320-
// discriminant in `values` to its corresponding variant in linear time.
321-
let mut tmp = BitSet::new_empty(in_out.domain_size());
322-
let mut discriminants = adt.discriminants(self.tcx);
323-
for (value, target) in values.iter().zip(targets.iter().copied()) {
324-
let (variant_idx, _) =
325-
discriminants.find(|&(_, discr)| discr.val == *value).expect(
326-
"Order of `AdtDef::discriminants` differed \
327-
from that of `SwitchInt::values`",
328-
);
316+
// MIR building adds discriminants to the `values` array in the same order as they
317+
// are yielded by `AdtDef::discriminants`. We rely on this to match each
318+
// discriminant in `values` to its corresponding variant in linear time.
319+
let mut tmp = BitSet::new_empty(in_out.domain_size());
320+
let mut discriminants = enum_def.discriminants(self.tcx);
321+
for (value, target) in values.iter().zip(targets.iter().copied()) {
322+
let (variant_idx, _) = discriminants.find(|&(_, discr)| discr.val == *value).expect(
323+
"Order of `AdtDef::discriminants` differed from that of `SwitchInt::values`",
324+
);
329325

330-
tmp.overwrite(in_out);
331-
self.analysis.apply_discriminant_switch_effect(
332-
&mut tmp,
333-
bb,
334-
enum_,
335-
adt,
336-
variant_idx,
337-
);
338-
self.propagate_bits_into_entry_set_for(&tmp, target, dirty_list);
339-
}
326+
tmp.overwrite(in_out);
327+
self.analysis.apply_discriminant_switch_effect(
328+
&mut tmp,
329+
bb,
330+
enum_place,
331+
enum_def,
332+
variant_idx,
333+
);
334+
self.propagate_bits_into_entry_set_for(&tmp, target, dirty_list);
335+
}
340336

341-
std::mem::drop(tmp);
337+
std::mem::drop(tmp);
342338

343-
// Propagate dataflow state along the "otherwise" edge.
344-
let otherwise = targets.last().copied().unwrap();
345-
self.propagate_bits_into_entry_set_for(&in_out, otherwise, dirty_list);
346-
}
339+
// Propagate dataflow state along the "otherwise" edge.
340+
let otherwise = targets.last().copied().unwrap();
341+
self.propagate_bits_into_entry_set_for(&in_out, otherwise, dirty_list);
342+
}
343+
}
347344

348-
_ => {
349-
for target in targets.iter().copied() {
350-
self.propagate_bits_into_entry_set_for(&in_out, target, dirty_list);
351-
}
345+
/// Look at the last statement of a block that ends with to see if it is an assignment of an enum
346+
/// discriminant to the local that determines the target of a `SwitchInt` like so:
347+
/// _42 = discriminant(..)
348+
/// SwitchInt(_42, ..)
349+
fn switch_on_enum_discriminant(
350+
tcx: TyCtxt<'tcx>,
351+
body: &mir::Body<'tcx>,
352+
block: &mir::BasicBlockData<'tcx>,
353+
switch_on: &mir::Place<'tcx>,
354+
) -> Option<&'tcx ty::AdtDef> {
355+
match block.statements.last().map(|stmt| &stmt.kind) {
356+
Some(mir::StatementKind::Assign(box (lhs, mir::Rvalue::Discriminant(discriminated))))
357+
if lhs == switch_on =>
358+
{
359+
match &discriminated.ty(body, tcx).ty.kind {
360+
ty::Adt(def, _) => Some(def),
361+
362+
// `Rvalue::Discriminant` is also used to get the active yield point for a
363+
// generator, but we do not need edge-specific effects in that case. This may
364+
// change in the future.
365+
ty::Generator(..) => None,
366+
367+
t => bug!("`discriminant` called on unexpected type {:?}", t),
352368
}
353369
}
370+
371+
_ => None,
354372
}
355373
}
356374

0 commit comments

Comments
 (0)