Skip to content

Commit 7f18b25

Browse files
authored
Rollup merge of #154776 - enthropy7:fix-ice-discriminant-none, r=RalfJung
Fix ICE in read_discriminant for enums with non-contiguous discriminants so, investigation of #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](b840338) 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.
2 parents 66457ac + a082578 commit 7f18b25

3 files changed

Lines changed: 32 additions & 8 deletions

File tree

compiler/rustc_const_eval/src/interpret/discriminant.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,20 +121,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
121121
// discriminants are int-like.
122122
let discr_val = self.int_to_int_or_float(&tag_val, discr_layout).unwrap();
123123
let discr_bits = discr_val.to_scalar().to_bits(discr_layout.size)?;
124-
// Convert discriminant to variant index. Since we validated the tag against the
125-
// layout range above, this cannot fail.
124+
// Convert discriminant to variant index. The tag may pass the layout range
125+
// check above but still not match any actual variant discriminant (e.g.,
126+
// non-contiguous discriminants with a wrapping valid_range).
126127
let index = match *ty.kind() {
127128
ty::Adt(adt, _) => {
128-
adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits).unwrap()
129+
adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
129130
}
130131
ty::Coroutine(def_id, args) => {
131132
let args = args.as_coroutine();
132-
args.discriminants(def_id, *self.tcx)
133-
.find(|(_, var)| var.val == discr_bits)
134-
.unwrap()
133+
args.discriminants(def_id, *self.tcx).find(|(_, var)| var.val == discr_bits)
135134
}
136135
_ => span_bug!(self.cur_span(), "tagged layout for non-adt non-coroutine"),
137-
};
136+
}
137+
.ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?;
138138
// Return the cast value, and the index.
139139
index.0
140140
}

tests/ui/consts/const-eval/ub-enum.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,17 @@ const TEST_ICE_89765: () = {
108108
};
109109
};
110110

111+
// # Regression test for https://github.com/rust-lang/rust/issues/153758
112+
// Discriminants at i64::MIN and i64::MAX produce a wrapping valid_range that covers
113+
// all values. A value like 0 passes the range check but doesn't match any variant.
114+
#[repr(i64)]
115+
#[derive(Copy, Clone)]
116+
enum WideRangeDiscriminants {
117+
A = i64::MIN,
118+
B = i64::MAX,
119+
}
120+
121+
const BAD_WIDE_RANGE_ENUM: WideRangeDiscriminants = unsafe { mem::transmute(0_i64) };
122+
//~^ ERROR expected a valid enum tag
123+
111124
fn main() {}

tests/ui/consts/const-eval/ub-enum.stderr

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,17 @@ LL | std::mem::discriminant(&*(&() as *const () as *const Never));
129129
note: inside `discriminant::<Never>`
130130
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
131131

132-
error: aborting due to 14 previous errors
132+
error[E0080]: constructing invalid value of type WideRangeDiscriminants: at .<enum-tag>, encountered 0x0, but expected a valid enum tag
133+
--> $DIR/ub-enum.rs:121:1
134+
|
135+
LL | const BAD_WIDE_RANGE_ENUM: WideRangeDiscriminants = unsafe { mem::transmute(0_i64) };
136+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
137+
|
138+
= 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.
139+
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
140+
HEX_DUMP
141+
}
142+
143+
error: aborting due to 15 previous errors
133144

134145
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)