about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2020-08-16 10:24:10 +0200
committerRalf Jung <post@ralfj.de>2020-08-31 10:14:30 +0200
commit44defaea3a2dd2e7e40336d3609df12b83db424a (patch)
treee0d7f02d3a24db1c77603ef088f27bd7b363b56e
parentec0924f9649ea472782df6d21595714cb594f038 (diff)
downloadrust-44defaea3a2dd2e7e40336d3609df12b83db424a.tar.gz
rust-44defaea3a2dd2e7e40336d3609df12b83db424a.zip
also detect DerefMut in nested union fields
-rw-r--r--compiler/rustc_typeck/src/check/place_op.rs29
-rw-r--r--src/test/ui/union/union-deref.rs10
-rw-r--r--src/test/ui/union/union-deref.stderr13
3 files changed, 36 insertions, 16 deletions
diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs
index be2bc491c31..54b3fdd837e 100644
--- a/compiler/rustc_typeck/src/check/place_op.rs
+++ b/compiler/rustc_typeck/src/check/place_op.rs
@@ -211,13 +211,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
 
         // Fix up autoderefs and derefs.
+        let mut inside_union = false;
         for (i, &expr) in exprs.iter().rev().enumerate() {
             debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
 
+            let mut source = self.node_ty(expr.hir_id);
+            if matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnDeref, _)) {
+                // Clear previous flag; after a pointer indirection it does not apply any more.
+                inside_union = false;
+            }
+            if source.ty_adt_def().map_or(false, |adt| adt.is_union()) {
+                inside_union = true;
+            }
             // Fix up the autoderefs. Autorefs can only occur immediately preceding
             // overloaded place ops, and will be fixed by them in order to get
             // the correct region.
-            let mut source = self.node_ty(expr.hir_id);
             // Do not mutate adjustments in place, but rather take them,
             // and replace them after mutating them, to avoid having the
             // typeck results borrowed during (`deref_mut`) method resolution.
@@ -238,17 +246,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             }
                             // If this is a union field, also throw an error.
                             // Union fields should not get mutable auto-deref'd (see RFC 2514).
-                            if let hir::ExprKind::Field(ref outer_expr, _) = expr.kind {
-                                let ty = self.node_ty(outer_expr.hir_id);
-                                if ty.ty_adt_def().map_or(false, |adt| adt.is_union()) {
-                                    let mut err = self.tcx.sess.struct_span_err(
-                                        expr.span,
-                                        "not automatically applying `DerefMut` on union field",
-                                    );
-                                    err.help("writing to this field calls the destructor for the old value");
-                                    err.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor");
-                                    err.emit();
-                                }
+                            if inside_union {
+                                let mut err = self.tcx.sess.struct_span_err(
+                                    expr.span,
+                                    "not automatically applying `DerefMut` on union field",
+                                );
+                                err.help("writing to this field calls the destructor for the old value");
+                                err.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor");
+                                err.emit();
                             }
                         }
                     }
diff --git a/src/test/ui/union/union-deref.rs b/src/test/ui/union/union-deref.rs
index 61bbe73354f..4578110cd54 100644
--- a/src/test/ui/union/union-deref.rs
+++ b/src/test/ui/union/union-deref.rs
@@ -4,10 +4,16 @@
 
 use std::mem::ManuallyDrop;
 
-union U<T> { x:(), f: ManuallyDrop<(T,)> }
+union U1<T> { x:(), f: ManuallyDrop<(T,)> }
+
+union U2<T> { x:(), f: (ManuallyDrop<(T,)>,) }
 
 fn main() {
-    let mut u : U<Vec<i32>> = U { x: () };
+    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 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 union field
 }
diff --git a/src/test/ui/union/union-deref.stderr b/src/test/ui/union/union-deref.stderr
index 66cc90cbd3d..93374436d2f 100644
--- a/src/test/ui/union/union-deref.stderr
+++ b/src/test/ui/union/union-deref.stderr
@@ -1,5 +1,5 @@
 error: not automatically applying `DerefMut` on union field
-  --> $DIR/union-deref.rs:12:14
+  --> $DIR/union-deref.rs:14:14
    |
 LL |     unsafe { u.f.0 = Vec::new() };
    |              ^^^
@@ -7,5 +7,14 @@ LL |     unsafe { u.f.0 = Vec::new() };
    = help: writing to this field 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 previous error
+error: not automatically applying `DerefMut` on union field
+  --> $DIR/union-deref.rs:18:14
+   |
+LL |     unsafe { u.f.0.0 = Vec::new() };
+   |              ^^^^^^^
+   |
+   = help: writing to this field 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 2 previous errors