about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-10-21 22:00:45 +0200
committerGitHub <noreply@github.com>2019-10-21 22:00:45 +0200
commitaba84894d1879a7429d5bc20d0bfdca3471685de (patch)
tree5c11f19bd3b62b335a5a189f49b0ef2a6c3bc2f2 /src
parent10f12fe3e73f3b6f7e6d6f8bbd87b1a8b4e74a07 (diff)
parent875bdd5dbe663a6dafd785b86c8964a90653eeb7 (diff)
downloadrust-aba84894d1879a7429d5bc20d0bfdca3471685de.tar.gz
rust-aba84894d1879a7429d5bc20d0bfdca3471685de.zip
Rollup merge of #62330 - SimonSapin:no-drop-in-union-fields, r=RalfJung
Change untagged_unions to not allow union fields with drop

This is a rebase of #56440, massaged to solve merge conflicts and make the test suite pass.

Change untagged_unions to not allow union fields with drop

Union fields may now never have a type with attached destructor. This for example allows unions to use arbitrary field types only by wrapping them in `ManuallyDrop` (or similar).

The stable rule remains, that union fields must be `Copy`. We use the new rule for the `untagged_union` feature.

Tracking issue: https://github.com/rust-lang/rust/issues/55149
Diffstat (limited to 'src')
-rw-r--r--src/doc/rustc/src/lints/listing/warn-by-default.md24
-rw-r--r--src/librustc/Cargo.toml2
-rw-r--r--src/librustc/ty/util.rs2
-rw-r--r--src/librustc_lint/builtin.rs30
-rw-r--r--src/librustc_lint/lib.rs3
-rw-r--r--src/librustc_typeck/check/mod.rs28
-rw-r--r--src/librustc_typeck/error_codes.rs4
-rw-r--r--src/libstd/lib.rs1
-rw-r--r--src/libstd/panicking.rs17
-rw-r--r--src/test/ui/associated-type-bounds/union-bounds.rs15
-rw-r--r--src/test/ui/drop/dynamic-drop.rs12
-rw-r--r--src/test/ui/feature-gates/feature-gate-associated_type_bounds.rs6
-rw-r--r--src/test/ui/feature-gates/feature-gate-untagged_unions.rs4
-rw-r--r--src/test/ui/feature-gates/feature-gate-untagged_unions.stderr29
-rw-r--r--src/test/ui/rfc-2093-infer-outlives/explicit-union.rs5
-rw-r--r--src/test/ui/rfc-2093-infer-outlives/explicit-union.stderr4
-rw-r--r--src/test/ui/rfc-2093-infer-outlives/nested-union.rs5
-rw-r--r--src/test/ui/rfc-2093-infer-outlives/nested-union.stderr4
-rw-r--r--src/test/ui/self/self-in-typedefs.rs11
-rw-r--r--src/test/ui/union/issue-41073.rs24
-rw-r--r--src/test/ui/union/issue-41073.stderr15
-rw-r--r--src/test/ui/union/union-borrow-move-parent-sibling.rs61
-rw-r--r--src/test/ui/union/union-borrow-move-parent-sibling.stderr34
-rw-r--r--src/test/ui/union/union-custom-drop.rs19
-rw-r--r--src/test/ui/union/union-custom-drop.stderr15
-rw-r--r--src/test/ui/union/union-derive-clone.rs13
-rw-r--r--src/test/ui/union/union-derive-clone.stderr12
-rw-r--r--src/test/ui/union/union-derive-rpass.rs5
-rw-r--r--src/test/ui/union/union-drop-assign.rs11
-rw-r--r--src/test/ui/union/union-drop.rs11
-rw-r--r--src/test/ui/union/union-generic-rpass.rs18
-rw-r--r--src/test/ui/union/union-manuallydrop-rpass.rs42
-rw-r--r--src/test/ui/union/union-nodrop.rs15
-rw-r--r--src/test/ui/union/union-overwrite.rs14
-rw-r--r--src/test/ui/union/union-unsafe.rs34
-rw-r--r--src/test/ui/union/union-unsafe.stderr66
-rw-r--r--src/test/ui/union/union-with-drop-fields-lint-rpass.rs32
-rw-r--r--src/test/ui/union/union-with-drop-fields-lint.stderr26
-rw-r--r--src/test/ui/union/union-with-drop-fields.rs (renamed from src/test/ui/union/union-with-drop-fields-lint.rs)7
-rw-r--r--src/test/ui/union/union-with-drop-fields.stderr39
40 files changed, 454 insertions, 265 deletions
diff --git a/src/doc/rustc/src/lints/listing/warn-by-default.md b/src/doc/rustc/src/lints/listing/warn-by-default.md
index e486240fda8..813d7c4bafe 100644
--- a/src/doc/rustc/src/lints/listing/warn-by-default.md
+++ b/src/doc/rustc/src/lints/listing/warn-by-default.md
@@ -596,30 +596,6 @@ warning: function cannot return without recursing
   |
 ```
 
-## unions-with-drop-fields
-
-This lint detects use of unions that contain fields with possibly non-trivial drop code. Some
-example code that triggers this lint:
-
-```rust
-#![feature(untagged_unions)]
-
-union U {
-    s: String,
-}
-```
-
-This will produce:
-
-```text
-warning: union contains a field with possibly non-trivial drop code, drop code of union fields is ignored when dropping the union
- --> src/main.rs:4:5
-  |
-4 |     s: String,
-  |     ^^^^^^^^^
-  |
-```
-
 ## unknown-lints
 
 This lint detects unrecognized lint attribute. Some
diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml
index cf9f36ca37c..93274ef0c92 100644
--- a/src/librustc/Cargo.toml
+++ b/src/librustc/Cargo.toml
@@ -36,5 +36,5 @@ parking_lot = "0.9"
 byteorder = { version = "1.3" }
 chalk-engine = { version = "0.9.0", default-features=false }
 rustc_fs_util = { path = "../librustc_fs_util" }
-smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
+smallvec = { version = "0.6.8", features = ["union", "may_dangle"] }
 measureme = "0.3"
diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs
index e1eab2c6579..5555dace45b 100644
--- a/src/librustc/ty/util.rs
+++ b/src/librustc/ty/util.rs
@@ -818,6 +818,8 @@ impl<'tcx> ty::TyS<'tcx> {
     ///
     /// (Note that this implies that if `ty` has a destructor attached,
     /// then `needs_drop` will definitely return `true` for `ty`.)
+    ///
+    /// Note that this method is used to check eligible types in unions.
     #[inline]
     pub fn needs_drop(&'tcx self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
         tcx.needs_drop_raw(param_env.and(self)).0
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 08f6f43ab0c..5d9a97cc21e 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -981,35 +981,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnstableFeatures {
 }
 
 declare_lint! {
-    UNIONS_WITH_DROP_FIELDS,
-    Warn,
-    "use of unions that contain fields with possibly non-trivial drop code"
-}
-
-declare_lint_pass!(
-    /// Lint for unions that contain fields with possibly non-trivial destructors.
-    UnionsWithDropFields => [UNIONS_WITH_DROP_FIELDS]
-);
-
-impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnionsWithDropFields {
-    fn check_item(&mut self, ctx: &LateContext<'_, '_>, item: &hir::Item) {
-        if let hir::ItemKind::Union(ref vdata, _) = item.kind {
-            for field in vdata.fields() {
-                let field_ty = ctx.tcx.type_of(
-                    ctx.tcx.hir().local_def_id(field.hir_id));
-                if field_ty.needs_drop(ctx.tcx, ctx.param_env) {
-                    ctx.span_lint(UNIONS_WITH_DROP_FIELDS,
-                                  field.span,
-                                  "union contains a field with possibly non-trivial drop code, \
-                                   drop code of union fields is ignored when dropping the union");
-                    return;
-                }
-            }
-        }
-    }
-}
-
-declare_lint! {
     pub UNREACHABLE_PUB,
     Allow,
     "`pub` items not reachable from crate root"
@@ -1288,7 +1259,6 @@ declare_lint_pass!(
         NO_MANGLE_GENERIC_ITEMS,
         MUTABLE_TRANSMUTES,
         UNSTABLE_FEATURES,
-        UNIONS_WITH_DROP_FIELDS,
         UNREACHABLE_PUB,
         TYPE_ALIAS_BOUNDS,
         TRIVIAL_BOUNDS
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index e3860e229d6..f83755181f8 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -164,9 +164,6 @@ macro_rules! late_lint_mod_passes {
             // Depends on referenced function signatures in expressions
             MutableTransmutes: MutableTransmutes,
 
-            // Depends on types of fields, checks if they implement Drop
-            UnionsWithDropFields: UnionsWithDropFields,
-
             TypeAliasBounds: TypeAliasBounds,
 
             TrivialConstraints: TrivialConstraints,
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 73f35dde4f6..73a025182a7 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -1387,9 +1387,37 @@ fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
     def.destructor(tcx); // force the destructor to be evaluated
     check_representable(tcx, span, def_id);
     check_transparent(tcx, span, def_id);
+    check_union_fields(tcx, span, def_id);
     check_packed(tcx, span, def_id);
 }
 
+/// When the `#![feature(untagged_unions)]` gate is active,
+/// check that the fields of the `union` does not contain fields that need dropping.
+fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: DefId) -> bool {
+    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;
+        for field in fields {
+            let field_ty = field.ty(tcx, substs);
+            // We are currently checking the type this field came from, so it must be local.
+            let field_span = tcx.hir().span_if_local(field.did).unwrap();
+            let param_env = tcx.param_env(field.did);
+            if field_ty.needs_drop(tcx, param_env) {
+                struct_span_err!(tcx.sess, field_span, E0740,
+                                    "unions may not contain fields that need dropping")
+                            .span_note(field_span,
+                                        "`std::mem::ManuallyDrop` can be used to wrap the type")
+                            .emit();
+                return false;
+            }
+        }
+    } else {
+        span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind);
+    }
+    return true;
+}
+
 /// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
 /// projections that would result in "inheriting lifetimes".
 fn check_opaque<'tcx>(
