Skip to content

Commit aba8489

Browse files
authored
Rollup merge of rust-lang#62330 - SimonSapin:no-drop-in-union-fields, r=RalfJung
Change untagged_unions to not allow union fields with drop This is a rebase of rust-lang#56440, massaged to solve merge conflicts and make the test suite pass. Change untagged_unions to not allow union fields with drop Union fields may now never have a type with attached destructor. This for example allows unions to use arbitrary field types only by wrapping them in `ManuallyDrop` (or similar). The stable rule remains, that union fields must be `Copy`. We use the new rule for the `untagged_union` feature. Tracking issue: rust-lang#55149
2 parents 10f12fe + 875bdd5 commit aba8489

40 files changed

+454
-265
lines changed

src/doc/rustc/src/lints/listing/warn-by-default.md

-24
Original file line numberDiff line numberDiff line change
@@ -596,30 +596,6 @@ warning: function cannot return without recursing
596596
|
597597
```
598598

599-
## unions-with-drop-fields
600-
601-
This lint detects use of unions that contain fields with possibly non-trivial drop code. Some
602-
example code that triggers this lint:
603-
604-
```rust
605-
#![feature(untagged_unions)]
606-
607-
union U {
608-
s: String,
609-
}
610-
```
611-
612-
This will produce:
613-
614-
```text
615-
warning: union contains a field with possibly non-trivial drop code, drop code of union fields is ignored when dropping the union
616-
--> src/main.rs:4:5
617-
|
618-
4 | s: String,
619-
| ^^^^^^^^^
620-
|
621-
```
622-
623599
## unknown-lints
624600

625601
This lint detects unrecognized lint attribute. Some

src/librustc/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,5 @@ parking_lot = "0.9"
3636
byteorder = { version = "1.3" }
3737
chalk-engine = { version = "0.9.0", default-features=false }
3838
rustc_fs_util = { path = "../librustc_fs_util" }
39-
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
39+
smallvec = { version = "0.6.8", features = ["union", "may_dangle"] }
4040
measureme = "0.3"

src/librustc/ty/util.rs

+2
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,8 @@ impl<'tcx> ty::TyS<'tcx> {
818818
///
819819
/// (Note that this implies that if `ty` has a destructor attached,
820820
/// then `needs_drop` will definitely return `true` for `ty`.)
821+
///
822+
/// Note that this method is used to check eligible types in unions.
821823
#[inline]
822824
pub fn needs_drop(&'tcx self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
823825
tcx.needs_drop_raw(param_env.and(self)).0

src/librustc_lint/builtin.rs

-30
Original file line numberDiff line numberDiff line change
@@ -980,35 +980,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnstableFeatures {
980980
}
981981
}
982982

983-
declare_lint! {
984-
UNIONS_WITH_DROP_FIELDS,
985-
Warn,
986-
"use of unions that contain fields with possibly non-trivial drop code"
987-
}
988-
989-
declare_lint_pass!(
990-
/// Lint for unions that contain fields with possibly non-trivial destructors.
991-
UnionsWithDropFields => [UNIONS_WITH_DROP_FIELDS]
992-
);
993-
994-
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnionsWithDropFields {
995-
fn check_item(&mut self, ctx: &LateContext<'_, '_>, item: &hir::Item) {
996-
if let hir::ItemKind::Union(ref vdata, _) = item.kind {
997-
for field in vdata.fields() {
998-
let field_ty = ctx.tcx.type_of(
999-
ctx.tcx.hir().local_def_id(field.hir_id));
1000-
if field_ty.needs_drop(ctx.tcx, ctx.param_env) {
1001-
ctx.span_lint(UNIONS_WITH_DROP_FIELDS,
1002-
field.span,
1003-
"union contains a field with possibly non-trivial drop code, \
1004-
drop code of union fields is ignored when dropping the union");
1005-
return;
1006-
}
1007-
}
1008-
}
1009-
}
1010-
}
1011-
1012983
declare_lint! {
1013984
pub UNREACHABLE_PUB,
1014985
Allow,
@@ -1288,7 +1259,6 @@ declare_lint_pass!(
12881259
NO_MANGLE_GENERIC_ITEMS,
12891260
MUTABLE_TRANSMUTES,
12901261
UNSTABLE_FEATURES,
1291-
UNIONS_WITH_DROP_FIELDS,
12921262
UNREACHABLE_PUB,
12931263
TYPE_ALIAS_BOUNDS,
12941264
TRIVIAL_BOUNDS

src/librustc_lint/lib.rs

-3
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,6 @@ macro_rules! late_lint_mod_passes {
164164
// Depends on referenced function signatures in expressions
165165
MutableTransmutes: MutableTransmutes,
166166

167-
// Depends on types of fields, checks if they implement Drop
168-
UnionsWithDropFields: UnionsWithDropFields,
169-
170167
TypeAliasBounds: TypeAliasBounds,
171168

172169
TrivialConstraints: TrivialConstraints,

src/librustc_typeck/check/mod.rs

+28
Original file line numberDiff line numberDiff line change
@@ -1387,9 +1387,37 @@ fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) {
13871387
def.destructor(tcx); // force the destructor to be evaluated
13881388
check_representable(tcx, span, def_id);
13891389
check_transparent(tcx, span, def_id);
1390+
check_union_fields(tcx, span, def_id);
13901391
check_packed(tcx, span, def_id);
13911392
}
13921393

1394+
/// When the `#![feature(untagged_unions)]` gate is active,
1395+
/// check that the fields of the `union` does not contain fields that need dropping.
1396+
fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: DefId) -> bool {
1397+
let item_type = tcx.type_of(item_def_id);
1398+
if let ty::Adt(def, substs) = item_type.kind {
1399+
assert!(def.is_union());
1400+
let fields = &def.non_enum_variant().fields;
1401+
for field in fields {
1402+
let field_ty = field.ty(tcx, substs);
1403+
// We are currently checking the type this field came from, so it must be local.
1404+
let field_span = tcx.hir().span_if_local(field.did).unwrap();
1405+
let param_env = tcx.param_env(field.did);
1406+
if field_ty.needs_drop(tcx, param_env) {
1407+
struct_span_err!(tcx.sess, field_span, E0740,
1408+
"unions may not contain fields that need dropping")
1409+
.span_note(field_span,
1410+
"`std::mem::ManuallyDrop` can be used to wrap the type")
1411+
.emit();
1412+
return false;
1413+
}
1414+
}
1415+
} else {
1416+
span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind);
1417+
}
1418+
return true;
1419+
}
1420+
13931421
/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
13941422
/// projections that would result in "inheriting lifetimes".
13951423
fn check_opaque<'tcx>(

