Skip to content

Commit 44defae

Browse files
committed
also detect DerefMut in nested union fields
1 parent ec0924f commit 44defae

File tree

3 files changed

+36
-16
lines changed

3 files changed

+36
-16
lines changed

compiler/rustc_typeck/src/check/place_op.rs

+17-12
Original file line numberDiff line numberDiff line change
@@ -211,13 +211,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
211211
debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
212212

213213
// Fix up autoderefs and derefs.
214+
let mut inside_union = false;
214215
for (i, &expr) in exprs.iter().rev().enumerate() {
215216
debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
216217

218+
let mut source = self.node_ty(expr.hir_id);
219+
if matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnDeref, _)) {
220+
// Clear previous flag; after a pointer indirection it does not apply any more.
221+
inside_union = false;
222+
}
223+
if source.ty_adt_def().map_or(false, |adt| adt.is_union()) {
224+
inside_union = true;
225+
}
217226
// Fix up the autoderefs. Autorefs can only occur immediately preceding
218227
// overloaded place ops, and will be fixed by them in order to get
219228
// the correct region.
220-
let mut source = self.node_ty(expr.hir_id);
221229
// Do not mutate adjustments in place, but rather take them,
222230
// and replace them after mutating them, to avoid having the
223231
// typeck results borrowed during (`deref_mut`) method resolution.
@@ -238,17 +246,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
238246
}
239247
// If this is a union field, also throw an error.
240248
// Union fields should not get mutable auto-deref'd (see RFC 2514).
241-
if let hir::ExprKind::Field(ref outer_expr, _) = expr.kind {
242-
let ty = self.node_ty(outer_expr.hir_id);
243-
if ty.ty_adt_def().map_or(false, |adt| adt.is_union()) {
244-
let mut err = self.tcx.sess.struct_span_err(
245-
expr.span,
246-
"not automatically applying `DerefMut` on union field",
247-
);
248-
err.help("writing to this field calls the destructor for the old value");
249-
err.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor");
250-
err.emit();
251-
}
249+
if inside_union {
250+
let mut err = self.tcx.sess.struct_span_err(
251+
expr.span,
252+
"not automatically applying `DerefMut` on union field",
253+
);
254+
err.help("writing to this field calls the destructor for the old value");
255+
err.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor");
256+
err.emit();
252257
}
253258
}
254259
}

src/test/ui/union/union-deref.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@
44

55
use std::mem::ManuallyDrop;
66

7-
union U<T> { x:(), f: ManuallyDrop<(T,)> }
7+
union U1<T> { x:(), f: ManuallyDrop<(T,)> }
8+
9+
union U2<T> { x:(), f: (ManuallyDrop<(T,)>,) }
810

911
fn main() {
10-
let mut u : U<Vec<i32>> = U { x: () };
12+
let mut u : U1<Vec<i32>> = U1 { x: () };
1113
unsafe { (*u.f).0 = Vec::new() }; // explicit deref, this compiles
1214
unsafe { u.f.0 = Vec::new() }; //~ERROR not automatically applying `DerefMut` on union field
15+
16+
let mut u : U2<Vec<i32>> = U2 { x: () };
17+
unsafe { (*u.f.0).0 = Vec::new() }; // explicit deref, this compiles
18+
unsafe { u.f.0.0 = Vec::new() }; //~ERROR not automatically applying `DerefMut` on union field
1319
}

src/test/ui/union/union-deref.stderr

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
error: not automatically applying `DerefMut` on union field
2-
--> $DIR/union-deref.rs:12:14
2+
--> $DIR/union-deref.rs:14:14
33
|
44
LL | unsafe { u.f.0 = Vec::new() };
55
| ^^^
66
|
77
= help: writing to this field calls the destructor for the old value
88
= help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor
99

10-
error: aborting due to previous error
10+
error: not automatically applying `DerefMut` on union field
11+
--> $DIR/union-deref.rs:18:14
12+
|
13+
LL | unsafe { u.f.0.0 = Vec::new() };
14+
| ^^^^^^^
15+
|
16+
= help: writing to this field calls the destructor for the old value
17+
= help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor
18+
19+
error: aborting due to 2 previous errors
1120

0 commit comments

Comments
 (0)