about summary refs log tree commit diff
path: root/src/test/ui/union
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/test/ui/union
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/test/ui/union')
-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
21 files changed, 346 insertions, 167 deletions
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`.