src/librustc_typeck/error_codes.rs

+4
Original file line numberDiff line numberDiff line change
@@ -4863,6 +4863,10 @@ assert_eq!(1, discriminant(&Enum::Struct{a: 7, b: 11}));
48634863
```
48644864
"##,
48654865

4866+
E0740: r##"
4867+
A `union` cannot have fields with destructors.
4868+
"##,
4869+
48664870
E0733: r##"
48674871
Recursion in an `async fn` requires boxing. For example, this will not compile:
48684872

src/libstd/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@
275275
#![feature(link_args)]
276276
#![feature(linkage)]
277277
#![feature(log_syntax)]
278+
#![feature(manually_drop_take)]
278279
#![feature(maybe_uninit_ref)]
279280
#![feature(maybe_uninit_slice)]
280281
#![feature(needs_panic_runtime)]

src/libstd/panicking.rs

+8-9
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ use core::panic::{BoxMeUp, PanicInfo, Location};
1212
use crate::any::Any;
1313
use crate::fmt;
1414
use crate::intrinsics;
15-
use crate::mem;
16-
use crate::ptr;
15+
use crate::mem::{self, ManuallyDrop};
1716
use crate::raw;
1817
use crate::sync::atomic::{AtomicBool, Ordering};
1918
use crate::sys::stdio::panic_output;
@@ -227,10 +226,9 @@ pub use realstd::rt::update_panic_count;
227226

228227
/// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
229228
pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
230-
#[allow(unions_with_drop_fields)]
231229
union Data<F, R> {
232-
f: F,
233-
r: R,
230+
f: ManuallyDrop<F>,
231+
r: ManuallyDrop<R>,
234232
}
235233

236234
// We do some sketchy operations with ownership here for the sake of
@@ -261,7 +259,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
261259
let mut any_data = 0;
262260
let mut any_vtable = 0;
263261
let mut data = Data {
264-
f,
262+
f: ManuallyDrop::new(f)
265263
};
266264

267265
let r = __rust_maybe_catch_panic(do_call::<F, R>,
@@ -271,7 +269,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
271269

272270
return if r == 0 {
273271
debug_assert!(update_panic_count(0) == 0);
274-
Ok(data.r)
272+
Ok(ManuallyDrop::into_inner(data.r))
275273
} else {
276274
update_panic_count(-1);
277275
debug_assert!(update_panic_count(0) == 0);
@@ -284,8 +282,9 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
284282
fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
285283
unsafe {
286284
let data = data as *mut Data<F, R>;
287-
let f = ptr::read(&mut (*data).f);
288-
ptr::write(&mut (*data).r, f());
285+
let data = &mut (*data);
286+
let f = ManuallyDrop::take(&mut data.f);
287+
data.r = ManuallyDrop::new(f());
289288
}
290289
}
291290
}

src/test/ui/associated-type-bounds/union-bounds.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
#![feature(associated_type_bounds)]
44
#![feature(untagged_unions)]
55

6-
#![allow(unions_with_drop_fields, unused_assignments)]
6+
#![allow(unused_assignments)]
77

8-
trait Tr1 { type As1; }
9-
trait Tr2 { type As2; }
10-
trait Tr3 { type As3; }
11-
trait Tr4<'a> { type As4; }
12-
trait Tr5 { type As5; }
8+
trait Tr1: Copy { type As1: Copy; }
9+
trait Tr2: Copy { type As2: Copy; }
10+
trait Tr3: Copy { type As3: Copy; }
11+
trait Tr4<'a>: Copy { type As4: Copy; }
12+
trait Tr5: Copy { type As5: Copy; }
1313

1414
impl Tr1 for &str { type As1 = bool; }
1515
impl Tr2 for bool { type As2 = u8; }
@@ -71,7 +71,8 @@ where
7171
let _: &'a T = &x.f0;
7272
}
7373

74-
union UnSelf<T> where Self: Tr1<As1: Tr2> {
74+
#[derive(Copy, Clone)]
75+
union UnSelf<T> where Self: Tr1<As1: Tr2>, T: Copy {
7576
f0: T,
7677
f1: <Self as Tr1>::As1,
7778
f2: <<Self as Tr1>::As1 as Tr2>::As2,

src/test/ui/drop/dynamic-drop.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#![feature(slice_patterns)]
99

1010
use std::cell::{Cell, RefCell};
11+
use std::mem::ManuallyDrop;
1112
use std::ops::Generator;
1213
use std::panic;
1314
use std::pin::Pin;
@@ -152,17 +153,16 @@ fn assignment1(a: &Allocator, c0: bool) {
152153
_v = _w;
153154
}
154155

155-
#[allow(unions_with_drop_fields)]
156156
union Boxy<T> {
157-
a: T,
158-
b: T,
157+
a: ManuallyDrop<T>,
158+
b: ManuallyDrop<T>,
159159
}
160160

161161
fn union1(a: &Allocator) {
162162
unsafe {
163-
let mut u = Boxy { a: a.alloc() };
164-
u.b = a.alloc();
165-
drop(u.a);
163+
let mut u = Boxy { a: ManuallyDrop::new(a.alloc()) };
164+
*u.b = a.alloc(); // drops first alloc
165+
drop(ManuallyDrop::into_inner(u.a));
166166
}
167167
}
168168

src/test/ui/feature-gates/feature-gate-associated_type_bounds.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![feature(untagged_unions)]
22

3-
trait Tr1 { type As1; }
4-
trait Tr2 { type As2; }
3+
trait Tr1 { type As1: Copy; }
4+
trait Tr2 { type As2: Copy; }
55

66
struct S1;
77
#[derive(Copy, Clone)]
@@ -32,7 +32,7 @@ enum _En1<T: Tr1<As1: Tr2>> {
3232

3333
union _Un1<T: Tr1<As1: Tr2>> {
3434
//~^ ERROR associated type bounds are unstable
35-
outest: T,
35+
outest: std::mem::ManuallyDrop<T>,
3636
outer: T::As1,
3737
inner: <T::As1 as Tr2>::As2,
3838
}

src/test/ui/feature-gates/feature-gate-untagged_unions.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ union U2<T: Copy> { // OK
77
}
88

99
union U3 { //~ ERROR unions with non-`Copy` fields are unstable
10-
a: String,
10+
a: String, //~ ERROR unions may not contain fields that need dropping
1111
}
1212

1313
union U4<T> { //~ ERROR unions with non-`Copy` fields are unstable
14-
a: T,
14+
a: T, //~ ERROR unions may not contain fields that need dropping
1515
}
1616

1717
union U5 { //~ ERROR unions with `Drop` implementations are unstable

src/test/ui/feature-gates/feature-gate-untagged_unions.stderr

+27-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,31 @@ LL | | }
3131
= note: for more information, see https://github.com/rust-lang/rust/issues/32836
3232
= help: add `#![feature(untagged_unions)]` to the crate attributes to enable
3333