diff --git a/src/librustc_typeck/error_codes.rs b/src/librustc_typeck/error_codes.rs
index 8321fdd400f..3ecbf620cbc 100644
--- a/src/librustc_typeck/error_codes.rs
+++ b/src/librustc_typeck/error_codes.rs
@@ -4863,6 +4863,10 @@ assert_eq!(1, discriminant(&Enum::Struct{a: 7, b: 11}));
 ```
 "##,
 
+E0740: r##"
+A `union` cannot have fields with destructors.
+"##,
+
 E0733: r##"
 Recursion in an `async fn` requires boxing. For example, this will not compile:
 
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 50a1226cc1d..93d3e4ea3df 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -275,6 +275,7 @@
 #![feature(link_args)]
 #![feature(linkage)]
 #![feature(log_syntax)]
+#![feature(manually_drop_take)]
 #![feature(maybe_uninit_ref)]
 #![feature(maybe_uninit_slice)]
 #![feature(needs_panic_runtime)]
diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs
index 638ce1679b8..2dde81bb0ec 100644
--- a/src/libstd/panicking.rs
+++ b/src/libstd/panicking.rs
@@ -12,8 +12,7 @@ use core::panic::{BoxMeUp, PanicInfo, Location};
 use crate::any::Any;
 use crate::fmt;
 use crate::intrinsics;
-use crate::mem;
-use crate::ptr;
+use crate::mem::{self, ManuallyDrop};
 use crate::raw;
 use crate::sync::atomic::{AtomicBool, Ordering};
 use crate::sys::stdio::panic_output;
@@ -227,10 +226,9 @@ pub use realstd::rt::update_panic_count;
 
 /// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
 pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
-    #[allow(unions_with_drop_fields)]
     union Data<F, R> {
-        f: F,
-        r: R,
+        f: ManuallyDrop<F>,
+        r: ManuallyDrop<R>,
     }
 
     // We do some sketchy operations with ownership here for the sake of
@@ -261,7 +259,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
     let mut any_data = 0;
     let mut any_vtable = 0;
     let mut data = Data {
-        f,
+        f: ManuallyDrop::new(f)
     };
 
     let r = __rust_maybe_catch_panic(do_call::<F, R>,
@@ -271,7 +269,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
 
     return if r == 0 {
         debug_assert!(update_panic_count(0) == 0);
-        Ok(data.r)
+        Ok(ManuallyDrop::into_inner(data.r))
     } else {
         update_panic_count(-1);
         debug_assert!(update_panic_count(0) == 0);
@@ -284,8 +282,9 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
     fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
         unsafe {
             let data = data as *mut Data<F, R>;
-            let f = ptr::read(&mut (*data).f);
-            ptr::write(&mut (*data).r, f());
+            let data = &mut (*data);
+            let f = ManuallyDrop::take(&mut data.f);
+            data.r = ManuallyDrop::new(f());
         }
     }
 }
diff --git a/src/test/ui/associated-type-bounds/union-bounds.rs b/src/test/ui/associated-type-bounds/union-bounds.rs
index ce482fff401..97c5acf1f72 100644
--- a/src/test/ui/associated-type-bounds/union-bounds.rs
+++ b/src/test/ui/associated-type-bounds/union-bounds.rs
@@ -3,13 +3,13 @@
 #![feature(associated_type_bounds)]
 #![feature(untagged_unions)]
 
-#![allow(unions_with_drop_fields, unused_assignments)]
+#![allow(unused_assignments)]
 
-trait Tr1 { type As1; }
-trait Tr2 { type As2; }
-trait Tr3 { type As3; }
-trait Tr4<'a> { type As4; }
-trait Tr5 { type As5; }
+trait Tr1: Copy { type As1: Copy; }
+trait Tr2: Copy { type As2: Copy; }
+trait Tr3: Copy { type As3: Copy; }
+trait Tr4<'a>: Copy { type As4: Copy; }
+trait Tr5: Copy { type As5: Copy; }
 
 impl Tr1 for &str { type As1 = bool; }
 impl Tr2 for bool { type As2 = u8; }
@@ -71,7 +71,8 @@ where
     let _: &'a T = &x.f0;
 }
 
-union UnSelf<T> where Self: Tr1<As1: Tr2> {
+#[derive(Copy, Clone)]
+union UnSelf<T> where Self: Tr1<As1: Tr2>, T: Copy {
     f0: T,
     f1: <Self as Tr1>::As1,
     f2: <<Self as Tr1>::As1 as Tr2>::As2,
diff --git a/src/test/ui/drop/dynamic-drop.rs b/src/test/ui/drop/dynamic-drop.rs
index 7fd3f420a6d..5a7568fe2cd 100644
--- a/src/test/ui/drop/dynamic-drop.rs
+++ b/src/test/ui/drop/dynamic-drop.rs
@@ -8,6 +8,7 @@
 #![feature(slice_patterns)]
 
 use std::cell::{Cell, RefCell};
+use std::mem::ManuallyDrop;
 use std::ops::Generator;
 use std::panic;
 use std::pin::Pin;
@@ -152,17 +153,16 @@ fn assignment1(a: &Allocator, c0: bool) {
     _v = _w;
 }
 
-#[allow(unions_with_drop_fields)]
 union Boxy<T> {
-    a: T,
-    b: T,
+    a: ManuallyDrop<T>,
+    b: ManuallyDrop<T>,
 }
 
 fn union1(a: &Allocator) {
     unsafe {
-        let mut u = Boxy { a: a.alloc() };
-        u.b = a.alloc();
-        drop(u.a);
+        let mut u = Boxy { a: ManuallyDrop::new(a.alloc()) };
+        *u.b = a.alloc(); // drops first alloc
+        drop(ManuallyDrop::into_inner(u.a));
     }
 }
 
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 3d75251e156..0faa9090f4e 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 @@
 #![feature(untagged_unions)]
 
-trait Tr1 { type As1; }
-trait Tr2 { type As2; }
+trait Tr1 { type As1: Copy; }
+trait Tr2 { type As2: Copy; }
 
 struct S1;
 #[derive(Copy, Clone)]
@@ -32,7 +32,7 @@ enum _En1<T: Tr1<As1: Tr2>> {
 
 union _Un1<T: Tr1<As1: Tr2>> {
 //~^ ERROR associated type bounds are unstable
-    outest: T,
+    outest: std::mem::ManuallyDrop<T>,
     outer: T::As1,
     inner: <T::As1 as Tr2>::As2,
 }
diff --git a/src/test/ui/feature-gates/feature-gate-untagged_unions.rs b/src/test/ui/feature-gates/feature-gate-untagged_unions.rs
index 3bac3d853e9..9ee0e6f681d 100644
--- a/src/test/ui/feature-gates/feature-gate-untagged_unions.rs
+++ b/src/test/ui/feature-gates/feature-gate-untagged_unions.rs
@@ -7,11 +7,11 @@ union U2<T: Copy> { // OK
 }
 
 union U3 { //~ ERROR unions with non-`Copy` fields are unstable
-    a: String,
+    a: String, //~ ERROR unions may not contain fields that need dropping
 }
 
 union U4<T> { //~ ERROR unions with non-`Copy` fields are unstable
-    a: T,
+    a: T, //~ ERROR unions may not contain fields that need dropping
 }
 
 union U5 { //~ ERROR unions with `Drop` implementations are unstable
diff --git a/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr b/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr
index f59a34e2c81..1885518a458 100644
--- a/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr
+++ b/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr
@@ -31,6 +31,31 @@ LL | | }
    = note: for more information, see https://github.com/rust-lang/rust/issues/32836
    = help: add `#![feature(untagged_unions)]` to the crate attributes to enable
 
