also detect DerefMut in nested union fields

This commit is contained in:
Ralf Jung 2020-08-16 10:24:10 +02:00
parent ec0924f964
commit 44defaea3a
3 changed files with 36 additions and 16 deletions

View file

@ -211,13 +211,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs); debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
// Fix up autoderefs and derefs. // Fix up autoderefs and derefs.
let mut inside_union = false;
for (i, &expr) in exprs.iter().rev().enumerate() { for (i, &expr) in exprs.iter().rev().enumerate() {
debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr); 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 // Fix up the autoderefs. Autorefs can only occur immediately preceding
// overloaded place ops, and will be fixed by them in order to get // overloaded place ops, and will be fixed by them in order to get
// the correct region. // the correct region.
let mut source = self.node_ty(expr.hir_id);
// Do not mutate adjustments in place, but rather take them, // Do not mutate adjustments in place, but rather take them,
// and replace them after mutating them, to avoid having the // and replace them after mutating them, to avoid having the
// typeck results borrowed during (`deref_mut`) method resolution. // typeck results borrowed during (`deref_mut`) method resolution.
@ -238,9 +246,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
// If this is a union field, also throw an error. // If this is a union field, also throw an error.
// Union fields should not get mutable auto-deref'd (see RFC 2514). // Union fields should not get mutable auto-deref'd (see RFC 2514).
if let hir::ExprKind::Field(ref outer_expr, _) = expr.kind { if inside_union {
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( let mut err = self.tcx.sess.struct_span_err(
expr.span, expr.span,
"not automatically applying `DerefMut` on union field", "not automatically applying `DerefMut` on union field",
@ -251,7 +257,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
} }
} }
}
source = adjustment.target; source = adjustment.target;
} }
self.typeck_results.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments); self.typeck_results.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);

View file

@ -4,10 +4,16 @@
use std::mem::ManuallyDrop; 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() { 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() }; // explicit deref, this compiles
unsafe { u.f.0 = Vec::new() }; //~ERROR not automatically applying `DerefMut` on union field 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
} }

View file

@ -1,5 +1,5 @@
error: not automatically applying `DerefMut` on union field 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() }; 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: 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 = 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