Don't ICE when evaluating writes to uninhabited enum variants

This commit is contained in:
Wesley Wiser 2019-10-09 06:08:46 -04:00
parent 8cf6c23d66
commit b71ea80172
5 changed files with 48 additions and 13 deletions

View File

@ -363,6 +363,8 @@ pub enum UndefinedBehaviorInfo {
UbExperimental(String),
/// Unreachable code was executed.
Unreachable,
/// An enum discriminant was set to a value which was outside the range of valid values.
InvalidDiscriminant(ScalarMaybeUndef),
}
impl fmt::Debug for UndefinedBehaviorInfo {
@ -373,6 +375,8 @@ impl fmt::Debug for UndefinedBehaviorInfo {
write!(f, "{}", msg),
Unreachable =>
write!(f, "entered unreachable code"),
InvalidDiscriminant(val) =>
write!(f, "encountered invalid enum discriminant {}", val),
}
}
}
@ -400,7 +404,6 @@ pub enum UnsupportedOpInfo<'tcx> {
InvalidMemoryAccess,
InvalidFunctionPointer,
InvalidBool,
InvalidDiscriminant(ScalarMaybeUndef),
PointerOutOfBounds {
ptr: Pointer,
msg: CheckInAllocMsg,
@ -485,8 +488,6 @@ impl fmt::Debug for UnsupportedOpInfo<'tcx> {
write!(f, "incorrect alloc info: expected size {} and align {}, \
got size {} and align {}",
size.bytes(), align.bytes(), size2.bytes(), align2.bytes()),
InvalidDiscriminant(val) =>
write!(f, "encountered invalid enum discriminant {}", val),
InvalidMemoryAccess =>
write!(f, "tried to access memory through an invalid pointer"),
DanglingPointerDeref =>

View File

@ -647,7 +647,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let bits_discr = raw_discr
.not_undef()
.and_then(|raw_discr| self.force_bits(raw_discr, discr_val.layout.size))
.map_err(|_| err_unsup!(InvalidDiscriminant(raw_discr.erase_tag())))?;
.map_err(|_| err_ub!(InvalidDiscriminant(raw_discr.erase_tag())))?;
let real_discr = if discr_val.layout.ty.is_signed() {
// going from layout tag type to typeck discriminant type
// requires first sign extending with the discriminant layout
@ -677,7 +677,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
_ => bug!("tagged layout for non-adt non-generator"),
}.ok_or_else(
|| err_unsup!(InvalidDiscriminant(raw_discr.erase_tag()))
|| err_ub!(InvalidDiscriminant(raw_discr.erase_tag()))
)?;
(real_discr, index.0)
},
@ -689,7 +689,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let variants_start = niche_variants.start().as_u32();
let variants_end = niche_variants.end().as_u32();
let raw_discr = raw_discr.not_undef().map_err(|_| {
err_unsup!(InvalidDiscriminant(ScalarMaybeUndef::Undef))
err_ub!(InvalidDiscriminant(ScalarMaybeUndef::Undef))
})?;
match raw_discr.to_bits_or_ptr(discr_val.layout.size, self) {
Err(ptr) => {
@ -697,7 +697,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let ptr_valid = niche_start == 0 && variants_start == variants_end &&
!self.memory.ptr_may_be_null(ptr);
if !ptr_valid {
throw_unsup!(InvalidDiscriminant(raw_discr.erase_tag().into()))
throw_ub!(InvalidDiscriminant(raw_discr.erase_tag().into()))
}
(dataful_variant.as_u32() as u128, dataful_variant)
},

View File

@ -1031,9 +1031,13 @@ where
variant_index: VariantIdx,
dest: PlaceTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx> {
let variant_scalar = Scalar::from_u32(variant_index.as_u32()).into();
match dest.layout.variants {
layout::Variants::Single { index } => {
assert_eq!(index, variant_index);
if index != variant_index {
throw_ub!(InvalidDiscriminant(variant_scalar));
}
}
layout::Variants::Multiple {
discr_kind: layout::DiscriminantKind::Tag,
@ -1041,7 +1045,9 @@ where
discr_index,
..
} => {
assert!(dest.layout.ty.variant_range(*self.tcx).unwrap().contains(&variant_index));
if !dest.layout.ty.variant_range(*self.tcx).unwrap().contains(&variant_index) {
throw_ub!(InvalidDiscriminant(variant_scalar));
}
let discr_val =
dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val;
@ -1064,9 +1070,9 @@ where
discr_index,
..
} => {
assert!(
variant_index.as_usize() < dest.layout.ty.ty_adt_def().unwrap().variants.len(),
);
if !variant_index.as_usize() < dest.layout.ty.ty_adt_def().unwrap().variants.len() {
throw_ub!(InvalidDiscriminant(variant_scalar));
}
if variant_index != dataful_variant {
let variants_start = niche_variants.start().as_u32();
let variant_index_relative = variant_index.as_u32()

View File

@ -344,7 +344,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
match self.walk_value(op) {
Ok(()) => Ok(()),
Err(err) => match err.kind {
err_unsup!(InvalidDiscriminant(val)) =>
err_ub!(InvalidDiscriminant(val)) =>
throw_validation_failure!(
val, self.path, "a valid enum discriminant"
),

View File

@ -0,0 +1,28 @@
// run-pass
#![allow(dead_code)]
enum Empty { }
enum Test1 {
A(u8),
B(Empty),
}
enum Test2 {
A(u8),
B(Empty),
C,
}
fn bar() -> Option<Empty> {
None
}
fn main() {
if let Some(x) = bar() {
Test1::B(x);
}
if let Some(x) = bar() {
Test2::B(x);
}
}