-error: aborting due to 3 previous errors
+error[E0740]: unions may not contain fields that need dropping
+  --> $DIR/feature-gate-untagged_unions.rs:10:5
+   |
+LL |     a: String,
+   |     ^^^^^^^^^
+   |
+note: `std::mem::ManuallyDrop` can be used to wrap the type
+  --> $DIR/feature-gate-untagged_unions.rs:10:5
+   |
+LL |     a: String,
+   |     ^^^^^^^^^
+
+error[E0740]: unions may not contain fields that need dropping
+  --> $DIR/feature-gate-untagged_unions.rs:14:5
+   |
+LL |     a: T,
+   |     ^^^^
+   |
+note: `std::mem::ManuallyDrop` can be used to wrap the type
+  --> $DIR/feature-gate-untagged_unions.rs:14:5
+   |
+LL |     a: T,
+   |     ^^^^
+
+error: aborting due to 5 previous errors
 
-For more information about this error, try `rustc --explain E0658`.
+Some errors have detailed explanations: E0658, E0740.
+For more information about an error, try `rustc --explain E0658`.
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 a16fb7670a6..ea8a3c177e9 100644
--- a/src/test/ui/rfc-2093-infer-outlives/explicit-union.rs
+++ b/src/test/ui/rfc-2093-infer-outlives/explicit-union.rs
@@ -1,13 +1,12 @@
 #![feature(rustc_attrs)]
 #![feature(untagged_unions)]
-#![allow(unions_with_drop_fields)]
 
 #[rustc_outlives]
-union Foo<'b, U> { //~ ERROR rustc_outlives
+union Foo<'b, U: Copy> { //~ ERROR rustc_outlives
     bar: Bar<'b, U>
 }
 
-union Bar<'a, T> where T: 'a {
+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 adf62651cac..8aa246e8bfe 100644
--- a/src/test/ui/rfc-2093-infer-outlives/explicit-union.stderr
+++ b/src/test/ui/rfc-2093-infer-outlives/explicit-union.stderr
@@ -1,7 +1,7 @@
 error: rustc_outlives
-  --> $DIR/explicit-union.rs:6:1
+  --> $DIR/explicit-union.rs:5:1
    |
-LL | / union Foo<'b, U> {
+LL | / union Foo<'b, U: Copy> {
 LL | |     bar: Bar<'b, U>
 LL | | }
    | |_^
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 0ff667fbeba..0da3cc2ba1b 100644
--- a/src/test/ui/rfc-2093-infer-outlives/nested-union.rs
+++ b/src/test/ui/rfc-2093-infer-outlives/nested-union.rs
@@ -1,14 +1,13 @@
 #![feature(rustc_attrs)]
 #![feature(untagged_unions)]
-#![allow(unions_with_drop_fields)]
 
 #[rustc_outlives]
-union Foo<'a, T> { //~ ERROR rustc_outlives
+union Foo<'a, T: Copy> { //~ ERROR rustc_outlives
     field1: Bar<'a, T>
 }
 
 // Type U needs to outlive lifetime 'b
