diff --git a/Cargo.lock b/Cargo.lock
index 2bf07149cc86c..f8790be242afc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3664,6 +3664,7 @@ dependencies = [
  "rustc_arena",
  "rustc_ast",
  "rustc_attr",
+ "rustc_const_eval",
  "rustc_data_structures",
  "rustc_errors",
  "rustc_fs_util",
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index 6937e658ed5ee..9201bb035eadc 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -55,6 +55,7 @@ mod simd;
 pub(crate) use cpuid::codegen_cpuid_call;
 pub(crate) use llvm::codegen_llvm_intrinsic_call;
 
+use rustc_const_eval::might_permit_raw_init::might_permit_raw_init;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::subst::SubstsRef;
 use rustc_span::symbol::{kw, sym, Symbol};
@@ -673,10 +674,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
             }
 
             if intrinsic == sym::assert_zero_valid
-                && !layout.might_permit_raw_init(
-                    fx,
-                    InitKind::Zero,
-                    fx.tcx.sess.opts.debugging_opts.strict_init_checks) {
+                && !might_permit_raw_init(fx.tcx, layout, InitKind::Zero) {
 
                 with_no_trimmed_paths!({
                     crate::base::codegen_panic(
@@ -689,10 +687,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
             }
 
             if intrinsic == sym::assert_uninit_valid
-                && !layout.might_permit_raw_init(
-                    fx,
-                    InitKind::Uninit,
-                    fx.tcx.sess.opts.debugging_opts.strict_init_checks) {
+                && !might_permit_raw_init(fx.tcx, layout, InitKind::Uninit) {
 
                 with_no_trimmed_paths!({
                     crate::base::codegen_panic(
diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs
index be2d3108c5fa9..23724538194a4 100644
--- a/compiler/rustc_codegen_cranelift/src/lib.rs
+++ b/compiler/rustc_codegen_cranelift/src/lib.rs
@@ -8,6 +8,7 @@
 extern crate rustc_middle;
 extern crate rustc_ast;
 extern crate rustc_codegen_ssa;
+extern crate rustc_const_eval;
 extern crate rustc_data_structures;
 extern crate rustc_errors;
 extern crate rustc_fs_util;
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index faabea92f5a6c..81c8b9ceb136e 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -40,6 +40,7 @@ rustc_metadata = { path = "../rustc_metadata" }
 rustc_query_system = { path = "../rustc_query_system" }
 rustc_target = { path = "../rustc_target" }
 rustc_session = { path = "../rustc_session" }
+rustc_const_eval = { path = "../rustc_const_eval" }
 
 [dependencies.object]
 version = "0.29.0"
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index b8e3cb32ef633..a0a9065180a65 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -528,7 +528,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         source_info: mir::SourceInfo,
         target: Option<mir::BasicBlock>,
         cleanup: Option<mir::BasicBlock>,
-        strict_validity: bool,
     ) -> bool {
         // Emit a panic or a no-op for `assert_*` intrinsics.
         // These are intrinsics that compile to panics so that we can get a message
@@ -546,13 +545,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             _ => None,
         });
         if let Some(intrinsic) = panic_intrinsic {
+            use rustc_const_eval::might_permit_raw_init::might_permit_raw_init;
             use AssertIntrinsic::*;
+
             let ty = instance.unwrap().substs.type_at(0);
             let layout = bx.layout_of(ty);
             let do_panic = match intrinsic {
                 Inhabited => layout.abi.is_uninhabited(),
-                ZeroValid => !layout.might_permit_raw_init(bx, InitKind::Zero, strict_validity),
-                UninitValid => !layout.might_permit_raw_init(bx, InitKind::Uninit, strict_validity),
+                ZeroValid => !might_permit_raw_init(bx.tcx(), layout, InitKind::Zero),
+                UninitValid => !might_permit_raw_init(bx.tcx(), layout, InitKind::Uninit),
             };
             if do_panic {
                 let msg_str = with_no_visible_paths!({
@@ -687,7 +688,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             source_info,
             target,
             cleanup,
-            self.cx.tcx().sess.opts.debugging_opts.strict_init_checks,
         ) {
             return;
         }
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 29ab1d187719c..e00e667fb71e2 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -104,7 +104,7 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
 }
 
 impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
-    pub(super) fn new(const_eval_limit: Limit, can_access_statics: bool) -> Self {
+    pub(crate) fn new(const_eval_limit: Limit, can_access_statics: bool) -> Self {
         CompileTimeInterpreter {
             steps_remaining: const_eval_limit.0,
             stack: Vec::new(),
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 93b64d9d37a49..0503c93beccf5 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -22,6 +22,8 @@ use super::{
     Pointer,
 };
 
+use crate::might_permit_raw_init::might_permit_raw_init;
+
 mod caller_location;
 mod type_name;
 
@@ -413,35 +415,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                         ),
                     )?;
                 }
-                if intrinsic_name == sym::assert_zero_valid
-                    && !layout.might_permit_raw_init(
-                        self,
-                        InitKind::Zero,
-                        self.tcx.sess.opts.debugging_opts.strict_init_checks,
-                    )
-                {
-                    M::abort(
-                        self,
-                        format!(
-                            "aborted execution: attempted to zero-initialize type `{}`, which is invalid",
-                            ty
-                        ),
-                    )?;
+
+                if intrinsic_name == sym::assert_zero_valid {
+                    let should_panic = !might_permit_raw_init(*self.tcx, layout, InitKind::Zero);
+
+                    if should_panic {
+                        M::abort(
+                            self,
+                            format!(
+                                "aborted execution: attempted to zero-initialize type `{}`, which is invalid",
+                                ty
+                            ),
+                        )?;
+                    }
                 }
-                if intrinsic_name == sym::assert_uninit_valid
-                    && !layout.might_permit_raw_init(
-                        self,
-                        InitKind::Uninit,
-                        self.tcx.sess.opts.debugging_opts.strict_init_checks,
-                    )
-                {
-                    M::abort(
-                        self,
-                        format!(
-                            "aborted execution: attempted to leave type `{}` uninitialized, which is invalid",
-                            ty
-                        ),
-                    )?;
+
+                if intrinsic_name == sym::assert_uninit_valid {
+                    let should_panic = !might_permit_raw_init(*self.tcx, layout, InitKind::Uninit);
+
+                    if should_panic {
+                        M::abort(
+                            self,
+                            format!(
+                                "aborted execution: attempted to leave type `{}` uninitialized, which is invalid",
+                                ty
+                            ),
+                        )?;
+                    }
                 }
             }
             sym::simd_insert => {
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index d65d4f7eb720e..70bfd109c437d 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -33,6 +33,7 @@ extern crate rustc_middle;
 pub mod const_eval;
 mod errors;
 pub mod interpret;
+pub mod might_permit_raw_init;
 pub mod transform;
 pub mod util;
 
diff --git a/compiler/rustc_const_eval/src/might_permit_raw_init.rs b/compiler/rustc_const_eval/src/might_permit_raw_init.rs
new file mode 100644
index 0000000000000..7a3741c541a1f
--- /dev/null
+++ b/compiler/rustc_const_eval/src/might_permit_raw_init.rs
@@ -0,0 +1,44 @@
+use crate::const_eval::CompileTimeInterpreter;
+use crate::interpret::{InterpCx, MemoryKind, OpTy};
+use rustc_middle::ty::layout::LayoutCx;
+use rustc_middle::ty::{layout::TyAndLayout, ParamEnv, TyCtxt};
+use rustc_session::Limit;
+use rustc_target::abi::InitKind;
+
+pub fn might_permit_raw_init<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    ty: TyAndLayout<'tcx>,
+    kind: InitKind,
+) -> bool {
+    let strict = tcx.sess.opts.debugging_opts.strict_init_checks;
+
+    if strict {
+        let machine = CompileTimeInterpreter::new(Limit::new(0), false);
+
+        let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);
+
+        // We could panic here... Or we could just return "yeah it's valid whatever". Or let
+        // codegen_panic_intrinsic return an error that halts compilation.
+        // I'm not exactly sure *when* this can fail. OOM?
+        let allocated = cx
+            .allocate(ty, MemoryKind::Machine(crate::const_eval::MemoryKind::Heap))
+            .expect("failed to allocate for uninit check");
+
+        if kind == InitKind::Zero {
+            // Again, unclear what to do here if it fails.
+            cx.write_bytes_ptr(
+                allocated.ptr,
+                std::iter::repeat(0_u8).take(ty.layout.size().bytes_usize()),
+            )
+            .expect("failed to write bytes for zero valid check");
+        }
+
+        let ot: OpTy<'_, _> = allocated.into();
+
+        // Assume that if it failed, it's a validation failure.
+        cx.validate_operand(&ot).is_ok()
+    } else {
+        let layout_cx = LayoutCx { tcx, param_env: ParamEnv::reveal_all() };
+        ty.might_permit_raw_init(&layout_cx, kind)
+    }
+}
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index b54f0ef361a8d..2255b5e510660 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -528,13 +528,6 @@ declare_features! (
     (incomplete, unsized_locals, "1.30.0", Some(48055), None),
     /// Allows unsized tuple coercion.
     (active, unsized_tuple_coercion, "1.20.0", Some(42877), None),
-    /// Allows `union`s to implement `Drop`. Moreover, `union`s may now include fields
-    /// that don't implement `Copy` as long as they don't have any drop glue.
-    /// This is checked recursively. On encountering type variable where no progress can be made,
-    /// `T: Copy` is used as a substitute for "no drop glue".
-    ///
-    /// NOTE: A limited form of `union U { ... }` was accepted in 1.19.0.
-    (active, untagged_unions, "1.13.0", Some(55149), None),
     /// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute.
     (active, used_with_arg, "1.60.0", Some(93798), None),
     /// Allows `extern "wasm" fn`
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index 54626caaf53ea..2ddaf9201098e 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -180,6 +180,9 @@ declare_features! (
     /// Allows using items which are missing stability attributes
     (removed, unmarked_api, "1.0.0", None, None, None),
     (removed, unsafe_no_drop_flag, "1.0.0", None, None, None),
+    /// Allows `union` fields that don't implement `Copy` as long as they don't have any drop glue.
+    (removed, untagged_unions, "1.13.0", Some(55149), None,
+     Some("unions with `Copy` and `ManuallyDrop` fields are stable; there is no intent to stabilize more")),
     /// Allows `#[unwind(..)]`.
     ///
     /// Permits specifying whether a function should permit unwinding or abort on unwind.
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 9fc2249b29019..49155f76800ce 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -3132,6 +3132,59 @@ declare_lint! {
     "detects unexpected names and values in `#[cfg]` conditions",
 }
 
+declare_lint! {
+    /// The `repr_transparent_external_private_fields` lint
+    /// detects types marked `#[repr(trasparent)]` that (transitively)
+    /// contain an external ZST type marked `#[non_exhaustive]`
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore (needs external crate)
+    /// #![deny(repr_transparent_external_private_fields)]
+    /// use foo::NonExhaustiveZst;
+    ///
+    /// #[repr(transparent)]
+    /// struct Bar(u32, ([u32; 0], NonExhaustiveZst));
+    /// ```
+    ///
+    /// This will produce:
+    ///
+    /// ```text
+    /// error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+    ///  --> src/main.rs:5:28
+    ///   |
+    /// 5 | struct Bar(u32, ([u32; 0], NonExhaustiveZst));
+    ///   |                            ^^^^^^^^^^^^^^^^
+    ///   |
+    /// note: the lint level is defined here
+    ///  --> src/main.rs:1:9
+    ///   |
+    /// 1 | #![deny(repr_transparent_external_private_fields)]
+    ///   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+    ///   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+    ///   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+    ///   = note: this struct contains `NonExhaustiveZst`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+    /// ```
+    ///
+    /// ### Explanation
+    ///
+    /// Previous, Rust accepted fields that contain external private zero-sized types,
+    /// even though it should not be a breaking change to add a non-zero-sized field to
+    /// that private type.
+    ///
+    /// This is a [future-incompatible] lint to transition this
+    /// to a hard error in the future. See [issue #78586] for more details.
+    ///
+    /// [issue #78586]: https://github.com/rust-lang/rust/issues/78586
+    /// [future-incompatible]: ../index.md#future-incompatible-lints
+    pub REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
+    Warn,
+    "tranparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields",
+    @future_incompatible = FutureIncompatibleInfo {
+        reference: "issue #78586 <https://github.com/rust-lang/rust/issues/78586>",
+    };
+}
+
 declare_lint_pass! {
     /// Does nothing as a lint pass, but registers some `Lint`s
     /// that are used by other parts of the compiler.
@@ -3237,6 +3290,7 @@ declare_lint_pass! {
         DEPRECATED_WHERE_CLAUSE_LOCATION,
         TEST_UNSTABLE_LINT,
         FFI_UNWIND_CALLS,
+        REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
     ]
 }
 
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 620f0380d53b7..6a6ed3dc728d9 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -35,7 +35,6 @@ pub enum UnsafetyViolationDetails {
     UseOfMutableStatic,
     UseOfExternStatic,
     DerefOfRawPointer,
-    AssignToDroppingUnionField,
     AccessToUnionField,
     MutationOfLayoutConstrainedField,
     BorrowOfLayoutConstrainedField,
@@ -78,11 +77,6 @@ impl UnsafetyViolationDetails {
                 "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
                  and cause data races: all of these are undefined behavior",
             ),
-            AssignToDroppingUnionField => (
-                "assignment to union field that might need dropping",
-                "the previous content of the field will be dropped, which causes undefined \
-                 behavior if the field was not properly initialized",
-            ),
             AccessToUnionField => (
                 "access to union field",
                 "the field may not be properly initialized: using uninitialized data will cause \
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 8585199faaf5a..54d3b7cdda62c 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -431,16 +431,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                 let lhs = &self.thir[lhs];
                 if let ty::Adt(adt_def, _) = lhs.ty.kind() && adt_def.is_union() {
                     if let Some((assigned_ty, assignment_span)) = self.assignment_info {
-                        // To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
-                        if !(assigned_ty
-                            .ty_adt_def()
-                            .map_or(false, |adt| adt.is_manually_drop())
-                            || assigned_ty
-                                .is_copy_modulo_regions(self.tcx.at(expr.span), self.param_env))
-                        {
-                            self.requires_unsafe(assignment_span, AssignToDroppingUnionField);
-                        } else {
-                            // write to non-drop union field, safe
+                        if assigned_ty.needs_drop(self.tcx, self.tcx.param_env(adt_def.did())) {
+                            // This would be unsafe, but should be outright impossible since we reject such unions.
+                            self.tcx.sess.delay_span_bug(assignment_span, "union fields that need dropping should be impossible");
                         }
                     } else {
                         self.requires_unsafe(expr.span, AccessToUnionField);
@@ -537,7 +530,6 @@ enum UnsafeOpKind {
     UseOfMutableStatic,
     UseOfExternStatic,
     DerefOfRawPointer,
-    AssignToDroppingUnionField,
     AccessToUnionField,
     MutationOfLayoutConstrainedField,
     BorrowOfLayoutConstrainedField,
@@ -555,7 +547,6 @@ impl UnsafeOpKind {
             UseOfMutableStatic => "use of mutable static",
             UseOfExternStatic => "use of extern static",
             DerefOfRawPointer => "dereference of raw pointer",
-            AssignToDroppingUnionField => "assignment to union field that might need dropping",
             AccessToUnionField => "access to union field",
             MutationOfLayoutConstrainedField => "mutation of layout constrained field",
             BorrowOfLayoutConstrainedField => {
@@ -600,11 +591,6 @@ impl UnsafeOpKind {
                 "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
                  and cause data races: all of these are undefined behavior",
             ),
-            AssignToDroppingUnionField => (
-                Cow::Borrowed(self.simple_description()),
-                "the previous content of the field will be dropped, which causes undefined \
-                 behavior if the field was not properly initialized",
-            ),
             AccessToUnionField => (
                 Cow::Borrowed(self.simple_description()),
                 "the field may not be properly initialized: using uninitialized data will cause \
diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs
index 1f73b7da815c5..ded1f0462cb01 100644
--- a/compiler/rustc_mir_transform/src/check_unsafety.rs
+++ b/compiler/rustc_mir_transform/src/check_unsafety.rs
@@ -219,22 +219,15 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
                     // We have to check the actual type of the assignment, as that determines if the
                     // old value is being dropped.
                     let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty;
-                    // To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
-                    let manually_drop = assigned_ty
-                        .ty_adt_def()
-                        .map_or(false, |adt_def| adt_def.is_manually_drop());
-                    let nodrop = manually_drop
-                        || assigned_ty.is_copy_modulo_regions(
-                            self.tcx.at(self.source_info.span),
-                            self.param_env,
+                    if assigned_ty.needs_drop(
+                        self.tcx,
+                        self.tcx.param_env(base_ty.ty_adt_def().unwrap().did()),
+                    ) {
+                        // This would be unsafe, but should be outright impossible since we reject such unions.
+                        self.tcx.sess.delay_span_bug(
+                            self.source_info.span,
+                            "union fields that need dropping should be impossible",
                         );
-                    if !nodrop {
-                        self.require_unsafe(
-                            UnsafetyViolationKind::General,
-                            UnsafetyViolationDetails::AssignToDroppingUnionField,
-                        );
-                    } else {
-                        // write to non-drop union field, safe
                     }
                 } else {
                     self.require_unsafe(
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 12050dceb60a6..a06213ca5f442 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -13,13 +13,12 @@ use rustc_hir::{FieldDef, Generics, HirId, Item, ItemKind, TraitRef, Ty, TyKind,
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::privacy::AccessLevels;
 use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index};
-use rustc_middle::ty::{self, query::Providers, TyCtxt};
+use rustc_middle::ty::{query::Providers, TyCtxt};
 use rustc_session::lint;
 use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED};
-use rustc_session::parse::feature_err;
 use rustc_session::Session;
 use rustc_span::symbol::{sym, Symbol};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
 
 use std::cmp::Ordering;
@@ -766,39 +765,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                 }
             }
 
-            // There's no good place to insert stability check for non-Copy unions,
-            // so semi-randomly perform it here in stability.rs
-            hir::ItemKind::Union(..) if !self.tcx.features().untagged_unions => {
-                let ty = self.tcx.type_of(item.def_id);
-                let ty::Adt(adt_def, substs) = ty.kind() else { bug!() };
-
-                // Non-`Copy` fields are unstable, except for `ManuallyDrop`.
-                let param_env = self.tcx.param_env(item.def_id);
-                for field in &adt_def.non_enum_variant().fields {
-                    let field_ty = field.ty(self.tcx, substs);
-                    if !field_ty.ty_adt_def().map_or(false, |adt_def| adt_def.is_manually_drop())
-                        && !field_ty.is_copy_modulo_regions(self.tcx.at(DUMMY_SP), param_env)
-                    {
-                        if field_ty.needs_drop(self.tcx, param_env) {
-                            // Avoid duplicate error: This will error later anyway because fields
-                            // that need drop are not allowed.
-                            self.tcx.sess.delay_span_bug(
-                                item.span,
-                                "union should have been rejected due to potentially dropping field",
-                            );
-                        } else {
-                            feature_err(
-                                &self.tcx.sess.parse_sess,
-                                sym::untagged_unions,
-                                self.tcx.def_span(field.did),
-                                "unions with non-`Copy` fields other than `ManuallyDrop<T>` are unstable",
-                            )
-                            .emit();
-                        }
-                    }
-                }
-            }
-
             _ => (/* pass */),
         }
         intravisit::walk_item(self, item);
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index d1eafd6ac5fb8..2c3340ba4448b 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -1372,7 +1372,7 @@ pub struct PointeeInfo {
 
 /// Used in `might_permit_raw_init` to indicate the kind of initialisation
 /// that is checked to be valid
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum InitKind {
     Zero,
     Uninit,
@@ -1487,14 +1487,18 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
     ///
     /// `init_kind` indicates if the memory is zero-initialized or left uninitialized.
     ///
-    /// `strict` is an opt-in debugging flag added in #97323 that enables more checks.
+    /// This code is intentionally conservative, and will not detect
+    /// * zero init of an enum whose 0 variant does not allow zero initialization
+    /// * making uninitialized types who have a full valid range (ints, floats, raw pointers)
+    /// * Any form of invalid value being made inside an array (unless the value is uninhabited)
     ///
-    /// This is conservative: in doubt, it will answer `true`.
+    /// A strict form of these checks that uses const evaluation exists in
+    /// [`rustc_const_eval::might_permit_raw_init`], and a tracking issue for making these checks
+    /// stricter is <https://github.com/rust-lang/rust/issues/66151>.
     ///
-    /// FIXME: Once we removed all the conservatism, we could alternatively
-    /// create an all-0/all-undef constant and run the const value validator to see if
-    /// this is a valid value for the given type.
-    pub fn might_permit_raw_init<C>(self, cx: &C, init_kind: InitKind, strict: bool) -> bool
+    /// FIXME: Once all the conservatism is removed from here, and the checks are ran by default,
+    /// we can use the const evaluation checks always instead.
+    pub fn might_permit_raw_init<C>(self, cx: &C, init_kind: InitKind) -> bool
     where
         Self: Copy,
         Ty: TyAbiInterface<'a, C>,
@@ -1507,13 +1511,8 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
                     s.valid_range(cx).contains(0)
                 }
                 InitKind::Uninit => {
-                    if strict {
-                        // The type must be allowed to be uninit (which means "is a union").
-                        s.is_uninit_valid()
-                    } else {
-                        // The range must include all values.
-                        s.is_always_valid(cx)
-                    }
+                    // The range must include all values.
+                    s.is_always_valid(cx)
                 }
             }
         };
@@ -1534,19 +1533,12 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
         // If we have not found an error yet, we need to recursively descend into fields.
         match &self.fields {
             FieldsShape::Primitive | FieldsShape::Union { .. } => {}
-            FieldsShape::Array { count, .. } => {
+            FieldsShape::Array { .. } => {
                 // FIXME(#66151): For now, we are conservative and do not check arrays by default.
-                if strict
-                    && *count > 0
-                    && !self.field(cx, 0).might_permit_raw_init(cx, init_kind, strict)
-                {
-                    // Found non empty array with a type that is unhappy about this kind of initialization
-                    return false;
-                }
             }
             FieldsShape::Arbitrary { offsets, .. } => {
                 for idx in 0..offsets.len() {
-                    if !self.field(cx, idx).might_permit_raw_init(cx, init_kind, strict) {
+                    if !self.field(cx, idx).might_permit_raw_init(cx, init_kind) {
                         // We found a field that is unhappy with this kind of initialization.
                         return false;
                     }
diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs
index e9709b64d930e..dfcd35d2178e7 100644
--- a/compiler/rustc_typeck/src/check/check.rs
+++ b/compiler/rustc_typeck/src/check/check.rs
@@ -17,6 +17,7 @@ use rustc_infer::infer::outlives::env::OutlivesEnvironment;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
 use rustc_infer::traits::Obligation;
+use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
 use rustc_middle::ty::subst::GenericArgKind;
@@ -401,11 +402,37 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
     let item_type = tcx.type_of(item_def_id);
     if let ty::Adt(def, substs) = item_type.kind() {
         assert!(def.is_union());
-        let fields = &def.non_enum_variant().fields;
+
+        fn allowed_union_field<'tcx>(
+            ty: Ty<'tcx>,
+            tcx: TyCtxt<'tcx>,
+            param_env: ty::ParamEnv<'tcx>,
+            span: Span,
+        ) -> bool {
+            // We don't just accept all !needs_drop fields, due to semver concerns.
+            match ty.kind() {
+                ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check)
+                ty::Tuple(tys) => {
+                    // allow tuples of allowed types
+                    tys.iter().all(|ty| allowed_union_field(ty, tcx, param_env, span))
+                }
+                ty::Array(elem, _len) => {
+                    // Like `Copy`, we do *not* special-case length 0.
+                    allowed_union_field(*elem, tcx, param_env, span)
+                }
+                _ => {
+                    // Fallback case: allow `ManuallyDrop` and things that are `Copy`.
+                    ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop())
+                        || ty.is_copy_modulo_regions(tcx.at(span), param_env)
+                }
+            }
+        }
+
         let param_env = tcx.param_env(item_def_id);
-        for field in fields {
+        for field in &def.non_enum_variant().fields {
             let field_ty = field.ty(tcx, substs);
-            if field_ty.needs_drop(tcx, param_env) {
+
+            if !allowed_union_field(field_ty, tcx, param_env, span) {
                 let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) {
                     // We are currently checking the type this field came from, so it must be local.
                     Some(Node::Field(field)) => (field.span, field.ty.span),
@@ -432,6 +459,9 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
                 )
                 .emit();
                 return false;
+            } else if field_ty.needs_drop(tcx, param_env) {
+                // This should never happen. But we can get here e.g. in case of name resolution errors.
+                tcx.sess.delay_span_bug(span, "we should never accept maybe-dropping union fields");
             }
         }
     } else {
@@ -1318,7 +1348,8 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
         }
     }
 
-    // For each field, figure out if it's known to be a ZST and align(1)
+    // For each field, figure out if it's known to be a ZST and align(1), with "known"
+    // respecting #[non_exhaustive] attributes.
     let field_infos = adt.all_fields().map(|field| {
         let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did));
         let param_env = tcx.param_env(field.did);
@@ -1327,16 +1358,56 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
         let span = tcx.hir().span_if_local(field.did).unwrap();
         let zst = layout.map_or(false, |layout| layout.is_zst());
         let align1 = layout.map_or(false, |layout| layout.align.abi.bytes() == 1);
-        (span, zst, align1)
+        if !zst {
+            return (span, zst, align1, None);
+        }
+
+        fn check_non_exhaustive<'tcx>(
+            tcx: TyCtxt<'tcx>,
+            t: Ty<'tcx>,
+        ) -> ControlFlow<(&'static str, DefId, SubstsRef<'tcx>, bool)> {
+            match t.kind() {
+                ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)),
+                ty::Array(ty, _) => check_non_exhaustive(tcx, *ty),
+                ty::Adt(def, subst) => {
+                    if !def.did().is_local() {
+                        let non_exhaustive = def.is_variant_list_non_exhaustive()
+                            || def
+                                .variants()
+                                .iter()
+                                .any(ty::VariantDef::is_field_list_non_exhaustive);
+                        let has_priv = def.all_fields().any(|f| !f.vis.is_public());
+                        if non_exhaustive || has_priv {
+                            return ControlFlow::Break((
+                                def.descr(),
+                                def.did(),
+                                subst,
+                                non_exhaustive,
+                            ));
+                        }
+                    }
+                    def.all_fields()
+                        .map(|field| field.ty(tcx, subst))
+                        .try_for_each(|t| check_non_exhaustive(tcx, t))
+                }
+                _ => ControlFlow::Continue(()),
+            }
+        }
+
+        (span, zst, align1, check_non_exhaustive(tcx, ty).break_value())
     });
 
-    let non_zst_fields =
-        field_infos.clone().filter_map(|(span, zst, _align1)| if !zst { Some(span) } else { None });
+    let non_zst_fields = field_infos
+        .clone()
+        .filter_map(|(span, zst, _align1, _non_exhaustive)| if !zst { Some(span) } else { None });
     let non_zst_count = non_zst_fields.clone().count();
     if non_zst_count >= 2 {
         bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp);
     }
-    for (span, zst, align1) in field_infos {
+    let incompatible_zst_fields =
+        field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count();
+    let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2;
+    for (span, zst, align1, non_exhaustive) in field_infos {
         if zst && !align1 {
             struct_span_err!(
                 tcx.sess,
@@ -1348,6 +1419,25 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD
             .span_label(span, "has alignment larger than 1")
             .emit();
         }
+        if incompat && let Some((descr, def_id, substs, non_exhaustive)) = non_exhaustive {
+            tcx.struct_span_lint_hir(
+                REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
+                tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
+                span,
+                |lint| {
+                    let note = if non_exhaustive {
+                        "is marked with `#[non_exhaustive]`"
+                    } else {
+                        "contains private fields"
+                    };
+                    let field_ty = tcx.def_path_str_with_substs(def_id, substs);
+                    lint.build("zero-sized fields in repr(transparent) cannot contain external non-exhaustive types")
+                        .note(format!("this {descr} contains `{field_ty}`, which {note}, \
+                            and makes it not a breaking change to become non-zero-sized in the future."))
+                        .emit();
+                },
+            )
+        }
     }
 }
 
diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs
index dd712fd7ed71d..f98ae46c58730 100644
--- a/compiler/rustc_typeck/src/lib.rs
+++ b/compiler/rustc_typeck/src/lib.rs
@@ -72,6 +72,7 @@ This API is completely unstable and subject to change.
 #![feature(once_cell)]
 #![feature(slice_partition_dedup)]
 #![feature(try_blocks)]
+#![feature(is_some_with)]
 #![recursion_limit = "256"]
 
 #[macro_use]
diff --git a/src/test/ui/associated-type-bounds/duplicate.rs b/src/test/ui/associated-type-bounds/duplicate.rs
index e1dc6f8f4b63d..6e464f69510ec 100644
--- a/src/test/ui/associated-type-bounds/duplicate.rs
+++ b/src/test/ui/associated-type-bounds/duplicate.rs
@@ -1,8 +1,8 @@
 #![feature(associated_type_bounds)]
 #![feature(type_alias_impl_trait)]
-#![feature(untagged_unions)]
 
 use std::iter;
+use std::mem::ManuallyDrop;
 
 struct SI1<T: Iterator<Item: Copy, Item: Send>> {
     //~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719]
@@ -74,36 +74,36 @@ where
 
 union UI1<T: Iterator<Item: Copy, Item: Send>> {
     //~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719]
-    f: T,
+    f: ManuallyDrop<T>,
 }
 union UI2<T: Iterator<Item: Copy, Item: Copy>> {
     //~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719]
-    f: T,
+    f: ManuallyDrop<T>,
 }
 union UI3<T: Iterator<Item: 'static, Item: 'static>> {
     //~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719]
-    f: T,
+    f: ManuallyDrop<T>,
 }
 union UW1<T>
 where
     T: Iterator<Item: Copy, Item: Send>,
     //~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719]
 {
-    f: T,
+    f: ManuallyDrop<T>,
 }
 union UW2<T>
 where
     T: Iterator<Item: Copy, Item: Copy>,
     //~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719]
 {
-    f: T,
+    f: ManuallyDrop<T>,
 }
 union UW3<T>
 where
     T: Iterator<Item: 'static, Item: 'static>,
     //~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719]
 {
-    f: T,
+    f: ManuallyDrop<T>,
 }
 
 fn FI1<T: Iterator<Item: Copy, Item: Send>>() {}
diff --git a/src/test/ui/associated-type-bounds/inside-adt.rs b/src/test/ui/associated-type-bounds/inside-adt.rs
index 5af057387509d..f26037f070766 100644
--- a/src/test/ui/associated-type-bounds/inside-adt.rs
+++ b/src/test/ui/associated-type-bounds/inside-adt.rs
@@ -1,5 +1,6 @@
 #![feature(associated_type_bounds)]
-#![feature(untagged_unions)]
+
+use std::mem::ManuallyDrop;
 
 struct S1 { f: dyn Iterator<Item: Copy> }
 //~^ ERROR associated type bounds are not allowed within structs, enums, or unions
@@ -17,12 +18,12 @@ enum E3 { V(dyn Iterator<Item: 'static>) }
 //~^ ERROR associated type bounds are not allowed within structs, enums, or unions
 //~| ERROR the size for values of type `(dyn Iterator<Item = impl Sized> + 'static)`
 
-union U1 { f: dyn Iterator<Item: Copy> }
+union U1 { f: ManuallyDrop<dyn Iterator<Item: Copy>> }
 //~^ ERROR associated type bounds are not allowed within structs, enums, or unions
 //~| ERROR the size for values of type `(dyn Iterator<Item = impl Copy> + 'static)`
-union U2 { f: Box<dyn Iterator<Item: Copy>> }
+union U2 { f: ManuallyDrop<Box<dyn Iterator<Item: Copy>>> }
 //~^ ERROR associated type bounds are not allowed within structs, enums, or unions
-union U3 { f: dyn Iterator<Item: 'static> }
+union U3 { f: ManuallyDrop<dyn Iterator<Item: 'static>> }
 //~^ ERROR associated type bounds are not allowed within structs, enums, or unions
 //~| ERROR the size for values of type `(dyn Iterator<Item = impl Sized> + 'static)`
 
diff --git a/src/test/ui/associated-type-bounds/inside-adt.stderr b/src/test/ui/associated-type-bounds/inside-adt.stderr
index 0cacd78724732..978390fa71235 100644
--- a/src/test/ui/associated-type-bounds/inside-adt.stderr
+++ b/src/test/ui/associated-type-bounds/inside-adt.stderr
@@ -1,59 +1,59 @@
 error: associated type bounds are not allowed within structs, enums, or unions
-  --> $DIR/inside-adt.rs:4:29
+  --> $DIR/inside-adt.rs:5:29
    |
 LL | struct S1 { f: dyn Iterator<Item: Copy> }
    |                             ^^^^^^^^^^
 
 error: associated type bounds are not allowed within structs, enums, or unions
-  --> $DIR/inside-adt.rs:6:33
+  --> $DIR/inside-adt.rs:7:33
    |
 LL | struct S2 { f: Box<dyn Iterator<Item: Copy>> }
    |                                 ^^^^^^^^^^
 
 error: associated type bounds are not allowed within structs, enums, or unions
-  --> $DIR/inside-adt.rs:8:29
+  --> $DIR/inside-adt.rs:9:29
    |
 LL | struct S3 { f: dyn Iterator<Item: 'static> }
    |                             ^^^^^^^^^^^^^
 
 error: associated type bounds are not allowed within structs, enums, or unions
-  --> $DIR/inside-adt.rs:11:26
+  --> $DIR/inside-adt.rs:12:26
    |
 LL | enum E1 { V(dyn Iterator<Item: Copy>) }
    |                          ^^^^^^^^^^
 
 error: associated type bounds are not allowed within structs, enums, or unions
-  --> $DIR/inside-adt.rs:14:30
+  --> $DIR/inside-adt.rs:15:30
    |
 LL | enum E2 { V(Box<dyn Iterator<Item: Copy>>) }
    |                              ^^^^^^^^^^
 
 error: associated type bounds are not allowed within structs, enums, or unions
-  --> $DIR/inside-adt.rs:16:26
+  --> $DIR/inside-adt.rs:17:26
    |
 LL | enum E3 { V(dyn Iterator<Item: 'static>) }
    |                          ^^^^^^^^^^^^^
 
 error: associated type bounds are not allowed within structs, enums, or unions
-  --> $DIR/inside-adt.rs:20:28
+  --> $DIR/inside-adt.rs:21:41
    |
-LL | union U1 { f: dyn Iterator<Item: Copy> }
-   |                            ^^^^^^^^^^
+LL | union U1 { f: ManuallyDrop<dyn Iterator<Item: Copy>> }
+   |                                         ^^^^^^^^^^
 
 error: associated type bounds are not allowed within structs, enums, or unions
-  --> $DIR/inside-adt.rs:23:32
+  --> $DIR/inside-adt.rs:24:45
    |
-LL | union U2 { f: Box<dyn Iterator<Item: Copy>> }
-   |                                ^^^^^^^^^^
+LL | union U2 { f: ManuallyDrop<Box<dyn Iterator<Item: Copy>>> }
+   |                                             ^^^^^^^^^^
 
 error: associated type bounds are not allowed within structs, enums, or unions
-  --> $DIR/inside-adt.rs:25:28
+  --> $DIR/inside-adt.rs:26:41
    |
-LL | union U3 { f: dyn Iterator<Item: 'static> }
-   |                            ^^^^^^^^^^^^^
+LL | union U3 { f: ManuallyDrop<dyn Iterator<Item: 'static>> }
+   |                                         ^^^^^^^^^^^^^
 
 error[E0277]: the size for values of type `(dyn Iterator<Item = impl Copy> + 'static)` cannot be known at compilation time
-  --> $DIR/inside-adt.rs:11:13
+  --> $DIR/inside-adt.rs:12:13
    |
 LL | enum E1 { V(dyn Iterator<Item: Copy>) }
    |             ^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
@@ -71,7 +71,7 @@ LL | enum E1 { V(Box<dyn Iterator<Item: Copy>>) }
    |             ++++                        +
 
 error[E0277]: the size for values of type `(dyn Iterator<Item = impl Sized> + 'static)` cannot be known at compilation time
-  --> $DIR/inside-adt.rs:16:13
+  --> $DIR/inside-adt.rs:17:13
    |
 LL | enum E3 { V(dyn Iterator<Item: 'static>) }
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
@@ -89,40 +89,42 @@ LL | enum E3 { V(Box<dyn Iterator<Item: 'static>>) }
    |             ++++                           +
 
 error[E0277]: the size for values of type `(dyn Iterator<Item = impl Copy> + 'static)` cannot be known at compilation time
-  --> $DIR/inside-adt.rs:20:15
+  --> $DIR/inside-adt.rs:21:15
    |
-LL | union U1 { f: dyn Iterator<Item: Copy> }
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+LL | union U1 { f: ManuallyDrop<dyn Iterator<Item: Copy>> }
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
-   = help: the trait `Sized` is not implemented for `(dyn Iterator<Item = impl Copy> + 'static)`
+   = help: within `ManuallyDrop<(dyn Iterator<Item = impl Copy> + 'static)>`, the trait `Sized` is not implemented for `(dyn Iterator<Item = impl Copy> + 'static)`
+   = note: required because it appears within the type `ManuallyDrop<(dyn Iterator<Item = impl Copy> + 'static)>`
    = 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 | union U1 { f: &dyn Iterator<Item: Copy> }
+LL | union U1 { f: &ManuallyDrop<dyn Iterator<Item: Copy>> }
    |               +
 help: the `Box` type always has a statically known size and allocates its contents in the heap
    |
-LL | union U1 { f: Box<dyn Iterator<Item: Copy>> }
-   |               ++++                        +
+LL | union U1 { f: Box<ManuallyDrop<dyn Iterator<Item: Copy>>> }
+   |               ++++                                      +
 
 error[E0277]: the size for values of type `(dyn Iterator<Item = impl Sized> + 'static)` cannot be known at compilation time
-  --> $DIR/inside-adt.rs:25:15
+  --> $DIR/inside-adt.rs:26:15
    |
-LL | union U3 { f: dyn Iterator<Item: 'static> }
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+LL | union U3 { f: ManuallyDrop<dyn Iterator<Item: 'static>> }
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
-   = help: the trait `Sized` is not implemented for `(dyn Iterator<Item = impl Sized> + 'static)`
+   = help: within `ManuallyDrop<(dyn Iterator<Item = impl Sized> + 'static)>`, the trait `Sized` is not implemented for `(dyn Iterator<Item = impl Sized> + 'static)`
+   = note: required because it appears within the type `ManuallyDrop<(dyn Iterator<Item = impl Sized> + 'static)>`
    = 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 | union U3 { f: &dyn Iterator<Item: 'static> }
+LL | union U3 { f: &ManuallyDrop<dyn Iterator<Item: 'static>> }
    |               +
 help: the `Box` type always has a statically known size and allocates its contents in the heap
    |
-LL | union U3 { f: Box<dyn Iterator<Item: 'static>> }
-   |               ++++                           +
+LL | union U3 { f: Box<ManuallyDrop<dyn Iterator<Item: 'static>>> }
+   |               ++++                                         +
 
 error: aborting due to 13 previous errors
 
diff --git a/src/test/ui/associated-type-bounds/union-bounds.rs b/src/test/ui/associated-type-bounds/union-bounds.rs
index 97c5acf1f72ca..46e5aef04031a 100644
--- a/src/test/ui/associated-type-bounds/union-bounds.rs
+++ b/src/test/ui/associated-type-bounds/union-bounds.rs
@@ -1,7 +1,6 @@
 // run-pass
 
 #![feature(associated_type_bounds)]
-#![feature(untagged_unions)]
 
 #![allow(unused_assignments)]
 
diff --git a/src/test/ui/binding/issue-53114-safety-checks.rs b/src/test/ui/binding/issue-53114-safety-checks.rs
index 5042ad024afff..d0eb28c571411 100644
--- a/src/test/ui/binding/issue-53114-safety-checks.rs
+++ b/src/test/ui/binding/issue-53114-safety-checks.rs
@@ -3,9 +3,9 @@
 // captures the behavior of how `_` bindings are handled with respect to how we
 // flag expressions that are meant to request unsafe blocks.
 
-#![feature(untagged_unions)]
-
+#[derive(Copy, Clone)]
 struct I(i64);
+#[derive(Copy, Clone)]
 struct F(f64);
 
 union U { a: I, b: F }
diff --git a/src/test/ui/borrowck/borrowck-union-move-assign.rs b/src/test/ui/borrowck/borrowck-union-move-assign.rs
index a24f42d2ddf87..4c96ccdb25aaa 100644
--- a/src/test/ui/borrowck/borrowck-union-move-assign.rs
+++ b/src/test/ui/borrowck/borrowck-union-move-assign.rs
@@ -1,31 +1,31 @@
-#![feature(untagged_unions)]
+use std::mem::ManuallyDrop;
 
 // Non-copy
 struct A;
 struct B;
 
 union U {
-    a: A,
-    b: B,
+    a: ManuallyDrop<A>,
+    b: ManuallyDrop<B>,
 }
 
 fn main() {
     unsafe {
         {
-            let mut u = U { a: A };
+            let mut u = U { a: ManuallyDrop::new(A) };
             let a = u.a;
             let a = u.a; //~ ERROR use of moved value: `u`
         }
         {
-            let mut u = U { a: A };
+            let mut u = U { a: ManuallyDrop::new(A) };
             let a = u.a;
-            u.a = A;
+            u.a = ManuallyDrop::new(A);
             let a = u.a; // OK
         }
         {
-            let mut u = U { a: A };
+            let mut u = U { a: ManuallyDrop::new(A) };
             let a = u.a;
-            u.b = B;
+            u.b = ManuallyDrop::new(B);
             let a = u.a; // OK
         }
     }
diff --git a/src/test/ui/borrowck/borrowck-union-move-assign.stderr b/src/test/ui/borrowck/borrowck-union-move-assign.stderr
index 0b1714fd75dc0..af6f6fac40870 100644
--- a/src/test/ui/borrowck/borrowck-union-move-assign.stderr
+++ b/src/test/ui/borrowck/borrowck-union-move-assign.stderr
@@ -1,7 +1,7 @@
 error[E0382]: use of moved value: `u`
   --> $DIR/borrowck-union-move-assign.rs:17:21
    |
-LL |             let mut u = U { a: A };
+LL |             let mut u = U { a: ManuallyDrop::new(A) };
    |                 ----- move occurs because `u` has type `U`, which does not implement the `Copy` trait
 LL |             let a = u.a;
    |                     --- value moved here
diff --git a/src/test/ui/borrowck/borrowck-union-move.rs b/src/test/ui/borrowck/borrowck-union-move.rs
index d0aa6dff74410..510547ad5bb74 100644
--- a/src/test/ui/borrowck/borrowck-union-move.rs
+++ b/src/test/ui/borrowck/borrowck-union-move.rs
@@ -1,12 +1,12 @@
-#![feature(untagged_unions)]
+use std::mem::ManuallyDrop;
 
 #[derive(Clone, Copy)]
 struct Copy;
 struct NonCopy;
 
 union Unn {
-    n1: NonCopy,
-    n2: NonCopy,
+    n1: ManuallyDrop<NonCopy>,
+    n2: ManuallyDrop<NonCopy>,
 }
 union Ucc {
     c1: Copy,
@@ -14,24 +14,24 @@ union Ucc {
 }
 union Ucn {
     c: Copy,
-    n: NonCopy,
+    n: ManuallyDrop<NonCopy>,
 }
 
 fn main() {
     unsafe {
         // 2 NonCopy
         {
-            let mut u = Unn { n1: NonCopy };
+            let mut u = Unn { n1: ManuallyDrop::new(NonCopy) };
             let a = u.n1;
             let a = u.n1; //~ ERROR use of moved value: `u`
         }
         {
-            let mut u = Unn { n1: NonCopy };
+            let mut u = Unn { n1: ManuallyDrop::new(NonCopy) };
             let a = u.n1;
             let a = u; //~ ERROR use of moved value: `u`
         }
         {
-            let mut u = Unn { n1: NonCopy };
+            let mut u = Unn { n1: ManuallyDrop::new(NonCopy) };
             let a = u.n1;
             let a = u.n2; //~ ERROR use of moved value: `u`
         }
diff --git a/src/test/ui/borrowck/borrowck-union-move.stderr b/src/test/ui/borrowck/borrowck-union-move.stderr
index abbb0142a9c30..731607fbdd1f7 100644
--- a/src/test/ui/borrowck/borrowck-union-move.stderr
+++ b/src/test/ui/borrowck/borrowck-union-move.stderr
@@ -1,7 +1,7 @@
 error[E0382]: use of moved value: `u`
   --> $DIR/borrowck-union-move.rs:26:21
    |
-LL |             let mut u = Unn { n1: NonCopy };
+LL |             let mut u = Unn { n1: ManuallyDrop::new(NonCopy) };
    |                 ----- move occurs because `u` has type `Unn`, which does not implement the `Copy` trait
 LL |             let a = u.n1;
    |                     ---- value moved here
@@ -11,7 +11,7 @@ LL |             let a = u.n1;
 error[E0382]: use of moved value: `u`
   --> $DIR/borrowck-union-move.rs:31:21
    |
-LL |             let mut u = Unn { n1: NonCopy };
+LL |             let mut u = Unn { n1: ManuallyDrop::new(NonCopy) };
    |                 ----- move occurs because `u` has type `Unn`, which does not implement the `Copy` trait
 LL |             let a = u.n1;
    |                     ---- value moved here
@@ -21,7 +21,7 @@ LL |             let a = u;
 error[E0382]: use of moved value: `u`
   --> $DIR/borrowck-union-move.rs:36:21
    |
-LL |             let mut u = Unn { n1: NonCopy };
+LL |             let mut u = Unn { n1: ManuallyDrop::new(NonCopy) };
    |                 ----- move occurs because `u` has type `Unn`, which does not implement the `Copy` trait
 LL |             let a = u.n1;
    |                     ---- value moved here
diff --git a/src/test/ui/borrowck/move-from-union-field-issue-66500.rs b/src/test/ui/borrowck/move-from-union-field-issue-66500.rs
index 8fbf120fc1c78..0bd2147f46331 100644
--- a/src/test/ui/borrowck/move-from-union-field-issue-66500.rs
+++ b/src/test/ui/borrowck/move-from-union-field-issue-66500.rs
@@ -1,8 +1,6 @@
 // Moving from a reference/raw pointer should be an error, even when they're
 // the field of a union.
 
-#![feature(untagged_unions)]
-
 union Pointers {
     a: &'static String,
     b: &'static mut String,
diff --git a/src/test/ui/borrowck/move-from-union-field-issue-66500.stderr b/src/test/ui/borrowck/move-from-union-field-issue-66500.stderr
index 82c3fe3b12d1c..70078582713c6 100644
--- a/src/test/ui/borrowck/move-from-union-field-issue-66500.stderr
+++ b/src/test/ui/borrowck/move-from-union-field-issue-66500.stderr
@@ -1,23 +1,23 @@
 error[E0507]: cannot move out of `*u.a` which is behind a shared reference
-  --> $DIR/move-from-union-field-issue-66500.rs:14:5
+  --> $DIR/move-from-union-field-issue-66500.rs:12:5
    |
 LL |     *u.a
    |     ^^^^ move occurs because `*u.a` has type `String`, which does not implement the `Copy` trait
 
 error[E0507]: cannot move out of `*u.b` which is behind a mutable reference
-  --> $DIR/move-from-union-field-issue-66500.rs:18:5
+  --> $DIR/move-from-union-field-issue-66500.rs:16:5
    |
 LL |     *u.b
    |     ^^^^ move occurs because `*u.b` has type `String`, which does not implement the `Copy` trait
 
 error[E0507]: cannot move out of `*u.c` which is behind a raw pointer
-  --> $DIR/move-from-union-field-issue-66500.rs:22:5
+  --> $DIR/move-from-union-field-issue-66500.rs:20:5
    |
 LL |     *u.c
    |     ^^^^ move occurs because `*u.c` has type `String`, which does not implement the `Copy` trait
 
 error[E0507]: cannot move out of `*u.d` which is behind a raw pointer
-  --> $DIR/move-from-union-field-issue-66500.rs:26:5
+  --> $DIR/move-from-union-field-issue-66500.rs:24:5
    |
 LL |     *u.d
    |     ^^^^ move occurs because `*u.d` has type `String`, which does not implement the `Copy` trait
diff --git a/src/test/ui/consts/invalid-union.32bit.stderr b/src/test/ui/consts/invalid-union.32bit.stderr
index 7e9abc3ffa7e1..ae5f6b2baee40 100644
--- a/src/test/ui/consts/invalid-union.32bit.stderr
+++ b/src/test/ui/consts/invalid-union.32bit.stderr
@@ -1,5 +1,5 @@
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/invalid-union.rs:40:1
+  --> $DIR/invalid-union.rs:41:1
    |
 LL | fn main() {
    | ^^^^^^^^^ constructing invalid value at .<deref>.y.<enum-variant(B)>.0: encountered `UnsafeCell` in a `const`
@@ -10,7 +10,7 @@ LL | fn main() {
            }
 
 error: erroneous constant used
-  --> $DIR/invalid-union.rs:41:25
+  --> $DIR/invalid-union.rs:42:25
    |
 LL |     let _: &'static _ = &C;
    |                         ^^ referenced constant has errors
@@ -24,7 +24,7 @@ error: aborting due to 2 previous errors
 For more information about this error, try `rustc --explain E0080`.
 Future incompatibility report: Future breakage diagnostic:
 error: erroneous constant used
-  --> $DIR/invalid-union.rs:41:25
+  --> $DIR/invalid-union.rs:42:25
    |
 LL |     let _: &'static _ = &C;
    |                         ^^ referenced constant has errors
diff --git a/src/test/ui/consts/invalid-union.64bit.stderr b/src/test/ui/consts/invalid-union.64bit.stderr
index 81c1024424972..d50e74a16ec36 100644
--- a/src/test/ui/consts/invalid-union.64bit.stderr
+++ b/src/test/ui/consts/invalid-union.64bit.stderr
@@ -1,5 +1,5 @@
 error[E0080]: it is undefined behavior to use this value
-  --> $DIR/invalid-union.rs:40:1
+  --> $DIR/invalid-union.rs:41:1
    |
 LL | fn main() {
    | ^^^^^^^^^ constructing invalid value at .<deref>.y.<enum-variant(B)>.0: encountered `UnsafeCell` in a `const`
@@ -10,7 +10,7 @@ LL | fn main() {
            }
 
 error: erroneous constant used
-  --> $DIR/invalid-union.rs:41:25
+  --> $DIR/invalid-union.rs:42:25
    |
 LL |     let _: &'static _ = &C;
    |                         ^^ referenced constant has errors
@@ -24,7 +24,7 @@ error: aborting due to 2 previous errors
 For more information about this error, try `rustc --explain E0080`.
 Future incompatibility report: Future breakage diagnostic:
 error: erroneous constant used
-  --> $DIR/invalid-union.rs:41:25
+  --> $DIR/invalid-union.rs:42:25
    |
 LL |     let _: &'static _ = &C;
    |                         ^^ referenced constant has errors
diff --git a/src/test/ui/consts/invalid-union.rs b/src/test/ui/consts/invalid-union.rs
index f3f1af89b2c41..efeddf75cb557 100644
--- a/src/test/ui/consts/invalid-union.rs
+++ b/src/test/ui/consts/invalid-union.rs
@@ -9,8 +9,9 @@
 // build-fail
 // stderr-per-bitwidth
 #![feature(const_mut_refs)]
-#![feature(untagged_unions)]
+
 use std::cell::Cell;
+use std::mem::ManuallyDrop;
 
 #[repr(C)]
 struct S {
@@ -25,7 +26,7 @@ enum E {
 }
 
 union U {
-    cell: Cell<u32>,
+    cell: ManuallyDrop<Cell<u32>>,
 }
 
 const C: S = {
diff --git a/src/test/ui/consts/qualif-union.rs b/src/test/ui/consts/qualif-union.rs
index 2054b5b89ed6e..11c019be96432 100644
--- a/src/test/ui/consts/qualif-union.rs
+++ b/src/test/ui/consts/qualif-union.rs
@@ -1,18 +1,19 @@
 // Checks that unions use type based qualification. Regression test for issue #90268.
-#![feature(untagged_unions)]
+
 use std::cell::Cell;
+use std::mem::ManuallyDrop;
 
-union U { i: u32, c: Cell<u32> }
+union U { i: u32, c: ManuallyDrop<Cell<u32>> }
 
-const C1: Cell<u32> = {
-    unsafe { U { c: Cell::new(0) }.c }
+const C1: ManuallyDrop<Cell<u32>> = {
+    unsafe { U { c: ManuallyDrop::new(Cell::new(0)) }.c }
 };
 
-const C2: Cell<u32> = {
+const C2: ManuallyDrop<Cell<u32>> = {
     unsafe { U { i : 0 }.c }
 };
 
-const C3: Cell<u32> = {
+const C3: ManuallyDrop<Cell<u32>> = {
     let mut u = U { i: 0 };
     u.i = 1;
     unsafe { u.c }
diff --git a/src/test/ui/consts/qualif-union.stderr b/src/test/ui/consts/qualif-union.stderr
index fda8ad4a3bc81..8ec68ada048a5 100644
--- a/src/test/ui/consts/qualif-union.stderr
+++ b/src/test/ui/consts/qualif-union.stderr
@@ -1,5 +1,5 @@
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/qualif-union.rs:27:26
+  --> $DIR/qualif-union.rs:28:26
    |
 LL |     let _: &'static _ = &C1;
    |            ----------    ^^ creates a temporary which is freed while still in use
@@ -10,7 +10,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/qualif-union.rs:28:26
+  --> $DIR/qualif-union.rs:29:26
    |
 LL |     let _: &'static _ = &C2;
    |            ----------    ^^ creates a temporary which is freed while still in use
@@ -21,7 +21,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/qualif-union.rs:29:26
+  --> $DIR/qualif-union.rs:30:26
    |
 LL |     let _: &'static _ = &C3;
    |            ----------    ^^ creates a temporary which is freed while still in use
@@ -32,7 +32,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/qualif-union.rs:30:26
+  --> $DIR/qualif-union.rs:31:26
    |
 LL |     let _: &'static _ = &C4;
    |            ----------    ^^ creates a temporary which is freed while still in use
@@ -43,7 +43,7 @@ LL | }
    | - temporary value is freed at the end of this statement
 
 error[E0716]: temporary value dropped while borrowed
-  --> $DIR/qualif-union.rs:31:26
+  --> $DIR/qualif-union.rs:32:26
    |
 LL |     let _: &'static _ = &C5;
    |            ----------    ^^ creates a temporary which is freed while still in use
diff --git a/src/test/ui/feature-gates/feature-gate-associated_type_bounds.rs b/src/test/ui/feature-gates/feature-gate-associated_type_bounds.rs
index a93fb7977131d..4e020327447ff 100644
--- a/src/test/ui/feature-gates/feature-gate-associated_type_bounds.rs
+++ b/src/test/ui/feature-gates/feature-gate-associated_type_bounds.rs
@@ -1,7 +1,7 @@
 // compile-flags: -Zsave-analysis
 // This is also a regression test for #69415 and the above flag is needed.
 
-#![feature(untagged_unions)]
+use std::mem::ManuallyDrop;
 
 trait Tr1 { type As1: Copy; }
 trait Tr2 { type As2: Copy; }
@@ -36,9 +36,9 @@ enum _En1<T: Tr1<As1: Tr2>> {
 
 union _Un1<T: Tr1<As1: Tr2>> {
 //~^ ERROR associated type bounds are unstable
-    outest: std::mem::ManuallyDrop<T>,
-    outer: T::As1,
-    inner: <T::As1 as Tr2>::As2,
+    outest: ManuallyDrop<T>,
+    outer: ManuallyDrop<T::As1>,
+    inner: ManuallyDrop<<T::As1 as Tr2>::As2>,
 }
 
 type _TaWhere1<T> where T: Iterator<Item: Copy> = T;
diff --git a/src/test/ui/feature-gates/feature-gate-untagged_unions.rs b/src/test/ui/feature-gates/feature-gate-untagged_unions.rs
deleted file mode 100644
index af8d8e92b20bd..0000000000000
--- a/src/test/ui/feature-gates/feature-gate-untagged_unions.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-// ignore-tidy-linelength
-
-union U1 { // OK
-    a: u8,
-}
-
-union U2<T: Copy> { // OK
-    a: T,
-}
-
-union U22<T> { // OK
-    a: std::mem::ManuallyDrop<T>,
-}
-
-union U3 {
-    a: String, //~ ERROR unions cannot contain fields that may need dropping
-}
-
-union U32 { // field that does not drop but is not `Copy`, either -- this is the real feature gate test!
-    a: std::cell::RefCell<i32>, //~ ERROR unions with non-`Copy` fields other than `ManuallyDrop<T>` are unstable
-}
-
-union U4<T> {
-    a: T, //~ ERROR unions cannot contain fields that may need dropping
-}
-
-union U5 { // Having a drop impl is OK
-    a: u8,
-}
-
-impl Drop for U5 {
-    fn drop(&mut self) {}
-}
-
-fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr b/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr
deleted file mode 100644
index 9e4a89f80c852..0000000000000
--- a/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr
+++ /dev/null
@@ -1,37 +0,0 @@
-error[E0658]: unions with non-`Copy` fields other than `ManuallyDrop<T>` are unstable
-  --> $DIR/feature-gate-untagged_unions.rs:20:5
-   |
-LL |     a: std::cell::RefCell<i32>,
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #55149 <https://github.com/rust-lang/rust/issues/55149> for more information
-   = help: add `#![feature(untagged_unions)]` to the crate attributes to enable
-
-error[E0740]: unions cannot contain fields that may need dropping
-  --> $DIR/feature-gate-untagged_unions.rs:16:5
-   |
-LL |     a: String,
-   |     ^^^^^^^^^
-   |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
-   |
-LL |     a: std::mem::ManuallyDrop<String>,
-   |        +++++++++++++++++++++++      +
-
-error[E0740]: unions cannot contain fields that may need dropping
-  --> $DIR/feature-gate-untagged_unions.rs:24:5
-   |
-LL |     a: T,
-   |     ^^^^
-   |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
-   |
-LL |     a: std::mem::ManuallyDrop<T>,
-   |        +++++++++++++++++++++++ +
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0658, E0740.
-For more information about an error, try `rustc --explain E0658`.
diff --git a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs
index 3ffd35ecdb8da..255151a96032c 100644
--- a/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs
+++ b/src/test/ui/intrinsics/panic-uninitialized-zeroed.rs
@@ -57,6 +57,13 @@ enum LR_NonZero {
 
 struct ZeroSized;
 
+#[allow(dead_code)]
+#[repr(i32)]
+enum ZeroIsValid {
+    Zero(u8) = 0,
+    One(NonNull<()>) = 1,
+}
+
 fn test_panic_msg<T>(op: impl (FnOnce() -> T) + panic::UnwindSafe, msg: &str) {
     let err = panic::catch_unwind(op).err();
     assert_eq!(
@@ -152,33 +159,12 @@ fn main() {
             "attempted to zero-initialize type `*const dyn core::marker::Send`, which is invalid"
         );
 
-        /* FIXME(#66151) we conservatively do not error here yet.
-        test_panic_msg(
-            || mem::uninitialized::<LR_NonZero>(),
-            "attempted to leave type `LR_NonZero` uninitialized, which is invalid"
-        );
-        test_panic_msg(
-            || mem::zeroed::<LR_NonZero>(),
-            "attempted to zero-initialize type `LR_NonZero`, which is invalid"
-        );
-
-        test_panic_msg(
-            || mem::uninitialized::<ManuallyDrop<LR_NonZero>>(),
-            "attempted to leave type `std::mem::ManuallyDrop<LR_NonZero>` uninitialized, \
-             which is invalid"
-        );
-        test_panic_msg(
-            || mem::zeroed::<ManuallyDrop<LR_NonZero>>(),
-            "attempted to zero-initialize type `std::mem::ManuallyDrop<LR_NonZero>`, \
-             which is invalid"
-        );
-        */
-
         test_panic_msg(
             || mem::uninitialized::<(NonNull<u32>, u32, u32)>(),
             "attempted to leave type `(core::ptr::non_null::NonNull<u32>, u32, u32)` uninitialized, \
                 which is invalid"
         );
+
         test_panic_msg(
             || mem::zeroed::<(NonNull<u32>, u32, u32)>(),
             "attempted to zero-initialize type `(core::ptr::non_null::NonNull<u32>, u32, u32)`, \
@@ -196,11 +182,23 @@ fn main() {
                 which is invalid"
         );
 
+        test_panic_msg(
+            || mem::uninitialized::<LR_NonZero>(),
+            "attempted to leave type `LR_NonZero` uninitialized, which is invalid"
+        );
+
+        test_panic_msg(
+            || mem::uninitialized::<ManuallyDrop<LR_NonZero>>(),
+            "attempted to leave type `core::mem::manually_drop::ManuallyDrop<LR_NonZero>` uninitialized, \
+             which is invalid"
+        );
+
         test_panic_msg(
             || mem::uninitialized::<NoNullVariant>(),
             "attempted to leave type `NoNullVariant` uninitialized, \
                 which is invalid"
         );
+
         test_panic_msg(
             || mem::zeroed::<NoNullVariant>(),
             "attempted to zero-initialize type `NoNullVariant`, \
@@ -212,10 +210,12 @@ fn main() {
             || mem::uninitialized::<bool>(),
             "attempted to leave type `bool` uninitialized, which is invalid"
         );
+
         test_panic_msg(
             || mem::uninitialized::<LR>(),
             "attempted to leave type `LR` uninitialized, which is invalid"
         );
+
         test_panic_msg(
             || mem::uninitialized::<ManuallyDrop<LR>>(),
             "attempted to leave type `core::mem::manually_drop::ManuallyDrop<LR>` uninitialized, which is invalid"
@@ -229,6 +229,7 @@ fn main() {
         let _val = mem::zeroed::<Option<&'static i32>>();
         let _val = mem::zeroed::<MaybeUninit<NonNull<u32>>>();
         let _val = mem::zeroed::<[!; 0]>();
+        let _val = mem::zeroed::<ZeroIsValid>();
         let _val = mem::uninitialized::<MaybeUninit<bool>>();
         let _val = mem::uninitialized::<[!; 0]>();
         let _val = mem::uninitialized::<()>();
@@ -259,12 +260,33 @@ fn main() {
                 || mem::zeroed::<[NonNull<()>; 1]>(),
                 "attempted to zero-initialize type `[core::ptr::non_null::NonNull<()>; 1]`, which is invalid"
             );
+
+            // FIXME(#66151) we conservatively do not error here yet (by default).
+            test_panic_msg(
+                || mem::zeroed::<LR_NonZero>(),
+                "attempted to zero-initialize type `LR_NonZero`, which is invalid"
+            );
+
+            test_panic_msg(
+                || mem::zeroed::<ManuallyDrop<LR_NonZero>>(),
+                "attempted to zero-initialize type `core::mem::manually_drop::ManuallyDrop<LR_NonZero>`, \
+                 which is invalid"
+            );
         } else {
             // These are UB because they have not been officially blessed, but we await the resolution
             // of <https://github.com/rust-lang/unsafe-code-guidelines/issues/71> before doing
             // anything about that.
             let _val = mem::uninitialized::<i32>();
             let _val = mem::uninitialized::<*const ()>();
+
+            // These are UB, but best to test them to ensure we don't become unintentionally
+            // stricter.
+
+            // It's currently unchecked to create invalid enums and values inside arrays.
+            let _val = mem::zeroed::<LR_NonZero>();
+            let _val = mem::zeroed::<[LR_NonZero; 1]>();
+            let _val = mem::zeroed::<[NonNull<()>; 1]>();
+            let _val = mem::uninitialized::<[NonNull<()>; 1]>();
         }
     }
 }
diff --git a/src/test/ui/nll/issue-55651.rs b/src/test/ui/nll/issue-55651.rs
index 46255bf74a138..75ba482717460 100644
--- a/src/test/ui/nll/issue-55651.rs
+++ b/src/test/ui/nll/issue-55651.rs
@@ -1,27 +1,27 @@
 // check-pass
 
-#![feature(untagged_unions)]
+use std::mem::ManuallyDrop;
 
 struct A;
 struct B;
 
 union U {
-    a: A,
-    b: B,
+    a: ManuallyDrop<A>,
+    b: ManuallyDrop<B>,
 }
 
 fn main() {
     unsafe {
         {
-            let mut u = U { a: A };
+            let mut u = U { a: ManuallyDrop::new(A) };
             let a = u.a;
-            u.a = A;
+            u.a = ManuallyDrop::new(A);
             let a = u.a; // OK
         }
         {
-            let mut u = U { a: A };
+            let mut u = U { a: ManuallyDrop::new(A) };
             let a = u.a;
-            u.b = B;
+            u.b = ManuallyDrop::new(B);
             let a = u.a; // OK
         }
     }
diff --git a/src/test/ui/repr/auxiliary/repr-transparent-non-exhaustive.rs b/src/test/ui/repr/auxiliary/repr-transparent-non-exhaustive.rs
new file mode 100644
index 0000000000000..4bf6b54fe0787
--- /dev/null
+++ b/src/test/ui/repr/auxiliary/repr-transparent-non-exhaustive.rs
@@ -0,0 +1,18 @@
+#![crate_type = "lib"]
+
+pub struct Private { _priv: () }
+
+#[non_exhaustive]
+pub struct NonExhaustive {}
+
+#[non_exhaustive]
+pub enum NonExhaustiveEnum {}
+
+pub enum NonExhaustiveVariant {
+    #[non_exhaustive]
+    A,
+}
+
+pub struct ExternalIndirection<T> {
+    pub x: T,
+}
diff --git a/src/test/ui/repr/repr-packed-contains-align.rs b/src/test/ui/repr/repr-packed-contains-align.rs
index 67d87eb5cd520..bef5c7d8c62fc 100644
--- a/src/test/ui/repr/repr-packed-contains-align.rs
+++ b/src/test/ui/repr/repr-packed-contains-align.rs
@@ -1,16 +1,19 @@
-#![feature(untagged_unions)]
 #![allow(dead_code)]
 
 #[repr(align(16))]
+#[derive(Clone, Copy)]
 struct SA(i32);
 
+#[derive(Clone, Copy)]
 struct SB(SA);
 
 #[repr(align(16))]
+#[derive(Clone, Copy)]
 union UA {
     i: i32
 }
 
+#[derive(Clone, Copy)]
 union UB {
     a: UA
 }
diff --git a/src/test/ui/repr/repr-packed-contains-align.stderr b/src/test/ui/repr/repr-packed-contains-align.stderr
index 531004e8e202b..4c3a960cad2a6 100644
--- a/src/test/ui/repr/repr-packed-contains-align.stderr
+++ b/src/test/ui/repr/repr-packed-contains-align.stderr
@@ -1,5 +1,5 @@
 error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type
-  --> $DIR/repr-packed-contains-align.rs:19:1
+  --> $DIR/repr-packed-contains-align.rs:22:1
    |
 LL | struct SC(SA);
    | ^^^^^^^^^
@@ -11,7 +11,7 @@ LL | struct SA(i32);
    | ^^^^^^^^^
 
 error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type
-  --> $DIR/repr-packed-contains-align.rs:22:1
+  --> $DIR/repr-packed-contains-align.rs:25:1
    |
 LL | struct SD(SB);
    | ^^^^^^^^^
@@ -22,86 +22,86 @@ note: `SA` has a `#[repr(align)]` attribute
 LL | struct SA(i32);
    | ^^^^^^^^^
 note: `SD` contains a field of type `SB`
-  --> $DIR/repr-packed-contains-align.rs:22:11
+  --> $DIR/repr-packed-contains-align.rs:25:11
    |
 LL | struct SD(SB);
    |           ^^
 note: ...which contains a field of type `SA`
-  --> $DIR/repr-packed-contains-align.rs:7:11
+  --> $DIR/repr-packed-contains-align.rs:8:11
    |
 LL | struct SB(SA);
    |           ^^
 
 error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type
-  --> $DIR/repr-packed-contains-align.rs:25:1
+  --> $DIR/repr-packed-contains-align.rs:28:1
    |
 LL | struct SE(UA);
    | ^^^^^^^^^
    |
 note: `UA` has a `#[repr(align)]` attribute
-  --> $DIR/repr-packed-contains-align.rs:10:1
+  --> $DIR/repr-packed-contains-align.rs:12:1
    |
 LL | union UA {
    | ^^^^^^^^
 
 error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type
-  --> $DIR/repr-packed-contains-align.rs:28:1
+  --> $DIR/repr-packed-contains-align.rs:31:1
    |
 LL | struct SF(UB);
    | ^^^^^^^^^
    |
 note: `UA` has a `#[repr(align)]` attribute
-  --> $DIR/repr-packed-contains-align.rs:10:1
+  --> $DIR/repr-packed-contains-align.rs:12:1
    |
 LL | union UA {
    | ^^^^^^^^
 note: `SF` contains a field of type `UB`
-  --> $DIR/repr-packed-contains-align.rs:28:11
+  --> $DIR/repr-packed-contains-align.rs:31:11
    |
 LL | struct SF(UB);
    |           ^^
 note: ...which contains a field of type `UA`
-  --> $DIR/repr-packed-contains-align.rs:15:5
+  --> $DIR/repr-packed-contains-align.rs:18:5
    |
 LL |     a: UA
    |     ^
 
 error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type
-  --> $DIR/repr-packed-contains-align.rs:31:1
+  --> $DIR/repr-packed-contains-align.rs:34:1
    |
 LL | union UC {
    | ^^^^^^^^
    |
 note: `UA` has a `#[repr(align)]` attribute
-  --> $DIR/repr-packed-contains-align.rs:10:1
+  --> $DIR/repr-packed-contains-align.rs:12:1
    |
 LL | union UA {
    | ^^^^^^^^
 
 error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type
-  --> $DIR/repr-packed-contains-align.rs:36:1
+  --> $DIR/repr-packed-contains-align.rs:39:1
    |
 LL | union UD {
    | ^^^^^^^^
    |
 note: `UA` has a `#[repr(align)]` attribute
-  --> $DIR/repr-packed-contains-align.rs:10:1
+  --> $DIR/repr-packed-contains-align.rs:12:1
    |
 LL | union UA {
    | ^^^^^^^^
 note: `UD` contains a field of type `UB`
-  --> $DIR/repr-packed-contains-align.rs:37:5
+  --> $DIR/repr-packed-contains-align.rs:40:5
    |
 LL |     n: UB
    |     ^
 note: ...which contains a field of type `UA`
-  --> $DIR/repr-packed-contains-align.rs:15:5
+  --> $DIR/repr-packed-contains-align.rs:18:5
    |
 LL |     a: UA
    |     ^
 
 error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type
-  --> $DIR/repr-packed-contains-align.rs:41:1
+  --> $DIR/repr-packed-contains-align.rs:44:1
    |
 LL | union UE {
    | ^^^^^^^^
@@ -113,7 +113,7 @@ LL | struct SA(i32);
    | ^^^^^^^^^
 
 error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type
-  --> $DIR/repr-packed-contains-align.rs:46:1
+  --> $DIR/repr-packed-contains-align.rs:49:1
    |
 LL | union UF {
    | ^^^^^^^^
@@ -124,12 +124,12 @@ note: `SA` has a `#[repr(align)]` attribute
 LL | struct SA(i32);
    | ^^^^^^^^^
 note: `UF` contains a field of type `SB`
-  --> $DIR/repr-packed-contains-align.rs:47:5
+  --> $DIR/repr-packed-contains-align.rs:50:5
    |
 LL |     n: SB
    |     ^
 note: ...which contains a field of type `SA`
-  --> $DIR/repr-packed-contains-align.rs:7:11
+  --> $DIR/repr-packed-contains-align.rs:8:11
    |
 LL | struct SB(SA);
    |           ^^
diff --git a/src/test/ui/repr/repr-transparent-non-exhaustive.rs b/src/test/ui/repr/repr-transparent-non-exhaustive.rs
new file mode 100644
index 0000000000000..9ccd8610dad47
--- /dev/null
+++ b/src/test/ui/repr/repr-transparent-non-exhaustive.rs
@@ -0,0 +1,96 @@
+#![deny(repr_transparent_external_private_fields)]
+
+// aux-build: repr-transparent-non-exhaustive.rs
+extern crate repr_transparent_non_exhaustive;
+
+use repr_transparent_non_exhaustive::{
+    Private,
+    NonExhaustive,
+    NonExhaustiveEnum,
+    NonExhaustiveVariant,
+    ExternalIndirection,
+};
+
+pub struct InternalPrivate {
+    _priv: (),
+}
+
+#[non_exhaustive]
+pub struct InternalNonExhaustive;
+
+pub struct InternalIndirection<T> {
+    x: T,
+}
+
+pub type Sized = i32;
+
+#[repr(transparent)]
+pub struct T1(Sized, InternalPrivate);
+#[repr(transparent)]
+pub struct T2(Sized, InternalNonExhaustive);
+#[repr(transparent)]
+pub struct T3(Sized, InternalIndirection<(InternalPrivate, InternalNonExhaustive)>);
+#[repr(transparent)]
+pub struct T4(Sized, ExternalIndirection<(InternalPrivate, InternalNonExhaustive)>);
+
+#[repr(transparent)]
+pub struct T5(Sized, Private);
+//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T6(Sized, NonExhaustive);
+//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T7(Sized, NonExhaustiveEnum);
+//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T8(Sized, NonExhaustiveVariant);
+//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T9(Sized, InternalIndirection<Private>);
+//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T10(Sized, InternalIndirection<NonExhaustive>);
+//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T11(Sized, InternalIndirection<NonExhaustiveEnum>);
+//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T12(Sized, InternalIndirection<NonExhaustiveVariant>);
+//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T13(Sized, ExternalIndirection<Private>);
+//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T14(Sized, ExternalIndirection<NonExhaustive>);
+//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T15(Sized, ExternalIndirection<NonExhaustiveEnum>);
+//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+#[repr(transparent)]
+pub struct T16(Sized, ExternalIndirection<NonExhaustiveVariant>);
+//~^ ERROR zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+//~| WARN this was previously accepted by the compiler
+
+fn main() {}
diff --git a/src/test/ui/repr/repr-transparent-non-exhaustive.stderr b/src/test/ui/repr/repr-transparent-non-exhaustive.stderr
new file mode 100644
index 0000000000000..3b1e334a0cbe2
--- /dev/null
+++ b/src/test/ui/repr/repr-transparent-non-exhaustive.stderr
@@ -0,0 +1,127 @@
+error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:37:22
+   |
+LL | pub struct T5(Sized, Private);
+   |                      ^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/repr-transparent-non-exhaustive.rs:1:9
+   |
+LL | #![deny(repr_transparent_external_private_fields)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:42:22
+   |
+LL | pub struct T6(Sized, NonExhaustive);
+   |                      ^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:47:22
+   |
+LL | pub struct T7(Sized, NonExhaustiveEnum);
+   |                      ^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this enum contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:52:22
+   |
+LL | pub struct T8(Sized, NonExhaustiveVariant);
+   |                      ^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:57:22
+   |
+LL | pub struct T9(Sized, InternalIndirection<Private>);
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:62:23
+   |
+LL | pub struct T10(Sized, InternalIndirection<NonExhaustive>);
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:67:23
+   |
+LL | pub struct T11(Sized, InternalIndirection<NonExhaustiveEnum>);
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this enum contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:72:23
+   |
+LL | pub struct T12(Sized, InternalIndirection<NonExhaustiveVariant>);
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:77:23
+   |
+LL | pub struct T13(Sized, ExternalIndirection<Private>);
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this struct contains `Private`, which contains private fields, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:82:23
+   |
+LL | pub struct T14(Sized, ExternalIndirection<NonExhaustive>);
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this struct contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:87:23
+   |
+LL | pub struct T15(Sized, ExternalIndirection<NonExhaustiveEnum>);
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this enum contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: zero-sized fields in repr(transparent) cannot contain external non-exhaustive types
+  --> $DIR/repr-transparent-non-exhaustive.rs:92:23
+   |
+LL | pub struct T16(Sized, ExternalIndirection<NonExhaustiveVariant>);
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #78586 <https://github.com/rust-lang/rust/issues/78586>
+   = note: this enum contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, and makes it not a breaking change to become non-zero-sized in the future.
+
+error: aborting due to 12 previous errors
+
diff --git a/src/test/ui/rfc-2093-infer-outlives/explicit-union.rs b/src/test/ui/rfc-2093-infer-outlives/explicit-union.rs
index ea8a3c177e9d7..871208b5ba785 100644
--- a/src/test/ui/rfc-2093-infer-outlives/explicit-union.rs
+++ b/src/test/ui/rfc-2093-infer-outlives/explicit-union.rs
@@ -1,11 +1,11 @@
 #![feature(rustc_attrs)]
-#![feature(untagged_unions)]
 
 #[rustc_outlives]
 union Foo<'b, U: Copy> { //~ ERROR rustc_outlives
     bar: Bar<'b, U>
 }
 
+#[derive(Clone, Copy)]
 union Bar<'a, T: Copy> where T: 'a {
     x: &'a (),
     y: T,
diff --git a/src/test/ui/rfc-2093-infer-outlives/explicit-union.stderr b/src/test/ui/rfc-2093-infer-outlives/explicit-union.stderr
index 2c6d06aa8c7f5..16b64bdc29dd3 100644
--- a/src/test/ui/rfc-2093-infer-outlives/explicit-union.stderr
+++ b/src/test/ui/rfc-2093-infer-outlives/explicit-union.stderr
@@ -1,5 +1,5 @@
 error: rustc_outlives
-  --> $DIR/explicit-union.rs:5:1
+  --> $DIR/explicit-union.rs:4:1
    |
 LL | union Foo<'b, U: Copy> {
    | ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/test/ui/rfc-2093-infer-outlives/nested-union.rs b/src/test/ui/rfc-2093-infer-outlives/nested-union.rs
index 0da3cc2ba1b04..27ebd0b54db5d 100644
--- a/src/test/ui/rfc-2093-infer-outlives/nested-union.rs
+++ b/src/test/ui/rfc-2093-infer-outlives/nested-union.rs
@@ -1,5 +1,4 @@
 #![feature(rustc_attrs)]
-#![feature(untagged_unions)]
 
 #[rustc_outlives]
 union Foo<'a, T: Copy> { //~ ERROR rustc_outlives
@@ -7,6 +6,7 @@ union Foo<'a, T: Copy> { //~ ERROR rustc_outlives
 }
 
 // Type U needs to outlive lifetime 'b
+#[derive(Clone, Copy)]
 union Bar<'b, U: Copy> {
     field2: &'b U
 }
diff --git a/src/test/ui/rfc-2093-infer-outlives/nested-union.stderr b/src/test/ui/rfc-2093-infer-outlives/nested-union.stderr
index 0116a2a68ceb2..a785c63ce3d99 100644
--- a/src/test/ui/rfc-2093-infer-outlives/nested-union.stderr
+++ b/src/test/ui/rfc-2093-infer-outlives/nested-union.stderr
@@ -1,5 +1,5 @@
 error: rustc_outlives
-  --> $DIR/nested-union.rs:5:1
+  --> $DIR/nested-union.rs:4:1
    |
 LL | union Foo<'a, T: Copy> {
    | ^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/test/ui/union/field_checks.rs b/src/test/ui/union/field_checks.rs
new file mode 100644
index 0000000000000..d5d1e44ac855c
--- /dev/null
+++ b/src/test/ui/union/field_checks.rs
@@ -0,0 +1,65 @@
+use std::mem::ManuallyDrop;
+
+union U1 { // OK
+    a: u8,
+}
+
+union U2<T: Copy> { // OK
+    a: T,
+}
+
+union U22<T> { // OK
+    a: ManuallyDrop<T>,
+}
+
+union U23<T> { // OK
+    a: (ManuallyDrop<T>, i32),
+}
+
+union U24<T> { // OK
+    a: [ManuallyDrop<T>; 2],
+}
+
+union U3 {
+    a: String, //~ ERROR unions cannot contain fields that may need dropping
+}
+
+union U32 { // field that does not drop but is not `Copy`, either
+    a: std::cell::RefCell<i32>, //~ ERROR unions cannot contain fields that may need dropping
+}
+
+union U4<T> {
+    a: T, //~ ERROR unions cannot contain fields that may need dropping
+}
+
+union U5 { // Having a drop impl is OK
+    a: u8,
+}
+
+impl Drop for U5 {
+    fn drop(&mut self) {}
+}
+
+union U5Nested { // a nested union that drops is NOT OK
+    nest: U5, //~ ERROR unions cannot contain fields that may need dropping
+}
+
+union U5Nested2 { // for now we don't special-case empty arrays
+    nest: [U5; 0], //~ ERROR unions cannot contain fields that may need dropping
+}
+
+union U6 { // OK
+    s: &'static i32,
+    m: &'static mut i32,
+}
+
+union U7<T> { // OK
+    f: (&'static mut i32, ManuallyDrop<T>, i32),
+}
+
+union U8<T> { // OK
+    f1: [(&'static mut i32, i32); 8],
+    f2: [ManuallyDrop<T>; 2],
+}
+
+fn main() {}
diff --git a/src/test/ui/union/field_checks.stderr b/src/test/ui/union/field_checks.stderr
new file mode 100644
index 0000000000000..1f97e97ac6ede
--- /dev/null
+++ b/src/test/ui/union/field_checks.stderr
@@ -0,0 +1,63 @@
+error[E0740]: unions cannot contain fields that may need dropping
+  --> $DIR/field_checks.rs:24:5
+   |
+LL |     a: String,
+   |     ^^^^^^^^^
+   |
+   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   |
+LL |     a: std::mem::ManuallyDrop<String>,
+   |        +++++++++++++++++++++++      +
+
+error[E0740]: unions cannot contain fields that may need dropping
+  --> $DIR/field_checks.rs:28:5
+   |
+LL |     a: std::cell::RefCell<i32>,
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   |
+LL |     a: std::mem::ManuallyDrop<std::cell::RefCell<i32>>,
+   |        +++++++++++++++++++++++                       +
+
+error[E0740]: unions cannot contain fields that may need dropping
+  --> $DIR/field_checks.rs:32:5
+   |
+LL |     a: T,
+   |     ^^^^
+   |
+   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   |
+LL |     a: std::mem::ManuallyDrop<T>,
+   |        +++++++++++++++++++++++ +
+
+error[E0740]: unions cannot contain fields that may need dropping
+  --> $DIR/field_checks.rs:44:5
+   |
+LL |     nest: U5,
+   |     ^^^^^^^^
+   |
+   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   |
+LL |     nest: std::mem::ManuallyDrop<U5>,
+   |           +++++++++++++++++++++++  +
+
+error[E0740]: unions cannot contain fields that may need dropping
+  --> $DIR/field_checks.rs:48:5
+   |
+LL |     nest: [U5; 0],
+   |     ^^^^^^^^^^^^^
+   |
+   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
+   |
+LL |     nest: std::mem::ManuallyDrop<[U5; 0]>,
+   |           +++++++++++++++++++++++       +
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0740`.
diff --git a/src/test/ui/union/issue-41073.rs b/src/test/ui/union/issue-41073.rs
index 80474b807e7f3..4dfdc606bb409 100644
--- a/src/test/ui/union/issue-41073.rs
+++ b/src/test/ui/union/issue-41073.rs
@@ -1,5 +1,3 @@
-#![feature(untagged_unions)]
-
 union Test {
     a: A, //~ ERROR unions cannot contain fields that may need dropping
     b: B
diff --git a/src/test/ui/union/issue-41073.stderr b/src/test/ui/union/issue-41073.stderr
index 7d4208b10da80..b3887fa0f90be 100644
--- a/src/test/ui/union/issue-41073.stderr
+++ b/src/test/ui/union/issue-41073.stderr
@@ -1,5 +1,5 @@
 error[E0740]: unions cannot contain fields that may need dropping
-  --> $DIR/issue-41073.rs:4:5
+  --> $DIR/issue-41073.rs:2:5
    |
 LL |     a: A,
    |     ^^^^
diff --git a/src/test/ui/union/union-borrow-move-parent-sibling.mirunsafeck.stderr b/src/test/ui/union/union-borrow-move-parent-sibling.mirunsafeck.stderr
index e785a2ee7335d..ca02de4c61bb8 100644
--- a/src/test/ui/union/union-borrow-move-parent-sibling.mirunsafeck.stderr
+++ b/src/test/ui/union/union-borrow-move-parent-sibling.mirunsafeck.stderr
@@ -1,49 +1,69 @@
-error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0`)
+error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x`)
   --> $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 a = &mut (*u.x).0;
+   |                    --- mutable borrow occurs here (via `u.x`)
 LL |     let b = &u.y;
-   |             ^^^^ immutable borrow of `u.y` -- which overlaps with `u.x.0` -- occurs here
+   |             ^^^^ immutable borrow of `u.y` -- which overlaps with `u.x` -- 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`
+   = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x`
+
+error[E0507]: cannot move out of dereference of `ManuallyDrop<((MockVec<u8>, MockVec<u8>), MockVec<u8>)>`
+  --> $DIR/union-borrow-move-parent-sibling.rs:62:13
+   |
+LL |     let a = u.x.0;
+   |             ^^^^^
+   |             |
+   |             move occurs because value has type `(MockVec<u8>, MockVec<u8>)`, which does not implement the `Copy` trait
+   |             help: consider borrowing here: `&u.x.0`
 
 error[E0382]: use of moved value: `u`
-  --> $DIR/union-borrow-move-parent-sibling.rs:63:13
+  --> $DIR/union-borrow-move-parent-sibling.rs:64:13
    |
-LL |     let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
+LL |     let u = U { x: ManuallyDrop::new(((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 a = u.x;
+   |             --- 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
+error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x`)
+  --> $DIR/union-borrow-move-parent-sibling.rs:70:13
    |
-LL |     let a = &mut (u.x.0).0;
-   |             -------------- mutable borrow occurs here (via `u.x.0.0`)
+LL |     let a = &mut ((*u.x).0).0;
+   |                     --- mutable borrow occurs here (via `u.x`)
 LL |     let b = &u.y;
-   |             ^^^^ immutable borrow of `u.y` -- which overlaps with `u.x.0.0` -- occurs here
+   |             ^^^^ immutable borrow of `u.y` -- which overlaps with `u.x` -- 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`
+   = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x`
 
-error[E0382]: use of moved value: `u`
+error[E0507]: cannot move out of dereference of `ManuallyDrop<((MockVec<u8>, MockVec<u8>), MockVec<u8>)>`
   --> $DIR/union-borrow-move-parent-sibling.rs:76:13
    |
-LL |     let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
+LL |     let a = (u.x.0).0;
+   |             ^^^^^^^^^
+   |             |
+   |             move occurs because value has type `MockVec<u8>`, which does not implement the `Copy` trait
+   |             help: consider borrowing here: `&(u.x.0).0`
+
+error[E0382]: use of moved value: `u`
+  --> $DIR/union-borrow-move-parent-sibling.rs:78:13
+   |
+LL |     let u = U { x: ManuallyDrop::new(((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 a = u.x;
+   |             --- 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
+  --> $DIR/union-borrow-move-parent-sibling.rs:84:13
    |
 LL |     let a = &mut *u.y;
    |                   --- mutable borrow occurs here (via `u.y`)
@@ -54,7 +74,7 @@ LL |     use_borrow(a);
    |
    = note: `u.x` is a field of the union `U`, so it overlaps the field `u.y`
 
-error: aborting due to 5 previous errors
+error: aborting due to 7 previous errors
 
-Some errors have detailed explanations: E0382, E0502.
+Some errors have detailed explanations: E0382, E0502, E0507.
 For more information about an error, try `rustc --explain E0382`.
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 e56d87255dbaf..83781c5e55092 100644
--- a/src/test/ui/union/union-borrow-move-parent-sibling.rs
+++ b/src/test/ui/union/union-borrow-move-parent-sibling.rs
@@ -1,10 +1,10 @@
 // revisions: mirunsafeck thirunsafeck
 // [thirunsafeck]compile-flags: -Z thir-unsafeck
 
-#![feature(untagged_unions)]
 #![allow(unused)]
 
 use std::ops::{Deref, DerefMut};
+use std::mem::ManuallyDrop;
 
 #[derive(Default)]
 struct MockBox<T> {
@@ -44,47 +44,49 @@ impl<T> DerefMut for MockVec<T> {
 
 
 union U {
-    x: ((MockVec<u8>, MockVec<u8>), MockVec<u8>),
-    y: MockBox<MockVec<u8>>,
+    x: ManuallyDrop<((MockVec<u8>, MockVec<u8>), MockVec<u8>)>,
+    y: ManuallyDrop<MockBox<MockVec<u8>>>,
 }
 
 fn use_borrow<T>(_: &T) {}
 
 unsafe fn parent_sibling_borrow() {
-    let mut u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
-    let a = &mut u.x.0;
+    let mut u = U { x: ManuallyDrop::new(((MockVec::new(), MockVec::new()), MockVec::new())) };
+    let a = &mut (*u.x).0;
     let b = &u.y; //~ ERROR cannot borrow `u` (via `u.y`)
     use_borrow(a);
 }
 
 unsafe fn parent_sibling_move() {
-    let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
-    let a = u.x.0;
+    let u = U { x: ManuallyDrop::new(((MockVec::new(), MockVec::new()), MockVec::new())) };
+    let a = u.x.0; //~ERROR cannot move out of dereference
+    let a = u.x;
     let b = u.y; //~ ERROR use of moved value: `u`
 }
 
 unsafe fn grandparent_sibling_borrow() {
-    let mut u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
-    let a = &mut (u.x.0).0;
+    let mut u = U { x: ManuallyDrop::new(((MockVec::new(), MockVec::new()), MockVec::new())) };
+    let a = &mut ((*u.x).0).0;
     let b = &u.y; //~ ERROR cannot borrow `u` (via `u.y`)
     use_borrow(a);
 }
 
 unsafe fn grandparent_sibling_move() {
-    let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
-    let a = (u.x.0).0;
+    let u = U { x: ManuallyDrop::new(((MockVec::new(), MockVec::new()), MockVec::new())) };
+    let a = (u.x.0).0; //~ERROR cannot move out of dereference
+    let a = u.x;
     let b = u.y; //~ ERROR use of moved value: `u`
 }
 
 unsafe fn deref_sibling_borrow() {
-    let mut u = U { y: MockBox::default() };
+    let mut u = U { y: ManuallyDrop::new(MockBox::default()) };
     let a = &mut *u.y;
     let b = &u.x; //~ ERROR cannot borrow `u` (via `u.x`)
     use_borrow(a);
 }
 
 unsafe fn deref_sibling_move() {
-    let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
+    let u = U { x: ManuallyDrop::new(((MockVec::new(), MockVec::new()), MockVec::new())) };
     // No way to test deref-move without Box in union
     // let a = *u.y;
     // let b = u.x; ERROR use of moved value: `u`
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
index e785a2ee7335d..ca02de4c61bb8 100644
--- a/src/test/ui/union/union-borrow-move-parent-sibling.thirunsafeck.stderr
+++ b/src/test/ui/union/union-borrow-move-parent-sibling.thirunsafeck.stderr
@@ -1,49 +1,69 @@
-error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0`)
+error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x`)
   --> $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 a = &mut (*u.x).0;
+   |                    --- mutable borrow occurs here (via `u.x`)
 LL |     let b = &u.y;
-   |             ^^^^ immutable borrow of `u.y` -- which overlaps with `u.x.0` -- occurs here
+   |             ^^^^ immutable borrow of `u.y` -- which overlaps with `u.x` -- 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`
+   = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x`
+
+error[E0507]: cannot move out of dereference of `ManuallyDrop<((MockVec<u8>, MockVec<u8>), MockVec<u8>)>`
+  --> $DIR/union-borrow-move-parent-sibling.rs:62:13
+   |
+LL |     let a = u.x.0;
+   |             ^^^^^
+   |             |
+   |             move occurs because value has type `(MockVec<u8>, MockVec<u8>)`, which does not implement the `Copy` trait
+   |             help: consider borrowing here: `&u.x.0`
 
 error[E0382]: use of moved value: `u`
-  --> $DIR/union-borrow-move-parent-sibling.rs:63:13
+  --> $DIR/union-borrow-move-parent-sibling.rs:64:13
    |
-LL |     let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
+LL |     let u = U { x: ManuallyDrop::new(((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 a = u.x;
+   |             --- 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
+error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x`)
+  --> $DIR/union-borrow-move-parent-sibling.rs:70:13
    |
-LL |     let a = &mut (u.x.0).0;
-   |             -------------- mutable borrow occurs here (via `u.x.0.0`)
+LL |     let a = &mut ((*u.x).0).0;
+   |                     --- mutable borrow occurs here (via `u.x`)
 LL |     let b = &u.y;
-   |             ^^^^ immutable borrow of `u.y` -- which overlaps with `u.x.0.0` -- occurs here
+   |             ^^^^ immutable borrow of `u.y` -- which overlaps with `u.x` -- 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`
+   = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x`
 
-error[E0382]: use of moved value: `u`
+error[E0507]: cannot move out of dereference of `ManuallyDrop<((MockVec<u8>, MockVec<u8>), MockVec<u8>)>`
   --> $DIR/union-borrow-move-parent-sibling.rs:76:13
    |
-LL |     let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
+LL |     let a = (u.x.0).0;
+   |             ^^^^^^^^^
+   |             |
+   |             move occurs because value has type `MockVec<u8>`, which does not implement the `Copy` trait
+   |             help: consider borrowing here: `&(u.x.0).0`
+
+error[E0382]: use of moved value: `u`
+  --> $DIR/union-borrow-move-parent-sibling.rs:78:13
+   |
+LL |     let u = U { x: ManuallyDrop::new(((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 a = u.x;
+   |             --- 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
+  --> $DIR/union-borrow-move-parent-sibling.rs:84:13
    |
 LL |     let a = &mut *u.y;
    |                   --- mutable borrow occurs here (via `u.y`)
@@ -54,7 +74,7 @@ LL |     use_borrow(a);
    |
    = note: `u.x` is a field of the union `U`, so it overlaps the field `u.y`
 
-error: aborting due to 5 previous errors
+error: aborting due to 7 previous errors
 
-Some errors have detailed explanations: E0382, E0502.
+Some errors have detailed explanations: E0382, E0502, E0507.
 For more information about an error, try `rustc --explain E0382`.
diff --git a/src/test/ui/union/union-custom-drop.rs b/src/test/ui/union/union-custom-drop.rs
deleted file mode 100644
index 4b333631ec0f7..0000000000000
--- a/src/test/ui/union/union-custom-drop.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-// test for a union with a field that's a union with a manual impl Drop
-// Ensures we do not treat all unions as not having any drop glue.
-
-#![feature(untagged_unions)]
-
-union Foo {
-    bar: Bar, //~ ERROR unions cannot contain fields that may need dropping
-}
-
-union Bar {
-    a: i32,
-    b: u32,
-}
-
-impl Drop for Bar {
-    fn drop(&mut self) {}
-}
-
-fn main() {}
diff --git a/src/test/ui/union/union-custom-drop.stderr b/src/test/ui/union/union-custom-drop.stderr
deleted file mode 100644
index b5579eeef0977..0000000000000
--- a/src/test/ui/union/union-custom-drop.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error[E0740]: unions cannot contain fields that may need dropping
-  --> $DIR/union-custom-drop.rs:7:5
-   |
-LL |     bar: Bar,
-   |     ^^^^^^^^
-   |
-   = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
-help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
-   |
-LL |     bar: std::mem::ManuallyDrop<Bar>,
-   |          +++++++++++++++++++++++   +
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0740`.
diff --git a/src/test/ui/union/union-deref.mirunsafeck.stderr b/src/test/ui/union/union-deref.mirunsafeck.stderr
index ff37e6fd9177a..be5e60ab88a59 100644
--- a/src/test/ui/union/union-deref.mirunsafeck.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:17:14
+  --> $DIR/union-deref.rs:16: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:19:19
+  --> $DIR/union-deref.rs:18: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:21:14
+  --> $DIR/union-deref.rs:20: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:25:14
+  --> $DIR/union-deref.rs:24: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:27:19
+  --> $DIR/union-deref.rs:26: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:29:14
+  --> $DIR/union-deref.rs:28: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 4bf2ba2f1bfce..5aa28d93f96ed 100644
--- a/src/test/ui/union/union-deref.rs
+++ b/src/test/ui/union/union-deref.rs
@@ -3,7 +3,6 @@
 
 //! Test the part of RFC 2514 that is about not applying `DerefMut` coercions
 //! of union fields.
-#![feature(untagged_unions)]
 
 use std::mem::ManuallyDrop;
 
diff --git a/src/test/ui/union/union-deref.thirunsafeck.stderr b/src/test/ui/union/union-deref.thirunsafeck.stderr
index ff37e6fd9177a..be5e60ab88a59 100644
--- a/src/test/ui/union/union-deref.thirunsafeck.stderr
+++ b/src/test/ui/union/union-deref.thirunsafeck.stderr
@@ -1,5 +1,5 @@
 error: not automatically applying `DerefMut` on `ManuallyDrop` union field
-  --> $DIR/union-deref.rs:17:14
+  --> $DIR/union-deref.rs:16: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:19:19
+  --> $DIR/union-deref.rs:18: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:21:14
+  --> $DIR/union-deref.rs:20: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:25:14
+  --> $DIR/union-deref.rs:24: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:27:19
+  --> $DIR/union-deref.rs:26: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:29:14
+  --> $DIR/union-deref.rs:28:14
    |
 LL |     unsafe { u.f.0.0.push(0) };
    |              ^^^^^
diff --git a/src/test/ui/union/union-move.mirunsafeck.stderr b/src/test/ui/union/union-move.mirunsafeck.stderr
index f55fbea6336e3..53050cf539eaf 100644
--- a/src/test/ui/union/union-move.mirunsafeck.stderr
+++ b/src/test/ui/union/union-move.mirunsafeck.stderr
@@ -27,7 +27,7 @@ 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
+   |                  move occurs because `x.f1_nocopy` has type `ManuallyDrop<RefCell<i32>>`, which does not implement the `Copy` trait
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/union/union-move.rs b/src/test/ui/union/union-move.rs
index 8f78c30d67a55..b8b1ac8046a03 100644
--- a/src/test/ui/union/union-move.rs
+++ b/src/test/ui/union/union-move.rs
@@ -3,20 +3,20 @@
 
 //! Test the behavior of moving out of non-`Copy` union fields.
 //! Avoid types that `Drop`, we want to focus on moving.
-#![feature(untagged_unions)]
 
 use std::cell::RefCell;
+use std::mem::ManuallyDrop;
 
 fn move_out<T>(x: T) {}
 
 union U1 {
-    f1_nocopy: RefCell<i32>,
-    f2_nocopy: RefCell<i32>,
+    f1_nocopy: ManuallyDrop<RefCell<i32>>,
+    f2_nocopy: ManuallyDrop<RefCell<i32>>,
     f3_copy: i32,
 }
 
 union U2 {
-    f1_nocopy: RefCell<i32>,
+    f1_nocopy: ManuallyDrop<RefCell<i32>>,
 }
 impl Drop for U2 {
     fn drop(&mut self) {}
diff --git a/src/test/ui/union/union-move.thirunsafeck.stderr b/src/test/ui/union/union-move.thirunsafeck.stderr
index f55fbea6336e3..53050cf539eaf 100644
--- a/src/test/ui/union/union-move.thirunsafeck.stderr
+++ b/src/test/ui/union/union-move.thirunsafeck.stderr
@@ -27,7 +27,7 @@ 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
+   |                  move occurs because `x.f1_nocopy` has type `ManuallyDrop<RefCell<i32>>`, which does not implement the `Copy` trait
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/union/union-nonrepresentable.rs b/src/test/ui/union/union-nonrepresentable.rs
index 4dbd97ea957ba..4bdf7c6872fa5 100644
--- a/src/test/ui/union/union-nonrepresentable.rs
+++ b/src/test/ui/union/union-nonrepresentable.rs
@@ -1,8 +1,6 @@
-#![feature(untagged_unions)]
-
 union U { //~ ERROR recursive type `U` has infinite size
     a: u8,
-    b: U,
+    b: std::mem::ManuallyDrop<U>,
 }
 
 fn main() {}
diff --git a/src/test/ui/union/union-nonrepresentable.stderr b/src/test/ui/union/union-nonrepresentable.stderr
index 7da7c870e704b..9804b1418b208 100644
--- a/src/test/ui/union/union-nonrepresentable.stderr
+++ b/src/test/ui/union/union-nonrepresentable.stderr
@@ -1,16 +1,16 @@
 error[E0072]: recursive type `U` has infinite size
-  --> $DIR/union-nonrepresentable.rs:3:1
+  --> $DIR/union-nonrepresentable.rs:1:1
    |
 LL | union U {
    | ^^^^^^^ recursive type has infinite size
 LL |     a: u8,
-LL |     b: U,
-   |        - recursive without indirection
+LL |     b: std::mem::ManuallyDrop<U>,
+   |        ------------------------- recursive without indirection
    |
 help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `U` representable
    |
-LL |     b: Box<U>,
-   |        ++++ +
+LL |     b: Box<std::mem::ManuallyDrop<U>>,
+   |        ++++                         +
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/union/union-sized-field.rs b/src/test/ui/union/union-sized-field.rs
index b84cb3eff56f6..cb852eff0c60a 100644
--- a/src/test/ui/union/union-sized-field.rs
+++ b/src/test/ui/union/union-sized-field.rs
@@ -1,18 +1,18 @@
-#![feature(untagged_unions)]
+use std::mem::ManuallyDrop;
 
 union Foo<T: ?Sized> {
-    value: T,
+    value: ManuallyDrop<T>,
     //~^ ERROR the size for values of type
 }
 
 struct Foo2<T: ?Sized> {
-    value: T,
+    value: ManuallyDrop<T>,
     //~^ ERROR the size for values of type
     t: u32,
 }
 
 enum Foo3<T: ?Sized> {
-    Value(T),
+    Value(ManuallyDrop<T>),
     //~^ ERROR the size for values of type
 }
 
diff --git a/src/test/ui/union/union-sized-field.stderr b/src/test/ui/union/union-sized-field.stderr
index 3fe6e71f3b863..771e8f2619995 100644
--- a/src/test/ui/union/union-sized-field.stderr
+++ b/src/test/ui/union/union-sized-field.stderr
@@ -3,9 +3,10 @@ error[E0277]: the size for values of type `T` cannot be known at compilation tim
    |
 LL | union Foo<T: ?Sized> {
    |           - this type parameter needs to be `std::marker::Sized`
-LL |     value: T,
-   |            ^ doesn't have a size known at compile-time
+LL |     value: ManuallyDrop<T>,
+   |            ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
+   = note: required because it appears within the type `ManuallyDrop<T>`
    = 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: consider removing the `?Sized` bound to make the type parameter `Sized`
@@ -15,21 +16,22 @@ LL + union Foo<T> {
    |
 help: borrowed types always have a statically known size
    |
-LL |     value: &T,
+LL |     value: &ManuallyDrop<T>,
    |            +
 help: the `Box` type always has a statically known size and allocates its contents in the heap
    |
-LL |     value: Box<T>,
-   |            ++++ +
+LL |     value: Box<ManuallyDrop<T>>,
+   |            ++++               +
 
 error[E0277]: the size for values of type `T` cannot be known at compilation time
   --> $DIR/union-sized-field.rs:9:12
    |
 LL | struct Foo2<T: ?Sized> {
    |             - this type parameter needs to be `std::marker::Sized`
-LL |     value: T,
-   |            ^ doesn't have a size known at compile-time
+LL |     value: ManuallyDrop<T>,
+   |            ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
+   = note: required because it appears within the type `ManuallyDrop<T>`
    = note: only the last field of a struct may have a dynamically sized type
    = help: change the field's type to have a statically known size
 help: consider removing the `?Sized` bound to make the type parameter `Sized`
@@ -39,21 +41,22 @@ LL + struct Foo2<T> {
    |
 help: borrowed types always have a statically known size
    |
-LL |     value: &T,
+LL |     value: &ManuallyDrop<T>,
    |            +
 help: the `Box` type always has a statically known size and allocates its contents in the heap
    |
-LL |     value: Box<T>,
-   |            ++++ +
+LL |     value: Box<ManuallyDrop<T>>,
+   |            ++++               +
 
 error[E0277]: the size for values of type `T` cannot be known at compilation time
   --> $DIR/union-sized-field.rs:15:11
    |
 LL | enum Foo3<T: ?Sized> {
    |           - this type parameter needs to be `std::marker::Sized`
-LL |     Value(T),
-   |           ^ doesn't have a size known at compile-time
+LL |     Value(ManuallyDrop<T>),
+   |           ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
+   = note: required because it appears within the type `ManuallyDrop<T>`
    = note: no field of an enum variant may have a dynamically sized type
    = help: change the field's type to have a statically known size
 help: consider removing the `?Sized` bound to make the type parameter `Sized`
@@ -63,12 +66,12 @@ LL + enum Foo3<T> {
    |
 help: borrowed types always have a statically known size
    |
-LL |     Value(&T),
+LL |     Value(&ManuallyDrop<T>),
    |           +
 help: the `Box` type always has a statically known size and allocates its contents in the heap
    |
-LL |     Value(Box<T>),
-   |           ++++ +
+LL |     Value(Box<ManuallyDrop<T>>),
+   |           ++++               +
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/union/union-unsafe.mir.stderr b/src/test/ui/union/union-unsafe.mir.stderr
index 318b00ddea94e..544213dbc5543 100644
--- a/src/test/ui/union/union-unsafe.mir.stderr
+++ b/src/test/ui/union/union-unsafe.mir.stderr
@@ -1,29 +1,13 @@
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:34:5
+  --> $DIR/union-unsafe.rs:33:5
    |
 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]: assignment to union field that might need dropping is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:39:5
-   |
-LL |     u.a = (RefCell::new(0), 1);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to union field that might need dropping
-   |
-   = 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:40:5
-   |
-LL |     u.a.0 = RefCell::new(0);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^ assignment to union field that might need dropping
-   |
-   = 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:47:6
+  --> $DIR/union-unsafe.rs:46:6
    |
 LL |     *u3.a = T::default();
    |      ^^^^ access to union field
@@ -31,7 +15,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:53:6
+  --> $DIR/union-unsafe.rs:52:6
    |
 LL |     *u3.a = T::default();
    |      ^^^^ access to union field
@@ -39,7 +23,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:61:13
+  --> $DIR/union-unsafe.rs:60:13
    |
 LL |     let a = u1.a;
    |             ^^^^ access to union field
@@ -47,7 +31,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:64:14
+  --> $DIR/union-unsafe.rs:63:14
    |
 LL |     let U1 { a } = u1;
    |              ^ access to union field
@@ -55,7 +39,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:65:12
+  --> $DIR/union-unsafe.rs:64:12
    |
 LL |     if let U1 { a: 12 } = u1 {}
    |            ^^^^^^^^^^^^ access to union field
@@ -63,7 +47,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:70:6
+  --> $DIR/union-unsafe.rs:69:6
    |
 LL |     *u2.a = String::from("new");
    |      ^^^^ access to union field
@@ -71,7 +55,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:74:6
+  --> $DIR/union-unsafe.rs:73:6
    |
 LL |     *u3.a = 1;
    |      ^^^^ access to union field
@@ -79,13 +63,13 @@ 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:78:6
+  --> $DIR/union-unsafe.rs:77: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 11 previous errors
+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-unsafe.rs b/src/test/ui/union/union-unsafe.rs
index 3cb3a18cb7544..5e1837a901d46 100644
--- a/src/test/ui/union/union-unsafe.rs
+++ b/src/test/ui/union/union-unsafe.rs
@@ -1,7 +1,6 @@
 // revisions: mir thir
 // [thir]compile-flags: -Z thir-unsafeck
 
-#![feature(untagged_unions)]
 use std::mem::ManuallyDrop;
 use std::cell::RefCell;
 
@@ -26,7 +25,7 @@ union URef {
 }
 
 union URefCell { // field that does not drop but is not `Copy`, either
-    a: (RefCell<i32>, i32),
+    a: (ManuallyDrop<RefCell<i32>>, i32),
 }
 
 fn deref_union_field(mut u: URef) {
@@ -36,8 +35,8 @@ fn deref_union_field(mut u: URef) {
 
 fn assign_noncopy_union_field(mut u: URefCell) {
     // FIXME(thir-unsafeck)
-    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
+    u.a = (ManuallyDrop::new(RefCell::new(0)), 1); // OK (assignment does not drop)
+    u.a.0 = ManuallyDrop::new(RefCell::new(0)); // OK (assignment does not drop)
     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
index a8c3886657f43..f959fe5bdb5c5 100644
--- a/src/test/ui/union/union-unsafe.thir.stderr
+++ b/src/test/ui/union/union-unsafe.thir.stderr
@@ -1,29 +1,13 @@
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:34:6
+  --> $DIR/union-unsafe.rs:33: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]: assignment to union field that might need dropping is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:39:5
-   |
-LL |     u.a = (RefCell::new(0), 1);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to union field that might need dropping
-   |
-   = 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:40:5
-   |
-LL |     u.a.0 = RefCell::new(0);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^ assignment to union field that might need dropping
-   |
-   = 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:47:6
+  --> $DIR/union-unsafe.rs:46:6
    |
 LL |     *u3.a = T::default();
    |      ^^^^ access to union field
@@ -31,7 +15,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:53:6
+  --> $DIR/union-unsafe.rs:52:6
    |
 LL |     *u3.a = T::default();
    |      ^^^^ access to union field
@@ -39,7 +23,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:61:13
+  --> $DIR/union-unsafe.rs:60:13
    |
 LL |     let a = u1.a;
    |             ^^^^ access to union field
@@ -47,7 +31,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:64:14
+  --> $DIR/union-unsafe.rs:63:14
    |
 LL |     let U1 { a } = u1;
    |              ^ access to union field
@@ -55,7 +39,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:65:8
+  --> $DIR/union-unsafe.rs:64:8
    |
 LL |     if let U1 { a: 12 } = u1 {}
    |        ^^^^^^^^^^^^^^^^^^^^^ access to union field
@@ -63,7 +47,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:70:6
+  --> $DIR/union-unsafe.rs:69:6
    |
 LL |     *u2.a = String::from("new");
    |      ^^^^ access to union field
@@ -71,7 +55,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:74:6
+  --> $DIR/union-unsafe.rs:73:6
    |
 LL |     *u3.a = 1;
    |      ^^^^ access to union field
@@ -79,13 +63,13 @@ 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:78:6
+  --> $DIR/union-unsafe.rs:77: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 11 previous errors
+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.mirunsafeck.stderr b/src/test/ui/union/union-unsized.mirunsafeck.stderr
index 36e782ac0424d..59ab835fba22d 100644
--- a/src/test/ui/union/union-unsized.mirunsafeck.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:7:8
+  --> $DIR/union-unsized.rs:5: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:15:8
+  --> $DIR/union-unsized.rs:13: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 e9792f527dc71..8e897d7d3c6d6 100644
--- a/src/test/ui/union/union-unsized.rs
+++ b/src/test/ui/union/union-unsized.rs
@@ -1,8 +1,6 @@
 // revisions: mirunsafeck thirunsafeck
 // [thirunsafeck]compile-flags: -Z thir-unsafeck
 
-#![feature(untagged_unions)]
-
 union U {
     a: str,
     //~^ ERROR the size for values of type
diff --git a/src/test/ui/union/union-unsized.thirunsafeck.stderr b/src/test/ui/union/union-unsized.thirunsafeck.stderr
index 36e782ac0424d..59ab835fba22d 100644
--- a/src/test/ui/union/union-unsized.thirunsafeck.stderr
+++ b/src/test/ui/union/union-unsized.thirunsafeck.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the size for values of type `str` cannot be known at compilation time
-  --> $DIR/union-unsized.rs:7:8
+  --> $DIR/union-unsized.rs:5: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:15:8
+  --> $DIR/union-unsized.rs:13:8
    |
 LL |     b: str,
    |        ^^^ doesn't have a size known at compile-time
diff --git a/src/test/ui/unsafe/union-assignop.mirunsafeck.stderr b/src/test/ui/unsafe/union-assignop.mirunsafeck.stderr
index cd338ac9e3a27..0ecd5203dd9d9 100644
--- a/src/test/ui/unsafe/union-assignop.mirunsafeck.stderr
+++ b/src/test/ui/unsafe/union-assignop.mirunsafeck.stderr
@@ -1,5 +1,5 @@
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/union-assignop.rs:20:5
+  --> $DIR/union-assignop.rs:19:5
    |
 LL |     foo.a += 5;
    |     ^^^^^^^^^^ access to union field
@@ -7,20 +7,20 @@ LL |     foo.a += 5;
    = 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-assignop.rs:21:5
+  --> $DIR/union-assignop.rs:20:6
    |
-LL |     foo.b += Dropping;
-   |     ^^^^^ access to union field
+LL |     *foo.b += NonCopy;
+   |      ^^^^^ access to union field
    |
    = 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-assignop.rs:22:5
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-assignop.rs:21:6
    |
-LL |     foo.b = Dropping;
-   |     ^^^^^^^^^^^^^^^^ assignment to union field that might need dropping
+LL |     *foo.b = NonCopy;
+   |      ^^^^^ access to union field
    |
-   = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
+   = 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-assignop.rs:23:5
@@ -46,14 +46,6 @@ LL |     foo.b = foo.b;
    |
    = 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-assignop.rs:27:5
-   |
-LL |     foo.b = foo.b;
-   |     ^^^^^^^^^^^^^ assignment to union field that might need dropping
-   |
-   = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
-
-error: aborting due to 7 previous errors
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/unsafe/union-assignop.rs b/src/test/ui/unsafe/union-assignop.rs
index c4be20aa567b7..5e667cd10d59f 100644
--- a/src/test/ui/unsafe/union-assignop.rs
+++ b/src/test/ui/unsafe/union-assignop.rs
@@ -1,30 +1,29 @@
 // revisions: mirunsafeck thirunsafeck
 // [thirunsafeck]compile-flags: -Z thir-unsafeck
 
-#![feature(untagged_unions)]
-
 use std::ops::AddAssign;
+use std::mem::ManuallyDrop;
 
-struct Dropping;
-impl AddAssign for Dropping {
+struct NonCopy;
+impl AddAssign for NonCopy {
     fn add_assign(&mut self, _: Self) {}
 }
 
 union Foo {
     a: u8, // non-dropping
-    b: Dropping, // treated as dropping
+    b: ManuallyDrop<NonCopy>,
 }
 
 fn main() {
     let mut foo = Foo { a: 42 };
     foo.a += 5; //~ ERROR access to union field is unsafe
-    foo.b += Dropping; //~ ERROR access to union field is unsafe
-    foo.b = Dropping; //~ ERROR assignment to union field that might need dropping is unsafe
+    *foo.b += NonCopy; //~ ERROR access to union field is unsafe
+    *foo.b = NonCopy; //~ ERROR access to union field is unsafe
+    foo.b = ManuallyDrop::new(NonCopy);
     foo.a; //~ ERROR access to union field is unsafe
     let foo = Foo { a: 42 };
     foo.b; //~ ERROR access to union field is unsafe
     let mut foo = Foo { a: 42 };
     foo.b = foo.b;
     //~^ ERROR access to union field is unsafe
-    //~| ERROR assignment to union field that might need dropping
 }
diff --git a/src/test/ui/unsafe/union-assignop.thirunsafeck.stderr b/src/test/ui/unsafe/union-assignop.thirunsafeck.stderr
index 71de421a2553e..24b357e762bba 100644
--- a/src/test/ui/unsafe/union-assignop.thirunsafeck.stderr
+++ b/src/test/ui/unsafe/union-assignop.thirunsafeck.stderr
@@ -1,5 +1,5 @@
 error[E0133]: access to union field is unsafe and requires unsafe function or block
-  --> $DIR/union-assignop.rs:20:5
+  --> $DIR/union-assignop.rs:19:5
    |
 LL |     foo.a += 5;
    |     ^^^^^ access to union field
@@ -7,20 +7,20 @@ LL |     foo.a += 5;
    = 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-assignop.rs:21:5
+  --> $DIR/union-assignop.rs:20:6
    |
-LL |     foo.b += Dropping;
-   |     ^^^^^ access to union field
+LL |     *foo.b += NonCopy;
+   |      ^^^^^ access to union field
    |
    = 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-assignop.rs:22:5
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-assignop.rs:21:6
    |
-LL |     foo.b = Dropping;
-   |     ^^^^^^^^^^^^^^^^ assignment to union field that might need dropping
+LL |     *foo.b = NonCopy;
+   |      ^^^^^ access to union field
    |
-   = note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
+   = 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-assignop.rs:23:5
@@ -38,14 +38,6 @@ LL |     foo.b;
    |
    = 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-assignop.rs:27:5
-   |
-LL |     foo.b = foo.b;
-   |     ^^^^^^^^^^^^^ assignment to union field that might need dropping
-   |
-   = 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-assignop.rs:27:13
    |
@@ -54,6 +46,6 @@ LL |     foo.b = foo.b;
    |
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
-error: aborting due to 7 previous errors
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/unsafe/union-modification.rs b/src/test/ui/unsafe/union-modification.rs
index 5c70b78df7c12..9a53ef9085200 100644
--- a/src/test/ui/unsafe/union-modification.rs
+++ b/src/test/ui/unsafe/union-modification.rs
@@ -2,8 +2,6 @@
 // revisions: mir thir
 // [thir]compile-flags: -Z thir-unsafeck
 
-#![feature(untagged_unions)]
-
 union Foo {
     bar: i8,
     _blah: isize,
diff --git a/src/test/ui/unsafe/union.rs b/src/test/ui/unsafe/union.rs
index 5fe09933cfc48..4338d78eabb9d 100644
--- a/src/test/ui/unsafe/union.rs
+++ b/src/test/ui/unsafe/union.rs
@@ -1,19 +1,19 @@
 // revisions: mir thir
 // [thir]compile-flags: -Z thir-unsafeck
 
-#![feature(untagged_unions)]
-
 union Foo {
     bar: i8,
     zst: (),
     pizza: Pizza,
 }
 
+#[derive(Clone, Copy)]
 struct Pizza {
     topping: Option<PizzaTopping>
 }
 
 #[allow(dead_code)]
+#[derive(Clone, Copy)]
 enum PizzaTopping {
     Cheese,
     Pineapple,
diff --git a/src/tools/rust-analyzer b/src/tools/rust-analyzer
index 75b22326dad19..5342f47f42766 160000
--- a/src/tools/rust-analyzer
+++ b/src/tools/rust-analyzer
@@ -1 +1 @@
-Subproject commit 75b22326dad1914c22484ab6672de5cae94f7457
+Subproject commit 5342f47f4276641ddb5f0a5e08fb307742d6cdc4
diff --git a/triagebot.toml b/triagebot.toml
index ba9ed20cc64a2..8d9fab84ca13e 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -165,6 +165,19 @@ exclude_labels = [
     "T-*",
 ]
 
+[autolabel."A-bootstrap"]
+trigger_files = [
+    "x.py",
+    "src/bootstrap",
+    "src/tools/rust-installer",
+]
+
+[autolabel."T-infra"]
+trigger_files = [
+    "src/ci",
+    "src/tools/bump-stage0",
+]
+
 [notify-zulip."I-prioritize"]
 zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts
 topic = "#{number} {title}"