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 { x:(), f: ManuallyDrop<(T,)> } +union U1 { x:(), f: ManuallyDrop<(T,)> } + +union U2 { x:(), f: (ManuallyDrop<(T,)>,) } fn main() { - let mut u : U> = U { x: () }; + let mut u : U1> = 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> = 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