34-
error: aborting due to 3 previous errors
34+
error[E0740]: unions may not contain fields that need dropping
35+
--> $DIR/feature-gate-untagged_unions.rs:10:5
36+
|
37+
LL | a: String,
38+
| ^^^^^^^^^
39+
|
40+
note: `std::mem::ManuallyDrop` can be used to wrap the type
41+
--> $DIR/feature-gate-untagged_unions.rs:10:5
42+
|
43+
LL | a: String,
44+
| ^^^^^^^^^
45+
46+
error[E0740]: unions may not contain fields that need dropping
47+
--> $DIR/feature-gate-untagged_unions.rs:14:5
48+
|
49+
LL | a: T,
50+
| ^^^^
51+
|
52+
note: `std::mem::ManuallyDrop` can be used to wrap the type
53+
--> $DIR/feature-gate-untagged_unions.rs:14:5
54+
|
55+
LL | a: T,
56+
| ^^^^
57+
58+
error: aborting due to 5 previous errors
3559

36-
For more information about this error, try `rustc --explain E0658`.
60+
Some errors have detailed explanations: E0658, E0740.
61+
For more information about an error, try `rustc --explain E0658`.

src/test/ui/rfc-2093-infer-outlives/explicit-union.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
#![feature(rustc_attrs)]
22
#![feature(untagged_unions)]
3-
#![allow(unions_with_drop_fields)]
43

