Skip to content

Commit 64856e2

Browse files
committed
adjust union access unsafety check logic to take into account Deref and the actual type of the assignment
1 parent 3ac1df8 commit 64856e2

File tree

3 files changed

+74
-24
lines changed

3 files changed

+74
-24
lines changed

compiler/rustc_mir/src/transform/check_unsafety.rs

+20-15
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
190190
}
191191
}
192192

193-
for (i, elem) in place.projection.iter().enumerate() {
193+
for (i, _elem) in place.projection.iter().enumerate() {
194194
let proj_base = &place.projection[..i];
195195
if context.is_borrow() {
196196
if util::is_disaligned(self.tcx, self.body, self.param_env, *place) {
@@ -236,31 +236,36 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
236236
UnsafetyViolationDetails::DerefOfRawPointer,
237237
),
238238
ty::Adt(adt, _) if adt.is_union() => {
239-
if context == PlaceContext::MutatingUse(MutatingUseContext::Store)
239+
let assign_to_field = context
240+
== PlaceContext::MutatingUse(MutatingUseContext::Store)
240241
|| context == PlaceContext::MutatingUse(MutatingUseContext::Drop)
241-
|| context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput)
242-
{
243-
let elem_ty = match elem {
244-
ProjectionElem::Field(_, ty) => ty,
245-
_ => span_bug!(
246-
self.source_info.span,
247-
"non-field projection {:?} from union?",
248-
place
249-
),
250-
};
251-
let manually_drop = elem_ty
242+
|| context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput);
243+
// If there is a `Deref` further along the projection chain, this is *not* an
244+
// assignment to a union field. In that case the union field is just read to
245+
// obtain the pointer/reference.
246+
let assign_to_field = assign_to_field
247+
&& !place.projection[i..]
248+
.iter()
249+
.any(|elem| matches!(elem, ProjectionElem::Deref));
250+
// If this is just an assignment, determine if the assigned type needs dropping.
251+
if assign_to_field {
252+
// We have to check the actual type of the assignment, as that determines if the
253+
// old value is being dropped.
254+
let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty;
255+
// To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
256+
let manually_drop = assigned_ty
252257
.ty_adt_def()
253258
.map_or(false, |adt_def| adt_def.is_manually_drop());
254259
let nodrop = manually_drop
255-
|| elem_ty.is_copy_modulo_regions(
260+
|| assigned_ty.is_copy_modulo_regions(
256261
self.tcx.at(self.source_info.span),
257262
self.param_env,
258263
);
259264
if !nodrop {
260265
self.require_unsafe(
261266
UnsafetyViolationKind::GeneralAndConstFn,
262267
UnsafetyViolationDetails::AssignToDroppingUnionField,
263-
)
268+
);
264269
} else {
265270
// write to non-drop union field, safe
266271
}

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

+21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
#![feature(untagged_unions)]
12
use std::mem::ManuallyDrop;
3+
use std::cell::RefCell;
24

35
union U1 {
46
a: u8
@@ -16,6 +18,25 @@ union U4<T: Copy> {
1618
a: T
1719
}
1820

21+
union URef {
22+
p: &'static mut i32,
23+
}
24+
25+
union URefCell { // field that does not drop but is not `Copy`, either
26+
a: (RefCell<i32>, i32),
27+
}
28+
29+
fn deref_union_field(mut u: URef) {
30+
// Not an assignment but an access to the union field!
31+
*(u.p) = 13; //~ ERROR access to union field is unsafe
32+
}
33+
34+
fn assign_noncopy_union_field(mut u: URefCell) {
35+
u.a = (RefCell::new(0), 1); //~ ERROR assignment to union field that needs dropping
36+
u.a.0 = RefCell::new(0); //~ ERROR assignment to union field that needs dropping
37+
u.a.1 = 1; // OK
38+
}
39+
1940
fn generic_noncopy<T: Default>() {
2041
let mut u3 = U3 { a: ManuallyDrop::new(T::default()) };
2142
u3.a = ManuallyDrop::new(T::default()); // OK (assignment does not drop)

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

+33-9
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,91 @@
11
error[E0133]: access to union field is unsafe and requires unsafe function or block
2-
--> $DIR/union-unsafe.rs:22:6
2+
--> $DIR/union-unsafe.rs:31:5
3+
|
4+
LL | *(u.p) = 13;
5+
| ^^^^^^^^^^^ access to union field
6+
|
7+
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
8+
9+
error[E0133]: assignment to union field that needs dropping is unsafe and requires unsafe function or block
10+
--> $DIR/union-unsafe.rs:35:5
11+
|
12+
LL | u.a = (RefCell::new(0), 1);
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to union field that needs dropping
14+
|
15+
= note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
16+
17+
error[E0133]: assignment to union field that needs dropping is unsafe and requires unsafe function or block
18+
--> $DIR/union-unsafe.rs:36:5
19+
|
20+
LL | u.a.0 = RefCell::new(0);
21+
| ^^^^^^^^^^^^^^^^^^^^^^^ assignment to union field that needs dropping
22+
|
23+
= note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
24+
25+
error[E0133]: access to union field is unsafe and requires unsafe function or block
26+
--> $DIR/union-unsafe.rs:43:6
327
|
428
LL | *u3.a = T::default();
529
| ^^^^ access to union field
630
|
731
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
832

933
error[E0133]: access to union field is unsafe and requires unsafe function or block
10-
--> $DIR/union-unsafe.rs:28:6
34+
--> $DIR/union-unsafe.rs:49:6
1135
|
1236
LL | *u3.a = T::default();
1337
| ^^^^ access to union field
1438
|
1539
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
1640

1741
error[E0133]: access to union field is unsafe and requires unsafe function or block
18-
--> $DIR/union-unsafe.rs:36:13
42+
--> $DIR/union-unsafe.rs:57:13
1943
|
2044
LL | let a = u1.a;
2145
| ^^^^ access to union field
2246
|
2347
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
2448

2549
error[E0133]: access to union field is unsafe and requires unsafe function or block
26-
--> $DIR/union-unsafe.rs:39:14
50+
--> $DIR/union-unsafe.rs:60:14
2751
|
2852
LL | let U1 { a } = u1;
2953
| ^ access to union field
3054
|
3155
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
3256

3357
error[E0133]: access to union field is unsafe and requires unsafe function or block
34-
--> $DIR/union-unsafe.rs:40:20
58+
--> $DIR/union-unsafe.rs:61:20
3559
|
3660
LL | if let U1 { a: 12 } = u1 {}
3761
| ^^ access to union field
3862
|
3963
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
4064

4165
error[E0133]: access to union field is unsafe and requires unsafe function or block
42-
--> $DIR/union-unsafe.rs:45:6
66+
--> $DIR/union-unsafe.rs:66:6
4367
|
4468
LL | *u2.a = String::from("new");
4569
| ^^^^ access to union field
4670
|
4771
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
4872

4973
error[E0133]: access to union field is unsafe and requires unsafe function or block
50-
--> $DIR/union-unsafe.rs:49:6
74+
--> $DIR/union-unsafe.rs:70:6
5175
|
5276
LL | *u3.a = 1;
5377
| ^^^^ access to union field
5478
|
5579
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
5680

5781
error[E0133]: access to union field is unsafe and requires unsafe function or block
58-
--> $DIR/union-unsafe.rs:53:6
82+
--> $DIR/union-unsafe.rs:74:6
5983
|
6084
LL | *u3.a = String::from("new");
6185
| ^^^^ access to union field
6286
|
6387
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
6488

65-
error: aborting due to 8 previous errors
89+
error: aborting due to 11 previous errors
6690

6791
For more information about this error, try `rustc --explain E0133`.

0 commit comments

Comments
 (0)