Skip to content

Commit b6f6dc4

Browse files
committed
Don't ICE when evaluating writes to uninhabited enum variants
1 parent f2afa98 commit b6f6dc4

File tree

5 files changed

+49
-13
lines changed

5 files changed

+49
-13
lines changed

src/librustc/mir/interpret/error.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,8 @@ pub enum UndefinedBehaviorInfo {
363363
UbExperimental(String),
364364
/// Unreachable code was executed.
365365
Unreachable,
366+
/// An enum discriminant was set to a value which was outside the range of valid values.
367+
InvalidDiscriminant(ScalarMaybeUndef),
366368
}
367369

368370
impl fmt::Debug for UndefinedBehaviorInfo {
@@ -373,6 +375,8 @@ impl fmt::Debug for UndefinedBehaviorInfo {
373375
write!(f, "{}", msg),
374376
Unreachable =>
375377
write!(f, "entered unreachable code"),
378+
InvalidDiscriminant(val) =>
379+
write!(f, "encountered invalid enum discriminant {}", val),
376380
}
377381
}
378382
}
@@ -404,7 +408,6 @@ pub enum UnsupportedOpInfo<'tcx> {
404408
InvalidMemoryAccess,
405409
InvalidFunctionPointer,
406410
InvalidBool,
407-
InvalidDiscriminant(ScalarMaybeUndef),
408411
PointerOutOfBounds {
409412
ptr: Pointer,
410413
msg: CheckInAllocMsg,
@@ -489,8 +492,6 @@ impl fmt::Debug for UnsupportedOpInfo<'tcx> {
489492
write!(f, "incorrect alloc info: expected size {} and align {}, \
490493
got size {} and align {}",
491494
size.bytes(), align.bytes(), size2.bytes(), align2.bytes()),
492-
InvalidDiscriminant(val) =>
493-
write!(f, "encountered invalid enum discriminant {}", val),
494495
InvalidMemoryAccess =>
495496
write!(f, "tried to access memory through an invalid pointer"),
496497
DanglingPointerDeref =>

src/librustc_mir/interpret/operand.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
647647
let bits_discr = raw_discr
648648
.not_undef()
649649
.and_then(|raw_discr| self.force_bits(raw_discr, discr_val.layout.size))
650-
.map_err(|_| err_unsup!(InvalidDiscriminant(raw_discr.erase_tag())))?;
650+
.map_err(|_| err_ub!(InvalidDiscriminant(raw_discr.erase_tag())))?;
651651
let real_discr = if discr_val.layout.ty.is_signed() {
652652
// going from layout tag type to typeck discriminant type
653653
// requires first sign extending with the discriminant layout
@@ -677,7 +677,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
677677
_ => bug!("tagged layout for non-adt non-generator"),
678678

679679
}.ok_or_else(
680-
|| err_unsup!(InvalidDiscriminant(raw_discr.erase_tag()))
680+
|| err_ub!(InvalidDiscriminant(raw_discr.erase_tag()))
681681
)?;
682682
(real_discr, index.0)
683683
},
@@ -689,15 +689,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
689689
let variants_start = niche_variants.start().as_u32();
690690
let variants_end = niche_variants.end().as_u32();
691691
let raw_discr = raw_discr.not_undef().map_err(|_| {
692-
err_unsup!(InvalidDiscriminant(ScalarMaybeUndef::Undef))
692+
err_ub!(InvalidDiscriminant(ScalarMaybeUndef::Undef))
693693
})?;
694694
match raw_discr.to_bits_or_ptr(discr_val.layout.size, self) {
695695
Err(ptr) => {
696696
// The niche must be just 0 (which an inbounds pointer value never is)
697697
let ptr_valid = niche_start == 0 && variants_start == variants_end &&
698698
!self.memory.ptr_may_be_null(ptr);
699699
if !ptr_valid {
700-
throw_unsup!(InvalidDiscriminant(raw_discr.erase_tag().into()))
700+
throw_ub!(InvalidDiscriminant(raw_discr.erase_tag().into()))
701701
}
702702
(dataful_variant.as_u32() as u128, dataful_variant)
703703
},

src/librustc_mir/interpret/place.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,17 +1034,23 @@ where
10341034
variant_index: VariantIdx,
10351035
dest: PlaceTy<'tcx, M::PointerTag>,
10361036
) -> InterpResult<'tcx> {
1037+
let variant_scalar = Scalar::from_u32(variant_index.as_u32()).into();
1038+
10371039
match dest.layout.variants {
10381040
layout::Variants::Single { index } => {
1039-
assert_eq!(index, variant_index);
1041+
if index != variant_index {
1042+
throw_ub!(InvalidDiscriminant(variant_scalar));
1043+
}
10401044
}
10411045
layout::Variants::Multiple {
10421046
discr_kind: layout::DiscriminantKind::Tag,
10431047
discr: ref discr_layout,
10441048
discr_index,
10451049
..
10461050
} => {
1047-
assert!(dest.layout.ty.variant_range(*self.tcx).unwrap().contains(&variant_index));
1051+
if !dest.layout.ty.variant_range(*self.tcx).unwrap().contains(&variant_index) {
1052+
throw_ub!(InvalidDiscriminant(variant_scalar));
1053+
}
10481054
let discr_val =
10491055
dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val;
10501056

@@ -1067,9 +1073,9 @@ where
10671073
discr_index,
10681074
..
10691075
} => {
1070-
assert!(
1071-
variant_index.as_usize() < dest.layout.ty.ty_adt_def().unwrap().variants.len(),
1072-
);
1076+
if !variant_index.as_usize() < dest.layout.ty.ty_adt_def().unwrap().variants.len() {
1077+
throw_ub!(InvalidDiscriminant(variant_scalar));
1078+
}
10731079
if variant_index != dataful_variant {
10741080
let variants_start = niche_variants.start().as_u32();
10751081
let variant_index_relative = variant_index.as_u32()

src/librustc_mir/interpret/validity.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
344344
match self.walk_value(op) {
345345
Ok(()) => Ok(()),
346346
Err(err) => match err.kind {
347-
err_unsup!(InvalidDiscriminant(val)) =>
347+
err_ub!(InvalidDiscriminant(val)) =>
348348
throw_validation_failure!(
349349
val, self.path, "a valid enum discriminant"
350350
),
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// run-pass
2+
// ignore-wasm
3+
4+
#![allow(dead_code)]
5+
6+
enum Empty { }
7+
enum Test1 {
8+
A(u8),
9+
B(Empty),
10+
}
11+
enum Test2 {
12+
A(u8),
13+
B(Empty),
14+
C,
15+
}
16+
17+
fn bar() -> Option<Empty> {
18+
std::process::exit(0)
19+
}
20+
21+
fn main() {
22+
if let Some(x) = bar() {
23+
Test1::B(x);
24+
}
25+
26+
if let Some(x) = bar() {
27+
Test2::B(x);
28+
}
29+
}

0 commit comments

Comments
 (0)