diff options
Diffstat (limited to '')
81 files changed, 3144 insertions, 0 deletions
diff --git a/src/test/ui/union/auxiliary/union.rs b/src/test/ui/union/auxiliary/union.rs new file mode 100644 index 000000000..e785e35ae --- /dev/null +++ b/src/test/ui/union/auxiliary/union.rs @@ -0,0 +1,4 @@ +pub union U { + pub a: u8, + pub b: u16, +} diff --git a/src/test/ui/union/field_checks.rs b/src/test/ui/union/field_checks.rs new file mode 100644 index 000000000..d5d1e44ac --- /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 000000000..1f97e97ac --- /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 new file mode 100644 index 000000000..4dfdc606b --- /dev/null +++ b/src/test/ui/union/issue-41073.rs @@ -0,0 +1,22 @@ +union Test { + a: A, //~ ERROR unions cannot contain fields that may 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 000000000..b3887fa0f --- /dev/null +++ b/src/test/ui/union/issue-41073.stderr @@ -0,0 +1,15 @@ +error[E0740]: unions cannot contain fields that may need dropping + --> $DIR/issue-41073.rs:2:5 + | +LL | a: A, + | ^^^^ + | + = 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<A>, + | +++++++++++++++++++++++ + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0740`. diff --git a/src/test/ui/union/issue-81199.rs b/src/test/ui/union/issue-81199.rs new file mode 100644 index 000000000..628e7c6ed --- /dev/null +++ b/src/test/ui/union/issue-81199.rs @@ -0,0 +1,21 @@ +#[repr(C)] +union PtrRepr<T: ?Sized> { + const_ptr: *const T, + mut_ptr: *mut T, + components: PtrComponents<T>, + //~^ ERROR the trait bound +} + +#[repr(C)] +struct PtrComponents<T: Pointee + ?Sized> { + data_address: *const (), + metadata: <T as Pointee>::Metadata, +} + + + +pub trait Pointee { + type Metadata; +} + +fn main() {} diff --git a/src/test/ui/union/issue-81199.stderr b/src/test/ui/union/issue-81199.stderr new file mode 100644 index 000000000..5bb986753 --- /dev/null +++ b/src/test/ui/union/issue-81199.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `T: Pointee` is not satisfied + --> $DIR/issue-81199.rs:5:17 + | +LL | components: PtrComponents<T>, + | ^^^^^^^^^^^^^^^^ the trait `Pointee` is not implemented for `T` + | +note: required by a bound in `PtrComponents` + --> $DIR/issue-81199.rs:10:25 + | +LL | struct PtrComponents<T: Pointee + ?Sized> { + | ^^^^^^^ required by this bound in `PtrComponents` +help: consider further restricting this bound + | +LL | union PtrRepr<T: ?Sized + Pointee> { + | +++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/union/issue-99375.rs b/src/test/ui/union/issue-99375.rs new file mode 100644 index 000000000..175018a7d --- /dev/null +++ b/src/test/ui/union/issue-99375.rs @@ -0,0 +1,21 @@ +// check-pass + +union URes<R: Copy> { + uninit: (), + init: R, +} + +struct Params<F, R: Copy> { + function: F, + result: URes<R>, +} + +unsafe extern "C" fn do_call<F, R>(params: *mut Params<F, R>) +where + R: Copy, + F: Fn() -> R, +{ + (*params).result.init = ((*params).function)(); +} + +fn main() {} diff --git a/src/test/ui/union/union-align.rs b/src/test/ui/union/union-align.rs new file mode 100644 index 000000000..6a44f27db --- /dev/null +++ b/src/test/ui/union/union-align.rs @@ -0,0 +1,65 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![allow(dead_code)] + +use std::mem::{size_of, size_of_val, align_of, align_of_val}; + +#[repr(align(16))] +pub union U16 { + a: u8, + b: u32 +} + +fn main() { + assert_eq!(align_of::<U16>(), 16); + assert_eq!(size_of::<U16>(), 16); + let u = U16 { a: 10 }; + unsafe { + assert_eq!(align_of_val(&u.a), 1); + assert_eq!(size_of_val(&u.a), 1); + assert_eq!(u.a, 10); + } + + let u = U16 { b: 11 }; + unsafe { + assert_eq!(align_of_val(&u.b), 4); + assert_eq!(size_of_val(&u.b), 4); + assert_eq!(u.b, 11); + } + + hybrid::check_hybrid(); +} + +mod hybrid { + use std::mem::{size_of, align_of}; + + #[repr(align(16))] + #[derive(Copy, Clone)] + struct S1 { + a: u16, + b: u8, + } + + #[repr(align(32))] + union U { + s: S1, + c: u16, + } + + #[repr(align(64))] + struct S2 { + d: u8, + u: U, + } + + pub fn check_hybrid() { + assert_eq!(align_of::<S1>(), 16); + assert_eq!(size_of::<S1>(), 16); + assert_eq!(align_of::<U>(), 32); + assert_eq!(size_of::<U>(), 32); + assert_eq!(align_of::<S2>(), 64); + assert_eq!(size_of::<S2>(), 64); + } +} diff --git a/src/test/ui/union/union-backcomp.rs b/src/test/ui/union/union-backcomp.rs new file mode 100644 index 000000000..b19eab9f5 --- /dev/null +++ b/src/test/ui/union/union-backcomp.rs @@ -0,0 +1,28 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![allow(path_statements)] +#![allow(dead_code)] + +macro_rules! union { + () => (struct S;) +} + +union!(); + +fn union() {} + +fn main() { + union(); + + let union = 10; + + union; + + union as u8; + + union U { + a: u8, + } +} diff --git a/src/test/ui/union/union-basic.rs b/src/test/ui/union/union-basic.rs new file mode 100644 index 000000000..dcc552ac7 --- /dev/null +++ b/src/test/ui/union/union-basic.rs @@ -0,0 +1,62 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![allow(unused_imports)] + +// aux-build:union.rs + +extern crate union; +use std::mem::{size_of, align_of, zeroed}; + +union U { + a: u8, + b: u16 +} + +fn local() { + assert_eq!(size_of::<U>(), 2); + assert_eq!(align_of::<U>(), 2); + + let u = U { a: 10 }; + unsafe { + assert_eq!(u.a, 10); + let U { a } = u; + assert_eq!(a, 10); + } + + let mut w = U { b: 0 }; + unsafe { + assert_eq!(w.a, 0); + assert_eq!(w.b, 0); + w.a = 1; + assert_eq!(w.a, 1); + assert_eq!(w.b.to_le(), 1); + } +} + +fn xcrate() { + assert_eq!(size_of::<union::U>(), 2); + assert_eq!(align_of::<union::U>(), 2); + + let u = union::U { a: 10 }; + unsafe { + assert_eq!(u.a, 10); + let union::U { a } = u; + assert_eq!(a, 10); + } + + let mut w = union::U { b: 0 }; + unsafe { + assert_eq!(w.a, 0); + assert_eq!(w.b, 0); + w.a = 1; + assert_eq!(w.a, 1); + assert_eq!(w.b.to_le(), 1); + } +} + +fn main() { + local(); + xcrate(); +} 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 new file mode 100644 index 000000000..ca02de4c6 --- /dev/null +++ b/src/test/ui/union/union-borrow-move-parent-sibling.mirunsafeck.stderr @@ -0,0 +1,80 @@ +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`) +LL | let b = &u.y; + | ^^^^ 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` + +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:64: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; +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`) + --> $DIR/union-borrow-move-parent-sibling.rs:70:13 + | +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` -- 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` + +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 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; +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:84:13 + | +LL | let a = &mut *u.y; + | --- mutable borrow occurs here (via `u.y`) +LL | let b = &u.x; + | ^^^^ immutable borrow of `u.x` -- which overlaps with `u.y` -- occurs here +LL | use_borrow(a); + | - mutable borrow later used here + | + = note: `u.x` is a field of the union `U`, so it overlaps the field `u.y` + +error: aborting due to 7 previous errors + +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 new file mode 100644 index 000000000..83781c5e5 --- /dev/null +++ b/src/test/ui/union/union-borrow-move-parent-sibling.rs @@ -0,0 +1,96 @@ +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![allow(unused)] + +use std::ops::{Deref, DerefMut}; +use std::mem::ManuallyDrop; + +#[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: 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: 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: 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: 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: 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: 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: 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` +} + + +fn main() {} diff --git a/src/test/ui/union/union-borrow-move-parent-sibling.thirunsafeck.stderr b/src/test/ui/union/union-borrow-move-parent-sibling.thirunsafeck.stderr new file mode 100644 index 000000000..ca02de4c6 --- /dev/null +++ b/src/test/ui/union/union-borrow-move-parent-sibling.thirunsafeck.stderr @@ -0,0 +1,80 @@ +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`) +LL | let b = &u.y; + | ^^^^ 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` + +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:64: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; +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`) + --> $DIR/union-borrow-move-parent-sibling.rs:70:13 + | +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` -- 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` + +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 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; +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:84:13 + | +LL | let a = &mut *u.y; + | --- mutable borrow occurs here (via `u.y`) +LL | let b = &u.x; + | ^^^^ immutable borrow of `u.x` -- which overlaps with `u.y` -- occurs here +LL | use_borrow(a); + | - mutable borrow later used here + | + = note: `u.x` is a field of the union `U`, so it overlaps the field `u.y` + +error: aborting due to 7 previous errors + +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-const-codegen.rs b/src/test/ui/union/union-const-codegen.rs new file mode 100644 index 000000000..32a546cf3 --- /dev/null +++ b/src/test/ui/union/union-const-codegen.rs @@ -0,0 +1,19 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +union U { + a: u64, + b: u64, +} + +const C: U = U { b: 10 }; + +fn main() { + unsafe { + let a = C.a; + let b = C.b; + assert_eq!(a, 10); + assert_eq!(b, 10); + } +} diff --git a/src/test/ui/union/union-const-eval-field.rs b/src/test/ui/union/union-const-eval-field.rs new file mode 100644 index 000000000..ca48785cd --- /dev/null +++ b/src/test/ui/union/union-const-eval-field.rs @@ -0,0 +1,45 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +type Field1 = (i32, u32); +type Field2 = f32; +type Field3 = i64; + +union DummyUnion { + field1: Field1, + field2: Field2, + field3: Field3, +} + +const FLOAT1_AS_I32: i32 = 1065353216; +const UNION: DummyUnion = DummyUnion { field1: (FLOAT1_AS_I32, 0) }; + +const fn read_field1() -> Field1 { + const FIELD1: Field1 = unsafe { UNION.field1 }; + FIELD1 +} + +const fn read_field2() -> Field2 { + const FIELD2: Field2 = unsafe { UNION.field2 }; + FIELD2 +} + +const fn read_field3() -> Field3 { + const FIELD3: Field3 = unsafe { UNION.field3 }; + FIELD3 +} + +fn main() { + let foo = FLOAT1_AS_I32; + assert_eq!(read_field1().0, foo); + assert_eq!(read_field1().0, FLOAT1_AS_I32); + + let foo = 1.0; + assert_eq!(read_field2(), foo); + assert_eq!(read_field2(), 1.0); + + assert_eq!(read_field3(), unsafe { UNION.field3 }); + let foo = unsafe { UNION.field3 }; + assert_eq!(read_field3(), foo); +} diff --git a/src/test/ui/union/union-const-eval.rs b/src/test/ui/union/union-const-eval.rs new file mode 100644 index 000000000..32ee4a739 --- /dev/null +++ b/src/test/ui/union/union-const-eval.rs @@ -0,0 +1,15 @@ +// check-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +union U { + a: usize, + b: usize, +} + +const C: U = U { a: 10 }; + +fn main() { + let a: [u8; unsafe { C.a }]; + let b: [u8; unsafe { C.b }]; +} diff --git a/src/test/ui/union/union-const-pat.rs b/src/test/ui/union/union-const-pat.rs new file mode 100644 index 000000000..e7cb248a2 --- /dev/null +++ b/src/test/ui/union/union-const-pat.rs @@ -0,0 +1,13 @@ +union U { + a: usize, + b: usize, +} + +const C: U = U { a: 10 }; + +fn main() { + match C { + C => {} //~ ERROR cannot use unions in constant patterns + _ => {} + } +} diff --git a/src/test/ui/union/union-const-pat.stderr b/src/test/ui/union/union-const-pat.stderr new file mode 100644 index 000000000..dc87f4de5 --- /dev/null +++ b/src/test/ui/union/union-const-pat.stderr @@ -0,0 +1,8 @@ +error: cannot use unions in constant patterns + --> $DIR/union-const-pat.rs:10:9 + | +LL | C => {} + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/union/union-copy.rs b/src/test/ui/union/union-copy.rs new file mode 100644 index 000000000..5c3f8d908 --- /dev/null +++ b/src/test/ui/union/union-copy.rs @@ -0,0 +1,14 @@ +#[derive(Clone)] +union U { + a: u8 +} + +#[derive(Clone)] +union W { + a: std::mem::ManuallyDrop<String> +} + +impl Copy for U {} // OK +impl Copy for W {} //~ ERROR the trait `Copy` may not be implemented for this type + +fn main() {} diff --git a/src/test/ui/union/union-copy.stderr b/src/test/ui/union/union-copy.stderr new file mode 100644 index 000000000..8ecdafdde --- /dev/null +++ b/src/test/ui/union/union-copy.stderr @@ -0,0 +1,18 @@ +error[E0204]: the trait `Copy` may not be implemented for this type + --> $DIR/union-copy.rs:12:6 + | +LL | a: std::mem::ManuallyDrop<String> + | --------------------------------- this field does not implement `Copy` +... +LL | impl Copy for W {} + | ^^^^ + | +note: the `Copy` impl for `ManuallyDrop<String>` requires that `String: Copy` + --> $DIR/union-copy.rs:8:8 + | +LL | a: std::mem::ManuallyDrop<String> + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0204`. diff --git a/src/test/ui/union/union-deref.mirunsafeck.stderr b/src/test/ui/union/union-deref.mirunsafeck.stderr new file mode 100644 index 000000000..be5e60ab8 --- /dev/null +++ b/src/test/ui/union/union-deref.mirunsafeck.stderr @@ -0,0 +1,56 @@ +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:16:14 + | +LL | unsafe { u.f.0 = Vec::new() }; + | ^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:18:19 + | +LL | unsafe { &mut u.f.0 }; + | ^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:20:14 + | +LL | unsafe { u.f.0.push(0) }; + | ^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:24:14 + | +LL | unsafe { u.f.0.0 = Vec::new() }; + | ^^^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:26:19 + | +LL | unsafe { &mut u.f.0.0 }; + | ^^^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:28:14 + | +LL | unsafe { u.f.0.0.push(0) }; + | ^^^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/union/union-deref.rs b/src/test/ui/union/union-deref.rs new file mode 100644 index 000000000..5aa28d93f --- /dev/null +++ b/src/test/ui/union/union-deref.rs @@ -0,0 +1,29 @@ +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +//! Test the part of RFC 2514 that is about not applying `DerefMut` coercions +//! of union fields. + +use std::mem::ManuallyDrop; + +union U1<T> { x:(), f: ManuallyDrop<(T,)> } + +union U2<T> { x:(), f: (ManuallyDrop<(T,)>,) } + +fn main() { + let mut u : U1<Vec<i32>> = U1 { x: () }; + unsafe { (*u.f).0 = Vec::new() }; // explicit deref, this compiles + unsafe { u.f.0 = Vec::new() }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + unsafe { &mut (*u.f).0 }; // explicit deref, this compiles + unsafe { &mut u.f.0 }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + unsafe { (*u.f).0.push(0) }; // explicit deref, this compiles + unsafe { u.f.0.push(0) }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + + let mut u : U2<Vec<i32>> = U2 { x: () }; + unsafe { (*u.f.0).0 = Vec::new() }; // explicit deref, this compiles + unsafe { u.f.0.0 = Vec::new() }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + unsafe { &mut (*u.f.0).0 }; // explicit deref, this compiles + unsafe { &mut u.f.0.0 }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + unsafe { (*u.f.0).0.push(0) }; // explicit deref, this compiles + unsafe { u.f.0.0.push(0) }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field +} diff --git a/src/test/ui/union/union-deref.thirunsafeck.stderr b/src/test/ui/union/union-deref.thirunsafeck.stderr new file mode 100644 index 000000000..be5e60ab8 --- /dev/null +++ b/src/test/ui/union/union-deref.thirunsafeck.stderr @@ -0,0 +1,56 @@ +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:16:14 + | +LL | unsafe { u.f.0 = Vec::new() }; + | ^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:18:19 + | +LL | unsafe { &mut u.f.0 }; + | ^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:20:14 + | +LL | unsafe { u.f.0.push(0) }; + | ^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:24:14 + | +LL | unsafe { u.f.0.0 = Vec::new() }; + | ^^^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:26:19 + | +LL | unsafe { &mut u.f.0.0 }; + | ^^^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: not automatically applying `DerefMut` on `ManuallyDrop` union field + --> $DIR/union-deref.rs:28:14 + | +LL | unsafe { u.f.0.0.push(0) }; + | ^^^^^ + | + = help: writing to this reference calls the destructor for the old value + = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/union/union-derive-clone.mirunsafeck.stderr b/src/test/ui/union/union-derive-clone.mirunsafeck.stderr new file mode 100644 index 000000000..148fb5046 --- /dev/null +++ b/src/test/ui/union/union-derive-clone.mirunsafeck.stderr @@ -0,0 +1,49 @@ +error[E0277]: the trait bound `U1: Copy` is not satisfied + --> $DIR/union-derive-clone.rs:6:10 + | +LL | #[derive(Clone)] + | ^^^^^ the trait `Copy` is not implemented for `U1` + | +note: required by a bound in `AssertParamIsCopy` + --> $SRC_DIR/core/src/clone.rs:LL:COL + | +LL | pub struct AssertParamIsCopy<T: Copy + ?Sized> { + | ^^^^ required by this bound in `AssertParamIsCopy` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `U1` with `#[derive(Copy)]` + | +LL | #[derive(Copy)] + | + +error[E0599]: the method `clone` exists for union `U5<CloneNoCopy>`, but its trait bounds were not satisfied + --> $DIR/union-derive-clone.rs:38:15 + | +LL | union U5<T> { + | ----------- + | | + | method `clone` not found for this union + | doesn't satisfy `U5<CloneNoCopy>: Clone` +... +LL | struct CloneNoCopy; + | ------------------ doesn't satisfy `CloneNoCopy: Copy` +... +LL | let w = u.clone(); + | ^^^^^ method cannot be called on `U5<CloneNoCopy>` due to unsatisfied trait bounds + | +note: trait bound `CloneNoCopy: Copy` was not satisfied + --> $DIR/union-derive-clone.rs:28:10 + | +LL | #[derive(Clone, Copy)] + | ^^^^^ unsatisfied trait bound introduced in this `derive` macro + = note: the following trait bounds were not satisfied: + `CloneNoCopy: Copy` + which is required by `U5<CloneNoCopy>: Clone` +help: consider annotating `CloneNoCopy` with `#[derive(Clone, Copy)]` + | +LL | #[derive(Clone, Copy)] + | + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/union/union-derive-clone.rs b/src/test/ui/union/union-derive-clone.rs new file mode 100644 index 000000000..7aa62146e --- /dev/null +++ b/src/test/ui/union/union-derive-clone.rs @@ -0,0 +1,39 @@ +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +use std::mem::ManuallyDrop; + +#[derive(Clone)] //~ ERROR the trait bound `U1: Copy` is not satisfied +union U1 { + a: u8, +} + +#[derive(Clone)] +union U2 { + a: u8, // OK +} + +impl Copy for U2 {} + +#[derive(Clone, Copy)] +union U3 { + a: u8, // OK +} + +#[derive(Clone, Copy)] +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 = U5 { a: ManuallyDrop::new(CloneNoCopy) }; + let w = u.clone(); //~ ERROR the method +} diff --git a/src/test/ui/union/union-derive-clone.thirunsafeck.stderr b/src/test/ui/union/union-derive-clone.thirunsafeck.stderr new file mode 100644 index 000000000..148fb5046 --- /dev/null +++ b/src/test/ui/union/union-derive-clone.thirunsafeck.stderr @@ -0,0 +1,49 @@ +error[E0277]: the trait bound `U1: Copy` is not satisfied + --> $DIR/union-derive-clone.rs:6:10 + | +LL | #[derive(Clone)] + | ^^^^^ the trait `Copy` is not implemented for `U1` + | +note: required by a bound in `AssertParamIsCopy` + --> $SRC_DIR/core/src/clone.rs:LL:COL + | +LL | pub struct AssertParamIsCopy<T: Copy + ?Sized> { + | ^^^^ required by this bound in `AssertParamIsCopy` + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `U1` with `#[derive(Copy)]` + | +LL | #[derive(Copy)] + | + +error[E0599]: the method `clone` exists for union `U5<CloneNoCopy>`, but its trait bounds were not satisfied + --> $DIR/union-derive-clone.rs:38:15 + | +LL | union U5<T> { + | ----------- + | | + | method `clone` not found for this union + | doesn't satisfy `U5<CloneNoCopy>: Clone` +... +LL | struct CloneNoCopy; + | ------------------ doesn't satisfy `CloneNoCopy: Copy` +... +LL | let w = u.clone(); + | ^^^^^ method cannot be called on `U5<CloneNoCopy>` due to unsatisfied trait bounds + | +note: trait bound `CloneNoCopy: Copy` was not satisfied + --> $DIR/union-derive-clone.rs:28:10 + | +LL | #[derive(Clone, Copy)] + | ^^^^^ unsatisfied trait bound introduced in this `derive` macro + = note: the following trait bounds were not satisfied: + `CloneNoCopy: Copy` + which is required by `U5<CloneNoCopy>: Clone` +help: consider annotating `CloneNoCopy` with `#[derive(Clone, Copy)]` + | +LL | #[derive(Clone, Copy)] + | + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/union/union-derive-eq.mirunsafeck.stderr b/src/test/ui/union/union-derive-eq.mirunsafeck.stderr new file mode 100644 index 000000000..99505f316 --- /dev/null +++ b/src/test/ui/union/union-derive-eq.mirunsafeck.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `PartialEqNotEq: Eq` is not satisfied + --> $DIR/union-derive-eq.rs:16:5 + | +LL | #[derive(Eq)] + | -- in this derive macro expansion +LL | union U2 { +LL | a: PartialEqNotEq, + | ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq` + | +note: required by a bound in `AssertParamIsEq` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + | +LL | pub struct AssertParamIsEq<T: Eq + ?Sized> { + | ^^ required by this bound in `AssertParamIsEq` + = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `PartialEqNotEq` with `#[derive(Eq)]` + | +LL | #[derive(Eq)] + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/union/union-derive-eq.rs b/src/test/ui/union/union-derive-eq.rs new file mode 100644 index 000000000..b7e7f343f --- /dev/null +++ b/src/test/ui/union/union-derive-eq.rs @@ -0,0 +1,21 @@ +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#[derive(Eq)] // OK +union U1 { + a: u8, +} + +impl PartialEq for U1 { fn eq(&self, rhs: &Self) -> bool { true } } + +#[derive(PartialEq, Copy, Clone)] +struct PartialEqNotEq; + +#[derive(Eq)] +union U2 { + a: PartialEqNotEq, //~ ERROR the trait bound `PartialEqNotEq: Eq` is not satisfied +} + +impl PartialEq for U2 { fn eq(&self, rhs: &Self) -> bool { true } } + +fn main() {} diff --git a/src/test/ui/union/union-derive-eq.thirunsafeck.stderr b/src/test/ui/union/union-derive-eq.thirunsafeck.stderr new file mode 100644 index 000000000..99505f316 --- /dev/null +++ b/src/test/ui/union/union-derive-eq.thirunsafeck.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `PartialEqNotEq: Eq` is not satisfied + --> $DIR/union-derive-eq.rs:16:5 + | +LL | #[derive(Eq)] + | -- in this derive macro expansion +LL | union U2 { +LL | a: PartialEqNotEq, + | ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq` + | +note: required by a bound in `AssertParamIsEq` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + | +LL | pub struct AssertParamIsEq<T: Eq + ?Sized> { + | ^^ required by this bound in `AssertParamIsEq` + = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `PartialEqNotEq` with `#[derive(Eq)]` + | +LL | #[derive(Eq)] + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/union/union-derive-rpass.rs b/src/test/ui/union/union-derive-rpass.rs new file mode 100644 index 000000000..8276bc635 --- /dev/null +++ b/src/test/ui/union/union-derive-rpass.rs @@ -0,0 +1,42 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![allow(dead_code)] +#![allow(unused_variables)] + +// Some traits can be derived for unions. + +#[derive( + Copy, + Clone, + Eq, +)] +union U { + a: u8, + b: u16, +} + +impl PartialEq for U { fn eq(&self, rhs: &Self) -> bool { true } } + +#[derive( + Clone, + Copy, + Eq +)] +union W<T: Copy> { + a: T, +} + +impl<T: Copy> PartialEq for W<T> { fn eq(&self, rhs: &Self) -> bool { true } } + +fn main() { + let u = U { b: 0 }; + let u1 = u; + let u2 = u.clone(); + assert!(u1 == u2); + + let w = W { a: 0 }; + let w1 = w.clone(); + assert!(w == w1); +} diff --git a/src/test/ui/union/union-derive.rs b/src/test/ui/union/union-derive.rs new file mode 100644 index 000000000..652a6b24c --- /dev/null +++ b/src/test/ui/union/union-derive.rs @@ -0,0 +1,16 @@ +// Most traits cannot be derived for unions. + +#[derive( + PartialEq, //~ ERROR this trait cannot be derived for unions + PartialOrd, //~ ERROR this trait cannot be derived for unions + Ord, //~ ERROR this trait cannot be derived for unions + Hash, //~ ERROR this trait cannot be derived for unions + Default, //~ ERROR this trait cannot be derived for unions + Debug, //~ ERROR this trait cannot be derived for unions +)] +union U { + a: u8, + b: u16, +} + +fn main() {} diff --git a/src/test/ui/union/union-derive.stderr b/src/test/ui/union/union-derive.stderr new file mode 100644 index 000000000..6ef72c901 --- /dev/null +++ b/src/test/ui/union/union-derive.stderr @@ -0,0 +1,38 @@ +error: this trait cannot be derived for unions + --> $DIR/union-derive.rs:4:5 + | +LL | PartialEq, + | ^^^^^^^^^ + +error: this trait cannot be derived for unions + --> $DIR/union-derive.rs:5:5 + | +LL | PartialOrd, + | ^^^^^^^^^^ + +error: this trait cannot be derived for unions + --> $DIR/union-derive.rs:6:5 + | +LL | Ord, + | ^^^ + +error: this trait cannot be derived for unions + --> $DIR/union-derive.rs:7:5 + | +LL | Hash, + | ^^^^ + +error: this trait cannot be derived for unions + --> $DIR/union-derive.rs:8:5 + | +LL | Default, + | ^^^^^^^ + +error: this trait cannot be derived for unions + --> $DIR/union-derive.rs:9:5 + | +LL | Debug, + | ^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/test/ui/union/union-drop-assign.rs b/src/test/ui/union/union-drop-assign.rs new file mode 100644 index 000000000..215666bdd --- /dev/null +++ b/src/test/ui/union/union-drop-assign.rs @@ -0,0 +1,37 @@ +// run-pass +#![allow(unused_assignments)] + +// Drop works for union itself. + +use std::mem::ManuallyDrop; + +struct S; + +union U { + a: ManuallyDrop<S> +} + +impl Drop for S { + fn drop(&mut self) { + unsafe { CHECK += 10; } + } +} + +impl Drop for U { + fn drop(&mut self) { + unsafe { CHECK += 1; } + } +} + +static mut CHECK: u8 = 0; + +fn main() { + unsafe { + let mut u = U { a: ManuallyDrop::new(S) }; + assert_eq!(CHECK, 0); + u = U { a: ManuallyDrop::new(S) }; + assert_eq!(CHECK, 1); // union itself is assigned, union is dropped, field is not dropped + *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 new file mode 100644 index 000000000..c3d7d41ca --- /dev/null +++ b/src/test/ui/union/union-drop.rs @@ -0,0 +1,60 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![allow(dead_code)] +#![allow(unused_variables)] + +// Drop works for union itself. + +#[derive(Copy, Clone)] +struct S; + +union U { + a: u8 +} + +union W { + a: S, +} + +union Y { + a: S, +} + +impl Drop for U { + fn drop(&mut self) { + unsafe { CHECK += 1; } + } +} + +impl Drop for W { + fn drop(&mut self) { + unsafe { CHECK += 1; } + } +} + +static mut CHECK: u8 = 0; + +fn main() { + unsafe { + assert_eq!(CHECK, 0); + { + let u = U { a: 1 }; + } + assert_eq!(CHECK, 1); // 1, dtor of U is called + { + let w = W { a: S }; + } + assert_eq!(CHECK, 2); // 2, dtor of W is called + { + let y = Y { a: S }; + } + assert_eq!(CHECK, 2); // 2, Y has no dtor + { + let u2 = U { a: 1 }; + std::mem::forget(u2); + } + assert_eq!(CHECK, 2); // 2, dtor of U *not* called for u2 + } +} diff --git a/src/test/ui/union/union-empty.rs b/src/test/ui/union/union-empty.rs new file mode 100644 index 000000000..79b7e68ee --- /dev/null +++ b/src/test/ui/union/union-empty.rs @@ -0,0 +1,3 @@ +union U {} //~ ERROR unions cannot have zero fields + +fn main() {} diff --git a/src/test/ui/union/union-empty.stderr b/src/test/ui/union/union-empty.stderr new file mode 100644 index 000000000..a80b27e6e --- /dev/null +++ b/src/test/ui/union/union-empty.stderr @@ -0,0 +1,8 @@ +error: unions cannot have zero fields + --> $DIR/union-empty.rs:1:1 + | +LL | union U {} + | ^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/union/union-fields-1.mirunsafeck.stderr b/src/test/ui/union/union-fields-1.mirunsafeck.stderr new file mode 100644 index 000000000..0c9981c69 --- /dev/null +++ b/src/test/ui/union/union-fields-1.mirunsafeck.stderr @@ -0,0 +1,42 @@ +error: field `c` is never read + --> $DIR/union-fields-1.rs:9:5 + | +LL | union U1 { + | -- field in this union +... +LL | c: u8, + | ^ + | +note: the lint level is defined here + --> $DIR/union-fields-1.rs:4:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: field `a` is never read + --> $DIR/union-fields-1.rs:12:5 + | +LL | union U2 { + | -- field in this union +LL | a: u8, + | ^ + +error: field `a` is never read + --> $DIR/union-fields-1.rs:16:20 + | +LL | union NoDropLike { a: u8 } + | ---------- ^ + | | + | field in this union + +error: field `c` is never read + --> $DIR/union-fields-1.rs:21:5 + | +LL | union U { + | - field in this union +... +LL | c: u8, + | ^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/union/union-fields-1.rs b/src/test/ui/union/union-fields-1.rs new file mode 100644 index 000000000..cf2ef4c03 --- /dev/null +++ b/src/test/ui/union/union-fields-1.rs @@ -0,0 +1,35 @@ +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![deny(dead_code)] + +union U1 { + a: u8, // should not be reported + b: u8, // should not be reported + c: u8, //~ ERROR field `c` is never read +} +union U2 { + a: u8, //~ ERROR field `a` is never read + b: u8, // should not be reported + c: u8, // should not be reported +} +union NoDropLike { a: u8 } //~ ERROR field `a` is never read + +union U { + a: u8, // should not be reported + b: u8, // should not be reported + c: u8, //~ ERROR field `c` is never read +} +type A = U; + +fn main() { + let u = U1 { a: 0 }; + let _a = unsafe { u.b }; + + let u = U2 { c: 0 }; + let _b = unsafe { u.b }; + + let _u = NoDropLike { a: 10 }; + let u = A { a: 0 }; + let _b = unsafe { u.b }; +} diff --git a/src/test/ui/union/union-fields-1.thirunsafeck.stderr b/src/test/ui/union/union-fields-1.thirunsafeck.stderr new file mode 100644 index 000000000..0c9981c69 --- /dev/null +++ b/src/test/ui/union/union-fields-1.thirunsafeck.stderr @@ -0,0 +1,42 @@ +error: field `c` is never read + --> $DIR/union-fields-1.rs:9:5 + | +LL | union U1 { + | -- field in this union +... +LL | c: u8, + | ^ + | +note: the lint level is defined here + --> $DIR/union-fields-1.rs:4:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: field `a` is never read + --> $DIR/union-fields-1.rs:12:5 + | +LL | union U2 { + | -- field in this union +LL | a: u8, + | ^ + +error: field `a` is never read + --> $DIR/union-fields-1.rs:16:20 + | +LL | union NoDropLike { a: u8 } + | ---------- ^ + | | + | field in this union + +error: field `c` is never read + --> $DIR/union-fields-1.rs:21:5 + | +LL | union U { + | - field in this union +... +LL | c: u8, + | ^ + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/union/union-fields-2.mirunsafeck.stderr b/src/test/ui/union/union-fields-2.mirunsafeck.stderr new file mode 100644 index 000000000..90ad16402 --- /dev/null +++ b/src/test/ui/union/union-fields-2.mirunsafeck.stderr @@ -0,0 +1,84 @@ +error[E0784]: union expressions should have exactly one field + --> $DIR/union-fields-2.rs:10:13 + | +LL | let u = U {}; + | ^ + +error[E0784]: union expressions should have exactly one field + --> $DIR/union-fields-2.rs:12:13 + | +LL | let u = U { a: 0, b: 1 }; + | ^ + +error[E0560]: union `U` has no field named `c` + --> $DIR/union-fields-2.rs:13:29 + | +LL | let u = U { a: 0, b: 1, c: 2 }; + | ^ `U` does not have this field + | + = note: available fields are: `a`, `b` + +error[E0784]: union expressions should have exactly one field + --> $DIR/union-fields-2.rs:13:13 + | +LL | let u = U { a: 0, b: 1, c: 2 }; + | ^ + +error[E0784]: union expressions should have exactly one field + --> $DIR/union-fields-2.rs:15:13 + | +LL | let u = U { ..u }; + | ^ + +error[E0436]: functional record update syntax requires a struct + --> $DIR/union-fields-2.rs:15:19 + | +LL | let u = U { ..u }; + | ^ + +error: union patterns should have exactly one field + --> $DIR/union-fields-2.rs:18:9 + | +LL | let U {} = u; + | ^^^^ + +error: union patterns should have exactly one field + --> $DIR/union-fields-2.rs:20:9 + | +LL | let U { a, b } = u; + | ^^^^^^^^^^ + +error: union patterns should have exactly one field + --> $DIR/union-fields-2.rs:21:9 + | +LL | let U { a, b, c } = u; + | ^^^^^^^^^^^^^ + +error[E0026]: union `U` does not have a field named `c` + --> $DIR/union-fields-2.rs:21:19 + | +LL | let U { a, b, c } = u; + | ^ union `U` does not have this field + +error: union patterns should have exactly one field + --> $DIR/union-fields-2.rs:23:9 + | +LL | let U { .. } = u; + | ^^^^^^^^ + +error: `..` cannot be used in union patterns + --> $DIR/union-fields-2.rs:23:9 + | +LL | let U { .. } = u; + | ^^^^^^^^ + +error: `..` cannot be used in union patterns + --> $DIR/union-fields-2.rs:25:9 + | +LL | let U { a, .. } = u; + | ^^^^^^^^^^^ + +error: aborting due to 13 previous errors + +Some errors have detailed explanations: E0026, E0436, E0560, E0784. +For more information about an error, try `rustc --explain E0026`. diff --git a/src/test/ui/union/union-fields-2.rs b/src/test/ui/union/union-fields-2.rs new file mode 100644 index 000000000..e738b1847 --- /dev/null +++ b/src/test/ui/union/union-fields-2.rs @@ -0,0 +1,26 @@ +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +union U { + a: u8, + b: u16, +} + +fn main() { + let u = U {}; //~ ERROR union expressions should have exactly one field + let u = U { a: 0 }; // OK + let u = U { a: 0, b: 1 }; //~ ERROR union expressions should have exactly one field + let u = U { a: 0, b: 1, c: 2 }; //~ ERROR union expressions should have exactly one field + //~^ ERROR union `U` has no field named `c` + let u = U { ..u }; //~ ERROR union expressions should have exactly one field + //~^ ERROR functional record update syntax requires a struct + + let U {} = u; //~ ERROR union patterns should have exactly one field + let U { a } = u; // OK + let U { a, b } = u; //~ ERROR union patterns should have exactly one field + let U { a, b, c } = u; //~ ERROR union patterns should have exactly one field + //~^ ERROR union `U` does not have a field named `c` + let U { .. } = u; //~ ERROR union patterns should have exactly one field + //~^ ERROR `..` cannot be used in union patterns + let U { a, .. } = u; //~ ERROR `..` cannot be used in union patterns +} diff --git a/src/test/ui/union/union-fields-2.thirunsafeck.stderr b/src/test/ui/union/union-fields-2.thirunsafeck.stderr new file mode 100644 index 000000000..90ad16402 --- /dev/null +++ b/src/test/ui/union/union-fields-2.thirunsafeck.stderr @@ -0,0 +1,84 @@ +error[E0784]: union expressions should have exactly one field + --> $DIR/union-fields-2.rs:10:13 + | +LL | let u = U {}; + | ^ + +error[E0784]: union expressions should have exactly one field + --> $DIR/union-fields-2.rs:12:13 + | +LL | let u = U { a: 0, b: 1 }; + | ^ + +error[E0560]: union `U` has no field named `c` + --> $DIR/union-fields-2.rs:13:29 + | +LL | let u = U { a: 0, b: 1, c: 2 }; + | ^ `U` does not have this field + | + = note: available fields are: `a`, `b` + +error[E0784]: union expressions should have exactly one field + --> $DIR/union-fields-2.rs:13:13 + | +LL | let u = U { a: 0, b: 1, c: 2 }; + | ^ + +error[E0784]: union expressions should have exactly one field + --> $DIR/union-fields-2.rs:15:13 + | +LL | let u = U { ..u }; + | ^ + +error[E0436]: functional record update syntax requires a struct + --> $DIR/union-fields-2.rs:15:19 + | +LL | let u = U { ..u }; + | ^ + +error: union patterns should have exactly one field + --> $DIR/union-fields-2.rs:18:9 + | +LL | let U {} = u; + | ^^^^ + +error: union patterns should have exactly one field + --> $DIR/union-fields-2.rs:20:9 + | +LL | let U { a, b } = u; + | ^^^^^^^^^^ + +error: union patterns should have exactly one field + --> $DIR/union-fields-2.rs:21:9 + | +LL | let U { a, b, c } = u; + | ^^^^^^^^^^^^^ + +error[E0026]: union `U` does not have a field named `c` + --> $DIR/union-fields-2.rs:21:19 + | +LL | let U { a, b, c } = u; + | ^ union `U` does not have this field + +error: union patterns should have exactly one field + --> $DIR/union-fields-2.rs:23:9 + | +LL | let U { .. } = u; + | ^^^^^^^^ + +error: `..` cannot be used in union patterns + --> $DIR/union-fields-2.rs:23:9 + | +LL | let U { .. } = u; + | ^^^^^^^^ + +error: `..` cannot be used in union patterns + --> $DIR/union-fields-2.rs:25:9 + | +LL | let U { a, .. } = u; + | ^^^^^^^^^^^ + +error: aborting due to 13 previous errors + +Some errors have detailed explanations: E0026, E0436, E0560, E0784. +For more information about an error, try `rustc --explain E0026`. diff --git a/src/test/ui/union/union-generic-rpass.rs b/src/test/ui/union/union-generic-rpass.rs new file mode 100644 index 000000000..25f1f5050 --- /dev/null +++ b/src/test/ui/union/union-generic-rpass.rs @@ -0,0 +1,34 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![allow(dead_code)] + +use std::mem::ManuallyDrop; + +union MaybeItem<T: Iterator> { + elem: ManuallyDrop<T::Item>, + none: (), +} + +union U<A, B> where A: Copy, B: Copy { + a: A, + b: B, +} + +unsafe fn union_transmute<A, B>(a: A) -> B where A: Copy, B: Copy { + U { a }.b +} + +fn main() { + unsafe { + 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: ManuallyDrop::new(i.next().unwrap()) }; + assert_eq!(**mi.elem, 2); + } +} diff --git a/src/test/ui/union/union-generic.mirunsafeck.stderr b/src/test/ui/union/union-generic.mirunsafeck.stderr new file mode 100644 index 000000000..a4f0c400d --- /dev/null +++ b/src/test/ui/union/union-generic.mirunsafeck.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `Rc<u32>: Copy` is not satisfied + --> $DIR/union-generic.rs:11:13 + | +LL | let u = U { a: Rc::new(0u32) }; + | ^ the trait `Copy` is not implemented for `Rc<u32>` + | +note: required by a bound in `U` + --> $DIR/union-generic.rs:6:12 + | +LL | union U<T: Copy> { + | ^^^^ required by this bound in `U` + +error[E0277]: the trait bound `Rc<u32>: Copy` is not satisfied + --> $DIR/union-generic.rs:13:13 + | +LL | let u = U::<Rc<u32>> { a: Default::default() }; + | ^^^^^^^^^^^^ the trait `Copy` is not implemented for `Rc<u32>` + | +note: required by a bound in `U` + --> $DIR/union-generic.rs:6:12 + | +LL | union U<T: Copy> { + | ^^^^ required by this bound in `U` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/union/union-generic.rs b/src/test/ui/union/union-generic.rs new file mode 100644 index 000000000..3d68ecb87 --- /dev/null +++ b/src/test/ui/union/union-generic.rs @@ -0,0 +1,15 @@ +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +use std::rc::Rc; + +union U<T: Copy> { + a: T +} + +fn main() { + let u = U { a: Rc::new(0u32) }; + //~^ ERROR the trait bound `Rc<u32>: Copy` is not satisfied + let u = U::<Rc<u32>> { a: Default::default() }; + //~^ ERROR the trait bound `Rc<u32>: Copy` is not satisfied +} diff --git a/src/test/ui/union/union-generic.thirunsafeck.stderr b/src/test/ui/union/union-generic.thirunsafeck.stderr new file mode 100644 index 000000000..a4f0c400d --- /dev/null +++ b/src/test/ui/union/union-generic.thirunsafeck.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `Rc<u32>: Copy` is not satisfied + --> $DIR/union-generic.rs:11:13 + | +LL | let u = U { a: Rc::new(0u32) }; + | ^ the trait `Copy` is not implemented for `Rc<u32>` + | +note: required by a bound in `U` + --> $DIR/union-generic.rs:6:12 + | +LL | union U<T: Copy> { + | ^^^^ required by this bound in `U` + +error[E0277]: the trait bound `Rc<u32>: Copy` is not satisfied + --> $DIR/union-generic.rs:13:13 + | +LL | let u = U::<Rc<u32>> { a: Default::default() }; + | ^^^^^^^^^^^^ the trait `Copy` is not implemented for `Rc<u32>` + | +note: required by a bound in `U` + --> $DIR/union-generic.rs:6:12 + | +LL | union U<T: Copy> { + | ^^^^ required by this bound in `U` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/union/union-inherent-method.rs b/src/test/ui/union/union-inherent-method.rs new file mode 100644 index 000000000..b0fd22da7 --- /dev/null +++ b/src/test/ui/union/union-inherent-method.rs @@ -0,0 +1,16 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +union U { + a: u8, +} + +impl U { + fn method(&self) -> u8 { unsafe { self.a } } +} + +fn main() { + let u = U { a: 10 }; + assert_eq!(u.method(), 10); +} diff --git a/src/test/ui/union/union-lint-dead-code.mirunsafeck.stderr b/src/test/ui/union/union-lint-dead-code.mirunsafeck.stderr new file mode 100644 index 000000000..6e21584c3 --- /dev/null +++ b/src/test/ui/union/union-lint-dead-code.mirunsafeck.stderr @@ -0,0 +1,17 @@ +error: field `b` is never read + --> $DIR/union-lint-dead-code.rs:8:5 + | +LL | union Foo { + | --- field in this union +LL | x: usize, +LL | b: bool, + | ^ + | +note: the lint level is defined here + --> $DIR/union-lint-dead-code.rs:4:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/union/union-lint-dead-code.rs b/src/test/ui/union/union-lint-dead-code.rs new file mode 100644 index 000000000..65aaf0a1d --- /dev/null +++ b/src/test/ui/union/union-lint-dead-code.rs @@ -0,0 +1,18 @@ +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![deny(dead_code)] + +union Foo { + x: usize, + b: bool, //~ ERROR: field `b` is never read + _unused: u16, +} + +fn field_read(f: Foo) -> usize { + unsafe { f.x } +} + +fn main() { + let _ = field_read(Foo { x: 2 }); +} diff --git a/src/test/ui/union/union-lint-dead-code.thirunsafeck.stderr b/src/test/ui/union/union-lint-dead-code.thirunsafeck.stderr new file mode 100644 index 000000000..6e21584c3 --- /dev/null +++ b/src/test/ui/union/union-lint-dead-code.thirunsafeck.stderr @@ -0,0 +1,17 @@ +error: field `b` is never read + --> $DIR/union-lint-dead-code.rs:8:5 + | +LL | union Foo { + | --- field in this union +LL | x: usize, +LL | b: bool, + | ^ + | +note: the lint level is defined here + --> $DIR/union-lint-dead-code.rs:4:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/union/union-macro.rs b/src/test/ui/union/union-macro.rs new file mode 100644 index 000000000..7fd9d8221 --- /dev/null +++ b/src/test/ui/union/union-macro.rs @@ -0,0 +1,27 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![allow(unused_variables)] + +macro_rules! duplicate { + ($i: item) => { + mod m1 { + $i + } + mod m2 { + $i + } + } +} + +duplicate! { + pub union U { + pub a: u8 + } +} + +fn main() { + let u1 = m1::U { a: 0 }; + let u2 = m2::U { a: 0 }; +} 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 000000000..826bdf07c --- /dev/null +++ b/src/test/ui/union/union-manuallydrop-rpass.rs @@ -0,0 +1,44 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![allow(dead_code)] + +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-move.mirunsafeck.stderr b/src/test/ui/union/union-move.mirunsafeck.stderr new file mode 100644 index 000000000..53050cf53 --- /dev/null +++ b/src/test/ui/union/union-move.mirunsafeck.stderr @@ -0,0 +1,35 @@ +error[E0382]: use of moved value: `x` + --> $DIR/union-move.rs:29:18 + | +LL | fn test1(x: U1) { + | - move occurs because `x` has type `U1`, which does not implement the `Copy` trait +... +LL | move_out(x.f1_nocopy); + | ----------- value moved here +LL | move_out(x.f2_nocopy); + | ^^^^^^^^^^^ value used here after move + +error[E0382]: use of moved value: `x` + --> $DIR/union-move.rs:45:18 + | +LL | fn test3(x: U1) { + | - move occurs because `x` has type `U1`, which does not implement the `Copy` trait +... +LL | move_out(x.f2_nocopy); + | ----------- value moved here +LL | move_out(x.f3_copy); + | ^^^^^^^^^ value used here after move + +error[E0509]: cannot move out of type `U2`, which implements the `Drop` trait + --> $DIR/union-move.rs:52:18 + | +LL | move_out(x.f1_nocopy); + | ^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `x.f1_nocopy` has type `ManuallyDrop<RefCell<i32>>`, which does not implement the `Copy` trait + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0382, E0509. +For more information about an error, try `rustc --explain E0382`. diff --git a/src/test/ui/union/union-move.rs b/src/test/ui/union/union-move.rs new file mode 100644 index 000000000..b8b1ac804 --- /dev/null +++ b/src/test/ui/union/union-move.rs @@ -0,0 +1,56 @@ +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +//! Test the behavior of moving out of non-`Copy` union fields. +//! Avoid types that `Drop`, we want to focus on moving. + +use std::cell::RefCell; +use std::mem::ManuallyDrop; + +fn move_out<T>(x: T) {} + +union U1 { + f1_nocopy: ManuallyDrop<RefCell<i32>>, + f2_nocopy: ManuallyDrop<RefCell<i32>>, + f3_copy: i32, +} + +union U2 { + f1_nocopy: ManuallyDrop<RefCell<i32>>, +} +impl Drop for U2 { + fn drop(&mut self) {} +} + +fn test1(x: U1) { + // Moving out of a nocopy field prevents accessing other nocopy field. + unsafe { + move_out(x.f1_nocopy); + move_out(x.f2_nocopy); //~ ERROR use of moved value: `x` + } +} + +fn test2(x: U1) { + // "Moving" out of copy field doesn't prevent later field accesses. + unsafe { + move_out(x.f3_copy); + move_out(x.f2_nocopy); // no error + } +} + +fn test3(x: U1) { + // Moving out of a nocopy field prevents accessing other copy field. + unsafe { + move_out(x.f2_nocopy); + move_out(x.f3_copy); //~ ERROR use of moved value: `x` + } +} + +fn test4(x: U2) { + // Cannot move out of union that implements `Drop`. + unsafe { + move_out(x.f1_nocopy); //~ ERROR cannot move out of type `U2`, which implements the `Drop` + } +} + +fn main() {} diff --git a/src/test/ui/union/union-move.thirunsafeck.stderr b/src/test/ui/union/union-move.thirunsafeck.stderr new file mode 100644 index 000000000..53050cf53 --- /dev/null +++ b/src/test/ui/union/union-move.thirunsafeck.stderr @@ -0,0 +1,35 @@ +error[E0382]: use of moved value: `x` + --> $DIR/union-move.rs:29:18 + | +LL | fn test1(x: U1) { + | - move occurs because `x` has type `U1`, which does not implement the `Copy` trait +... +LL | move_out(x.f1_nocopy); + | ----------- value moved here +LL | move_out(x.f2_nocopy); + | ^^^^^^^^^^^ value used here after move + +error[E0382]: use of moved value: `x` + --> $DIR/union-move.rs:45:18 + | +LL | fn test3(x: U1) { + | - move occurs because `x` has type `U1`, which does not implement the `Copy` trait +... +LL | move_out(x.f2_nocopy); + | ----------- value moved here +LL | move_out(x.f3_copy); + | ^^^^^^^^^ value used here after move + +error[E0509]: cannot move out of type `U2`, which implements the `Drop` trait + --> $DIR/union-move.rs:52:18 + | +LL | move_out(x.f1_nocopy); + | ^^^^^^^^^^^ + | | + | cannot move out of here + | move occurs because `x.f1_nocopy` has type `ManuallyDrop<RefCell<i32>>`, which does not implement the `Copy` trait + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0382, E0509. +For more information about an error, try `rustc --explain E0382`. diff --git a/src/test/ui/union/union-nodrop.rs b/src/test/ui/union/union-nodrop.rs new file mode 100644 index 000000000..6e6b105a7 --- /dev/null +++ b/src/test/ui/union/union-nodrop.rs @@ -0,0 +1,62 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![allow(dead_code)] + +use std::mem::needs_drop; +use std::mem::ManuallyDrop; + +struct NeedDrop; + +impl Drop for NeedDrop { + fn drop(&mut self) {} +} + +// Constant expressios allow `NoDrop` to go out of scope, +// unlike a value of the interior type implementing `Drop`. +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: ManuallyDrop<T> } + +// Copy currently can't be implemented on drop-containing unions, +// this may change later +// https://github.com/rust-lang/rust/pull/38934#issuecomment-271219289 + +// // We should be able to implement Copy for NoDrop +// impl<T> Copy for NoDrop<T> {} +// impl<T> Clone for NoDrop<T> {fn clone(&self) -> Self { *self }} + +// // We should be able to implement Copy for things using NoDrop +// #[derive(Copy, Clone)] +struct Foo { + x: NoDrop<Box<u8>> +} + +struct Baz { + x: NoDrop<Box<u8>>, + y: Box<u8>, +} + +union ActuallyDrop<T> { inner: ManuallyDrop<T> } + +impl<T> Drop for ActuallyDrop<T> { + fn drop(&mut self) {} +} + +fn main() { + // NoDrop should not make needs_drop true + assert!(!needs_drop::<Foo>()); + assert!(!needs_drop::<NoDrop<u8>>()); + assert!(!needs_drop::<NoDrop<Box<u8>>>()); + // presence of other drop types should still work + assert!(needs_drop::<Baz>()); + // drop impl on union itself should work + assert!(needs_drop::<ActuallyDrop<u8>>()); + assert!(needs_drop::<ActuallyDrop<Box<u8>>>()); +} diff --git a/src/test/ui/union/union-nonrepresentable.rs b/src/test/ui/union/union-nonrepresentable.rs new file mode 100644 index 000000000..4bdf7c687 --- /dev/null +++ b/src/test/ui/union/union-nonrepresentable.rs @@ -0,0 +1,6 @@ +union U { //~ ERROR recursive type `U` has infinite size + a: u8, + 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 new file mode 100644 index 000000000..9804b1418 --- /dev/null +++ b/src/test/ui/union/union-nonrepresentable.stderr @@ -0,0 +1,17 @@ +error[E0072]: recursive type `U` has infinite size + --> $DIR/union-nonrepresentable.rs:1:1 + | +LL | union U { + | ^^^^^^^ recursive type has infinite size +LL | a: u8, +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<std::mem::ManuallyDrop<U>>, + | ++++ + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0072`. diff --git a/src/test/ui/union/union-nonzero.rs b/src/test/ui/union/union-nonzero.rs new file mode 100644 index 000000000..3f4f7ea1c --- /dev/null +++ b/src/test/ui/union/union-nonzero.rs @@ -0,0 +1,54 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![allow(dead_code)] + +// Tests that unions aren't subject to unsafe non-zero/niche-filling optimizations. +// +// For example, if a union `U` can contain both a `&T` and a `*const T`, there's definitely no +// bit-value that an `Option<U>` could reuse as `None`; this test makes sure that isn't done. +// +// Secondly, this tests the status quo (not a guarantee; subject to change!) to not apply such +// optimizations to types containing unions even if they're theoretically possible. (discussion: +// https://github.com/rust-lang/rust/issues/36394) +// +// Notably this nails down part of the behavior that `MaybeUninit` assumes: that an +// `Option<MaybeUninit<&u8>>` does not take advantage of non-zero optimization, and thus is a safe +// construct. + +use std::mem::{size_of, transmute}; + +union U1<A: Copy> { + a: A, +} + +union U2<A: Copy, B: Copy> { + a: A, + b: B, +} + +// Option<E> uses a value other than 0 and 1 as None +#[derive(Clone,Copy)] +enum E { + A = 0, + B = 1, +} + +fn main() { + // Unions do not participate in niche-filling/non-zero optimization... + assert!(size_of::<Option<U2<&u8, u8>>>() > size_of::<U2<&u8, u8>>()); + assert!(size_of::<Option<U2<&u8, ()>>>() > size_of::<U2<&u8, ()>>()); + assert!(size_of::<Option<U2<u8, E>>>() > size_of::<U2<u8, E>>()); + + // ...even when theoretically possible: + assert!(size_of::<Option<U1<&u8>>>() > size_of::<U1<&u8>>()); + assert!(size_of::<Option<U2<&u8, &u8>>>() > size_of::<U2<&u8, &u8>>()); + + // The unused bits of the () variant can have any value. + let zeroed: U2<&u8, ()> = unsafe { transmute(std::ptr::null::<u8>()) }; + + if let None = Some(zeroed) { + panic!() + } +} diff --git a/src/test/ui/union/union-overwrite.rs b/src/test/ui/union/union-overwrite.rs new file mode 100644 index 000000000..0eea14d9d --- /dev/null +++ b/src/test/ui/union/union-overwrite.rs @@ -0,0 +1,80 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#[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> +where + A: Copy, B: Copy +{ + a: Pair<A, A>, + b: B, +} + +#[repr(C)] +union W<A, B> +where + A: Copy, B: Copy +{ + a: A, + b: B, +} + +#[cfg(target_endian = "little")] +unsafe fn check() { + let mut u = U::<u8, u16> { b: 0xDE_DE }; + u.a.0 = 0xBE; + assert_eq!(u.b, 0xDE_BE); + + let mut u = U::<u16, u32> { b: 0xDEAD_DEAD }; + u.a.0 = 0xBEEF; + assert_eq!(u.b, 0xDEAD_BEEF); + + let mut u = U::<u32, u64> { b: 0xDEADBEEF_DEADBEEF }; + u.a.0 = 0xBAADF00D; + assert_eq!(u.b, 0xDEADBEEF_BAADF00D); + + let mut w = W::<Pair<Triple<u8>, u8>, u32> { b: 0xDEAD_DEAD }; + w.a.0 = Triple(0, 0, 0); + assert_eq!(w.b, 0xDE00_0000); + + let mut w = W::<Pair<u8, Triple<u8>>, u32> { b: 0xDEAD_DEAD }; + w.a.1 = Triple(0, 0, 0); + assert_eq!(w.b, 0x0000_00AD); +} + +#[cfg(target_endian = "big")] +unsafe fn check() { + let mut u = U::<u8, u16> { b: 0xDE_DE }; + u.a.0 = 0xBE; + assert_eq!(u.b, 0xBE_DE); + + let mut u = U::<u16, u32> { b: 0xDEAD_DEAD }; + u.a.0 = 0xBEEF; + assert_eq!(u.b, 0xBEEF_DEAD); + + let mut u = U::<u32, u64> { b: 0xDEADBEEF_DEADBEEF }; + u.a.0 = 0xBAADF00D; + assert_eq!(u.b, 0xBAADF00D_DEADBEEF); + + let mut w = W::<Pair<Triple<u8>, u8>, u32> { b: 0xDEAD_DEAD }; + w.a.0 = Triple(0, 0, 0); + assert_eq!(w.b, 0x0000_00AD); + + let mut w = W::<Pair<u8, Triple<u8>>, u32> { b: 0xDEAD_DEAD }; + w.a.1 = Triple(0, 0, 0); + assert_eq!(w.b, 0xDE00_0000); +} + +fn main() { + unsafe { + check(); + } +} diff --git a/src/test/ui/union/union-packed.rs b/src/test/ui/union/union-packed.rs new file mode 100644 index 000000000..9c6398bf5 --- /dev/null +++ b/src/test/ui/union/union-packed.rs @@ -0,0 +1,173 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![allow(dead_code)] +#![allow(non_snake_case)] + +use std::mem::{size_of, size_of_val, align_of, align_of_val}; + +struct S { + a: u16, + b: [u8; 3], +} + +#[repr(packed)] +struct Sp1 { + a: u16, + b: [u8; 3], +} + +#[repr(packed(2))] +struct Sp2 { + a: u16, + b: [u8; 3], +} + +union U { + a: u16, + b: [u8; 3], +} + +#[repr(packed)] +union Up1 { + a: u16, + b: [u8; 3], +} + +#[repr(packed(2))] +union Up2 { + a: u16, + b: [u8; 3], +} + +#[repr(C, packed(4))] +union Up4c { + a: u16, + b: [u8; 3], +} + +const CS: S = S { a: 0, b: [0, 0, 0] }; +const CSP1: Sp1 = Sp1 { a: 0, b: [0, 0, 0] }; +const CSP2: Sp2 = Sp2 { a: 0, b: [0, 0, 0] }; +const CU: U = U { b: [0, 0, 0] }; +const CUP1: Up1 = Up1 { b: [0, 0, 0] }; +const CUP2: Up2 = Up2 { b: [0, 0, 0] }; +const CUP4C: Up4c = Up4c { b: [0, 0, 0] }; + +fn main() { + let s = S { a: 0, b: [0, 0, 0] }; + assert_eq!(size_of::<S>(), 6); + assert_eq!(size_of_val(&s), 6); + assert_eq!(size_of_val(&CS), 6); + assert_eq!(align_of::<S>(), 2); + assert_eq!(align_of_val(&s), 2); + assert_eq!(align_of_val(&CS), 2); + + let sp1 = Sp1 { a: 0, b: [0, 0, 0] }; + assert_eq!(size_of::<Sp1>(), 5); + assert_eq!(size_of_val(&sp1), 5); + assert_eq!(size_of_val(&CSP1), 5); + assert_eq!(align_of::<Sp1>(), 1); + assert_eq!(align_of_val(&sp1), 1); + assert_eq!(align_of_val(&CSP1), 1); + + let sp2 = Sp2 { a: 0, b: [0, 0, 0] }; + assert_eq!(size_of::<Sp2>(), 6); + assert_eq!(size_of_val(&sp2), 6); + assert_eq!(size_of_val(&CSP2), 6); + assert_eq!(align_of::<Sp2>(), 2); + assert_eq!(align_of_val(&sp2), 2); + assert_eq!(align_of_val(&CSP2), 2); + + let u = U { b: [0, 0, 0] }; + assert_eq!(size_of::<U>(), 4); + assert_eq!(size_of_val(&u), 4); + assert_eq!(size_of_val(&CU), 4); + assert_eq!(align_of::<U>(), 2); + assert_eq!(align_of_val(&u), 2); + assert_eq!(align_of_val(&CU), 2); + + let Up1 = Up1 { b: [0, 0, 0] }; + assert_eq!(size_of::<Up1>(), 3); + assert_eq!(size_of_val(&Up1), 3); + assert_eq!(size_of_val(&CUP1), 3); + assert_eq!(align_of::<Up1>(), 1); + assert_eq!(align_of_val(&Up1), 1); + assert_eq!(align_of_val(&CUP1), 1); + + let up2 = Up2 { b: [0, 0, 0] }; + assert_eq!(size_of::<Up2>(), 4); + assert_eq!(size_of_val(&up2), 4); + assert_eq!(size_of_val(&CUP2), 4); + assert_eq!(align_of::<Up2>(), 2); + assert_eq!(align_of_val(&up2), 2); + assert_eq!(align_of_val(&CUP2), 2); + + let up4c = Up4c { b: [0, 0, 0] }; + assert_eq!(size_of::<Up4c>(), 4); + assert_eq!(size_of_val(&up4c), 4); + assert_eq!(size_of_val(&CUP4C), 4); + assert_eq!(align_of::<Up4c>(), 2); + assert_eq!(align_of_val(&up4c), 2); + assert_eq!(align_of_val(&CUP4C), 2); + + hybrid::check_hybrid(); +} + +mod hybrid { + use std::mem::{size_of, align_of}; + + #[repr(packed)] + #[derive(Copy, Clone)] + struct S1 { + a: u16, + b: u8, + } + + #[repr(packed)] + union U { + s: S1, + c: u16, + } + + #[repr(packed)] + struct S2 { + d: u8, + u: U, + } + + #[repr(C, packed(2))] + struct S1C { + a: u16, + b: u8, + } + + #[repr(C, packed(2))] + union UC { + s: S1, + c: u16, + } + + #[repr(C, packed(2))] + struct S2C { + d: u8, + u: UC, + } + + pub fn check_hybrid() { + assert_eq!(align_of::<S1>(), 1); + assert_eq!(size_of::<S1>(), 3); + assert_eq!(align_of::<U>(), 1); + assert_eq!(size_of::<U>(), 3); + assert_eq!(align_of::<S2>(), 1); + assert_eq!(size_of::<S2>(), 4); + + assert_eq!(align_of::<S1C>(), 2); + assert_eq!(size_of::<S1C>(), 4); + assert_eq!(align_of::<UC>(), 2); + assert_eq!(size_of::<UC>(), 4); + assert_eq!(align_of::<S2C>(), 2); + assert_eq!(size_of::<S2C>(), 6); + } +} diff --git a/src/test/ui/union/union-pat-refutability.rs b/src/test/ui/union/union-pat-refutability.rs new file mode 100644 index 000000000..d628a200a --- /dev/null +++ b/src/test/ui/union/union-pat-refutability.rs @@ -0,0 +1,57 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![allow(dead_code)] +#![allow(illegal_floating_point_literal_pattern)] + +#[repr(u32)] +enum Tag { I, F } + +#[repr(C)] +union U { + i: i32, + f: f32, +} + +#[repr(C)] +struct Value { + tag: Tag, + u: U, +} + +fn is_zero(v: Value) -> bool { + unsafe { + match v { + Value { tag: Tag::I, u: U { i: 0 } } => true, + Value { tag: Tag::F, u: U { f: 0.0 } } => true, + _ => false, + } + } +} + +union W { + a: u8, + b: u8, +} + +fn refut(w: W) { + unsafe { + match w { + W { a: 10 } => { + panic!(); + } + W { b } => { + assert_eq!(b, 11); + } + } + } +} + +fn main() { + let v = Value { tag: Tag::I, u: U { i: 1 } }; + assert_eq!(is_zero(v), false); + + let w = W { a: 11 }; + refut(w); +} diff --git a/src/test/ui/union/union-repr-c.rs b/src/test/ui/union/union-repr-c.rs new file mode 100644 index 000000000..1367835e6 --- /dev/null +++ b/src/test/ui/union/union-repr-c.rs @@ -0,0 +1,18 @@ +#![allow(unused)] +#![deny(improper_ctypes)] + +#[repr(C)] +union U { + a: u8, +} + +union W { + a: u8, +} + +extern "C" { + static FOREIGN1: U; // OK + static FOREIGN2: W; //~ ERROR `extern` block uses type `W` +} + +fn main() {} diff --git a/src/test/ui/union/union-repr-c.stderr b/src/test/ui/union/union-repr-c.stderr new file mode 100644 index 000000000..9abf440f7 --- /dev/null +++ b/src/test/ui/union/union-repr-c.stderr @@ -0,0 +1,21 @@ +error: `extern` block uses type `W`, which is not FFI-safe + --> $DIR/union-repr-c.rs:15:22 + | +LL | static FOREIGN2: W; + | ^ not FFI-safe + | +note: the lint level is defined here + --> $DIR/union-repr-c.rs:2:9 + | +LL | #![deny(improper_ctypes)] + | ^^^^^^^^^^^^^^^ + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this union + = note: this union has unspecified layout +note: the type is defined here + --> $DIR/union-repr-c.rs:9:1 + | +LL | union W { + | ^^^^^^^ + +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 new file mode 100644 index 000000000..cb852eff0 --- /dev/null +++ b/src/test/ui/union/union-sized-field.rs @@ -0,0 +1,19 @@ +use std::mem::ManuallyDrop; + +union Foo<T: ?Sized> { + value: ManuallyDrop<T>, + //~^ ERROR the size for values of type +} + +struct Foo2<T: ?Sized> { + value: ManuallyDrop<T>, + //~^ ERROR the size for values of type + t: u32, +} + +enum Foo3<T: ?Sized> { + Value(ManuallyDrop<T>), + //~^ ERROR the size for values of type +} + +fn main() {} diff --git a/src/test/ui/union/union-sized-field.stderr b/src/test/ui/union/union-sized-field.stderr new file mode 100644 index 000000000..771e8f261 --- /dev/null +++ b/src/test/ui/union/union-sized-field.stderr @@ -0,0 +1,78 @@ +error[E0277]: the size for values of type `T` cannot be known at compilation time + --> $DIR/union-sized-field.rs:4:12 + | +LL | union Foo<T: ?Sized> { + | - this type parameter needs to be `std::marker::Sized` +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` + | +LL - union Foo<T: ?Sized> { +LL + union Foo<T> { + | +help: borrowed types always have a statically known size + | +LL | value: &ManuallyDrop<T>, + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +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: 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` + | +LL - struct Foo2<T: ?Sized> { +LL + struct Foo2<T> { + | +help: borrowed types always have a statically known size + | +LL | value: &ManuallyDrop<T>, + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +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(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` + | +LL - enum Foo3<T: ?Sized> { +LL + enum Foo3<T> { + | +help: borrowed types always have a statically known size + | +LL | Value(&ManuallyDrop<T>), + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +LL | Value(Box<ManuallyDrop<T>>), + | ++++ + + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/union/union-suggest-field.mirunsafeck.stderr b/src/test/ui/union/union-suggest-field.mirunsafeck.stderr new file mode 100644 index 000000000..58b1f5cb0 --- /dev/null +++ b/src/test/ui/union/union-suggest-field.mirunsafeck.stderr @@ -0,0 +1,27 @@ +error[E0560]: union `U` has no field named `principle` + --> $DIR/union-suggest-field.rs:13:17 + | +LL | let u = U { principle: 0 }; + | ^^^^^^^^^ help: a field with a similar name exists: `principal` + +error[E0609]: no field `principial` on type `U` + --> $DIR/union-suggest-field.rs:17:15 + | +LL | let w = u.principial; + | ^^^^^^^^^^ help: a field with a similar name exists: `principal` + +error[E0615]: attempted to take value of method `calculate` on type `U` + --> $DIR/union-suggest-field.rs:21:15 + | +LL | let y = u.calculate; + | ^^^^^^^^^ method, not a field + | +help: use parentheses to call the method + | +LL | let y = u.calculate(); + | ++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0560, E0609, E0615. +For more information about an error, try `rustc --explain E0560`. diff --git a/src/test/ui/union/union-suggest-field.rs b/src/test/ui/union/union-suggest-field.rs new file mode 100644 index 000000000..601a22a06 --- /dev/null +++ b/src/test/ui/union/union-suggest-field.rs @@ -0,0 +1,24 @@ +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +union U { + principal: u8, +} + +impl U { + fn calculate(&self) {} +} + +fn main() { + let u = U { principle: 0 }; + //~^ ERROR union `U` has no field named `principle` + //~| HELP a field with a similar name exists + //~| SUGGESTION principal + let w = u.principial; //~ ERROR no field `principial` on type `U` + //~| HELP a field with a similar name exists + //~| SUGGESTION principal + + let y = u.calculate; //~ ERROR attempted to take value of method `calculate` on type `U` + //~| HELP use parentheses to call the method + //~| SUGGESTION () +} diff --git a/src/test/ui/union/union-suggest-field.thirunsafeck.stderr b/src/test/ui/union/union-suggest-field.thirunsafeck.stderr new file mode 100644 index 000000000..58b1f5cb0 --- /dev/null +++ b/src/test/ui/union/union-suggest-field.thirunsafeck.stderr @@ -0,0 +1,27 @@ +error[E0560]: union `U` has no field named `principle` + --> $DIR/union-suggest-field.rs:13:17 + | +LL | let u = U { principle: 0 }; + | ^^^^^^^^^ help: a field with a similar name exists: `principal` + +error[E0609]: no field `principial` on type `U` + --> $DIR/union-suggest-field.rs:17:15 + | +LL | let w = u.principial; + | ^^^^^^^^^^ help: a field with a similar name exists: `principal` + +error[E0615]: attempted to take value of method `calculate` on type `U` + --> $DIR/union-suggest-field.rs:21:15 + | +LL | let y = u.calculate; + | ^^^^^^^^^ method, not a field + | +help: use parentheses to call the method + | +LL | let y = u.calculate(); + | ++ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0560, E0609, E0615. +For more information about an error, try `rustc --explain E0560`. diff --git a/src/test/ui/union/union-trait-impl.rs b/src/test/ui/union/union-trait-impl.rs new file mode 100644 index 000000000..6134e91f3 --- /dev/null +++ b/src/test/ui/union/union-trait-impl.rs @@ -0,0 +1,19 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +use std::fmt; + +union U { + a: u8 +} + +impl fmt::Display for U { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + unsafe { write!(f, "Oh hai {}", self.a) } + } +} + +fn main() { + assert_eq!(U { a: 2 }.to_string(), "Oh hai 2"); +} diff --git a/src/test/ui/union/union-transmute.rs b/src/test/ui/union/union-transmute.rs new file mode 100644 index 000000000..1a3b32d55 --- /dev/null +++ b/src/test/ui/union/union-transmute.rs @@ -0,0 +1,27 @@ +// run-pass +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +union U { + a: (u8, u8), + b: u16, +} + +union W { + a: u32, + b: f32, +} + +fn main() { + unsafe { + let mut u = U { a: (1, 1) }; + assert_eq!(u.b, (1 << 8) + 1); + u.b = (2 << 8) + 2; + assert_eq!(u.a, (2, 2)); + + let mut w = W { a: 0b0_11111111_00000000000000000000000 }; + assert_eq!(w.b, f32::INFINITY); + w.b = f32::NEG_INFINITY; + assert_eq!(w.a, 0b1_11111111_00000000000000000000000); + } +} diff --git a/src/test/ui/union/union-unsafe.mir.stderr b/src/test/ui/union/union-unsafe.mir.stderr new file mode 100644 index 000000000..544213dbc --- /dev/null +++ b/src/test/ui/union/union-unsafe.mir.stderr @@ -0,0 +1,75 @@ +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $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]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:46: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:52: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:60:13 + | +LL | let a = u1.a; + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:63:14 + | +LL | let U1 { a } = u1; + | ^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:64:12 + | +LL | if let U1 { a: 12 } = u1 {} + | ^^^^^^^^^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:69: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:73:6 + | +LL | *u3.a = 1; + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs: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 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 new file mode 100644 index 000000000..5e1837a90 --- /dev/null +++ b/src/test/ui/union/union-unsafe.rs @@ -0,0 +1,78 @@ +// revisions: mir thir +// [thir]compile-flags: -Z thir-unsafeck + +use std::mem::ManuallyDrop; +use std::cell::RefCell; + +union U1 { + a: u8 +} + +union U2 { + a: ManuallyDrop<String> +} + +union U3<T> { + a: ManuallyDrop<T> +} + +union U4<T: Copy> { + a: T +} + +union URef { + p: &'static mut i32, +} + +union URefCell { // field that does not drop but is not `Copy`, either + a: (ManuallyDrop<RefCell<i32>>, i32), +} + +fn deref_union_field(mut u: URef) { + // Not an assignment but an access to the union field! + *(u.p) = 13; //~ ERROR access to union field is unsafe +} + +fn assign_noncopy_union_field(mut u: URefCell) { + // FIXME(thir-unsafeck) + 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 +} + +fn generic_noncopy<T: Default>() { + let mut u3 = U3 { a: ManuallyDrop::new(T::default()) }; + u3.a = ManuallyDrop::new(T::default()); // OK (assignment does not drop) + *u3.a = T::default(); //~ ERROR access to union field is unsafe +} + +fn generic_copy<T: Copy + Default>() { + 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 +} + +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: ManuallyDrop::new(String::from("old")) }; // OK + u2.a = ManuallyDrop::new(String::from("new")); // OK (assignment does not drop) + *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")); // OK (assignment does not drop) + *u3.a = String::from("new"); //~ ERROR access to union field is unsafe +} diff --git a/src/test/ui/union/union-unsafe.thir.stderr b/src/test/ui/union/union-unsafe.thir.stderr new file mode 100644 index 000000000..f959fe5bd --- /dev/null +++ b/src/test/ui/union/union-unsafe.thir.stderr @@ -0,0 +1,75 @@ +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs: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]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:46: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:52: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:60:13 + | +LL | let a = u1.a; + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:63:14 + | +LL | let U1 { a } = u1; + | ^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:64:8 + | +LL | if let U1 { a: 12 } = u1 {} + | ^^^^^^^^^^^^^^^^^^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs:69: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:73:6 + | +LL | *u3.a = 1; + | ^^^^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-unsafe.rs: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 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 new file mode 100644 index 000000000..59ab835fb --- /dev/null +++ b/src/test/ui/union/union-unsized.mirunsafeck.stderr @@ -0,0 +1,39 @@ +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/union-unsized.rs:5:8 + | +LL | a: str, + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: no field of a union may have a dynamically sized type + = help: change the field's type to have a statically known size +help: borrowed types always have a statically known size + | +LL | a: &str, + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +LL | a: Box<str>, + | ++++ + + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/union-unsized.rs:13:8 + | +LL | b: str, + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: no field of a union may have a dynamically sized type + = help: change the field's type to have a statically known size +help: borrowed types always have a statically known size + | +LL | b: &str, + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +LL | b: Box<str>, + | ++++ + + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/union/union-unsized.rs b/src/test/ui/union/union-unsized.rs new file mode 100644 index 000000000..8e897d7d3 --- /dev/null +++ b/src/test/ui/union/union-unsized.rs @@ -0,0 +1,17 @@ +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +union U { + a: str, + //~^ ERROR the size for values of type + + b: u8, +} + +union W { + a: u8, + b: str, + //~^ ERROR the size for values of type +} + +fn main() {} diff --git a/src/test/ui/union/union-unsized.thirunsafeck.stderr b/src/test/ui/union/union-unsized.thirunsafeck.stderr new file mode 100644 index 000000000..59ab835fb --- /dev/null +++ b/src/test/ui/union/union-unsized.thirunsafeck.stderr @@ -0,0 +1,39 @@ +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/union-unsized.rs:5:8 + | +LL | a: str, + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: no field of a union may have a dynamically sized type + = help: change the field's type to have a statically known size +help: borrowed types always have a statically known size + | +LL | a: &str, + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +LL | a: Box<str>, + | ++++ + + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/union-unsized.rs:13:8 + | +LL | b: str, + | ^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: no field of a union may have a dynamically sized type + = help: change the field's type to have a statically known size +help: borrowed types always have a statically known size + | +LL | b: &str, + | + +help: the `Box` type always has a statically known size and allocates its contents in the heap + | +LL | b: Box<str>, + | ++++ + + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/union/union-with-drop-fields.mirunsafeck.stderr b/src/test/ui/union/union-with-drop-fields.mirunsafeck.stderr new file mode 100644 index 000000000..93fe996d2 --- /dev/null +++ b/src/test/ui/union/union-with-drop-fields.mirunsafeck.stderr @@ -0,0 +1,39 @@ +error[E0740]: unions cannot contain fields that may need dropping + --> $DIR/union-with-drop-fields.rs:11: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/union-with-drop-fields.rs:19:5 + | +LL | a: S, + | ^^^^ + | + = 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<S>, + | +++++++++++++++++++++++ + + +error[E0740]: unions cannot contain fields that may need dropping + --> $DIR/union-with-drop-fields.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 + +For more information about this error, try `rustc --explain E0740`. diff --git a/src/test/ui/union/union-with-drop-fields.rs b/src/test/ui/union/union-with-drop-fields.rs new file mode 100644 index 000000000..a7a8b69e7 --- /dev/null +++ b/src/test/ui/union/union-with-drop-fields.rs @@ -0,0 +1,31 @@ +// revisions: mirunsafeck thirunsafeck +// [thirunsafeck]compile-flags: -Z thir-unsafeck + +#![allow(dead_code)] + +union U { + a: u8, // OK +} + +union W { + a: String, //~ ERROR unions cannot contain fields that may need dropping + b: String, // OK, only one field is reported +} + +struct S(String); + +// `S` doesn't implement `Drop` trait, but still has non-trivial destructor +union Y { + a: S, //~ ERROR unions cannot contain fields that may need dropping +} + +// We don't know if `T` is trivially-destructable or not until trans +union J<T> { + a: T, //~ ERROR unions cannot contain fields that may need dropping +} + +union H<T: Copy> { + a: T, // OK, `T` is `Copy`, no destructor +} + +fn main() {} diff --git a/src/test/ui/union/union-with-drop-fields.thirunsafeck.stderr b/src/test/ui/union/union-with-drop-fields.thirunsafeck.stderr new file mode 100644 index 000000000..93fe996d2 --- /dev/null +++ b/src/test/ui/union/union-with-drop-fields.thirunsafeck.stderr @@ -0,0 +1,39 @@ +error[E0740]: unions cannot contain fields that may need dropping + --> $DIR/union-with-drop-fields.rs:11: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/union-with-drop-fields.rs:19:5 + | +LL | a: S, + | ^^^^ + | + = 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<S>, + | +++++++++++++++++++++++ + + +error[E0740]: unions cannot contain fields that may need dropping + --> $DIR/union-with-drop-fields.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 + +For more information about this error, try `rustc --explain E0740`. |