Skip to content

Commit ec0924f

Browse files
committed
do not apply DerefMut on union field
1 parent 8ed5cb5 commit ec0924f

File tree

3 files changed

+39
-1
lines changed

3 files changed

+39
-1
lines changed

compiler/rustc_typeck/src/check/place_op.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
193193
/// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
194194
/// into `DerefMut` and `IndexMut` respectively.
195195
///
196-
/// This is a second pass of typechecking derefs/indices. We need this we do not
196+
/// This is a second pass of typechecking derefs/indices. We need this because we do not
197197
/// always know whether a place needs to be mutable or not in the first pass.
198198
/// This happens whether there is an implicit mutable reborrow, e.g. when the type
199199
/// is used as the receiver of a method call.
@@ -236,6 +236,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
236236
if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
237237
*deref = OverloadedDeref { region, mutbl };
238238
}
239+
// If this is a union field, also throw an error.
240+
// 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+
}
252+
}
239253
}
240254
}
241255
source = adjustment.target;

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//! Test the part of RFC 2514 that is about not applying `DerefMut` coercions
2+
//! of union fields.
3+
#![feature(untagged_unions)]
4+
5+
use std::mem::ManuallyDrop;
6+
7+
union U<T> { x:(), f: ManuallyDrop<(T,)> }
8+
9+
fn main() {
10+
let mut u : U<Vec<i32>> = U { x: () };
11+
unsafe { (*u.f).0 = Vec::new() }; // explicit deref, this compiles
12+
unsafe { u.f.0 = Vec::new() }; //~ERROR not automatically applying `DerefMut` on union field
13+
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: not automatically applying `DerefMut` on union field
2+
--> $DIR/union-deref.rs:12:14
3+
|
4+
LL | unsafe { u.f.0 = Vec::new() };
5+
| ^^^
6+
|
7+
= help: writing to this field calls the destructor for the old value
8+
= help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor
9+
10+
error: aborting due to previous error
11+

0 commit comments

Comments
 (0)