Skip to content

Commit 3ac1df8

Browse files
committedNov 20, 2020
consider assignments of union field of ManuallyDrop type safe
1 parent 3d3c8c5 commit 3ac1df8

File tree

4 files changed

+35
-56
lines changed

4 files changed

+35
-56
lines changed
 

‎compiler/rustc_middle/src/mir/query.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub enum UnsafetyViolationDetails {
4646
UseOfMutableStatic,
4747
UseOfExternStatic,
4848
DerefOfRawPointer,
49-
AssignToNonCopyUnionField,
49+
AssignToDroppingUnionField,
5050
AccessToUnionField,
5151
MutationOfLayoutConstrainedField,
5252
BorrowOfLayoutConstrainedField,
@@ -94,8 +94,8 @@ impl UnsafetyViolationDetails {
9494
"raw pointers may be NULL, dangling or unaligned; they can violate aliasing rules \
9595
and cause data races: all of these are undefined behavior",
9696
),
97-
AssignToNonCopyUnionField => (
98-
"assignment to non-`Copy` union field",
97+
AssignToDroppingUnionField => (
98+
"assignment to union field that needs dropping",
9999
"the previous content of the field will be dropped, which causes undefined \
100100
behavior if the field was not properly initialized",
101101
),

‎compiler/rustc_mir/src/transform/check_unsafety.rs

+28-25
Original file line numberDiff line numberDiff line change
@@ -235,37 +235,40 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
235235
UnsafetyViolationKind::GeneralAndConstFn,
236236
UnsafetyViolationDetails::DerefOfRawPointer,
237237
),
238-
ty::Adt(adt, _) => {
239-
if adt.is_union() {
240-
if context == PlaceContext::MutatingUse(MutatingUseContext::Store)
241-
|| context == PlaceContext::MutatingUse(MutatingUseContext::Drop)
242-
|| context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput)
243-
{
244-
let elem_ty = match elem {
245-
ProjectionElem::Field(_, ty) => ty,
246-
_ => span_bug!(
247-
self.source_info.span,
248-
"non-field projection {:?} from union?",
249-
place
250-
),
251-
};
252-
if !elem_ty.is_copy_modulo_regions(
238+
ty::Adt(adt, _) if adt.is_union() => {
239+
if context == PlaceContext::MutatingUse(MutatingUseContext::Store)
240+
|| 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
252+
.ty_adt_def()
253+
.map_or(false, |adt_def| adt_def.is_manually_drop());
254+
let nodrop = manually_drop
255+
|| elem_ty.is_copy_modulo_regions(
253256
self.tcx.at(self.source_info.span),
254257
self.param_env,
255-
) {
256-
self.require_unsafe(
257-
UnsafetyViolationKind::GeneralAndConstFn,
258-
UnsafetyViolationDetails::AssignToNonCopyUnionField,
259-
)
260-
} else {
261-
// write to non-move union, safe
262-
}
263-
} else {
258+
);
259+
if !nodrop {
264260
self.require_unsafe(
265261
UnsafetyViolationKind::GeneralAndConstFn,
266-
UnsafetyViolationDetails::AccessToUnionField,
262+
UnsafetyViolationDetails::AssignToDroppingUnionField,
267263
)
264+
} else {
265+
// write to non-drop union field, safe
268266
}
267+
} else {
268+
self.require_unsafe(
269+
UnsafetyViolationKind::GeneralAndConstFn,
270+
UnsafetyViolationDetails::AccessToUnionField,
271+
)
269272
}
270273
}
271274
_ => {}

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ union U4<T: Copy> {
1818

1919
fn generic_noncopy<T: Default>() {
2020
let mut u3 = U3 { a: ManuallyDrop::new(T::default()) };
21-
u3.a = ManuallyDrop::new(T::default()); //~ ERROR assignment to non-`Copy` union field is unsafe
21+
u3.a = ManuallyDrop::new(T::default()); // OK (assignment does not drop)
2222
*u3.a = T::default(); //~ ERROR access to union field is unsafe
2323
}
2424

@@ -41,14 +41,14 @@ fn main() {
4141
// let U1 { .. } = u1; // OK
4242

4343
let mut u2 = U2 { a: ManuallyDrop::new(String::from("old")) }; // OK
44-
u2.a = ManuallyDrop::new(String::from("new")); //~ ERROR assignment to non-`Copy` union
44+
u2.a = ManuallyDrop::new(String::from("new")); // OK (assignment does not drop)
4545
*u2.a = String::from("new"); //~ ERROR access to union field is unsafe
4646

4747
let mut u3 = U3 { a: ManuallyDrop::new(0) }; // OK
4848
u3.a = ManuallyDrop::new(1); // OK
4949
*u3.a = 1; //~ ERROR access to union field is unsafe
5050

5151
let mut u3 = U3 { a: ManuallyDrop::new(String::from("old")) }; // OK
52-
u3.a = ManuallyDrop::new(String::from("new")); //~ ERROR assignment to non-`Copy` union
52+
u3.a = ManuallyDrop::new(String::from("new")); // OK (assignment does not drop)
5353
*u3.a = String::from("new"); //~ ERROR access to union field is unsafe
5454
}

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

+1-25
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
error[E0133]: assignment to non-`Copy` union field is unsafe and requires unsafe function or block
2-
--> $DIR/union-unsafe.rs:21:5
3-
|
4-
LL | u3.a = ManuallyDrop::new(T::default());
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to non-`Copy` union field
6-
|
7-
= note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
8-
91
error[E0133]: access to union field is unsafe and requires unsafe function or block
102
--> $DIR/union-unsafe.rs:22:6
113
|
@@ -46,14 +38,6 @@ LL | if let U1 { a: 12 } = u1 {}
4638
|
4739
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
4840

49-
error[E0133]: assignment to non-`Copy` union field is unsafe and requires unsafe function or block
50-
--> $DIR/union-unsafe.rs:44:5
51-
|
52-
LL | u2.a = ManuallyDrop::new(String::from("new"));
53-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to non-`Copy` union field
54-
|
55-
= note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
56-
5741
error[E0133]: access to union field is unsafe and requires unsafe function or block
5842
--> $DIR/union-unsafe.rs:45:6
5943
|
@@ -70,14 +54,6 @@ LL | *u3.a = 1;
7054
|
7155
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
7256

73-
error[E0133]: assignment to non-`Copy` union field is unsafe and requires unsafe function or block
74-
--> $DIR/union-unsafe.rs:52:5
75-
|
76-
LL | u3.a = ManuallyDrop::new(String::from("new"));
77-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to non-`Copy` union field
78-
|
79-
= note: the previous content of the field will be dropped, which causes undefined behavior if the field was not properly initialized
80-
8157
error[E0133]: access to union field is unsafe and requires unsafe function or block
8258
--> $DIR/union-unsafe.rs:53:6
8359
|
@@ -86,6 +62,6 @@ LL | *u3.a = String::from("new");
8662
|
8763
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
8864

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

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

0 commit comments

Comments
 (0)
Please sign in to comment.