about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSmitty <me@smitop.com>2021-05-13 10:42:25 -0400
committerSmitty <me@smitop.com>2021-07-09 13:51:28 -0400
commit74d0d74dae5ddde15de0201d689d56b794aa0e86 (patch)
tree2c5a1cc5c0649234ad73bfd83f6f129b904fc8a2
parent969a6c2481c41cea793708f7fdd2f96a3397143f (diff)
downloadrust-74d0d74dae5ddde15de0201d689d56b794aa0e86.tar.gz
rust-74d0d74dae5ddde15de0201d689d56b794aa0e86.zip
Check for union field accesses in THIR unsafeck
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs130
-rw-r--r--compiler/rustc_mir_build/src/thir/visit.rs6
-rw-r--r--src/test/ui/union/union-align.rs3
-rw-r--r--src/test/ui/union/union-backcomp.rs3
-rw-r--r--src/test/ui/union/union-basic.rs3
-rw-r--r--src/test/ui/union/union-borrow-move-parent-sibling.mirunsafeck.stderr (renamed from src/test/ui/union/union-borrow-move-parent-sibling.stderr)10
-rw-r--r--src/test/ui/union/union-borrow-move-parent-sibling.rs3
-rw-r--r--src/test/ui/union/union-borrow-move-parent-sibling.thirunsafeck.stderr60
-rw-r--r--src/test/ui/union/union-const-codegen.rs2
-rw-r--r--src/test/ui/union/union-const-eval-field.rs2
-rw-r--r--src/test/ui/union/union-const-eval.rs3
-rw-r--r--src/test/ui/union/union-deref.mirunsafeck.stderr (renamed from src/test/ui/union/union-deref.stderr)12
-rw-r--r--src/test/ui/union/union-deref.rs3
-rw-r--r--src/test/ui/union/union-deref.thirunsafeck.stderr56
-rw-r--r--src/test/ui/union/union-derive-clone.mirunsafeck.stderr (renamed from src/test/ui/union/union-derive-clone.stderr)4
-rw-r--r--src/test/ui/union/union-derive-clone.rs3
-rw-r--r--src/test/ui/union/union-derive-clone.thirunsafeck.stderr36
-rw-r--r--src/test/ui/union/union-derive-eq.mirunsafeck.stderr (renamed from src/test/ui/union/union-derive-eq.stderr)2
-rw-r--r--src/test/ui/union/union-derive-eq.rs3
-rw-r--r--src/test/ui/union/union-derive-eq.thirunsafeck.stderr16
-rw-r--r--src/test/ui/union/union-derive-rpass.rs3
-rw-r--r--src/test/ui/union/union-drop.thirunsafeck.stderr22
-rw-r--r--src/test/ui/union/union-fields-1.mirunsafeck.stderr (renamed from src/test/ui/union/union-fields-1.stderr)10
-rw-r--r--src/test/ui/union/union-fields-1.rs3
-rw-r--r--src/test/ui/union/union-fields-1.thirunsafeck.stderr32
-rw-r--r--src/test/ui/union/union-fields-2.mirunsafeck.stderr (renamed from src/test/ui/union/union-fields-2.stderr)26
-rw-r--r--src/test/ui/union/union-fields-2.rs3
-rw-r--r--src/test/ui/union/union-fields-2.thirunsafeck.stderr84
-rw-r--r--src/test/ui/union/union-generic-rpass.rs3
-rw-r--r--src/test/ui/union/union-generic.mirunsafeck.stderr (renamed from src/test/ui/union/union-generic.stderr)4
-rw-r--r--src/test/ui/union/union-generic.rs3
-rw-r--r--src/test/ui/union/union-generic.thirunsafeck.stderr21
-rw-r--r--src/test/ui/union/union-inherent-method.rs2
-rw-r--r--src/test/ui/union/union-lint-dead-code.mirunsafeck.stderr (renamed from src/test/ui/union/union-lint-dead-code.stderr)4
-rw-r--r--src/test/ui/union/union-lint-dead-code.rs3
-rw-r--r--src/test/ui/union/union-lint-dead-code.thirunsafeck.stderr14
-rw-r--r--src/test/ui/union/union-macro.rs3
-rw-r--r--src/test/ui/union/union-manuallydrop-rpass.rs5
-rw-r--r--src/test/ui/union/union-move.mirunsafeck.stderr (renamed from src/test/ui/union/union-move.stderr)6
-rw-r--r--src/test/ui/union/union-move.rs3
-rw-r--r--src/test/ui/union/union-move.thirunsafeck.stderr35
-rw-r--r--src/test/ui/union/union-nodrop.rs2
-rw-r--r--src/test/ui/union/union-nonzero.rs3
-rw-r--r--src/test/ui/union/union-overwrite.rs2
-rw-r--r--src/test/ui/union/union-packed.rs3
-rw-r--r--src/test/ui/union/union-pat-refutability.rs3
-rw-r--r--src/test/ui/union/union-suggest-field.mirunsafeck.stderr (renamed from src/test/ui/union/union-suggest-field.stderr)6
-rw-r--r--src/test/ui/union/union-suggest-field.rs3
-rw-r--r--src/test/ui/union/union-suggest-field.thirunsafeck.stderr27
-rw-r--r--src/test/ui/union/union-trait-impl.rs2
-rw-r--r--src/test/ui/union/union-transmute.rs2
-rw-r--r--src/test/ui/union/union-unsafe.mir.stderr (renamed from src/test/ui/union/union-unsafe.stderr)22
-rw-r--r--src/test/ui/union/union-unsafe.rs8
-rw-r--r--src/test/ui/union/union-unsafe.thir.stderr75
-rw-r--r--src/test/ui/union/union-unsized.mirunsafeck.stderr (renamed from src/test/ui/union/union-unsized.stderr)4
-rw-r--r--src/test/ui/union/union-unsized.rs3
-rw-r--r--src/test/ui/union/union-unsized.thirunsafeck.stderr39
-rw-r--r--src/test/ui/union/union-with-drop-fields.mirunsafeck.stderr (renamed from src/test/ui/union/union-with-drop-fields.stderr)12
-rw-r--r--src/test/ui/union/union-with-drop-fields.rs4
-rw-r--r--src/test/ui/union/union-with-drop-fields.thirunsafeck.stderr39
-rw-r--r--src/test/ui/unsafe/access_union_field.mir.stderr19
-rw-r--r--src/test/ui/unsafe/access_union_field.rs15
-rw-r--r--src/test/ui/unsafe/access_union_field.thir.stderr19
-rw-r--r--src/test/ui/unsafe/union.mir.stderr16
-rw-r--r--src/test/ui/unsafe/union.rs80
-rw-r--r--src/test/ui/unsafe/union_access_through_block.rs18
-rw-r--r--src/test/ui/unsafe/union_destructure.mir.stderr16
-rw-r--r--src/test/ui/unsafe/union_destructure.rs51
-rw-r--r--src/test/ui/unsafe/union_wild_or_wild.rs12
69 files changed, 1083 insertions, 71 deletions
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 9bef8ac2413..0aec6318295 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -26,6 +26,8 @@ struct UnsafetyVisitor<'a, 'tcx> {
     /// calls to functions with `#[target_feature]` (RFC 2396).
     body_target_features: &'tcx Vec<Symbol>,
     is_const: bool,
+    in_possible_lhs_union_assign: bool,
+    in_union_destructure: bool,
 }
 
 impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
@@ -158,14 +160,115 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
         }
     }
 