-union Bar<'b, U> {
+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 fc4b1a22329..a42285a56d0 100644
--- a/src/test/ui/rfc-2093-infer-outlives/nested-union.stderr
+++ b/src/test/ui/rfc-2093-infer-outlives/nested-union.stderr
@@ -1,7 +1,7 @@
 error: rustc_outlives
-  --> $DIR/nested-union.rs:6:1
+  --> $DIR/nested-union.rs:5:1
    |
-LL | / union Foo<'a, T> {
+LL | / union Foo<'a, T: Copy> {
 LL | |     field1: Bar<'a, T>
 LL | | }
    | |_^
diff --git a/src/test/ui/self/self-in-typedefs.rs b/src/test/ui/self/self-in-typedefs.rs
index 73f23a9cc17..3b1eb9e1dfa 100644
--- a/src/test/ui/self/self-in-typedefs.rs
+++ b/src/test/ui/self/self-in-typedefs.rs
@@ -3,7 +3,8 @@
 #![feature(untagged_unions)]
 
 #![allow(dead_code)]
-#![allow(unions_with_drop_fields)]
+
+use std::mem::ManuallyDrop;
 
 enum A<'a, T: 'a>
 where
@@ -26,6 +27,14 @@ where
     Self: Send, T: PartialEq<Self>
 {
     foo: &'a Self,
+    bar: ManuallyDrop<T>,
+}
+
+union D<'a, T: 'a>
+where
+    Self: Send, T: PartialEq<Self> + Copy
+{
+    foo: &'a Self,
     bar: T,
 }
 
diff --git a/src/test/ui/union/issue-41073.rs b/src/test/ui/union/issue-41073.rs
new file mode 100644
index 00000000000..91e9a0d0b65
--- /dev/null
+++ b/src/test/ui/union/issue-41073.rs
@@ -0,0 +1,24 @@
+#![feature(untagged_unions)]
+
+union Test {
+    a: A, //~ ERROR unions may not contain fields that need dropping
+    b: B
+}
+
+#[derive(Debug)]
+struct A(i32);
+impl Drop for A {
+    fn drop(&mut self) { println!("A"); }
+}
+
+#[derive(Debug)]
+struct B(f32);
+impl Drop for B {
+    fn drop(&mut self) { println!("B"); }
+}
+
+fn main() {
+    let mut test = Test { a: A(3) };
+    println!("{:?}", unsafe { test.b });
+    unsafe { test.b = B(0.5); }
+}
diff --git a/src/test/ui/union/issue-41073.stderr b/src/test/ui/union/issue-41073.stderr
new file mode 100644
index 00000000000..2e9598b2271
--- /dev/null
+++ b/src/test/ui/union/issue-41073.stderr
@@ -0,0 +1,15 @@
+error[E0740]: unions may not contain fields that need dropping
+  --> $DIR/issue-41073.rs:4:5
+   |
+LL |     a: A,
+   |     ^^^^
+   |
+note: `std::mem::ManuallyDrop` can be used to wrap the type
+  --> $DIR/issue-41073.rs:4:5
+   |
+LL |     a: A,
+   |     ^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0740`.
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 1b6052f10ba..edf08e6ca67 100644
--- a/src/test/ui/union/union-borrow-move-parent-sibling.rs
+++ b/src/test/ui/union/union-borrow-move-parent-sibling.rs
@@ -1,51 +1,90 @@
 #![feature(untagged_unions)]
 #![allow(unused)]
 
-#[allow(unions_with_drop_fields)]
+use std::ops::{Deref, DerefMut};
+
+#[derive(Default)]
+struct MockBox<T> {
+    value: [T; 1],
+}
+
+impl<T> MockBox<T> {
+    fn new(value: T) -> Self { MockBox { value: [value] } }
+}
+
+impl<T> Deref for MockBox<T> {
+    type Target = T;
+    fn deref(&self) -> &T { &self.value[0] }
+}
+
+impl<T> DerefMut for MockBox<T> {
+    fn deref_mut(&mut self) -> &mut T { &mut self.value[0] }
+}
+
+#[derive(Default)]
+struct MockVec<T> {
+    value: [T; 0],
+}
+
+impl<T> MockVec<T> {
+    fn new() -> Self { MockVec { value: [] } }
+}
+
+impl<T> Deref for MockVec<T> {
+    type Target = [T];
+    fn deref(&self) -> &[T] { &self.value }
+}
+
+impl<T> DerefMut for MockVec<T> {
+    fn deref_mut(&mut self) -> &mut [T] { &mut self.value }
+}
+
+
 union U {
-    x: ((Vec<u8>, Vec<u8>), Vec<u8>),
-    y: Box<Vec<u8>>,
+    x: ((MockVec<u8>, MockVec<u8>), MockVec<u8>),
+    y: MockBox<MockVec<u8>>,
 }
 
 fn use_borrow<T>(_: &T) {}
 
 unsafe fn parent_sibling_borrow() {
-    let mut u = U { x: ((Vec::new(), Vec::new()), Vec::new()) };
+    let mut u = U { x: ((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: ((Vec::new(), Vec::new()), Vec::new()) };
+    let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
     let a = u.x.0;
     let b = u.y; //~ ERROR use of moved value: `u`
 }
 
 unsafe fn grandparent_sibling_borrow() {
-    let mut u = U { x: ((Vec::new(), Vec::new()), Vec::new()) };
+    let mut u = U { x: ((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: ((Vec::new(), Vec::new()), Vec::new()) };
+    let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
     let a = (u.x.0).0;
     let b = u.y; //~ ERROR use of moved value: `u`
 }
 
 unsafe fn deref_sibling_borrow() {
-    let mut u = U { y: Box::default() };
+    let mut u = U { y: 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: ((Vec::new(), Vec::new()), Vec::new()) };
-    let a = *u.y;
-    let b = u.x; //~ ERROR use of moved value: `u`
+    let u = U { x: ((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.stderr b/src/test/ui/union/union-borrow-move-parent-sibling.stderr
index 2f4c921ea08..8ba155bafb0 100644
--- a/src/test/ui/union/union-borrow-move-parent-sibling.stderr
+++ b/src/test/ui/union/union-borrow-move-parent-sibling.stderr
@@ -1,5 +1,5 @@
 error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0`)
-  --> $DIR/union-borrow-move-parent-sibling.rs:15:13
+  --> $DIR/union-borrow-move-parent-sibling.rs:53:13
    |
 LL |     let a = &mut u.x.0;
    |             ---------- mutable borrow occurs here (via `u.x.0`)
@@ -11,9 +11,9 @@ LL |     use_borrow(a);
    = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x.0`
 
 error[E0382]: use of moved value: `u`
-  --> $DIR/union-borrow-move-parent-sibling.rs:22:13
+  --> $DIR/union-borrow-move-parent-sibling.rs:60:13
    |
-LL |     let u = U { x: ((Vec::new(), Vec::new()), Vec::new()) };
+LL |     let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
    |         - move occurs because `u` has type `U`, which does not implement the `Copy` trait
 LL |     let a = u.x.0;
    |             ----- value moved here
@@ -21,7 +21,7 @@ LL |     let b = u.y;
    |             ^^^ value used here after move
 
 error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0.0`)
