about summary refs log tree commit diff
diff options
context:
space:
mode:
authorErik Desjardins <erikdesjardins@users.noreply.github.com>2021-03-24 13:00:36 -0400
committerErik Desjardins <erikdesjardins@users.noreply.github.com>2021-03-24 13:00:36 -0400
commitd5c1ad5ca1c014044a4bfd5c9c82fb277de304c3 (patch)
tree9cd2672c20d1cba7519f51f3d39aa6ea948949fd
parent79e5814f4520f2c51b5307421db45cd82d134e76 (diff)
downloadrust-d5c1ad5ca1c014044a4bfd5c9c82fb277de304c3.tar.gz
rust-d5c1ad5ca1c014044a4bfd5c9c82fb277de304c3.zip
RemoveZsts: don't touch unions
-rw-r--r--compiler/rustc_mir/src/transform/remove_zsts.rs34
-rw-r--r--src/test/mir-opt/remove_zsts_dont_touch_unions.get_union.RemoveZsts.after.mir13
-rw-r--r--src/test/mir-opt/remove_zsts_dont_touch_unions.rs19
3 files changed, 65 insertions, 1 deletions
diff --git a/compiler/rustc_mir/src/transform/remove_zsts.rs b/compiler/rustc_mir/src/transform/remove_zsts.rs
index 144cedf593e..70f7538dd57 100644
--- a/compiler/rustc_mir/src/transform/remove_zsts.rs
+++ b/compiler/rustc_mir/src/transform/remove_zsts.rs
@@ -1,7 +1,8 @@
 //! Removes assignments to ZST places.
 
 use crate::transform::MirPass;
-use rustc_middle::mir::{Body, StatementKind};
+use rustc_middle::mir::tcx::PlaceTy;
+use rustc_middle::mir::{Body, LocalDecls, Place, StatementKind};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 
 pub struct RemoveZsts;
@@ -28,6 +29,9 @@ impl<'tcx> MirPass<'tcx> for RemoveZsts {
                         if !layout.is_zst() {
                             continue;
                         }
+                        if involves_a_union(place, local_decls, tcx) {
+                            continue;
+                        }
                         if tcx.consider_optimizing(|| {
                             format!(
                                 "RemoveZsts - Place: {:?} SourceInfo: {:?}",
@@ -55,3 +59,31 @@ fn maybe_zst(ty: Ty<'_>) -> bool {
         _ => false,
     }
 }
+
+/// Miri lazily allocates memory for locals on assignment,
+/// so we must preserve writes to unions and union fields,
+/// or it will ICE on reads of those fields.
+fn involves_a_union<'tcx>(
+    place: Place<'tcx>,
+    local_decls: &LocalDecls<'tcx>,
+    tcx: TyCtxt<'tcx>,
+) -> bool {
+    let mut place_ty = PlaceTy::from_ty(local_decls[place.local].ty);
+    if is_union(place_ty.ty) {
+        return true;
+    }
+    for elem in place.projection {
+        place_ty = place_ty.projection_ty(tcx, elem);
+        if is_union(place_ty.ty) {
+            return true;
+        }
+    }
+    return false;
+}
+
+fn is_union(ty: Ty<'_>) -> bool {
+    match ty.kind() {
+        ty::Adt(def, _) if def.is_union() => true,
+        _ => false,
+    }
+}
diff --git a/src/test/mir-opt/remove_zsts_dont_touch_unions.get_union.RemoveZsts.after.mir b/src/test/mir-opt/remove_zsts_dont_touch_unions.get_union.RemoveZsts.after.mir
new file mode 100644
index 00000000000..33bd9eb9b19
--- /dev/null
+++ b/src/test/mir-opt/remove_zsts_dont_touch_unions.get_union.RemoveZsts.after.mir
@@ -0,0 +1,13 @@
+// MIR for `get_union` after RemoveZsts
+
+fn get_union() -> Foo {
+    let mut _0: Foo;                     // return place in scope 0 at $DIR/remove_zsts_dont_touch_unions.rs:12:19: 12:22
+    let mut _1: ();                      // in scope 0 at $DIR/remove_zsts_dont_touch_unions.rs:13:14: 13:16
+
+    bb0: {
+        StorageLive(_1);                 // scope 0 at $DIR/remove_zsts_dont_touch_unions.rs:13:14: 13:16
+        (_0.0: ()) = move _1;            // scope 0 at $DIR/remove_zsts_dont_touch_unions.rs:13:5: 13:18
+        StorageDead(_1);                 // scope 0 at $DIR/remove_zsts_dont_touch_unions.rs:13:17: 13:18
+        return;                          // scope 0 at $DIR/remove_zsts_dont_touch_unions.rs:14:2: 14:2
+    }
+}
diff --git a/src/test/mir-opt/remove_zsts_dont_touch_unions.rs b/src/test/mir-opt/remove_zsts_dont_touch_unions.rs
new file mode 100644
index 00000000000..7a6f86b8085
--- /dev/null
+++ b/src/test/mir-opt/remove_zsts_dont_touch_unions.rs
@@ -0,0 +1,19 @@
+// compile-flags: -Zmir-opt-level=3
+
+// Ensure RemoveZsts doesn't remove ZST assignments to union fields,
+// which causes problems in Miri.
+
+union Foo {
+    x: (),
+    y: u64,
+}
+
+// EMIT_MIR remove_zsts_dont_touch_unions.get_union.RemoveZsts.after.mir
+fn get_union() -> Foo {
+    Foo { x: () }
+}
+
+
+fn main() {
+    get_union();
+}