Skip to content

Commit

Permalink
Don't ICE when evaluating writes to uninhabited enum variants
Browse files Browse the repository at this point in the history
  • Loading branch information
wesleywiser committed Oct 18, 2019
1 parent 8cf6c23 commit b71ea80
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 13 deletions.
7 changes: 4 additions & 3 deletions src/librustc/mir/interpret/error.rs
Expand Up @@ -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 {
Expand All @@ -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),
}
}
}
Expand Down Expand Up @@ -400,7 +404,6 @@ pub enum UnsupportedOpInfo<'tcx> {
InvalidMemoryAccess,
InvalidFunctionPointer,
InvalidBool,
InvalidDiscriminant(ScalarMaybeUndef),
PointerOutOfBounds {
ptr: Pointer,
msg: CheckInAllocMsg,
Expand Down Expand Up @@ -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 =>
Expand Down
8 changes: 4 additions & 4 deletions src/librustc_mir/interpret/operand.rs
Expand Up @@ -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
Expand Down Expand Up @@ -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)
},
Expand All @@ -689,15 +689,15 @@ 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) => {
// The niche must be just 0 (which an inbounds pointer value never is)
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)
},
Expand Down
16 changes: 11 additions & 5 deletions src/librustc_mir/interpret/place.rs
Expand Up @@ -1031,17 +1031,23 @@ 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,
discr: ref discr_layout,
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;

Expand All @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/interpret/validity.rs
Expand Up @@ -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"
),
Expand Down
28 changes: 28 additions & 0 deletions src/test/ui/consts/const-eval/write-to-uninhabited-enum-variant.rs
@@ -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);
}
}

0 comments on commit b71ea80

Please sign in to comment.