-  --> $DIR/union-borrow-move-parent-sibling.rs:28:13
+  --> $DIR/union-borrow-move-parent-sibling.rs:66:13
    |
 LL |     let a = &mut (u.x.0).0;
    |             -------------- mutable borrow occurs here (via `u.x.0.0`)
@@ -33,38 +33,28 @@ LL |     use_borrow(a);
    = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x.0.0`
 
 error[E0382]: use of moved value: `u`
-  --> $DIR/union-borrow-move-parent-sibling.rs:35:13
+  --> $DIR/union-borrow-move-parent-sibling.rs:73:13
    |
-LL |     let u = U { x: ((Vec::new(), Vec::new()), Vec::new()) };
+LL |     let u = U { x: ((MockVec::new(), MockVec::new()), MockVec::new()) };
    |         - move occurs because `u` has type `U`, which does not implement the `Copy` trait
 LL |     let a = (u.x.0).0;
    |             --------- value moved here
 LL |     let b = u.y;
    |             ^^^ value used here after move
 
-error[E0502]: cannot borrow `u` (via `u.x`) as immutable because it is also borrowed as mutable (via `*u.y`)
-  --> $DIR/union-borrow-move-parent-sibling.rs:41:13
+error[E0502]: cannot borrow `u` (via `u.x`) as immutable because it is also borrowed as mutable (via `u.y`)
+  --> $DIR/union-borrow-move-parent-sibling.rs:79:13
    |
 LL |     let a = &mut *u.y;
-   |             --------- mutable borrow occurs here (via `*u.y`)
+   |                   --- mutable borrow occurs here (via `u.y`)
 LL |     let b = &u.x;
-   |             ^^^^ immutable borrow of `u.x` -- which overlaps with `*u.y` -- occurs here
+   |             ^^^^ immutable borrow of `u.x` -- which overlaps with `u.y` -- occurs here
 LL |     use_borrow(a);
    |                - mutable borrow later used here
    |
-   = note: `u.x` is a field of the union `U`, so it overlaps the field `*u.y`
+   = note: `u.x` is a field of the union `U`, so it overlaps the field `u.y`
 
-error[E0382]: use of moved value: `u`
-  --> $DIR/union-borrow-move-parent-sibling.rs:48:13
-   |
-LL |     let u = U { x: ((Vec::new(), Vec::new()), Vec::new()) };
-   |         - move occurs because `u` has type `U`, which does not implement the `Copy` trait
-LL |     let a = *u.y;
-   |             ---- value moved here
-LL |     let b = u.x;
-   |             ^^^ value used here after move
-
-error: aborting due to 6 previous errors
+error: aborting due to 5 previous errors
 
 Some errors have detailed explanations: E0382, E0502.
 For more information about an error, try `rustc --explain E0382`.
diff --git a/src/test/ui/union/union-custom-drop.rs b/src/test/ui/union/union-custom-drop.rs
new file mode 100644
index 00000000000..8f816cc1b73
--- /dev/null
+++ b/src/test/ui/union/union-custom-drop.rs
@@ -0,0 +1,19 @@
+// 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 may not contain fields that 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
new file mode 100644
index 00000000000..ee2333f905f
--- /dev/null
+++ b/src/test/ui/union/union-custom-drop.stderr
@@ -0,0 +1,15 @@
+error[E0740]: unions may not contain fields that need dropping
+  --> $DIR/union-custom-drop.rs:7:5
+   |
+LL |     bar: Bar,
+   |     ^^^^^^^^
+   |
+note: `std::mem::ManuallyDrop` can be used to wrap the type
+  --> $DIR/union-custom-drop.rs:7:5
+   |
+LL |     bar: 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-derive-clone.rs b/src/test/ui/union/union-derive-clone.rs
index 64c3caef449..60e280f53f5 100644
--- a/src/test/ui/union/union-derive-clone.rs
+++ b/src/test/ui/union/union-derive-clone.rs
@@ -1,5 +1,7 @@
 #![feature(untagged_unions)]
 
+use std::mem::ManuallyDrop;
+
 #[derive(Clone)] //~ ERROR the trait bound `U1: std::marker::Copy` is not satisfied
 union U1 {
     a: u8,
@@ -18,14 +20,19 @@ union U3 {
 }
 
 #[derive(Clone, Copy)]
-union U4<T> {
+union U4<T: Copy> {
     a: T, // OK
 }
 
+#[derive(Clone, Copy)]
+union U5<T> {
+    a: ManuallyDrop<T>, // OK
+}
+
 #[derive(Clone)]
 struct CloneNoCopy;
 
 fn main() {
-    let u = U4 { a: CloneNoCopy };
-    let w = u.clone(); //~ ERROR no method named `clone` found for type `U4<CloneNoCopy>`
+    let u = U5 { a: ManuallyDrop::new(CloneNoCopy) };
+    let w = u.clone(); //~ ERROR no method named `clone` found for type `U5<CloneNoCopy>`
 }
diff --git a/src/test/ui/union/union-derive-clone.stderr b/src/test/ui/union/union-derive-clone.stderr
index 4f4c779b12b..6893f9176f2 100644
--- a/src/test/ui/union/union-derive-clone.stderr
+++ b/src/test/ui/union/union-derive-clone.stderr
@@ -1,22 +1,22 @@
 error[E0277]: the trait bound `U1: std::marker::Copy` is not satisfied
-  --> $DIR/union-derive-clone.rs:3:10
+  --> $DIR/union-derive-clone.rs:5:10
    |
 LL | #[derive(Clone)]
    |          ^^^^^ the trait `std::marker::Copy` is not implemented for `U1`
    |
    = note: required by `std::clone::AssertParamIsCopy`
 
-error[E0599]: no method named `clone` found for type `U4<CloneNoCopy>` in the current scope
-  --> $DIR/union-derive-clone.rs:30:15
+error[E0599]: no method named `clone` found for type `U5<CloneNoCopy>` in the current scope
+  --> $DIR/union-derive-clone.rs:37:15
    |
-LL | union U4<T> {
+LL | union U5<T> {
    | ----------- method `clone` not found for this
 ...
 LL |     let w = u.clone();
-   |               ^^^^^ method not found in `U4<CloneNoCopy>`
+   |               ^^^^^ method not found in `U5<CloneNoCopy>`
    |
    = note: the method `clone` exists but the following trait bounds were not satisfied:
-           `U4<CloneNoCopy> : std::clone::Clone`
+           `U5<CloneNoCopy> : std::clone::Clone`
    = help: items from traits can only be used if the trait is implemented and in scope
    = note: the following trait defines an item `clone`, perhaps you need to implement it:
            candidate #1: `std::clone::Clone`
diff --git a/src/test/ui/union/union-derive-rpass.rs b/src/test/ui/union/union-derive-rpass.rs
index d4b82cdb250..b2f7ae679fd 100644
--- a/src/test/ui/union/union-derive-rpass.rs
+++ b/src/test/ui/union/union-derive-rpass.rs
@@ -1,7 +1,6 @@
 // run-pass
 #![allow(dead_code)]
 #![allow(unused_variables)]
-#![allow(unions_with_drop_fields)]
 
 // Some traits can be derived for unions.
 
@@ -24,11 +23,11 @@ impl PartialEq for U { fn eq(&self, rhs: &Self) -> bool { true } }
     Copy,
     Eq
 )]
-union W<T> {
+union W<T: Copy> {
     a: T,
 }
 
-impl<T> PartialEq for W<T> { fn eq(&self, rhs: &Self) -> bool { true } }
+impl<T: Copy> PartialEq for W<T> { fn eq(&self, rhs: &Self) -> bool { true } }
 
 fn main() {
     let u = U { b: 0 };
diff --git a/src/test/ui/union/union-drop-assign.rs b/src/test/ui/union/union-drop-assign.rs
index c4349c45464..f1511b0a601 100644
--- a/src/test/ui/union/union-drop-assign.rs
+++ b/src/test/ui/union/union-drop-assign.rs
@@ -1,15 +1,16 @@
 // run-pass
 #![allow(unused_assignments)]
-#![allow(unions_with_drop_fields)]
 
 // Drop works for union itself.
 
 #![feature(untagged_unions)]
 
+use std::mem::ManuallyDrop;
+
 struct S;
 
 union U {
-    a: S
+    a: ManuallyDrop<S>
 }
 
 impl Drop for S {
@@ -28,11 +29,11 @@ static mut CHECK: u8 = 0;
 
 fn main() {
     unsafe {
-        let mut u = U { a: S };
+        let mut u = U { a: ManuallyDrop::new(S) };
         assert_eq!(CHECK, 0);
-        u = U { a: S };
+        u = U { a: ManuallyDrop::new(S) };
         assert_eq!(CHECK, 1); // union itself is assigned, union is dropped, field is not dropped
-        u.a = S;
+        *u.a = S;
         assert_eq!(CHECK, 11); // union field is assigned, field is dropped
     }
 }
diff --git a/src/test/ui/union/union-drop.rs b/src/test/ui/union/union-drop.rs
index 2060950dda9..daa03ce6b6f 100644
--- a/src/test/ui/union/union-drop.rs
+++ b/src/test/ui/union/union-drop.rs
@@ -1,7 +1,6 @@
 // run-pass
 #![allow(dead_code)]
 #![allow(unused_variables)]
-#![allow(unions_with_drop_fields)]
 
 // Drop works for union itself.
 
@@ -21,12 +20,6 @@ union Y {
     a: S,
 }
 
-impl Drop for S {
-    fn drop(&mut self) {
-        unsafe { CHECK += 10; }
-    }
-}
-
 impl Drop for U {
     fn drop(&mut self) {
         unsafe { CHECK += 1; }
@@ -51,10 +44,10 @@ fn main() {
         {
             let w = W { a: S };
         }
-        assert_eq!(CHECK, 2); // 2, not 11, dtor of S is not called
+        assert_eq!(CHECK, 2); // 2, dtor of W is called
         {
             let y = Y { a: S };
         }
-        assert_eq!(CHECK, 2); // 2, not 12, dtor of S is not called
+        assert_eq!(CHECK, 2); // 2, dtor of Y is called
     }
 }
diff --git a/src/test/ui/union/union-generic-rpass.rs b/src/test/ui/union/union-generic-rpass.rs
index 6f2caf8dc5b..eb169c516d2 100644
--- a/src/test/ui/union/union-generic-rpass.rs
+++ b/src/test/ui/union/union-generic-rpass.rs
@@ -1,37 +1,33 @@
 // run-pass
 #![allow(dead_code)]
-#![allow(unions_with_drop_fields)]
 
 #![feature(untagged_unions)]
 
+use std::mem::ManuallyDrop;
+
 union MaybeItem<T: Iterator> {
-    elem: T::Item,
+    elem: ManuallyDrop<T::Item>,
     none: (),
 }
 
-union U<A, B> {
+union U<A, B> where A: Copy, B: Copy {
     a: A,
     b: B,
 }
 
-unsafe fn union_transmute<A, B>(a: A) -> B {
+unsafe fn union_transmute<A, B>(a: A) -> B where A: Copy, B: Copy {
     U { a: a }.b
 }
 
 fn main() {
     unsafe {
-        let u = U::<String, Vec<u8>> { a: String::from("abcd") };
-
-        assert_eq!(u.b.len(), 4);
-        assert_eq!(u.b[0], b'a');
-
         let b = union_transmute::<(u8, u8), u16>((1, 1));
         assert_eq!(b, (1 << 8) + 1);
 
         let v: Vec<u8> = vec![1, 2, 3];
         let mut i = v.iter();
         i.next();
-        let mi = MaybeItem::<std::slice::Iter<_>> { elem: i.next().unwrap() };
-        assert_eq!(*mi.elem, 2);
+        let mi = MaybeItem::<std::slice::Iter<_>> { elem: ManuallyDrop::new(i.next().unwrap()) };
+        assert_eq!(**mi.elem, 2);
     }
 }
diff --git a/src/test/ui/union/union-manuallydrop-rpass.rs b/src/test/ui/union/union-manuallydrop-rpass.rs
new file mode 100644
index 00000000000..a43a5050865
--- /dev/null
+++ b/src/test/ui/union/union-manuallydrop-rpass.rs
@@ -0,0 +1,42 @@
+#![feature(untagged_unions)]
+#![allow(dead_code)]
+// run-pass
+
+use std::mem::needs_drop;
+use std::mem::ManuallyDrop;
+
+struct NeedDrop;
+
+impl Drop for NeedDrop {
+    fn drop(&mut self) {}
+}
+
+union UnionOk1<T> {
+    empty: (),
+    value: ManuallyDrop<T>,
+}
+
+union UnionOk2 {
+    value: ManuallyDrop<NeedDrop>,
+}
+
+#[allow(dead_code)]
+union UnionOk3<T: Copy> {
+    empty: (),
+    value: T,
+}
+
+trait Foo { }
+
+trait ImpliesCopy : Copy { }
+
+#[allow(dead_code)]
+union UnionOk4<T: ImpliesCopy> {
+    value: T,
+}
+
+fn main() {
+    // NeedDrop should not make needs_drop true
+    assert!(!needs_drop::<UnionOk1<NeedDrop>>());
+    assert!(!needs_drop::<UnionOk3<&dyn Foo>>());
+}
diff --git a/src/test/ui/union/union-nodrop.rs b/src/test/ui/union/union-nodrop.rs
index 4cd64ddb5d6..59282bec59b 100644
--- a/src/test/ui/union/union-nodrop.rs
+++ b/src/test/ui/union/union-nodrop.rs
@@ -1,12 +1,11 @@
 // run-pass
 
-#![feature(core_intrinsics)]
 #![feature(untagged_unions)]
 
-#![allow(unions_with_drop_fields)]
 #![allow(dead_code)]
 
-use std::intrinsics::needs_drop;
+use std::mem::needs_drop;
+use std::mem::ManuallyDrop;
 
 struct NeedDrop;
 
@@ -16,10 +15,14 @@ impl Drop for NeedDrop {
 
 // Constant expressios allow `NoDrop` to go out of scope,
 // unlike a value of the interior type implementing `Drop`.
-static X: () = (NoDrop { inner: NeedDrop }, ()).1;
+static X: () = (NoDrop { inner: ManuallyDrop::new(NeedDrop) }, ()).1;
+
+const Y: () = (NoDrop { inner: ManuallyDrop::new(NeedDrop) }, ()).1;
+
+const fn _f() { (NoDrop { inner: ManuallyDrop::new(NeedDrop) }, ()).1 }
 
 // A union that scrubs the drop glue from its inner type
-union NoDrop<T> {inner: T}
+union NoDrop<T> { inner: ManuallyDrop<T> }
 
 // Copy currently can't be implemented on drop-containing unions,
 // this may change later
@@ -40,7 +43,7 @@ struct Baz {
     y: Box<u8>,
 }
 
-union ActuallyDrop<T> {inner: T}
+union ActuallyDrop<T> { inner: ManuallyDrop<T> }
 
 impl<T> Drop for ActuallyDrop<T> {
     fn drop(&mut self) {}
diff --git a/src/test/ui/union/union-overwrite.rs b/src/test/ui/union/union-overwrite.rs
index 64c60604ba9..8234beb74a8 100644
--- a/src/test/ui/union/union-overwrite.rs
+++ b/src/test/ui/union/union-overwrite.rs
@@ -1,21 +1,27 @@
 // run-pass
-#![allow(unions_with_drop_fields)]
-
 #![feature(untagged_unions)]
 
 #[repr(C)]
+#[derive(Copy, Clone)]
 struct Pair<T, U>(T, U);
 #[repr(C)]
+#[derive(Copy, Clone)]
 struct Triple<T>(T, T, T);
 
 #[repr(C)]
-union U<A, B> {
+union U<A, B>
+where
+    A: Copy, B: Copy
+{
     a: Pair<A, A>,
     b: B,
 }
 
 #[repr(C)]
-union W<A, B> {
+union W<A, B>
+where
+    A: Copy, B: Copy
+{
     a: A,
     b: B,
 }
diff --git a/src/test/ui/union/union-unsafe.rs b/src/test/ui/union/union-unsafe.rs
index 6cfde35fe4c..8535cbd019c 100644
--- a/src/test/ui/union/union-unsafe.rs
+++ b/src/test/ui/union/union-unsafe.rs
@@ -1,15 +1,16 @@
 #![feature(untagged_unions)]
+use std::mem::ManuallyDrop;
 
 union U1 {
     a: u8
 }
 
 union U2 {
-    a: String
+    a: ManuallyDrop<String>
 }
 
 union U3<T> {
-    a: T
+    a: ManuallyDrop<T>
 }
 
 union U4<T: Copy> {
@@ -17,13 +18,16 @@ union U4<T: Copy> {
 }
 
 fn generic_noncopy<T: Default>() {
-    let mut u3 = U3 { a: T::default() };
-    u3.a = T::default(); //~ ERROR assignment to non-`Copy` union field is unsafe
+    let mut u3 = U3 { a: ManuallyDrop::new(T::default()) };
+    u3.a = ManuallyDrop::new(T::default()); //~ ERROR assignment to non-`Copy` union field is unsafe
+    *u3.a = T::default(); //~ ERROR access to union field is unsafe
 }
 
 fn generic_copy<T: Copy + Default>() {
-    let mut u3 = U3 { a: T::default() };
-    u3.a = T::default(); // OK
+    let mut u3 = U3 { a: ManuallyDrop::new(T::default()) };
+    u3.a = ManuallyDrop::new(T::default()); // OK
+    *u3.a = T::default(); //~ ERROR access to union field is unsafe
+
     let mut u4 = U4 { a: T::default() };
     u4.a = T::default(); // OK
 }
@@ -32,14 +36,20 @@ fn main() {
     let mut u1 = U1 { a: 10 }; // OK
     let a = u1.a; //~ ERROR access to union field is unsafe
     u1.a = 11; // OK
+
     let U1 { a } = u1; //~ ERROR access to union field is unsafe
     if let U1 { a: 12 } = u1 {} //~ ERROR access to union field is unsafe
     // let U1 { .. } = u1; // OK
 
-    let mut u2 = U2 { a: String::from("old") }; // OK
-    u2.a = String::from("new"); //~ ERROR assignment to non-`Copy` union field is unsafe
-    let mut u3 = U3 { a: 0 }; // OK
-    u3.a = 1; // OK
-    let mut u3 = U3 { a: String::from("old") }; // OK
-    u3.a = String::from("new"); //~ ERROR assignment to non-`Copy` union field is unsafe
+    let mut u2 = U2 { a: ManuallyDrop::new(String::from("old")) }; // OK
+    u2.a = ManuallyDrop::new(String::from("new")); //~ ERROR assignment to non-`Copy` union
+    *u2.a = String::from("new"); //~ ERROR access to union field is unsafe
+
+    let mut u3 = U3 { a: ManuallyDrop::new(0) }; // OK
+    u3.a = ManuallyDrop::new(1); // OK
+    *u3.a = 1; //~ ERROR access to union field is unsafe
+
+    let mut u3 = U3 { a: ManuallyDrop::new(String::from("old")) }; // OK
+    u3.a = ManuallyDrop::new(String::from("new")); //~ ERROR assignment to non-`Copy` union
+    *u3.a = String::from("new"); //~ ERROR access to union field is unsafe
 }
diff --git a/src/test/ui/union/union-unsafe.stderr b/src/test/ui/union/union-unsafe.stderr
index ab62508fcf6..e020dab63f8 100644
--- a/src/test/ui/union/union-unsafe.stderr
+++ b/src/test/ui/union/union-unsafe.stderr
@@ -1,13 +1,29 @@
 error[E0133]: assignment to non-`Copy` union field is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:21:5
+  --> $DIR/union-unsafe.rs:22:5
    |
-LL |     u3.a = T::default();
-   |     ^^^^ assignment to non-`Copy` union field
+LL |     u3.a = ManuallyDrop::new(T::default());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to non-`Copy` union field
    |
    = 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:33:13
+  --> $DIR/union-unsafe.rs:23:6
+   |
+LL |     *u3.a = T::default();
+   |      ^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-unsafe.rs:29:6
+   |
+LL |     *u3.a = T::default();
+   |      ^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-unsafe.rs:37:13
    |
 LL |     let a = u1.a;
    |             ^^^^ access to union field
@@ -15,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:35:14
+  --> $DIR/union-unsafe.rs:40:14
    |
 LL |     let U1 { a } = u1;
    |              ^ access to union field
@@ -23,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:36:20
+  --> $DIR/union-unsafe.rs:41:20
    |
 LL |     if let U1 { a: 12 } = u1 {}
    |                    ^^ access to union field
@@ -31,21 +47,45 @@ LL |     if let U1 { a: 12 } = u1 {}
    = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
 
 error[E0133]: assignment to non-`Copy` union field is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:40:5
+  --> $DIR/union-unsafe.rs:45:5
    |
-LL |     u2.a = String::from("new");
-   |     ^^^^ assignment to non-`Copy` union field
+LL |     u2.a = ManuallyDrop::new(String::from("new"));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to non-`Copy` union field
    |
    = 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:46:6
+   |
+LL |     *u2.a = String::from("new");
+   |      ^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-unsafe.rs:50:6
+   |
+LL |     *u3.a = 1;
+   |      ^^^^ access to union field
+   |
+   = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
+
 error[E0133]: assignment to non-`Copy` union field is unsafe and requires unsafe function or block
-  --> $DIR/union-unsafe.rs:44:5
+  --> $DIR/union-unsafe.rs:53:5
    |
-LL |     u3.a = String::from("new");
-   |     ^^^^ assignment to non-`Copy` union field
+LL |     u3.a = ManuallyDrop::new(String::from("new"));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to non-`Copy` union field
    |
    = 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 6 previous errors
+error[E0133]: access to union field is unsafe and requires unsafe function or block
+  --> $DIR/union-unsafe.rs:54: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
 
 For more information about this error, try `rustc --explain E0133`.
diff --git a/src/test/ui/union/union-with-drop-fields-lint-rpass.rs b/src/test/ui/union/union-with-drop-fields-lint-rpass.rs
deleted file mode 100644
index 4dbeb7c1e7e..00000000000
--- a/src/test/ui/union/union-with-drop-fields-lint-rpass.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-// run-pass
-
-#![feature(untagged_unions)]
-#![allow(dead_code)]
-#![allow(unions_with_drop_fields)]
-
-union U {
-    a: u8, // OK
-}
-
-union W {
-    a: String, // OK
-    b: String, // OK
-}
-
-struct S(String);
-
-// `S` doesn't implement `Drop` trait, but still has non-trivial destructor
-union Y {
-    a: S, // OK
-}
-
-// We don't know if `T` is trivially-destructable or not until trans
-union J<T> {
-    a: T, // OK
-}
-
-union H<T: Copy> {
-    a: T, // OK
-}
-
-fn main() {}
diff --git a/src/test/ui/union/union-with-drop-fields-lint.stderr b/src/test/ui/union/union-with-drop-fields-lint.stderr
deleted file mode 100644
index 2f90f240d2e..00000000000
--- a/src/test/ui/union/union-with-drop-fields-lint.stderr
+++ /dev/null
@@ -1,26 +0,0 @@
-error: union contains a field with possibly non-trivial drop code, drop code of union fields is ignored when dropping the union
-  --> $DIR/union-with-drop-fields-lint.rs:10:5
-   |
-LL |     a: String,
-   |     ^^^^^^^^^
-   |
-note: lint level defined here
-  --> $DIR/union-with-drop-fields-lint.rs:3:9
-   |
-LL | #![deny(unions_with_drop_fields)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^
-
-error: union contains a field with possibly non-trivial drop code, drop code of union fields is ignored when dropping the union
-  --> $DIR/union-with-drop-fields-lint.rs:18:5
-   |
-LL |     a: S,
-   |     ^^^^
-
-error: union contains a field with possibly non-trivial drop code, drop code of union fields is ignored when dropping the union
-  --> $DIR/union-with-drop-fields-lint.rs:23:5
-   |
-LL |     a: T,
-   |     ^^^^
-
-error: aborting due to 3 previous errors
-
diff --git a/src/test/ui/union/union-with-drop-fields-lint.rs b/src/test/ui/union/union-with-drop-fields.rs
index 8e502aa55f9..e3c63a6d5b5 100644
--- a/src/test/ui/union/union-with-drop-fields-lint.rs
+++ b/src/test/ui/union/union-with-drop-fields.rs
@@ -1,13 +1,12 @@
 #![feature(untagged_unions)]
 #![allow(dead_code)]
-#![deny(unions_with_drop_fields)]
 
 union U {
     a: u8, // OK
 }
 
 union W {
-    a: String, //~ ERROR union contains a field with possibly non-trivial drop code
+    a: String, //~ ERROR unions may not contain fields that need dropping
     b: String, // OK, only one field is reported
 }
 
@@ -15,12 +14,12 @@ struct S(String);
 
 // `S` doesn't implement `Drop` trait, but still has non-trivial destructor
 union Y {
-    a: S, //~ ERROR union contains a field with possibly non-trivial drop code
+    a: S, //~ ERROR unions may not contain fields that need dropping
 }
 
 // We don't know if `T` is trivially-destructable or not until trans
 union J<T> {
-    a: T, //~ ERROR union contains a field with possibly non-trivial drop code
+    a: T, //~ ERROR unions may not contain fields that need dropping
 }
 
 union H<T: Copy> {
diff --git a/src/test/ui/union/union-with-drop-fields.stderr b/src/test/ui/union/union-with-drop-fields.stderr
new file mode 100644
index 00000000000..0e77279be61
--- /dev/null
+++ b/src/test/ui/union/union-with-drop-fields.stderr
@@ -0,0 +1,39 @@
+error[E0740]: unions may not contain fields that need dropping
+  --> $DIR/union-with-drop-fields.rs:9:5
+   |
+LL |     a: String,
+   |     ^^^^^^^^^
+   |
+note: `std::mem::ManuallyDrop` can be used to wrap the type
+  --> $DIR/union-with-drop-fields.rs:9:5
+   |
+LL |     a: String,
+   |     ^^^^^^^^^
+
+error[E0740]: unions may not contain fields that need dropping
+  --> $DIR/union-with-drop-fields.rs:17:5
+   |
+LL |     a: S,
+   |     ^^^^
+   |
+note: `std::mem::ManuallyDrop` can be used to wrap the type
+  --> $DIR/union-with-drop-fields.rs:17:5
+   |
+LL |     a: S,
+   |     ^^^^
+
+error[E0740]: unions may not contain fields that need dropping
+  --> $DIR/union-with-drop-fields.rs:22:5
+   |
+LL |     a: T,
+   |     ^^^^
+   |
+note: `std::mem::ManuallyDrop` can be used to wrap the type
+  --> $DIR/union-with-drop-fields.rs:22:5
+   |
+LL |     a: T,
+   |     ^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0740`.