about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2022-06-10 22:12:57 -0700
committerRalf Jung <post@ralfj.de>2022-07-13 18:27:28 -0400
commitec7152cdf68506df2b316a47b113666d75712954 (patch)
tree837f17f312e2217c7458d4fbf867f313e38d5f97
parent848d23b57bb788cfdf0df6ec5d83b56b7c19f8ff (diff)
downloadrust-ec7152cdf68506df2b316a47b113666d75712954.tar.gz
rust-ec7152cdf68506df2b316a47b113666d75712954.zip
allow unions with mutable references and tuples of allowed types
-rw-r--r--compiler/rustc_passes/src/stability.rs27
-rw-r--r--src/test/ui/feature-gates/feature-gate-untagged_unions.rs22
-rw-r--r--src/test/ui/feature-gates/feature-gate-untagged_unions.stderr22
3 files changed, 59 insertions, 12 deletions
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index a1697978d66..187a3f95d52 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -772,17 +772,27 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                 let ty = self.tcx.type_of(item.def_id);
                 let ty::Adt(adt_def, substs) = ty.kind() else { bug!() };
 
-                #[allow(rustc::usage_of_qualified_ty)] // `Ty` is the wrong type here, we really want `ty::Ty`.
+                #[allow(rustc::usage_of_qualified_ty)] // `Ty` is `hir::Ty` here, we really want `ty::Ty`.
                 fn allowed_union_field<'tcx>(
                     tcx: TyCtxt<'tcx>,
                     param_env: ty::ParamEnv<'tcx>,
                     ty: ty::Ty<'tcx>,
                 ) -> bool {
-                    ty.ty_adt_def().map_or(false, |adt_def| adt_def.is_manually_drop())
-                        || ty.is_copy_modulo_regions(tcx.at(DUMMY_SP), param_env)
+                    // We don't just accept all !needs_drop fields, due to semver concerns.
+                    match ty.kind() {
+                        ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check)
+                        ty::Tuple(tys) => {
+                            // allow tuples of allowed types
+                            tys.iter().all(|ty| allowed_union_field(tcx, param_env, ty))
+                        }
+                        _ => {
+                            ty.ty_adt_def().map_or(false, |adt_def| adt_def.is_manually_drop())
+                                || ty.is_copy_modulo_regions(tcx.at(DUMMY_SP), param_env)
+                        }
+                    }
                 }
 
-                // Non-`Copy` fields are unstable, except for `ManuallyDrop`.
+                // `allowed_union_field` determines which fields are allowed on stable.
                 let param_env = self.tcx.param_env(item.def_id);
                 for field in &adt_def.non_enum_variant().fields {
                     let field_ty = field.ty(self.tcx, substs);
@@ -799,10 +809,17 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
                                 &self.tcx.sess.parse_sess,
                                 sym::untagged_unions,
                                 self.tcx.def_span(field.did),
-                                "unions with non-`Copy` fields other than `ManuallyDrop<T>` are unstable",
+                                "unions with non-`Copy` fields other than `ManuallyDrop<T>`, \
+                                references, and tuples of such types are unstable",
                             )
                             .emit();
                         }
+                    } else {
+                        // We allow this field. Make extra sure it does not drop.
+                        assert!(
+                            !field_ty.needs_drop(self.tcx, param_env),
+                            "we should accept no maybe-dropping union fields"
+                        );
                     }
                 }
             }
diff --git a/src/test/ui/feature-gates/feature-gate-untagged_unions.rs b/src/test/ui/feature-gates/feature-gate-untagged_unions.rs
index af8d8e92b20..7bc5c42b775 100644
--- a/src/test/ui/feature-gates/feature-gate-untagged_unions.rs
+++ b/src/test/ui/feature-gates/feature-gate-untagged_unions.rs
@@ -1,4 +1,5 @@
 // ignore-tidy-linelength
+use std::mem::ManuallyDrop;
 
 union U1 { // OK
     a: u8,
@@ -9,7 +10,11 @@ union U2<T: Copy> { // OK
 }
 
 union U22<T> { // OK
-    a: std::mem::ManuallyDrop<T>,
+    a: ManuallyDrop<T>,
+}
+
+union U23<T> { // OK
+    a: (ManuallyDrop<T>, i32),
 }
 
 union U3 {
@@ -17,7 +22,7 @@ union U3 {
 }
 
 union U32 { // field that does not drop but is not `Copy`, either -- this is the real feature gate test!
-    a: std::cell::RefCell<i32>, //~ ERROR unions with non-`Copy` fields other than `ManuallyDrop<T>` are unstable
+    a: std::cell::RefCell<i32>, //~ ERROR unions with non-`Copy` fields other than `ManuallyDrop<T>`, references, and tuples of such types are unstable
 }
 
 union U4<T> {
@@ -32,4 +37,17 @@ 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 U6 { // OK
+    s: &'static i32,
+    m: &'static mut i32,
+}
+
+union U7<T> { // OK
+    f: (&'static mut i32, ManuallyDrop<T>, i32),
+}
+
 fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr b/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr
index 9e4a89f80c8..e1f10c6de33 100644
--- a/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr
+++ b/src/test/ui/feature-gates/feature-gate-untagged_unions.stderr
@@ -1,5 +1,5 @@
-error[E0658]: unions with non-`Copy` fields other than `ManuallyDrop<T>` are unstable
-  --> $DIR/feature-gate-untagged_unions.rs:20:5
+error[E0658]: unions with non-`Copy` fields other than `ManuallyDrop<T>`, references, and tuples of such types are unstable
+  --> $DIR/feature-gate-untagged_unions.rs:25:5
    |
 LL |     a: std::cell::RefCell<i32>,
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     a: std::cell::RefCell<i32>,
    = help: add `#![feature(untagged_unions)]` to the crate attributes to enable
 
 error[E0740]: unions cannot contain fields that may need dropping
-  --> $DIR/feature-gate-untagged_unions.rs:16:5
+  --> $DIR/feature-gate-untagged_unions.rs:21:5
    |
 LL |     a: String,
    |     ^^^^^^^^^
@@ -20,7 +20,7 @@ LL |     a: std::mem::ManuallyDrop<String>,
    |        +++++++++++++++++++++++      +
 
 error[E0740]: unions cannot contain fields that may need dropping
-  --> $DIR/feature-gate-untagged_unions.rs:24:5
+  --> $DIR/feature-gate-untagged_unions.rs:29:5
    |
 LL |     a: T,
    |     ^^^^
@@ -31,7 +31,19 @@ help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>
 LL |     a: std::mem::ManuallyDrop<T>,
    |        +++++++++++++++++++++++ +
 
-error: aborting due to 3 previous errors
+error[E0740]: unions cannot contain fields that may need dropping
+  --> $DIR/feature-gate-untagged_unions.rs:41: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: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0658, E0740.
 For more information about an error, try `rustc --explain E0658`.