diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index a9cd688c31551..7cab665099482 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -585,20 +585,32 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { val.into() } - Aggregate(ref kind, ref fields) => Value::Aggregate { - fields: fields - .iter() - .map(|field| self.eval_operand(field).map_or(Value::Uninit, Value::Immediate)) - .collect(), - variant: match **kind { - AggregateKind::Adt(_, variant, _, _, _) => variant, - AggregateKind::Array(_) - | AggregateKind::Tuple - | AggregateKind::Closure(_, _) - | AggregateKind::Coroutine(_, _) - | AggregateKind::CoroutineClosure(_, _) => VariantIdx::new(0), - }, - }, + Aggregate(ref kind, ref fields) => { + // Do not const pop union fields as they can be + // made to produce values that don't match their + // underlying layout's type (see ICE #121534). + // If the last element of the `Adt` tuple + // is `Some` it indicates the ADT is a union + if let AggregateKind::Adt(_, _, _, _, Some(_)) = **kind { + return None; + }; + Value::Aggregate { + fields: fields + .iter() + .map(|field| { + self.eval_operand(field).map_or(Value::Uninit, Value::Immediate) + }) + .collect(), + variant: match **kind { + AggregateKind::Adt(_, variant, _, _, _) => variant, + AggregateKind::Array(_) + | AggregateKind::Tuple + | AggregateKind::Closure(_, _) + | AggregateKind::Coroutine(_, _) + | AggregateKind::CoroutineClosure(_, _) => VariantIdx::new(0), + }, + } + } Repeat(ref op, n) => { trace!(?op, ?n); diff --git a/tests/ui/lint/ice-unions-known-panics-lint-issue-121534.rs b/tests/ui/lint/ice-unions-known-panics-lint-issue-121534.rs new file mode 100644 index 0000000000000..9fadb828b3d2d --- /dev/null +++ b/tests/ui/lint/ice-unions-known-panics-lint-issue-121534.rs @@ -0,0 +1,21 @@ +// Regression test for #121534 +// Tests that no ICE occurs in KnownPanicsLint when it +// evaluates an operation whose operands have different +// layout types even though they have the same type. +// This situation can be contrived through the use of +// unions as in this test + +//@ build-pass +union Union { + u32_field: u32, + i32_field: i32, +} + +pub fn main() { + let u32_variant = Union { u32_field: 2 }; + let i32_variant = Union { i32_field: 3 }; + let a = unsafe { u32_variant.u32_field }; + let b = unsafe { i32_variant.u32_field }; + + let _diff = a - b; +}