From d0f65b413f795f01b0fa6f905e027064dc90eb13 Mon Sep 17 00:00:00 2001 From: Ojus Chugh Date: Mon, 28 Sep 2026 12:27:21 +0530 Subject: [PATCH] Treat union fields as aliasing each other in value_analysis --- .../rustc_mir_dataflow/src/value_analysis.rs | 6 ++ ...ld.main.DataflowConstProp.panic-abort.diff | 28 +++++++ ...d.main.DataflowConstProp.panic-unwind.diff | 28 +++++++ .../dataflow-const-prop/union_field.rs | 24 ++++++ tests/mir-opt/jump_threading.rs | 30 ++++++++ ...union_field.JumpThreading.panic-abort.diff | 73 +++++++++++++++++++ ...nion_field.JumpThreading.panic-unwind.diff | 73 +++++++++++++++++++ 7 files changed, 262 insertions(+) create mode 100644 tests/mir-opt/dataflow-const-prop/union_field.main.DataflowConstProp.panic-abort.diff create mode 100644 tests/mir-opt/dataflow-const-prop/union_field.main.DataflowConstProp.panic-unwind.diff create mode 100644 tests/mir-opt/dataflow-const-prop/union_field.rs create mode 100644 tests/mir-opt/jump_threading.union_field.JumpThreading.panic-abort.diff create mode 100644 tests/mir-opt/jump_threading.union_field.JumpThreading.panic-unwind.diff diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index e213a295c0bd0..739aab620993f 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -797,6 +797,12 @@ impl<'tcx> Map<'tcx> { }; let elems = place.projection.iter().map(|&elem| elem.try_into()).chain(tail_elem.map(Ok)); for elem in elems { + // All fields of a union alias each other. + if self.places[index].ty.is_union() { + self.for_each_value_inside(index, f); + return; + } + // A field aliases the parent place. if let Some(vi) = self.places[index].value_index { f(vi); diff --git a/tests/mir-opt/dataflow-const-prop/union_field.main.DataflowConstProp.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/union_field.main.DataflowConstProp.panic-abort.diff new file mode 100644 index 0000000000000..f78a75bcb3bed --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/union_field.main.DataflowConstProp.panic-abort.diff @@ -0,0 +1,28 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let mut _1: U; + scope 1 { + debug u => _1; + let _2: u8; + scope 2 { + debug a => _2; + } + } + + bb0: { + StorageLive(_1); + _1 = U { a: const 0_u8 }; + (_1.0: u8) = const 1_u8; + (_1.1: u8) = const 5_u8; + StorageLive(_2); + _2 = copy (_1.0: u8); + _0 = const (); + StorageDead(_2); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/union_field.main.DataflowConstProp.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/union_field.main.DataflowConstProp.panic-unwind.diff new file mode 100644 index 0000000000000..f78a75bcb3bed --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/union_field.main.DataflowConstProp.panic-unwind.diff @@ -0,0 +1,28 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let mut _1: U; + scope 1 { + debug u => _1; + let _2: u8; + scope 2 { + debug a => _2; + } + } + + bb0: { + StorageLive(_1); + _1 = U { a: const 0_u8 }; + (_1.0: u8) = const 1_u8; + (_1.1: u8) = const 5_u8; + StorageLive(_2); + _2 = copy (_1.0: u8); + _0 = const (); + StorageDead(_2); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/union_field.rs b/tests/mir-opt/dataflow-const-prop/union_field.rs new file mode 100644 index 0000000000000..89bb30e0fca7f --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/union_field.rs @@ -0,0 +1,24 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +// Writing to one union field must also invalidate what is known about the other fields, +// since they share the same storage. + +//@ test-mir-pass: DataflowConstProp + +// EMIT_MIR union_field.main.DataflowConstProp.diff + +union U { + a: u8, + b: u8, +} + +// CHECK-LABEL: fn main( +fn main() { + // CHECK: debug a => [[a:_.*]]; + + let mut u = U { a: 0 }; + u.a = 1; + u.b = 5; + + // CHECK: [[a]] = copy ({{_.*}}.0: u8); + let a = unsafe { u.a }; // should not be propagated +} diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs index b480bc097b99f..31ceb5b7f64d9 100644 --- a/tests/mir-opt/jump_threading.rs +++ b/tests/mir-opt/jump_threading.rs @@ -656,6 +656,34 @@ fn chained_conditions() -> u8 { } } +union U { + a: u8, + b: u8, +} + +fn union_field(x: u8) -> u8 { + // CHECK-LABEL: fn union_field( + // CHECK: ({{_.*}}.1: u8) = const 5_u8; + // CHECK-NOT: ({{_.*}}.1: u8) = const 5_u8; + + // Test for #161898, where the write to `u.b` did not invalidate what was known about `u.a`. + let mut u = U { a: x }; + let r = unsafe { + match u.a { + 0 => 100, + _ => 200, + } + }; + u.b = 5; + let r2 = unsafe { + match u.a { + 0 => 1, + _ => 2, + } + }; + r + r2 +} + fn main() { // CHECK-LABEL: fn main( too_complex(Ok(0)); @@ -673,6 +701,7 @@ fn main() { floats(); bitwise_not(); logical_not(); + union_field(0); } // EMIT_MIR jump_threading.too_complex.JumpThreading.diff @@ -693,3 +722,4 @@ fn main() { // EMIT_MIR jump_threading.bitwise_not.JumpThreading.diff // EMIT_MIR jump_threading.logical_not.JumpThreading.diff // EMIT_MIR jump_threading.chained_conditions.JumpThreading.diff +// EMIT_MIR jump_threading.union_field.JumpThreading.diff diff --git a/tests/mir-opt/jump_threading.union_field.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.union_field.JumpThreading.panic-abort.diff new file mode 100644 index 0000000000000..41fe308542b22 --- /dev/null +++ b/tests/mir-opt/jump_threading.union_field.JumpThreading.panic-abort.diff @@ -0,0 +1,73 @@ +- // MIR for `union_field` before JumpThreading ++ // MIR for `union_field` after JumpThreading + + fn union_field(_1: u8) -> u8 { + debug x => _1; + let mut _0: u8; + let mut _2: U; + let mut _3: u8; + let mut _6: u8; + let mut _7: u8; + scope 1 { + debug u => _2; + let _4: u8; + scope 2 { + debug r => _4; + let _5: u8; + scope 3 { + debug r2 => _5; + } + } + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = copy _1; + _2 = U { a: move _3 }; + StorageDead(_3); + StorageLive(_4); + switchInt(copy (_2.0: u8)) -> [0: bb2, otherwise: bb1]; + } + + bb1: { + _4 = const 200_u8; + goto -> bb3; + } + + bb2: { + _4 = const 100_u8; + goto -> bb3; + } + + bb3: { + (_2.1: u8) = const 5_u8; + StorageLive(_5); + switchInt(copy (_2.0: u8)) -> [0: bb5, otherwise: bb4]; + } + + bb4: { + _5 = const 2_u8; + goto -> bb6; + } + + bb5: { + _5 = const 1_u8; + goto -> bb6; + } + + bb6: { + StorageLive(_6); + _6 = copy _4; + StorageLive(_7); + _7 = copy _5; + _0 = Add(move _6, move _7); + StorageDead(_7); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/jump_threading.union_field.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.union_field.JumpThreading.panic-unwind.diff new file mode 100644 index 0000000000000..41fe308542b22 --- /dev/null +++ b/tests/mir-opt/jump_threading.union_field.JumpThreading.panic-unwind.diff @@ -0,0 +1,73 @@ +- // MIR for `union_field` before JumpThreading ++ // MIR for `union_field` after JumpThreading + + fn union_field(_1: u8) -> u8 { + debug x => _1; + let mut _0: u8; + let mut _2: U; + let mut _3: u8; + let mut _6: u8; + let mut _7: u8; + scope 1 { + debug u => _2; + let _4: u8; + scope 2 { + debug r => _4; + let _5: u8; + scope 3 { + debug r2 => _5; + } + } + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = copy _1; + _2 = U { a: move _3 }; + StorageDead(_3); + StorageLive(_4); + switchInt(copy (_2.0: u8)) -> [0: bb2, otherwise: bb1]; + } + + bb1: { + _4 = const 200_u8; + goto -> bb3; + } + + bb2: { + _4 = const 100_u8; + goto -> bb3; + } + + bb3: { + (_2.1: u8) = const 5_u8; + StorageLive(_5); + switchInt(copy (_2.0: u8)) -> [0: bb5, otherwise: bb4]; + } + + bb4: { + _5 = const 2_u8; + goto -> bb6; + } + + bb5: { + _5 = const 1_u8; + goto -> bb6; + } + + bb6: { + StorageLive(_6); + _6 = copy _4; + StorageLive(_7); + _7 = copy _5; + _0 = Add(move _6, move _7); + StorageDead(_7); + StorageDead(_6); + StorageDead(_5); + StorageDead(_4); + StorageDead(_2); + return; + } + } +