Repository: rust-lang/rust
Pull request: Fix ICE in read_discriminant for enums with non-contiguous discriminants

so, investigation of rust-lang/rust#153758 took a while. it seems, that reverting back to older approach is the best we can do there. 

> `read_discriminant `ICEs (unwrap on `None`) when an enum's `valid_range` is wider than its set of actual discriminants. this happens for enums with gaps in their discriminant values — e.g. `{0, 2, 3, 4, 5} `has `` valid_range`` 0..=5 `` which includes `1`, or `{i64::MIN, i64::MAX}` has a wrapping range covering everything. [b840338](https://github.com/rust-lang/rust/commit/b8403383ff2d9a544a04d981482cb89c138db0b7) commit added a `valid_range` check and replaced the prior `.ok_or_else(|| err_ub!(InvalidTag(...)))` with `.unwrap()`, assuming the range check would guarantee a matching variant. that assumption is wrong for non-contiguous discriminants. my fix restores the fallible lookup, returning `InvalidTag` UB instead of panicking. also it affected crates - **sec1, ntex-mqtt**, when compiled with optimizations, where the `jump_threading` MIR pass constructs constants with tag values inside the `valid_range` but not matching any actual variant.

Changed files:
filename: compiler/rustc_const_eval/src/interpret/discriminant.rs
patch: @@ -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
             }

filename: tests/ui/consts/const-eval/ub-enum.rs
patch: @@ -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() {}

filename: tests/ui/consts/const-eval/ub-enum.stderr
patch: @@ -129,6 +129,17 @@ LL |         std::mem::discriminant(&*(&() as *const () as *const Never));
 note: inside `discriminant::<Never>`
   --> $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 .<enum-tag>, 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`.