diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index 3a7c1ea65f3d8..c50d9db8be639 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -121,20 +121,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // discriminants are int-like. let discr_val = self.int_to_int_or_float(&tag_val, discr_layout).unwrap(); let discr_bits = discr_val.to_scalar().to_bits(discr_layout.size)?; - // Convert discriminant to variant index. Since we validated the tag against the - // layout range above, this cannot fail. + // Convert discriminant to variant index. The tag may pass the layout range + // check above but still not match any actual variant discriminant (e.g., + // non-contiguous discriminants with a wrapping valid_range). let index = match *ty.kind() { ty::Adt(adt, _) => { - adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits).unwrap() + adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits) } ty::Coroutine(def_id, args) => { let args = args.as_coroutine(); - args.discriminants(def_id, *self.tcx) - .find(|(_, var)| var.val == discr_bits) - .unwrap() + args.discriminants(def_id, *self.tcx).find(|(_, var)| var.val == discr_bits) } _ => span_bug!(self.cur_span(), "tagged layout for non-adt non-coroutine"), - }; + } + .ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?; // Return the cast value, and the index. index.0 } diff --git a/tests/ui/consts/const-eval/ub-enum.rs b/tests/ui/consts/const-eval/ub-enum.rs index 61ab3581e3479..8feb78e0b11e8 100644 --- a/tests/ui/consts/const-eval/ub-enum.rs +++ b/tests/ui/consts/const-eval/ub-enum.rs @@ -108,4 +108,17 @@ const TEST_ICE_89765: () = { }; }; +// # Regression test for https://github.com/rust-lang/rust/issues/153758 +// Discriminants at i64::MIN and i64::MAX produce a wrapping valid_range that covers +// all values. A value like 0 passes the range check but doesn't match any variant. +#[repr(i64)] +#[derive(Copy, Clone)] +enum WideRangeDiscriminants { + A = i64::MIN, + B = i64::MAX, +} + +const BAD_WIDE_RANGE_ENUM: WideRangeDiscriminants = unsafe { mem::transmute(0_i64) }; +//~^ ERROR expected a valid enum tag + fn main() {} diff --git a/tests/ui/consts/const-eval/ub-enum.stderr b/tests/ui/consts/const-eval/ub-enum.stderr index a5ac10c7922c1..bb2c58796b1c4 100644 --- a/tests/ui/consts/const-eval/ub-enum.stderr +++ b/tests/ui/consts/const-eval/ub-enum.stderr @@ -129,6 +129,17 @@ LL | std::mem::discriminant(&*(&() as *const () as *const Never)); note: inside `discriminant::` --> $SRC_DIR/core/src/mem/mod.rs:LL:COL -error: aborting due to 14 previous errors +error[E0080]: constructing invalid value of type WideRangeDiscriminants: at ., encountered 0x0, but expected a valid enum tag + --> $DIR/ub-enum.rs:121:1 + | +LL | const BAD_WIDE_RANGE_ENUM: WideRangeDiscriminants = unsafe { mem::transmute(0_i64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value + | + = note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) { + HEX_DUMP + } + +error: aborting due to 15 previous errors For more information about this error, try `rustc --explain E0080`.