+    fn visit_pat(&mut self, pat: &Pat<'tcx>) {
+        use PatKind::*;
+
+        if self.in_union_destructure {
+            match *pat.kind {
+                // binding to a variable allows getting stuff out of variable
+                Binding { .. }
+                // match is conditional on having this value
+                | Constant { .. }
+                | Variant { .. }
+                | Leaf { .. }
+                | Deref { .. }
+                | Range { .. }
+                | Slice { .. }
+                | Array { .. } => {
+                    self.requires_unsafe(pat.span, AccessToUnionField);
+                    return; // don't walk pattern
+                }
+                // wildcard doesn't take anything
+                Wild |
+                // these just wrap other patterns
+                Or { .. } |
+                AscribeUserType { .. } => {}
+            }
+        };
+
+        if let ty::Adt(adt_def, _) = pat.ty.kind() {
+            // check for extracting values from union via destructuring
+            if adt_def.is_union() {
+                match *pat.kind {
+                    // assigning the whole union is okay
+                    // let x = Union { ... };
+                    // let y = x; // safe
+                    Binding { .. } |
+                    // binding to wildcard is okay since that never reads anything and stops double errors
+                    // with implict wildcard branches from `if let`s
+                    Wild |
+                    // doesn't have any effect on semantics
+                    AscribeUserType { .. } |
+                    // creating a union literal
+                    Constant { .. } => {},
+                    Variant { .. } | Leaf { .. } | Or { .. } => {
+                        // pattern matching with a union and not doing something like v = Union { bar: 5 }
+                        self.in_union_destructure = true;
+                        visit::walk_pat(self, pat);
+                        self.in_union_destructure = false;
+                        return; // don't walk pattern
+                    }
+                    Deref { .. } | Range { .. } | Slice { .. } | Array { .. } =>
+                        unreachable!("impossible union destructuring type"),
+                }
+            }
+        }
+
+        visit::walk_pat(self, pat);
+    }
+
     fn visit_expr(&mut self, expr: &Expr<'tcx>) {
+        // could we be in a the LHS of an assignment of a union?
+        match expr.kind {
+            ExprKind::Field { .. }
+            | ExprKind::VarRef { .. }
+            | ExprKind::UpvarRef { .. }
+            | ExprKind::Scope { .. }
+            | ExprKind::Cast { .. } => {}
+
+            ExprKind::AddressOf { .. }
+            | ExprKind::Adt { .. }
+            | ExprKind::Array { .. }
+            | ExprKind::Binary { .. }
+            | ExprKind::Block { .. }
+            | ExprKind::Borrow { .. }
+            | ExprKind::Literal { .. }
+            | ExprKind::ConstBlock { .. }
+            | ExprKind::Deref { .. }
+            | ExprKind::Index { .. }
+            | ExprKind::NeverToAny { .. }
+            | ExprKind::PlaceTypeAscription { .. }
+            | ExprKind::ValueTypeAscription { .. }
+            | ExprKind::Pointer { .. }
+            | ExprKind::Repeat { .. }
+            | ExprKind::StaticRef { .. }
+            | ExprKind::ThreadLocalRef { .. }
+            | ExprKind::Tuple { .. }
+            | ExprKind::Unary { .. }
+            | ExprKind::Call { .. }
+            | ExprKind::Assign { .. }
+            | ExprKind::AssignOp { .. }
+            | ExprKind::Break { .. }
+            | ExprKind::Closure { .. }
+            | ExprKind::Continue { .. }
+            | ExprKind::Return { .. }
+            | ExprKind::Yield { .. }
+            | ExprKind::Loop { .. }
+            | ExprKind::Match { .. }
+            | ExprKind::Box { .. }
+            | ExprKind::If { .. }
+            | ExprKind::InlineAsm { .. }
+            | ExprKind::LlvmInlineAsm { .. }
+            | ExprKind::LogicalOp { .. }
+            | ExprKind::Use { .. } => self.in_possible_lhs_union_assign = false,
+        };
         match expr.kind {
             ExprKind::Scope { value, lint_level: LintLevel::Explicit(hir_id), region_scope: _ } => {
                 let prev_id = self.hir_context;
                 self.hir_context = hir_id;
                 self.visit_expr(&self.thir[value]);
                 self.hir_context = prev_id;
-                return;
+                return; // don't visit the whole expression
             }
             ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
                 if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
@@ -246,9 +349,29 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                 // Unsafe blocks can be used in closures, make sure to take it into account
                 self.safety_context = closure_visitor.safety_context;
             }
+            ExprKind::Field { lhs, .. } => {
+                // assigning to union field is okay for AccessToUnionField
+                if let ty::Adt(adt_def, _) = &self.thir[lhs].ty.kind() {
+                    if adt_def.is_union() {
+                        if self.in_possible_lhs_union_assign {
+                            // FIXME: trigger AssignToDroppingUnionField unsafety if needed
+                        } else {
+                            self.requires_unsafe(expr.span, AccessToUnionField);
+                        }
+                    }
+                }
+            }
+            // don't have any special handling for AssignOp since it causes a read *and* write to lhs
+            ExprKind::Assign { lhs, rhs } => {
+                // assigning to a union is safe, check here so it doesn't get treated as a read later
+                self.in_possible_lhs_union_assign = true;
+                visit::walk_expr(self, &self.thir()[lhs]);
+                self.in_possible_lhs_union_assign = false;
+                visit::walk_expr(self, &self.thir()[rhs]);
+                return; // don't visit the whole expression
+            }
             _ => {}
         }
-
         visit::walk_expr(self, expr);
     }
 }
@@ -296,7 +419,6 @@ enum UnsafeOpKind {
     DerefOfRawPointer,
     #[allow(dead_code)] // FIXME
     AssignToDroppingUnionField,
-    #[allow(dead_code)] // FIXME
     AccessToUnionField,
     #[allow(dead_code)] // FIXME
     MutationOfLayoutConstrainedField,
@@ -417,6 +539,8 @@ pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalD
         body_unsafety,
         body_target_features,
         is_const,
+        in_possible_lhs_union_assign: false,
+        in_union_destructure: false,
     };
     visitor.visit_expr(&thir[expr]);
 }
diff --git a/compiler/rustc_mir_build/src/thir/visit.rs b/compiler/rustc_mir_build/src/thir/visit.rs
index f611bb6eb43..ce5d4362c08 100644
--- a/compiler/rustc_mir_build/src/thir/visit.rs
+++ b/compiler/rustc_mir_build/src/thir/visit.rs
@@ -153,8 +153,8 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
 }
 
 pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stmt<'tcx>) {
