Don't ICE when evaluating writes to uninhabited enum variants
This commit is contained in:
parent
8cf6c23d66
commit
b71ea80172
@ -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 =>
|
||||
|
@ -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)
|
||||
},
|
||||
|
@ -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()
|
||||
|
@ -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"
|
||||
),
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user