54
#[rustc_outlives]
6-
union Foo<'b, U> { //~ ERROR rustc_outlives
5+
union Foo<'b, U: Copy> { //~ ERROR rustc_outlives
76
bar: Bar<'b, U>
87
}
98

10-
union Bar<'a, T> where T: 'a {
9+
union Bar<'a, T: Copy> where T: 'a {
1110
x: &'a (),
1211
y: T,
1312
}

src/test/ui/rfc-2093-infer-outlives/explicit-union.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
error: rustc_outlives
2-
--> $DIR/explicit-union.rs:6:1
2+
--> $DIR/explicit-union.rs:5:1
33
|
4-
LL | / union Foo<'b, U> {
4+
LL | / union Foo<'b, U: Copy> {
55
LL | | bar: Bar<'b, U>
66
LL | | }
77
| |_^

src/test/ui/rfc-2093-infer-outlives/nested-union.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
#![feature(rustc_attrs)]
22
#![feature(untagged_unions)]
3-
#![allow(unions_with_drop_fields)]
43

54
#[rustc_outlives]
6-
union Foo<'a, T> { //~ ERROR rustc_outlives
5+
union Foo<'a, T: Copy> { //~ ERROR rustc_outlives
76
field1: Bar<'a, T>
87
}
98

109
// Type U needs to outlive lifetime 'b
11-
union Bar<'b, U> {
10+
union Bar<'b, U: Copy> {
1211
field2: &'b U
1312
}
1413

src/test/ui/rfc-2093-infer-outlives/nested-union.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
error: rustc_outlives
2-
--> $DIR/nested-union.rs:6:1
2+
--> $DIR/nested-union.rs:5:1
33
|
4-
LL | / union Foo<'a, T> {
4+
LL | / union Foo<'a, T: Copy> {
55
LL | | field1: Bar<'a, T>
66
LL | | }
77
| |_^

0 commit comments

Comments
 (0)