-    match stmt.kind {
-        StmtKind::Expr { expr, scope: _ } => visitor.visit_expr(&visitor.thir()[expr]),
+    match &stmt.kind {
+        StmtKind::Expr { expr, scope: _ } => visitor.visit_expr(&visitor.thir()[*expr]),
         StmtKind::Let {
             initializer,
             remainder_scope: _,
@@ -163,7 +163,7 @@ pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stm
             lint_level: _,
         } => {
             if let Some(init) = initializer {
-                visitor.visit_expr(&visitor.thir()[init]);
+                visitor.visit_expr(&visitor.thir()[*init]);
             }
             visitor.visit_pat(pattern);
         }
diff --git a/src/test/ui/union/union-align.rs b/src/test/ui/union/union-align.rs
index 1340ae43cd6..6a44f27dbb2 100644
--- a/src/test/ui/union/union-align.rs
+++ b/src/test/ui/union/union-align.rs
@@ -1,4 +1,7 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![allow(dead_code)]
 
 use std::mem::{size_of, size_of_val, align_of, align_of_val};
diff --git a/src/test/ui/union/union-backcomp.rs b/src/test/ui/union/union-backcomp.rs
index 4efd4c4d3d3..b19eab9f52b 100644
--- a/src/test/ui/union/union-backcomp.rs
+++ b/src/test/ui/union/union-backcomp.rs
@@ -1,4 +1,7 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![allow(path_statements)]
 #![allow(dead_code)]
 
diff --git a/src/test/ui/union/union-basic.rs b/src/test/ui/union/union-basic.rs
index 73cc9793f2c..dcc552ac75c 100644
--- a/src/test/ui/union/union-basic.rs
+++ b/src/test/ui/union/union-basic.rs
@@ -1,4 +1,7 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![allow(unused_imports)]
 
 // aux-build:union.rs
diff --git a/src/test/ui/union/union-borrow-move-parent-sibling.stderr b/src/test/ui/union/union-borrow-move-parent-sibling.mirunsafeck.stderr
index 8ba155bafb0..e785a2ee733 100644
--- a/src/test/ui/union/union-borrow-move-parent-sibling.stderr
+++ b/src/test/ui/union/union-borrow-move-parent-sibling.mirunsafeck.stderr
@@ -1,5 +1,5 @@
 error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0`)
-  --> $DIR/union-borrow-move-parent-sibling.rs:53:13
+  --> $DIR/union-borrow-move-parent-sibling.rs:56:13
    |
 LL |     let a = &mut u.x.0;
    |             ---------- mutable borrow occurs here (via `u.x.0`)
@@ -11,7 +11,7 @@ LL |     use_borrow(a);
    = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x.0`
 
 error[E0382]: use of moved value: `u`
-  --> $DIR/union-borrow-move-parent-sibling.rs:60:13
+  --> $DIR/union-borrow-move-parent-sibling.rs:63:13
    |
 LL |     let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
    |         - move occurs because `u` has type `U`, which does not implement the `Copy` trait
@@ -21,7 +21,7 @@ LL |     let b = u.y;
    |             ^^^ value used here after move
 
 error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0.0`)
-  --> $DIR/union-borrow-move-parent-sibling.rs:66:13
+  --> $DIR/union-borrow-move-parent-sibling.rs:69:13
    |
 LL |     let a = &mut (u.x.0).0;
    |             -------------- mutable borrow occurs here (via `u.x.0.0`)
@@ -33,7 +33,7 @@ LL |     use_borrow(a);
    = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x.0.0`
 
 error[E0382]: use of moved value: `u`
-  --> $DIR/union-borrow-move-parent-sibling.rs:73:13
+  --> $DIR/union-borrow-move-parent-sibling.rs:76:13
    |
 LL |     let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
    |         - move occurs because `u` has type `U`, which does not implement the `Copy` trait
@@ -43,7 +43,7 @@ LL |     let b = u.y;
    |             ^^^ value used here after move
 
 error[E0502]: cannot borrow `u` (via `u.x`) as immutable because it is also borrowed as mutable (via `u.y`)
-  --> $DIR/union-borrow-move-parent-sibling.rs:79:13
+  --> $DIR/union-borrow-move-parent-sibling.rs:82:13
    |
 LL |     let a = &mut *u.y;
    |                   --- mutable borrow occurs here (via `u.y`)
diff --git a/src/test/ui/union/union-borrow-move-parent-sibling.rs b/src/test/ui/union/union-borrow-move-parent-sibling.rs
index edf08e6ca67..e56d87255db 100644
--- a/src/test/ui/union/union-borrow-move-parent-sibling.rs
+++ b/src/test/ui/union/union-borrow-move-parent-sibling.rs
@@ -1,3 +1,6 @@
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![feature(untagged_unions)]
 #![allow(unused)]
 
diff --git a/src/test/ui/union/union-borrow-move-parent-sibling.thirunsafeck.stderr b/src/test/ui/union/union-borrow-move-parent-sibling.thirunsafeck.stderr
new file mode 100644
index 00000000000..e785a2ee733
--- /dev/null
+++ b/src/test/ui/union/union-borrow-move-parent-sibling.thirunsafeck.stderr
@@ -0,0 +1,60 @@
+error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0`)
+  --> $DIR/union-borrow-move-parent-sibling.rs:56:13
+   |
+LL |     let a = &mut u.x.0;
+   |             ---------- mutable borrow occurs here (via `u.x.0`)
+LL |     let b = &u.y;
+   |             ^^^^ immutable borrow of `u.y` -- which overlaps with `u.x.0` -- occurs here
+LL |     use_borrow(a);
+   |                - mutable borrow later used here
+   |
+   = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x.0`
+
+error[E0382]: use of moved value: `u`
+  --> $DIR/union-borrow-move-parent-sibling.rs:63:13
+   |
+LL |     let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
+   |         - move occurs because `u` has type `U`, which does not implement the `Copy` trait
+LL |     let a = u.x.0;
+   |             ----- value moved here
+LL |     let b = u.y;
+   |             ^^^ value used here after move
+
+error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0.0`)
+  --> $DIR/union-borrow-move-parent-sibling.rs:69:13
+   |
+LL |     let a = &mut (u.x.0).0;
+   |             -------------- mutable borrow occurs here (via `u.x.0.0`)
+LL |     let b = &u.y;
+   |             ^^^^ immutable borrow of `u.y` -- which overlaps with `u.x.0.0` -- occurs here
+LL |     use_borrow(a);
+   |                - mutable borrow later used here
+   |
+   = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x.0.0`
+
+error[E0382]: use of moved value: `u`
+  --> $DIR/union-borrow-move-parent-sibling.rs:76:13
+   |
+LL |     let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
+   |         - move occurs because `u` has type `U`, which does not implement the `Copy` trait
+LL |     let a = (u.x.0).0;
+   |             --------- value moved here
+LL |     let b = u.y;
+   |             ^^^ value used here after move
+
+error[E0502]: cannot borrow `u` (via `u.x`) as immutable because it is also borrowed as mutable (via `u.y`)
+  --> $DIR/union-borrow-move-parent-sibling.rs:82:13
+   |
+LL |     let a = &mut *u.y;
+   |                   --- mutable borrow occurs here (via `u.y`)
+LL |     let b = &u.x;
+   |             ^^^^ immutable borrow of `u.x` -- which overlaps with `u.y` -- occurs here
+LL |     use_borrow(a);
+   |                - mutable borrow later used here
+   |
+   = note: `u.x` is a field of the union `U`, so it overlaps the field `u.y`
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0382, E0502.
+For more information about an error, try `rustc --explain E0382`.
diff --git a/src/test/ui/union/union-const-codegen.rs b/src/test/ui/union/union-const-codegen.rs
index d5b30559595..32a546cf35f 100644
--- a/src/test/ui/union/union-const-codegen.rs
+++ b/src/test/ui/union/union-const-codegen.rs
@@ -1,4 +1,6 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
 
 union U {
     a: u64,
diff --git a/src/test/ui/union/union-const-eval-field.rs b/src/test/ui/union/union-const-eval-field.rs
index 15a20899a78..ca48785cd9f 100644
--- a/src/test/ui/union/union-const-eval-field.rs
+++ b/src/test/ui/union/union-const-eval-field.rs
@@ -1,4 +1,6 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
 
 type Field1 = (i32, u32);
 type Field2 = f32;
diff --git a/src/test/ui/union/union-const-eval.rs b/src/test/ui/union/union-const-eval.rs
index 90af8de447d..0b6788b4226 100644
--- a/src/test/ui/union/union-const-eval.rs
+++ b/src/test/ui/union/union-const-eval.rs
@@ -1,4 +1,7 @@
 // build-pass (FIXME(62277): could be check-pass?)
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![feature(const_fn_union)]
 
 union U {
diff --git a/src/test/ui/union/union-deref.stderr b/src/test/ui/union/union-deref.mirunsafeck.stderr
index 6af050bc4b7..ff37e6fd917 100644
--- a/src/test/ui/union/union-deref.stderr
+++ b/src/test/ui/union/union-deref.mirunsafeck.stderr
@@ -1,5 +1,5 @@
 error: not automatically applying `DerefMut` on `ManuallyDrop` union field
-  --> $DIR/union-deref.rs:14:14
+  --> $DIR/union-deref.rs:17:14
    |
 LL |     unsafe { u.f.0 = Vec::new() };
    |              ^^^
@@ -8,7 +8,7 @@ LL |     unsafe { u.f.0 = Vec::new() };
    = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor
 
 error: not automatically applying `DerefMut` on `ManuallyDrop` union field
-  --> $DIR/union-deref.rs:16:19
+  --> $DIR/union-deref.rs:19:19
    |
 LL |     unsafe { &mut u.f.0 };
    |                   ^^^
@@ -17,7 +17,7 @@ LL |     unsafe { &mut u.f.0 };
    = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor
 
 error: not automatically applying `DerefMut` on `ManuallyDrop` union field
-  --> $DIR/union-deref.rs:18:14
+  --> $DIR/union-deref.rs:21:14
    |
 LL |     unsafe { u.f.0.push(0) };
    |              ^^^
@@ -26,7 +26,7 @@ LL |     unsafe { u.f.0.push(0) };
    = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor
 
 error: not automatically applying `DerefMut` on `ManuallyDrop` union field
-  --> $DIR/union-deref.rs:22:14
+  --> $DIR/union-deref.rs:25:14
    |
 LL |     unsafe { u.f.0.0 = Vec::new() };
    |              ^^^^^
@@ -35,7 +35,7 @@ LL |     unsafe { u.f.0.0 = Vec::new() };
    = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor
 
 error: not automatically applying `DerefMut` on `ManuallyDrop` union field
-  --> $DIR/union-deref.rs:24:19
+  --> $DIR/union-deref.rs:27:19
    |
 LL |     unsafe { &mut u.f.0.0 };
    |                   ^^^^^
@@ -44,7 +44,7 @@ LL |     unsafe { &mut u.f.0.0 };
    = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor
 
 error: not automatically applying `DerefMut` on `ManuallyDrop` union field
-  --> $DIR/union-deref.rs:26:14
+  --> $DIR/union-deref.rs:29:14
    |
 LL |     unsafe { u.f.0.0.push(0) };
    |              ^^^^^
diff --git a/src/test/ui/union/union-deref.rs b/src/test/ui/union/union-deref.rs
index 48f5b36bd17..4bf2ba2f1bf 100644
--- a/src/test/ui/union/union-deref.rs
+++ b/src/test/ui/union/union-deref.rs
@@ -1,3 +1,6 @@
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 //! Test the part of RFC 2514 that is about not applying `DerefMut` coercions
 //! of union fields.
 #![feature(untagged_unions)]
diff --git a/src/test/ui/union/union-deref.thirunsafeck.stderr b/src/test/ui/union/union-deref.thirunsafeck.stderr
new file mode 100644
index 00000000000..ff37e6fd917
--- /dev/null
+++ b/src/test/ui/union/union-deref.thirunsafeck.stderr
@@ -0,0 +1,56 @@
+error: not automatically applying `DerefMut` on `ManuallyDrop` union field
+  --> $DIR/union-deref.rs:17:14
+   |
+LL |     unsafe { u.f.0 = Vec::new() };
+   |              ^^^
+   |
+   = help: writing to this reference calls the destructor for the old value
+   = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor
+
+error: not automatically applying `DerefMut` on `ManuallyDrop` union field
+  --> $DIR/union-deref.rs:19:19
+   |
+LL |     unsafe { &mut u.f.0 };
+   |                   ^^^
+   |
+   = help: writing to this reference calls the destructor for the old value
+   = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor
+
+error: not automatically applying `DerefMut` on `ManuallyDrop` union field
+  --> $DIR/union-deref.rs:21:14
+   |
+LL |     unsafe { u.f.0.push(0) };
+   |              ^^^
+   |
+   = help: writing to this reference calls the destructor for the old value
+   = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor
+
+error: not automatically applying `DerefMut` on `ManuallyDrop` union field
+  --> $DIR/union-deref.rs:25:14
+   |
+LL |     unsafe { u.f.0.0 = Vec::new() };
+   |              ^^^^^
+   |
+   = help: writing to this reference calls the destructor for the old value
+   = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor
+
+error: not automatically applying `DerefMut` on `ManuallyDrop` union field
+  --> $DIR/union-deref.rs:27:19
+   |
+LL |     unsafe { &mut u.f.0.0 };
+   |                   ^^^^^
+   |
+   = help: writing to this reference calls the destructor for the old value
+   = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor
+
+error: not automatically applying `DerefMut` on `ManuallyDrop` union field
+  --> $DIR/union-deref.rs:29:14
+   |
+LL |     unsafe { u.f.0.0.push(0) };
+   |              ^^^^^
+   |
+   = help: writing to this reference calls the destructor for the old value
+   = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor
+
+error: aborting due to 6 previous errors
+
diff --git a/src/test/ui/union/union-derive-clone.stderr b/src/test/ui/union/union-derive-clone.mirunsafeck.stderr
index 789bcc92fc6..41e828c6d95 100644
--- a/src/test/ui/union/union-derive-clone.stderr
+++ b/src/test/ui/union/union-derive-clone.mirunsafeck.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the trait bound `U1: Copy` is not satisfied
-  --> $DIR/union-derive-clone.rs:3:10
+  --> $DIR/union-derive-clone.rs:6:10
    |
 LL | #[derive(Clone)]
    |          ^^^^^ the trait `Copy` is not implemented for `U1`
@@ -12,7 +12,7 @@ LL | pub struct AssertParamIsCopy<T: Copy + ?Sized> {
    = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0599]: the method `clone` exists for union `U5<CloneNoCopy>`, but its trait bounds were not satisfied
-  --> $DIR/union-derive-clone.rs:35:15
+  --> $DIR/union-derive-clone.rs:38:15
    |
 LL | union U5<T> {
    | -----------
diff --git a/src/test/ui/union/union-derive-clone.rs b/src/test/ui/union/union-derive-clone.rs
index 7ab19edb471..7aa62146e54 100644
--- a/src/test/ui/union/union-derive-clone.rs
+++ b/src/test/ui/union/union-derive-clone.rs
@@ -1,3 +1,6 @@
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 use std::mem::ManuallyDrop;
 
 #[derive(Clone)] //~ ERROR the trait bound `U1: Copy` is not satisfied
diff --git a/src/test/ui/union/union-derive-clone.thirunsafeck.stderr b/src/test/ui/union/union-derive-clone.thirunsafeck.stderr
new file mode 100644
index 00000000000..41e828c6d95
--- /dev/null
+++ b/src/test/ui/union/union-derive-clone.thirunsafeck.stderr
@@ -0,0 +1,36 @@
+error[E0277]: the trait bound `U1: Copy` is not satisfied
+  --> $DIR/union-derive-clone.rs:6:10
+   |
+LL | #[derive(Clone)]
+   |          ^^^^^ the trait `Copy` is not implemented for `U1`
+   | 
+  ::: $SRC_DIR/core/src/clone.rs:LL:COL
+   |
+LL | pub struct AssertParamIsCopy<T: Copy + ?Sized> {
+   |                                 ---- required by this bound in `AssertParamIsCopy`
+   |
+   = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0599]: the method `clone` exists for union `U5<CloneNoCopy>`, but its trait bounds were not satisfied
+  --> $DIR/union-derive-clone.rs:38:15
+   |
+LL | union U5<T> {
+   | -----------
+   | |
+   | method `clone` not found for this
+   | doesn't satisfy `U5<CloneNoCopy>: Clone`
+...
+LL | struct CloneNoCopy;
+   | ------------------- doesn't satisfy `CloneNoCopy: Copy`
+...
+LL |     let w = u.clone();
+   |               ^^^^^ method cannot be called on `U5<CloneNoCopy>` due to unsatisfied trait bounds
+   |
+   = note: the following trait bounds were not satisfied:
+           `CloneNoCopy: Copy`
+           which is required by `U5<CloneNoCopy>: Clone`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0277, E0599.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/src/test/ui/union/union-derive-eq.stderr b/src/test/ui/union/union-derive-eq.mirunsafeck.stderr
index 3198e97552d..9477d8470fc 100644
--- a/src/test/ui/union/union-derive-eq.stderr
+++ b/src/test/ui/union/union-derive-eq.mirunsafeck.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the trait bound `PartialEqNotEq: Eq` is not satisfied
-  --> $DIR/union-derive-eq.rs:13:5
+  --> $DIR/union-derive-eq.rs:16:5
    |
 LL |     a: PartialEqNotEq,
    |     ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq`
diff --git a/src/test/ui/union/union-derive-eq.rs b/src/test/ui/union/union-derive-eq.rs
index e689f8c27d7..b7e7f343f8a 100644
--- a/src/test/ui/union/union-derive-eq.rs
+++ b/src/test/ui/union/union-derive-eq.rs
@@ -1,3 +1,6 @@
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #[derive(Eq)] // OK
 union U1 {
     a: u8,
diff --git a/src/test/ui/union/union-derive-eq.thirunsafeck.stderr b/src/test/ui/union/union-derive-eq.thirunsafeck.stderr
new file mode 100644
index 00000000000..9477d8470fc
--- /dev/null
+++ b/src/test/ui/union/union-derive-eq.thirunsafeck.stderr
@@ -0,0 +1,16 @@
+error[E0277]: the trait bound `PartialEqNotEq: Eq` is not satisfied
+  --> $DIR/union-derive-eq.rs:16:5
+   |
+LL |     a: PartialEqNotEq,
+   |     ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq`
+   | 
+  ::: $SRC_DIR/core/src/cmp.rs:LL:COL
+   |
+LL | pub struct AssertParamIsEq<T: Eq + ?Sized> {
+   |                               -- required by this bound in `AssertParamIsEq`
+   |
+   = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/union/union-derive-rpass.rs b/src/test/ui/union/union-derive-rpass.rs
index db18a81c1f6..8276bc635fc 100644
--- a/src/test/ui/union/union-derive-rpass.rs
+++ b/src/test/ui/union/union-derive-rpass.rs
@@ -1,4 +1,7 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![allow(dead_code)]
 #![allow(unused_variables)]
 
diff --git a/src/test/ui/union/union-drop.thirunsafeck.stderr b/src/test/ui/union/union-drop.thirunsafeck.stderr
new file mode 100644
index 00000000000..9766ae4e435
--- /dev/null
+++ b/src/test/ui/union/union-drop.thirunsafeck.stderr
@@ -0,0 +1,22 @@
+warning: unnecessary `unsafe` block
+  --> $DIR/union-drop.rs:27:9
+   |
+LL |         unsafe { CHECK += 1; }
+   |         ^^^^^^ unnecessary `unsafe` block
+   |
+   = note: `#[warn(unused_unsafe)]` on by default
+
+warning: unnecessary `unsafe` block
+  --> $DIR/union-drop.rs:33:9
+   |
+LL |         unsafe { CHECK += 1; }
+   |         ^^^^^^ unnecessary `unsafe` block
+
+warning: unnecessary `unsafe` block
+  --> $DIR/union-drop.rs:40:5
+   |
+LL |     unsafe {
+   |     ^^^^^^ unnecessary `unsafe` block
+
+warning: 3 warnings emitted
+
diff --git a/src/test/ui/union/union-fields-1.stderr b/src/test/ui/union/union-fields-1.mirunsafeck.stderr
index 87621cc01b1..9f1e2947c86 100644
--- a/src/test/ui/union/union-fields-1.stderr
+++ b/src/test/ui/union/union-fields-1.mirunsafeck.stderr
@@ -1,29 +1,29 @@
 error: field is never read: `c`
-  --> $DIR/union-fields-1.rs:6:5
+  --> $DIR/union-fields-1.rs:9:5
    |
 LL |     c: u8,
    |     ^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/union-fields-1.rs:1:9
+  --> $DIR/union-fields-1.rs:4:9
    |
 LL | #![deny(dead_code)]
    |         ^^^^^^^^^
 
 error: field is never read: `a`
-  --> $DIR/union-fields-1.rs:9:5
+  --> $DIR/union-fields-1.rs:12:5
    |
 LL |     a: u8,
    |     ^^^^^
 
 error: field is never read: `a`
-  --> $DIR/union-fields-1.rs:13:20
+  --> $DIR/union-fields-1.rs:16:20
    |
 LL | union NoDropLike { a: u8 }
    |                    ^^^^^
 
 error: field is never read: `c`
-  --> $DIR/union-fields-1.rs:18:5
+  --> $DIR/union-fields-1.rs:21:5
    |
 LL |     c: u8,
    |     ^^^^^
diff --git a/src/test/ui/union/union-fields-1.rs b/src/test/ui/union/union-fields-1.rs
index edef41de159..3d3e2355a26 100644
--- a/src/test/ui/union/union-fields-1.rs
+++ b/src/test/ui/union/union-fields-1.rs
@@ -1,3 +1,6 @@
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![deny(dead_code)]
 
 union U1 {
diff --git a/src/test/ui/union/union-fields-1.thirunsafeck.stderr b/src/test/ui/union/union-fields-1.thirunsafeck.stderr
new file mode 100644
index 00000000000..9f1e2947c86
--- /dev/null
+++ b/src/test/ui/union/union-fields-1.thirunsafeck.stderr
@@ -0,0 +1,32 @@
+error: field is never read: `c`
+  --> $DIR/union-fields-1.rs:9:5
+   |
+LL |     c: u8,
+   |     ^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/union-fields-1.rs:4:9
+   |
+LL | #![deny(dead_code)]
+   |         ^^^^^^^^^
+
+error: field is never read: `a`
+  --> $DIR/union-fields-1.rs:12:5
+   |
+LL |     a: u8,
+   |     ^^^^^
+
+error: field is never read: `a`
+  --> $DIR/union-fields-1.rs:16:20
+   |
+LL | union NoDropLike { a: u8 }
+   |                    ^^^^^
+
+error: field is never read: `c`
+  --> $DIR/union-fields-1.rs:21:5
+   |
+LL |     c: u8,
+   |     ^^^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/union/union-fields-2.stderr b/src/test/ui/union/union-fields-2.mirunsafeck.stderr
index 48654347285..867bf75dfdc 100644
--- a/src/test/ui/union/union-fields-2.stderr
+++ b/src/test/ui/union/union-fields-2.mirunsafeck.stderr
@@ -1,17 +1,17 @@
 error: union expressions should have exactly one field
-  --> $DIR/union-fields-2.rs:7:13
+  --> $DIR/union-fields-2.rs:10:13
    |
 LL |     let u = U {};
    |             ^
 
 error: union expressions should have exactly one field
-  --> $DIR/union-fields-2.rs:9:13
+  --> $DIR/union-fields-2.rs:12:13
    |
 LL |     let u = U { a: 0, b: 1 };
    |             ^
 
 error[E0560]: union `U` has no field named `c`
-  --> $DIR/union-fields-2.rs:10:29
+  --> $DIR/union-fields-2.rs:13:29
    |
 LL |     let u = U { a: 0, b: 1, c: 2 };
    |                             ^ `U` does not have this field
@@ -19,61 +19,61 @@ LL |     let u = U { a: 0, b: 1, c: 2 };
    = note: available fields are: `a`, `b`
 
 error: union expressions should have exactly one field
-  --> $DIR/union-fields-2.rs:10:13
+  --> $DIR/union-fields-2.rs:13:13
    |
 LL |     let u = U { a: 0, b: 1, c: 2 };
    |             ^
 
 error: union expressions should have exactly one field
-  --> $DIR/union-fields-2.rs:12:13
+  --> $DIR/union-fields-2.rs:15:13
    |
 LL |     let u = U { ..u };
    |             ^
 
 error[E0436]: functional record update syntax requires a struct
-  --> $DIR/union-fields-2.rs:12:19
+  --> $DIR/union-fields-2.rs:15:19
    |
 LL |     let u = U { ..u };
    |                   ^
 
 error: union patterns should have exactly one field
-  --> $DIR/union-fields-2.rs:15:9
+  --> $DIR/union-fields-2.rs:18:9
    |
 LL |     let U {} = u;
    |         ^^^^
 
 error: union patterns should have exactly one field
-  --> $DIR/union-fields-2.rs:17:9
+  --> $DIR/union-fields-2.rs:20:9
    |
 LL |     let U { a, b } = u;
    |         ^^^^^^^^^^
 
 error: union patterns should have exactly one field
-  --> $DIR/union-fields-2.rs:18:9
+  --> $DIR/union-fields-2.rs:21:9
    |
 LL |     let U { a, b, c } = u;
    |         ^^^^^^^^^^^^^
 
 error[E0026]: union `U` does not have a field named `c`
-  --> $DIR/union-fields-2.rs:18:19
+  --> $DIR/union-fields-2.rs:21:19
    |
 LL |     let U { a, b, c } = u;
    |                   ^ union `U` does not have this field
 
 error: union patterns should have exactly one field
-  --> $DIR/union-fields-2.rs:20:9
+  --> $DIR/union-fields-2.rs:23:9
    |
 LL |     let U { .. } = u;
    |         ^^^^^^^^
 
 error: `..` cannot be used in union patterns
-  --> $DIR/union-fields-2.rs:20:9
+  --> $DIR/union-fields-2.rs:23:9
    |
 LL |     let U { .. } = u;
    |         ^^^^^^^^
 
 error: `..` cannot be used in union patterns
-  --> $DIR/union-fields-2.rs:22:9
+  --> $DIR/union-fields-2.rs:25:9
    |
 LL |     let U { a, .. } = u;
    |         ^^^^^^^^^^^
diff --git a/src/test/ui/union/union-fields-2.rs b/src/test/ui/union/union-fields-2.rs
index 71b204fcdc5..e738b184703 100644
--- a/src/test/ui/union/union-fields-2.rs
+++ b/src/test/ui/union/union-fields-2.rs
@@ -1,3 +1,6 @@
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 union U {
     a: u8,
     b: u16,
diff --git a/src/test/ui/union/union-fields-2.thirunsafeck.stderr b/src/test/ui/union/union-fields-2.thirunsafeck.stderr
new file mode 100644
index 00000000000..867bf75dfdc
--- /dev/null
+++ b/src/test/ui/union/union-fields-2.thirunsafeck.stderr
@@ -0,0 +1,84 @@
+error: union expressions should have exactly one field
+  --> $DIR/union-fields-2.rs:10:13
+   |
+LL |     let u = U {};
+   |             ^
+
+error: union expressions should have exactly one field
+  --> $DIR/union-fields-2.rs:12:13
+   |
+LL |     let u = U { a: 0, b: 1 };
+   |             ^
+
+error[E0560]: union `U` has no field named `c`
+  --> $DIR/union-fields-2.rs:13:29
+   |
+LL |     let u = U { a: 0, b: 1, c: 2 };
+   |                             ^ `U` does not have this field
+   |
+   = note: available fields are: `a`, `b`
+
+error: union expressions should have exactly one field
+  --> $DIR/union-fields-2.rs:13:13
+   |
+LL |     let u = U { a: 0, b: 1, c: 2 };
+   |             ^
+
+error: union expressions should have exactly one field
+  --> $DIR/union-fields-2.rs:15:13
+   |
+LL |     let u = U { ..u };
+   |             ^
+
+error[E0436]: functional record update syntax requires a struct
+  --> $DIR/union-fields-2.rs:15:19
+   |
+LL |     let u = U { ..u };
+   |                   ^
+
+error: union patterns should have exactly one field
+  --> $DIR/union-fields-2.rs:18:9
+   |
+LL |     let U {} = u;
+   |         ^^^^
+
+error: union patterns should have exactly one field
+  --> $DIR/union-fields-2.rs:20:9
+   |
+LL |     let U { a, b } = u;
+   |         ^^^^^^^^^^
+
+error: union patterns should have exactly one field
+  --> $DIR/union-fields-2.rs:21:9
+   |
+LL |     let U { a, b, c } = u;
+   |         ^^^^^^^^^^^^^
+
+error[E0026]: union `U` does not have a field named `c`
+  --> $DIR/union-fields-2.rs:21:19
+   |
+LL |     let U { a, b, c } = u;
+   |                   ^ union `U` does not have this field
+
+error: union patterns should have exactly one field
+  --> $DIR/union-fields-2.rs:23:9
+   |
+LL |     let U { .. } = u;
+   |         ^^^^^^^^
+
+error: `..` cannot be used in union patterns
+  --> $DIR/union-fields-2.rs:23:9
+   |
+LL |     let U { .. } = u;
+   |         ^^^^^^^^
+
+error: `..` cannot be used in union patterns
+  --> $DIR/union-fields-2.rs:25:9
+   |
+LL |     let U { a, .. } = u;
+   |         ^^^^^^^^^^^
+
+error: aborting due to 13 previous errors
+
+Some errors have detailed explanations: E0026, E0436, E0560.
+For more information about an error, try `rustc --explain E0026`.
diff --git a/src/test/ui/union/union-generic-rpass.rs b/src/test/ui/union/union-generic-rpass.rs
index 69837f31cab..25f1f5050f9 100644
--- a/src/test/ui/union/union-generic-rpass.rs
+++ b/src/test/ui/union/union-generic-rpass.rs
@@ -1,4 +1,7 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![allow(dead_code)]
 
 use std::mem::ManuallyDrop;
diff --git a/src/test/ui/union/union-generic.stderr b/src/test/ui/union/union-generic.mirunsafeck.stderr
index c418b27ce65..fcd0bdec258 100644
--- a/src/test/ui/union/union-generic.stderr
+++ b/src/test/ui/union/union-generic.mirunsafeck.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the trait bound `Rc<u32>: Copy` is not satisfied
-  --> $DIR/union-generic.rs:8:13
+  --> $DIR/union-generic.rs:11:13
    |
 LL | union U<T: Copy> {
    | ---------------- required by `U`
@@ -8,7 +8,7 @@ LL |     let u = U { a: Rc::new(0u32) };
    |             ^ the trait `Copy` is not implemented for `Rc<u32>`
 
 error[E0277]: the trait bound `Rc<u32>: Copy` is not satisfied
-  --> $DIR/union-generic.rs:10:13
+  --> $DIR/union-generic.rs:13:13
    |
 LL | union U<T: Copy> {
    | ---------------- required by `U`
diff --git a/src/test/ui/union/union-generic.rs b/src/test/ui/union/union-generic.rs
index ff877892579..3d68ecb87d8 100644
--- a/src/test/ui/union/union-generic.rs
+++ b/src/test/ui/union/union-generic.rs
@@ -1,3 +1,6 @@
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 use std::rc::Rc;
 
 union U<T: Copy> {
diff --git a/src/test/ui/union/union-generic.thirunsafeck.stderr b/src/test/ui/union/union-generic.thirunsafeck.stderr
new file mode 100644
index 00000000000..fcd0bdec258
--- /dev/null
+++ b/src/test/ui/union/union-generic.thirunsafeck.stderr
@@ -0,0 +1,21 @@
+error[E0277]: the trait bound `Rc<u32>: Copy` is not satisfied
+  --> $DIR/union-generic.rs:11:13
+   |
+LL | union U<T: Copy> {
+   | ---------------- required by `U`
+...
+LL |     let u = U { a: Rc::new(0u32) };
+   |             ^ the trait `Copy` is not implemented for `Rc<u32>`
+
+error[E0277]: the trait bound `Rc<u32>: Copy` is not satisfied
+  --> $DIR/union-generic.rs:13:13
+   |
+LL | union U<T: Copy> {
+   | ---------------- required by `U`
+...
+LL |     let u = U::<Rc<u32>> { a: Default::default() };
+   |             ^^^^^^^^^^^^ the trait `Copy` is not implemented for `Rc<u32>`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/union/union-inherent-method.rs b/src/test/ui/union/union-inherent-method.rs
index 2e75cce7b10..b0fd22da73a 100644
--- a/src/test/ui/union/union-inherent-method.rs
+++ b/src/test/ui/union/union-inherent-method.rs
@@ -1,4 +1,6 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
 
 union U {
     a: u8,
diff --git a/src/test/ui/union/union-lint-dead-code.stderr b/src/test/ui/union/union-lint-dead-code.mirunsafeck.stderr
index 7de70ec3380..22d4428c902 100644
--- a/src/test/ui/union/union-lint-dead-code.stderr
+++ b/src/test/ui/union/union-lint-dead-code.mirunsafeck.stderr
@@ -1,11 +1,11 @@
 error: field is never read: `b`
-  --> $DIR/union-lint-dead-code.rs:5:5
+  --> $DIR/union-lint-dead-code.rs:8:5
    |
 LL |     b: bool,
    |     ^^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/union-lint-dead-code.rs:1:9
+  --> $DIR/union-lint-dead-code.rs:4:9
    |
 LL | #![deny(dead_code)]
    |         ^^^^^^^^^
diff --git a/src/test/ui/union/union-lint-dead-code.rs b/src/test/ui/union/union-lint-dead-code.rs
index ae5337c618a..64c28d72e9e 100644
--- a/src/test/ui/union/union-lint-dead-code.rs
+++ b/src/test/ui/union/union-lint-dead-code.rs
@@ -1,3 +1,6 @@
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![deny(dead_code)]
 
 union Foo {
diff --git a/src/test/ui/union/union-lint-dead-code.thirunsafeck.stderr b/src/test/ui/union/union-lint-dead-code.thirunsafeck.stderr
new file mode 100644
index 00000000000..22d4428c902
--- /dev/null
+++ b/src/test/ui/union/union-lint-dead-code.thirunsafeck.stderr
@@ -0,0 +1,14 @@
+error: field is never read: `b`
+  --> $DIR/union-lint-dead-code.rs:8:5
+   |
+LL |     b: bool,
+   |     ^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/union-lint-dead-code.rs:4:9
+   |
+LL | #![deny(dead_code)]
+   |         ^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/union/union-macro.rs b/src/test/ui/union/union-macro.rs
index e938cd5a614..7fd9d8221c6 100644
--- a/src/test/ui/union/union-macro.rs
+++ b/src/test/ui/union/union-macro.rs
@@ -1,4 +1,7 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![allow(unused_variables)]
 
 macro_rules! duplicate {
diff --git a/src/test/ui/union/union-manuallydrop-rpass.rs b/src/test/ui/union/union-manuallydrop-rpass.rs
index 977d12f1086..826bdf07cef 100644
--- a/src/test/ui/union/union-manuallydrop-rpass.rs
+++ b/src/test/ui/union/union-manuallydrop-rpass.rs
@@ -1,5 +1,8 @@
-#![allow(dead_code)]
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
+#![allow(dead_code)]
 
 use std::mem::needs_drop;
 use std::mem::ManuallyDrop;
diff --git a/src/test/ui/union/union-move.stderr b/src/test/ui/union/union-move.mirunsafeck.stderr
index 5679192b641..f55fbea6336 100644
--- a/src/test/ui/union/union-move.stderr
+++ b/src/test/ui/union/union-move.mirunsafeck.stderr
@@ -1,5 +1,5 @@
 error[E0382]: use of moved value: `x`
-  --> $DIR/union-move.rs:26:18
+  --> $DIR/union-move.rs:29:18
    |
 LL | fn test1(x: U1) {
    |          - move occurs because `x` has type `U1`, which does not implement the `Copy` trait
@@ -10,7 +10,7 @@ LL |         move_out(x.f2_nocopy);
    |                  ^^^^^^^^^^^ value used here after move
 
 error[E0382]: use of moved value: `x`
-  --> $DIR/union-move.rs:42:18
+  --> $DIR/union-move.rs:45:18
    |
 LL | fn test3(x: U1) {
    |          - move occurs because `x` has type `U1`, which does not implement the `Copy` trait
@@ -21,7 +21,7 @@ LL |         move_out(x.f3_copy);
    |                  ^^^^^^^^^ value used here after move
 
 error[E0509]: cannot move out of type `U2`, which implements the `Drop` trait
-  --> $DIR/union-move.rs:49:18
+  --> $DIR/union-move.rs:52:18
    |
 LL |         move_out(x.f1_nocopy);
    |                  ^^^^^^^^^^^
diff --git a/src/test/ui/union/union-move.rs b/src/test/ui/union/union-move.rs
index a0a2d0d6598..8f78c30d67a 100644
--- a/src/test/ui/union/union-move.rs
+++ b/src/test/ui/union/union-move.rs
@@ -1,3 +1,6 @@
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 //! Test the behavior of moving out of non-`Copy` union fields.
 //! Avoid types that `Drop`, we want to focus on moving.
 #![feature(untagged_unions)]
diff --git a/src/test/ui/union/union-move.thirunsafeck.stderr b/src/test/ui/union/union-move.thirunsafeck.stderr
new file mode 100644
index 00000000000..f55fbea6336
--- /dev/null
+++ b/src/test/ui/union/union-move.thirunsafeck.stderr
@@ -0,0 +1,35 @@
+error[E0382]: use of moved value: `x`
+  --> $DIR/union-move.rs:29:18
+   |
+LL | fn test1(x: U1) {
+   |          - move occurs because `x` has type `U1`, which does not implement the `Copy` trait
+...
+LL |         move_out(x.f1_nocopy);
+   |                  ----------- value moved here
+LL |         move_out(x.f2_nocopy);
+   |                  ^^^^^^^^^^^ value used here after move
+
+error[E0382]: use of moved value: `x`
+  --> $DIR/union-move.rs:45:18
+   |
+LL | fn test3(x: U1) {
+   |          - move occurs because `x` has type `U1`, which does not implement the `Copy` trait
+...
+LL |         move_out(x.f2_nocopy);
+   |                  ----------- value moved here
+LL |         move_out(x.f3_copy);
+   |                  ^^^^^^^^^ value used here after move
+
+error[E0509]: cannot move out of type `U2`, which implements the `Drop` trait
+  --> $DIR/union-move.rs:52:18
+   |
+LL |         move_out(x.f1_nocopy);
+   |                  ^^^^^^^^^^^
+   |                  |
+   |                  cannot move out of here
+   |                  move occurs because `x.f1_nocopy` has type `RefCell<i32>`, which does not implement the `Copy` trait
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0382, E0509.
+For more information about an error, try `rustc --explain E0382`.
diff --git a/src/test/ui/union/union-nodrop.rs b/src/test/ui/union/union-nodrop.rs
index bc58c5995cb..6e6b105a73e 100644
--- a/src/test/ui/union/union-nodrop.rs
+++ b/src/test/ui/union/union-nodrop.rs
@@ -1,4 +1,6 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
 
 #![allow(dead_code)]
 
diff --git a/src/test/ui/union/union-nonzero.rs b/src/test/ui/union/union-nonzero.rs
index bd84b46bf3d..d29e7a97180 100644
--- a/src/test/ui/union/union-nonzero.rs
+++ b/src/test/ui/union/union-nonzero.rs
@@ -1,4 +1,7 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![allow(dead_code)]
 
 // Tests that unions aren't subject to unsafe non-zero/niche-filling optimizations.
diff --git a/src/test/ui/union/union-overwrite.rs b/src/test/ui/union/union-overwrite.rs
index 399ed9ae458..0eea14d9de0 100644
--- a/src/test/ui/union/union-overwrite.rs
+++ b/src/test/ui/union/union-overwrite.rs
@@ -1,4 +1,6 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
 
 #[repr(C)]
 #[derive(Copy, Clone)]
diff --git a/src/test/ui/union/union-packed.rs b/src/test/ui/union/union-packed.rs
index 9cde44c06bd..9c6398bf5aa 100644
--- a/src/test/ui/union/union-packed.rs
+++ b/src/test/ui/union/union-packed.rs
@@ -1,4 +1,7 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![allow(dead_code)]
 #![allow(non_snake_case)]
 
diff --git a/src/test/ui/union/union-pat-refutability.rs b/src/test/ui/union/union-pat-refutability.rs
index ebb06726647..d628a200a07 100644
--- a/src/test/ui/union/union-pat-refutability.rs
+++ b/src/test/ui/union/union-pat-refutability.rs
@@ -1,4 +1,7 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![allow(dead_code)]
 #![allow(illegal_floating_point_literal_pattern)]
 
diff --git a/src/test/ui/union/union-suggest-field.stderr b/src/test/ui/union/union-suggest-field.mirunsafeck.stderr
index 461db171206..26fc25b96bd 100644
--- a/src/test/ui/union/union-suggest-field.stderr
+++ b/src/test/ui/union/union-suggest-field.mirunsafeck.stderr
@@ -1,17 +1,17 @@
 error[E0560]: union `U` has no field named `principle`
-  --> $DIR/union-suggest-field.rs:10:17
+  --> $DIR/union-suggest-field.rs:13:17
    |
 LL |     let u = U { principle: 0 };
    |                 ^^^^^^^^^ help: a field with a similar name exists: `principal`
 
 error[E0609]: no field `principial` on type `U`
-  --> $DIR/union-suggest-field.rs:14:15
+  --> $DIR/union-suggest-field.rs:17:15
    |
 LL |     let w = u.principial;
    |               ^^^^^^^^^^ help: a field with a similar name exists: `principal`
 
 error[E0615]: attempted to take value of method `calculate` on type `U`
-  --> $DIR/union-suggest-field.rs:18:15
+  --> $DIR/union-suggest-field.rs:21:15
    |
 LL |     let y = u.calculate;
    |               ^^^^^^^^^ method, not a field
diff --git a/src/test/ui/union/union-suggest-field.rs b/src/test/ui/union/union-suggest-field.rs
index 71b93e873c2..601a22a0600 100644
--- a/src/test/ui/union/union-suggest-field.rs
+++ b/src/test/ui/union/union-suggest-field.rs
@@ -1,3 +1,6 @@
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 union U {
     principal: u8,
 }
diff --git a/src/test/ui/union/union-suggest-field.thirunsafeck.stderr b/src/test/ui/union/union-suggest-field.thirunsafeck.stderr
new file mode 100644
index 00000000000..26fc25b96bd
--- /dev/null
+++ b/src/test/ui/union/union-suggest-field.thirunsafeck.stderr
@@ -0,0 +1,27 @@
+error[E0560]: union `U` has no field named `principle`
+  --> $DIR/union-suggest-field.rs:13:17
+   |
+LL |     let u = U { principle: 0 };
+   |                 ^^^^^^^^^ help: a field with a similar name exists: `principal`
+
+error[E0609]: no field `principial` on type `U`
+  --> $DIR/union-suggest-field.rs:17:15
+   |
+LL |     let w = u.principial;
+   |               ^^^^^^^^^^ help: a field with a similar name exists: `principal`
+
+error[E0615]: attempted to take value of method `calculate` on type `U`
+  --> $DIR/union-suggest-field.rs:21:15
+   |
+LL |     let y = u.calculate;
+   |               ^^^^^^^^^ method, not a field
+   |
+help: use parentheses to call the method
+   |
+LL |     let y = u.calculate();
+   |                        ^^
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0560, E0609, E0615.
+For more information about an error, try `rustc --explain E0560`.
diff --git a/src/test/ui/union/union-trait-impl.rs b/src/test/ui/union/union-trait-impl.rs
index 8a7ac817240..6134e91f31e 100644
--- a/src/test/ui/union/union-trait-impl.rs
+++ b/src/test/ui/union/union-trait-impl.rs
@@ -1,4 +1,6 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
 
 use std::fmt;
 
diff --git a/src/test/ui/union/union-transmute.rs b/src/test/ui/union/union-transmute.rs
index be8062f6276..1a3b32d55f3 100644
--- a/src/test/ui/union/union-transmute.rs
+++ b/src/test/ui/union/union-transmute.rs
@@ -1,4 +1,6 @@
 // run-pass
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
 
 union U {
     a: (u8, u8),
diff --git a/src/test/ui/union/union-unsafe.stderr b/src/test/ui/union/union-unsafe.mir.stderr
index a25c09144f7..84a7eb2ef2e 100644
--- a/src/test/ui/union/union-unsafe.stderr
+++ b/src/test/ui/union/union-unsafe.mir.stderr
@@ -1,5 +1,5 @@
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:31:5
+  --> $DIR/union-unsafe.rs:34:5
    |
 LL |     *(u.p) = 13;
    |     ^^^^^^^^^^^ access to union field
@@ -7,7 +7,7 @@ LL |     *(u.p) = 13;
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
 error[E0133]: assignment to union field that might need dropping is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:35:5
+  --> $DIR/union-unsafe.rs:39:5
    |
 LL |     u.a = (RefCell::new(0), 1);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to union field that might need dropping
@@ -15,7 +15,7 @@ LL |     u.a = (RefCell::new(0), 1);
    = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
 
 error[E0133]: assignment to union field that might need dropping is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:36:5
+  --> $DIR/union-unsafe.rs:40:5
    |
 LL |     u.a.0 = RefCell::new(0);
    |     ^^^^^^^^^^^^^^^^^^^^^^^ assignment to union field that might need dropping
@@ -23,7 +23,7 @@ LL |     u.a.0 = RefCell::new(0);
    = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
 
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:43:6
+  --> $DIR/union-unsafe.rs:47:6
    |
 LL |     *u3.a = T::default();
    |      ^^^^ access to union field
@@ -31,7 +31,7 @@ LL |     *u3.a = T::default();
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:49:6
+  --> $DIR/union-unsafe.rs:53:6
    |
 LL |     *u3.a = T::default();
    |      ^^^^ access to union field
@@ -39,7 +39,7 @@ LL |     *u3.a = T::default();
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:57:13
+  --> $DIR/union-unsafe.rs:61:13
    |
 LL |     let a = u1.a;
    |             ^^^^ access to union field
@@ -47,7 +47,7 @@ LL |     let a = u1.a;
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:60:14
+  --> $DIR/union-unsafe.rs:64:14
    |
 LL |     let U1 { a } = u1;
    |              ^ access to union field
@@ -55,7 +55,7 @@ LL |     let U1 { a } = u1;
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:61:20
+  --> $DIR/union-unsafe.rs:65:20
    |
 LL |     if let U1 { a: 12 } = u1 {}
    |                    ^^ access to union field
@@ -63,7 +63,7 @@ LL |     if let U1 { a: 12 } = u1 {}
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:66:6
+  --> $DIR/union-unsafe.rs:70:6
    |
 LL |     *u2.a = String::from("new");
    |      ^^^^ access to union field
@@ -71,7 +71,7 @@ LL |     *u2.a = String::from("new");
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:70:6
+  --> $DIR/union-unsafe.rs:74:6
    |
 LL |     *u3.a = 1;
    |      ^^^^ access to union field
@@ -79,7 +79,7 @@ LL |     *u3.a = 1;
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:74:6
+  --> $DIR/union-unsafe.rs:78:6
    |
 LL |     *u3.a = String::from("new");
    |      ^^^^ access to union field
diff --git a/src/test/ui/union/union-unsafe.rs b/src/test/ui/union/union-unsafe.rs
index 6adf0ac59b9..e8414903d54 100644
--- a/src/test/ui/union/union-unsafe.rs
+++ b/src/test/ui/union/union-unsafe.rs
@@ -1,3 +1,6 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
 #![feature(untagged_unions)]
 use std::mem::ManuallyDrop;
 use std::cell::RefCell;
@@ -32,8 +35,9 @@ fn deref_union_field(mut u: URef) {
 }
 
 fn assign_noncopy_union_field(mut u: URefCell) {
-    u.a = (RefCell::new(0), 1); //~ ERROR assignment to union field that might need dropping
-    u.a.0 = RefCell::new(0); //~ ERROR assignment to union field that might need dropping
+    // FIXME(thir-unsafeck)
+    u.a = (RefCell::new(0), 1); //[mir]~ ERROR assignment to union field that might need dropping
+    u.a.0 = RefCell::new(0); //[mir]~ ERROR assignment to union field that might need dropping
     u.a.1 = 1; // OK
 }
 
diff --git a/src/test/ui/union/union-unsafe.thir.stderr b/src/test/ui/union/union-unsafe.thir.stderr
new file mode 100644
index 00000000000..51f19879c81
--- /dev/null
+++ b/src/test/ui/union/union-unsafe.thir.stderr
@@ -0,0 +1,75 @@
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-unsafe.rs:34:6
+   |
+LL |     *(u.p) = 13;
+   |      ^^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-unsafe.rs:47:6
+   |
+LL |     *u3.a = T::default();
+   |      ^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-unsafe.rs:53:6
+   |
+LL |     *u3.a = T::default();
+   |      ^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-unsafe.rs:61:13
+   |
+LL |     let a = u1.a;
+   |             ^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-unsafe.rs:64:14
+   |
+LL |     let U1 { a } = u1;
+   |              ^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-unsafe.rs:65:20
+   |
+LL |     if let U1 { a: 12 } = u1 {}
+   |                    ^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-unsafe.rs:70:6
+   |
+LL |     *u2.a = String::from("new");
+   |      ^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-unsafe.rs:74:6
+   |
+LL |     *u3.a = 1;
+   |      ^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-unsafe.rs:78:6
+   |
+LL |     *u3.a = String::from("new");
+   |      ^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/union/union-unsized.stderr b/src/test/ui/union/union-unsized.mirunsafeck.stderr
index 454580dcbab..86a13c1e7ca 100644
--- a/src/test/ui/union/union-unsized.stderr
+++ b/src/test/ui/union/union-unsized.mirunsafeck.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the size for values of type `str` cannot be known at compilation time
-  --> $DIR/union-unsized.rs:4:8
+  --> $DIR/union-unsized.rs:7:8
    |
 LL |     a: str,
    |        ^^^ doesn't have a size known at compile-time
@@ -17,7 +17,7 @@ LL |     a: Box<str>,
    |        ^^^^   ^
 
 error[E0277]: the size for values of type `str` cannot be known at compilation time
-  --> $DIR/union-unsized.rs:12:8
+  --> $DIR/union-unsized.rs:15:8
    |
 LL |     b: str,
    |        ^^^ doesn't have a size known at compile-time
diff --git a/src/test/ui/union/union-unsized.rs b/src/test/ui/union/union-unsized.rs
index 16f2a73d425..e9792f527dc 100644
--- a/src/test/ui/union/union-unsized.rs
+++ b/src/test/ui/union/union-unsized.rs
@@ -1,3 +1,6 @@
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![feature(untagged_unions)]
 
 union U {
diff --git a/src/test/ui/union/union-unsized.thirunsafeck.stderr b/src/test/ui/union/union-unsized.thirunsafeck.stderr
new file mode 100644
index 00000000000..86a13c1e7ca
--- /dev/null
+++ b/src/test/ui/union/union-unsized.thirunsafeck.stderr
@@ -0,0 +1,39 @@
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/union-unsized.rs:7:8
+   |
+LL |     a: str,
+   |        ^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = note: no field of a union may have a dynamically sized type
+   = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+   |
+LL |     a: &str,
+   |        ^
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+   |
+LL |     a: Box<str>,
+   |        ^^^^   ^
+
+error[E0277]: the size for values of type `str` cannot be known at compilation time
+  --> $DIR/union-unsized.rs:15:8
+   |
+LL |     b: str,
+   |        ^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `str`
+   = note: no field of a union may have a dynamically sized type
+   = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+   |
+LL |     b: &str,
+   |        ^
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+   |
+LL |     b: Box<str>,
+   |        ^^^^   ^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/union/union-with-drop-fields.stderr b/src/test/ui/union/union-with-drop-fields.mirunsafeck.stderr
index 0e77279be61..2062fb7473f 100644
--- a/src/test/ui/union/union-with-drop-fields.stderr
+++ b/src/test/ui/union/union-with-drop-fields.mirunsafeck.stderr
@@ -1,35 +1,35 @@
 error[E0740]: unions may not contain fields that need dropping
-  --> $DIR/union-with-drop-fields.rs:9:5
+  --> $DIR/union-with-drop-fields.rs:11:5
    |
 LL |     a: String,
    |     ^^^^^^^^^
    |
 note: `std::mem::ManuallyDrop` can be used to wrap the type
-  --> $DIR/union-with-drop-fields.rs:9:5
+  --> $DIR/union-with-drop-fields.rs:11:5
    |
 LL |     a: String,
    |     ^^^^^^^^^
 
 error[E0740]: unions may not contain fields that need dropping
-  --> $DIR/union-with-drop-fields.rs:17:5
+  --> $DIR/union-with-drop-fields.rs:19:5
    |
 LL |     a: S,
    |     ^^^^
    |
 note: `std::mem::ManuallyDrop` can be used to wrap the type
-  --> $DIR/union-with-drop-fields.rs:17:5
+  --> $DIR/union-with-drop-fields.rs:19:5
    |
 LL |     a: S,
    |     ^^^^
 
 error[E0740]: unions may not contain fields that need dropping
-  --> $DIR/union-with-drop-fields.rs:22:5
+  --> $DIR/union-with-drop-fields.rs:24:5
    |
 LL |     a: T,
    |     ^^^^
    |
 note: `std::mem::ManuallyDrop` can be used to wrap the type
-  --> $DIR/union-with-drop-fields.rs:22:5
+  --> $DIR/union-with-drop-fields.rs:24:5
    |
 LL |     a: T,
    |     ^^^^
diff --git a/src/test/ui/union/union-with-drop-fields.rs b/src/test/ui/union/union-with-drop-fields.rs
index e3c63a6d5b5..96c293418b6 100644
--- a/src/test/ui/union/union-with-drop-fields.rs
+++ b/src/test/ui/union/union-with-drop-fields.rs
@@ -1,4 +1,6 @@
-#![feature(untagged_unions)]
+// revisions: mirunsafeck thirunsafeck
+// [thirunsafeck]compile-flags: -Z thir-unsafeck
+
 #![allow(dead_code)]
 
 union U {
diff --git a/src/test/ui/union/union-with-drop-fields.thirunsafeck.stderr b/src/test/ui/union/union-with-drop-fields.thirunsafeck.stderr
new file mode 100644
index 00000000000..2062fb7473f
--- /dev/null
+++ b/src/test/ui/union/union-with-drop-fields.thirunsafeck.stderr
@@ -0,0 +1,39 @@
+error[E0740]: unions may not contain fields that need dropping
+  --> $DIR/union-with-drop-fields.rs:11:5
+   |
+LL |     a: String,
+   |     ^^^^^^^^^
+   |
+note: `std::mem::ManuallyDrop` can be used to wrap the type
+  --> $DIR/union-with-drop-fields.rs:11:5
+   |
+LL |     a: String,
+   |     ^^^^^^^^^
+
+error[E0740]: unions may not contain fields that need dropping
+  --> $DIR/union-with-drop-fields.rs:19:5
+   |
+LL |     a: S,
+   |     ^^^^
+   |
+note: `std::mem::ManuallyDrop` can be used to wrap the type
+  --> $DIR/union-with-drop-fields.rs:19:5
+   |
+LL |     a: S,
+   |     ^^^^
+
+error[E0740]: unions may not contain fields that need dropping
+  --> $DIR/union-with-drop-fields.rs:24:5
+   |
+LL |     a: T,
+   |     ^^^^
+   |
+note: `std::mem::ManuallyDrop` can be used to wrap the type
+  --> $DIR/union-with-drop-fields.rs:24:5
+   |
+LL |     a: T,
+   |     ^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0740`.
diff --git a/src/test/ui/unsafe/access_union_field.mir.stderr b/src/test/ui/unsafe/access_union_field.mir.stderr
new file mode 100644
index 00000000000..98bc4077793
--- /dev/null
+++ b/src/test/ui/unsafe/access_union_field.mir.stderr
@@ -0,0 +1,19 @@
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/access_union_field.rs:13:13
+   |
+LL |     let a = foo.bar;
+   |             ^^^^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/access_union_field.rs:14:13
+   |
+LL |     let b = foo.baz;
+   |             ^^^^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/unsafe/access_union_field.rs b/src/test/ui/unsafe/access_union_field.rs
new file mode 100644
index 00000000000..5c4e695df58
--- /dev/null
+++ b/src/test/ui/unsafe/access_union_field.rs
@@ -0,0 +1,15 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
+#![allow(unused_variables)]
+
+union Foo {
+    bar: i8,
+    baz: u8,
+}
+
+fn main() {
+    let foo = Foo { bar: 5 };
+    let a = foo.bar; //~ ERROR access to union field is unsafe and requires unsafe function or block
+    let b = foo.baz; //~ ERROR access to union field is unsafe and requires unsafe function or block
+}
diff --git a/src/test/ui/unsafe/access_union_field.thir.stderr b/src/test/ui/unsafe/access_union_field.thir.stderr
new file mode 100644
index 00000000000..98bc4077793
--- /dev/null
+++ b/src/test/ui/unsafe/access_union_field.thir.stderr
@@ -0,0 +1,19 @@
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/access_union_field.rs:13:13
+   |
+LL |     let a = foo.bar;
+   |             ^^^^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/access_union_field.rs:14:13
+   |
+LL |     let b = foo.baz;
+   |             ^^^^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/unsafe/union.mir.stderr b/src/test/ui/unsafe/union.mir.stderr
new file mode 100644
index 00000000000..f7bd411a744
--- /dev/null
+++ b/src/test/ui/unsafe/union.mir.stderr
@@ -0,0 +1,16 @@
+warning: unnecessary `unsafe` block
+  --> $DIR/union.rs:61:5
+   |
+LL |     unsafe {
+   |     ^^^^^^ unnecessary `unsafe` block
+   |
+   = note: `#[warn(unused_unsafe)]` on by default
+
+warning: unnecessary `unsafe` block
+  --> $DIR/union.rs:66:5
+   |
+LL |     unsafe {
+   |     ^^^^^^ unnecessary `unsafe` block
+
+warning: 2 warnings emitted
+
diff --git a/src/test/ui/unsafe/union.rs b/src/test/ui/unsafe/union.rs
new file mode 100644
index 00000000000..0130fa67f23
--- /dev/null
+++ b/src/test/ui/unsafe/union.rs
@@ -0,0 +1,80 @@
+// run-pass
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
+#![feature(untagged_unions)]
+
+union Foo {
+    bar: i8,
+    zst: (),
+    pizza: Pizza,
+}
+
+struct Pizza {
+    topping: Option<PizzaTopping>
+}
+
+#[allow(dead_code)]
+enum PizzaTopping {
+    Cheese,
+    Pineapple,
+}
+
+struct FooHolder {
+    inner_foo: Foo
+}
+
+fn do_nothing(_x: &mut Foo) {}
+
+pub fn main() {
+    let mut foo = Foo { bar: 5 };
+    do_nothing(&mut foo);
+    foo.bar = 6;
+    unsafe { foo.bar += 1; }
+    assert_eq!(unsafe { foo.bar }, 7);
+    unsafe {
+        let Foo { bar: inner } = foo;
+        assert_eq!(inner, 7);
+    }
+    let foo = if let true = true { foo } else { foo };
+
+    unsafe {
+        match foo {
+            Foo { bar: _a } => {},
+        }
+    }
+    unsafe {
+        match foo {
+            Foo {
+                pizza: Pizza {
+                    topping: Some(PizzaTopping::Cheese) | Some(PizzaTopping::Pineapple) | None
+                }
+            } => {},
+        }
+    }
+    // binding to wildcard is okay
+    match foo {
+        Foo { bar: _ } => {},
+    }
+    let Foo { bar: _ } = foo;
+    // MIR unsafeck incorrectly thinks that it is safe to do these
+    unsafe { //[mir]~ WARNING
+        match foo {
+            Foo { zst: () } => {},
+        }
+    }
+    unsafe { //[mir]~ WARNING
+        match foo {
+            Foo { pizza: Pizza { .. } } => {},
+        }
+    }
+    let foo = Foo { bar: 5 };
+    let foo = if let 3 = if let true = true { 3 } else { 4 } { foo } else { foo };
+
+    let (_foo2, _random) = (foo, 42);
+
+    let mut foo_holder = FooHolder { inner_foo: Foo { bar: 5 } };
+    foo_holder.inner_foo.bar = 4;
+    assert_eq!(unsafe { foo_holder.inner_foo.bar }, 4);
+    drop(foo_holder);
+}
diff --git a/src/test/ui/unsafe/union_access_through_block.rs b/src/test/ui/unsafe/union_access_through_block.rs
new file mode 100644
index 00000000000..e4c0976b826
--- /dev/null
+++ b/src/test/ui/unsafe/union_access_through_block.rs
@@ -0,0 +1,18 @@
+// check-pass
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
+#[derive(Copy, Clone)]
+pub struct Foo { a: bool }
+
+pub union Bar {
+    a: Foo,
+    b: u32,
+}
+pub fn baz(mut bar: Bar) {
+    unsafe {
+        { bar.a }.a = true;
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/unsafe/union_destructure.mir.stderr b/src/test/ui/unsafe/union_destructure.mir.stderr
new file mode 100644
index 00000000000..818f5ce03b5
--- /dev/null
+++ b/src/test/ui/unsafe/union_destructure.mir.stderr
@@ -0,0 +1,16 @@
+warning: unnecessary `unsafe` block
+  --> $DIR/union_destructure.rs:35:5
+   |
+LL |     unsafe {
+   |     ^^^^^^ unnecessary `unsafe` block
+   |
+   = note: `#[warn(unused_unsafe)]` on by default
+
+warning: unnecessary `unsafe` block
+  --> $DIR/union_destructure.rs:41:5
+   |
+LL |     unsafe {
+   |     ^^^^^^ unnecessary `unsafe` block
+
+warning: 2 warnings emitted
+
diff --git a/src/test/ui/unsafe/union_destructure.rs b/src/test/ui/unsafe/union_destructure.rs
new file mode 100644
index 00000000000..6c88344b5fd
--- /dev/null
+++ b/src/test/ui/unsafe/union_destructure.rs
@@ -0,0 +1,51 @@
+// run-pass
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
+#[derive(Copy, Clone)]
+#[allow(dead_code)]
+struct Pie {
+    slices: u8,
+    size: u8,
+}
+
+union Foo {
+    #[allow(dead_code)]
+    bar: i8,
+    baz: Pie
+}
+
+fn main() {
+    let u = Foo { bar: 5 };
+    let (Some(Foo { bar: _ }) | None) = Some(u);
+    let u = Foo { bar: 6 };
+    let (Some(Foo { bar: _ }) | Some(Foo { bar: _ }) | None) = Some(u);
+    unsafe {
+        let u = Foo { bar: 7 };
+        let (Foo { bar } | Foo { bar }) = u;
+        assert_eq!(bar, 7)
+    }
+    let u = Foo { bar: 8 };
+    match Some(u) {
+        Some(Foo { bar: _ }) => 3,
+        None => 4,
+    };
+
+    let u = Foo { bar: 9 };
+    unsafe { //[mir]~ WARNING unnecessary `unsafe` block
+        match u {
+            Foo { baz: Pie { .. } } => {},
+        };
+    }
+    let u = Foo { bar: 10 };
+    unsafe { //[mir]~ WARNING unnecessary `unsafe` block
+        match u {
+            Foo { baz: Pie { slices: _, size: _ } } => {},
+        };
+    }
+
+    let u = Foo { bar: 11 };
+    match u {
+        Foo { baz: _ } => {},
+    };
+}
diff --git a/src/test/ui/unsafe/union_wild_or_wild.rs b/src/test/ui/unsafe/union_wild_or_wild.rs
new file mode 100644
index 00000000000..52a0a7abf27
--- /dev/null
+++ b/src/test/ui/unsafe/union_wild_or_wild.rs
@@ -0,0 +1,12 @@
+// check-pass
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
+union X { a: i8 }
+
+fn main() {
+    let x = X { a: 5 };
+    match x {
+        X { a: _ | _ } => {},
+    }
+}