Skip to content

Commit cbb07c2

Browse files
committed
Auto merge of #97995 - RalfJung:union-more-nodrop, r=Mark-Simulacrum
allow unions with mutable references and tuples of allowed types We currently allow shared references in unions, but not mutable references. That seems somewhat inconsistent. So let's allow all references, and while we are at it, let's make sure the set of allowed types is closed under tuples. This will need T-lang FCP (at least). Then remove the `tagged_unions` feature, since we do not plan to stabilize any more of it. Closes #55149
2 parents 87588a2 + 07fe988 commit cbb07c2

File tree

66 files changed

+507
-529
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+507
-529
lines changed

compiler/rustc_feature/src/active.rs

-7
Original file line numberDiff line numberDiff line change
@@ -525,13 +525,6 @@ declare_features! (
525525
(incomplete, unsized_locals, "1.30.0", Some(48055), None),
526526
/// Allows unsized tuple coercion.
527527
(active, unsized_tuple_coercion, "1.20.0", Some(42877), None),
528-
/// Allows `union`s to implement `Drop`. Moreover, `union`s may now include fields
529-
/// that don't implement `Copy` as long as they don't have any drop glue.
530-
/// This is checked recursively. On encountering type variable where no progress can be made,
531-
/// `T: Copy` is used as a substitute for "no drop glue".
532-
///
533-
/// NOTE: A limited form of `union U { ... }` was accepted in 1.19.0.
534-
(active, untagged_unions, "1.13.0", Some(55149), None),
535528
/// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute.
536529
(active, used_with_arg, "1.60.0", Some(93798), None),
537530
/// Allows `extern "wasm" fn`

compiler/rustc_feature/src/removed.rs

+3
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ declare_features! (
180180
/// Allows using items which are missing stability attributes
181181
(removed, unmarked_api, "1.0.0", None, None, None),
182182
(removed, unsafe_no_drop_flag, "1.0.0", None, None, None),
183+
/// Allows `union` fields that don't implement `Copy` as long as they don't have any drop glue.
184+
(removed, untagged_unions, "1.13.0", Some(55149), None,
185+
Some("unions with `Copy` and `ManuallyDrop` fields are stable; there is no intent to stabilize more")),
183186
/// Allows `#[unwind(..)]`.
184187
///
185188
/// Permits specifying whether a function should permit unwinding or abort on unwind.

compiler/rustc_middle/src/mir/query.rs

-6
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ pub enum UnsafetyViolationDetails {
3535
UseOfMutableStatic,
3636
UseOfExternStatic,
3737
DerefOfRawPointer,
38-
AssignToDroppingUnionField,
3938
AccessToUnionField,
4039
MutationOfLayoutConstrainedField,
4140
BorrowOfLayoutConstrainedField,
@@ -78,11 +77,6 @@ impl UnsafetyViolationDetails {
7877
"raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
7978
and cause data races: all of these are undefined behavior",
8079
),
81-
AssignToDroppingUnionField => (
82-
"assignment to union field that might need dropping",
83-
"the previous content of the field will be dropped, which causes undefined \
84-
behavior if the field was not properly initialized",
85-
),
8680
AccessToUnionField => (
8781
"access to union field",
8882
"the field may not be properly initialized: using uninitialized data will cause \

compiler/rustc_mir_build/src/check_unsafety.rs

+3-17
Original file line numberDiff line numberDiff line change
@@ -431,16 +431,9 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
431431
let lhs = &self.thir[lhs];
432432
if let ty::Adt(adt_def, _) = lhs.ty.kind() && adt_def.is_union() {
433433
if let Some((assigned_ty, assignment_span)) = self.assignment_info {
434-
// To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
435-
if !(assigned_ty
436-
.ty_adt_def()
437-
.map_or(false, |adt| adt.is_manually_drop())
438-
|| assigned_ty
439-
.is_copy_modulo_regions(self.tcx.at(expr.span), self.param_env))
440-
{
441-
self.requires_unsafe(assignment_span, AssignToDroppingUnionField);
442-
} else {
443-
// write to non-drop union field, safe
434+
if assigned_ty.needs_drop(self.tcx, self.tcx.param_env(adt_def.did())) {
435+
// This would be unsafe, but should be outright impossible since we reject such unions.
436+
self.tcx.sess.delay_span_bug(assignment_span, "union fields that need dropping should be impossible");
444437
}
445438
} else {
446439
self.requires_unsafe(expr.span, AccessToUnionField);
@@ -537,7 +530,6 @@ enum UnsafeOpKind {
537530
UseOfMutableStatic,
538531
UseOfExternStatic,
539532
DerefOfRawPointer,
540-
AssignToDroppingUnionField,
541533
AccessToUnionField,
542534
MutationOfLayoutConstrainedField,
543535
BorrowOfLayoutConstrainedField,
@@ -555,7 +547,6 @@ impl UnsafeOpKind {
555547
UseOfMutableStatic => "use of mutable static",
556548
UseOfExternStatic => "use of extern static",
557549
DerefOfRawPointer => "dereference of raw pointer",
558-
AssignToDroppingUnionField => "assignment to union field that might need dropping",
559550
AccessToUnionField => "access to union field",
560551
MutationOfLayoutConstrainedField => "mutation of layout constrained field",
561552
BorrowOfLayoutConstrainedField => {
@@ -600,11 +591,6 @@ impl UnsafeOpKind {
600591
"raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
601592
and cause data races: all of these are undefined behavior",
602593
),
603-
AssignToDroppingUnionField => (
604-
Cow::Borrowed(self.simple_description()),
605-
"the previous content of the field will be dropped, which causes undefined \
606-
behavior if the field was not properly initialized",
607-
),
608594
AccessToUnionField => (
609595
Cow::Borrowed(self.simple_description()),
610596
"the field may not be properly initialized: using uninitialized data will cause \

compiler/rustc_mir_transform/src/check_unsafety.rs

+8-15
Original file line numberDiff line numberDiff line change
@@ -219,22 +219,15 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
219219
// We have to check the actual type of the assignment, as that determines if the
220220
// old value is being dropped.
221221
let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty;
222-
// To avoid semver hazard, we only consider `Copy` and `ManuallyDrop` non-dropping.
223-
let manually_drop = assigned_ty
224-
.ty_adt_def()
225-
.map_or(false, |adt_def| adt_def.is_manually_drop());
226-
let nodrop = manually_drop
227-
|| assigned_ty.is_copy_modulo_regions(
228-
self.tcx.at(self.source_info.span),
229-
self.param_env,
222+
if assigned_ty.needs_drop(
223+
self.tcx,
224+
self.tcx.param_env(base_ty.ty_adt_def().unwrap().did()),
225+
) {
226+
// This would be unsafe, but should be outright impossible since we reject such unions.
227+
self.tcx.sess.delay_span_bug(
228+
self.source_info.span,
229+
"union fields that need dropping should be impossible",
230230
);
231-
if !nodrop {
232-
self.require_unsafe(
233-
UnsafetyViolationKind::General,
234-
UnsafetyViolationDetails::AssignToDroppingUnionField,
235-
);
236-
} else {
237-
// write to non-drop union field, safe
238231
}
239232
} else {
240233
self.require_unsafe(

compiler/rustc_passes/src/stability.rs

+2-36
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,12 @@ use rustc_hir::{FieldDef, Generics, HirId, Item, ItemKind, TraitRef, Ty, TyKind,
1313
use rustc_middle::hir::nested_filter;
1414
use rustc_middle::middle::privacy::AccessLevels;
1515
use rustc_middle::middle::stability::{AllowUnstable, DeprecationEntry, Index};
16-
use rustc_middle::ty::{self, query::Providers, TyCtxt};
16+
use rustc_middle::ty::{query::Providers, TyCtxt};
1717
use rustc_session::lint;
1818
use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED};
19-
use rustc_session::parse::feature_err;
2019
use rustc_session::Session;
2120
use rustc_span::symbol::{sym, Symbol};
22-
use rustc_span::{Span, DUMMY_SP};
21+
use rustc_span::Span;
2322
use rustc_target::spec::abi::Abi;
2423

2524
use std::cmp::Ordering;
@@ -766,39 +765,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
766765
}
767766
}
768767

769-
// There's no good place to insert stability check for non-Copy unions,
770-
// so semi-randomly perform it here in stability.rs
771-
hir::ItemKind::Union(..) if !self.tcx.features().untagged_unions => {
772-
let ty = self.tcx.type_of(item.def_id);
773-
let ty::Adt(adt_def, substs) = ty.kind() else { bug!() };
774-
775-
// Non-`Copy` fields are unstable, except for `ManuallyDrop`.
776-
let param_env = self.tcx.param_env(item.def_id);
777-
for field in &adt_def.non_enum_variant().fields {
778-
let field_ty = field.ty(self.tcx, substs);
779-
if !field_ty.ty_adt_def().map_or(false, |adt_def| adt_def.is_manually_drop())
780-
&& !field_ty.is_copy_modulo_regions(self.tcx.at(DUMMY_SP), param_env)
781-
{
782-
if field_ty.needs_drop(self.tcx, param_env) {
783-
// Avoid duplicate error: This will error later anyway because fields
784-
// that need drop are not allowed.
785-
self.tcx.sess.delay_span_bug(
786-
item.span,
787-
"union should have been rejected due to potentially dropping field",
788-
);
789-
} else {
790-
feature_err(
791-
&self.tcx.sess.parse_sess,
792-
sym::untagged_unions,
793-
self.tcx.def_span(field.did),
794-
"unions with non-`Copy` fields other than `ManuallyDrop<T>` are unstable",
795-
)
796-
.emit();
797-
}
798-
}
799-
}
800-
}
801-
802768
_ => (/* pass */),
803769
}
804770
intravisit::walk_item(self, item);

compiler/rustc_typeck/src/check/check.rs

+32-3
Original file line numberDiff line numberDiff line change
@@ -402,11 +402,37 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
402402
let item_type = tcx.type_of(item_def_id);
403403
if let ty::Adt(def, substs) = item_type.kind() {
404404
assert!(def.is_union());
405-
let fields = &def.non_enum_variant().fields;
405+
406+
fn allowed_union_field<'tcx>(
407+
ty: Ty<'tcx>,
408+
tcx: TyCtxt<'tcx>,
409+
param_env: ty::ParamEnv<'tcx>,
410+
span: Span,
411+
) -> bool {
412+
// We don't just accept all !needs_drop fields, due to semver concerns.
413+
match ty.kind() {
414+
ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check)
415+
ty::Tuple(tys) => {
416+
// allow tuples of allowed types
417+
tys.iter().all(|ty| allowed_union_field(ty, tcx, param_env, span))
418+
}
419+
ty::Array(elem, _len) => {
420+
// Like `Copy`, we do *not* special-case length 0.
421+
allowed_union_field(*elem, tcx, param_env, span)
422+
}
423+
_ => {
424+
// Fallback case: allow `ManuallyDrop` and things that are `Copy`.
425+
ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop())
426+
|| ty.is_copy_modulo_regions(tcx.at(span), param_env)
427+
}
428+
}
429+
}
430+
406431
let param_env = tcx.param_env(item_def_id);
407-
for field in fields {
432+
for field in &def.non_enum_variant().fields {
408433
let field_ty = field.ty(tcx, substs);
409-
if field_ty.needs_drop(tcx, param_env) {
434+
435+
if !allowed_union_field(field_ty, tcx, param_env, span) {
410436
let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) {
411437
// We are currently checking the type this field came from, so it must be local.
412438
Some(Node::Field(field)) => (field.span, field.ty.span),
@@ -433,6 +459,9 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
433459
)
434460
.emit();
435461
return false;
462+
} else if field_ty.needs_drop(tcx, param_env) {
463+
// This should never happen. But we can get here e.g. in case of name resolution errors.
464+
tcx.sess.delay_span_bug(span, "we should never accept maybe-dropping union fields");
436465
}
437466
}
438467
} else {

compiler/rustc_typeck/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ This API is completely unstable and subject to change.
7272
#![feature(once_cell)]
7373
#![feature(slice_partition_dedup)]
7474
#![feature(try_blocks)]
75+
#![feature(is_some_with)]
7576
#![recursion_limit = "256"]
7677

7778
#[macro_use]

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

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

54
use std::iter;
5+
use std::mem::ManuallyDrop;
66

77
struct SI1<T: Iterator<Item: Copy, Item: Send>> {
88
//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719]
@@ -74,36 +74,36 @@ where
7474

7575
union UI1<T: Iterator<Item: Copy, Item: Send>> {
7676
//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719]
77-
f: T,
77+
f: ManuallyDrop<T>,
7878
}
7979
union UI2<T: Iterator<Item: Copy, Item: Copy>> {
8080
//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719]
81-
f: T,
81+
f: ManuallyDrop<T>,
8282
}
8383
union UI3<T: Iterator<Item: 'static, Item: 'static>> {
8484
//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719]
85-
f: T,
85+
f: ManuallyDrop<T>,
8686
}
8787
union UW1<T>
8888
where
8989
T: Iterator<Item: Copy, Item: Send>,
9090
//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719]
9191
{
92-
f: T,
92+
f: ManuallyDrop<T>,
9393
}
9494
union UW2<T>
9595
where
9696
T: Iterator<Item: Copy, Item: Copy>,
9797
//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719]
9898
{
99-
f: T,
99+
f: ManuallyDrop<T>,
100100
}
101101
union UW3<T>
102102
where
103103
T: Iterator<Item: 'static, Item: 'static>,
104104
//~^ ERROR the value of the associated type `Item` (from trait `Iterator`) is already specified [E0719]
105105
{
106-
f: T,
106+
f: ManuallyDrop<T>,
107107
}
108108

109109
fn FI1<T: Iterator<Item: Copy, Item: Send>>() {}

src/test/ui/associated-type-bounds/inside-adt.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![feature(associated_type_bounds)]
2-
#![feature(untagged_unions)]
2+
3+
use std::mem::ManuallyDrop;
34

45
struct S1 { f: dyn Iterator<Item: Copy> }
56
//~^ ERROR associated type bounds are not allowed within structs, enums, or unions
@@ -17,12 +18,12 @@ enum E3 { V(dyn Iterator<Item: 'static>) }
1718
//~^ ERROR associated type bounds are not allowed within structs, enums, or unions
1819
//~| ERROR the size for values of type `(dyn Iterator<Item = impl Sized> + 'static)`
1920

20-
union U1 { f: dyn Iterator<Item: Copy> }
21+
union U1 { f: ManuallyDrop<dyn Iterator<Item: Copy>> }
2122
//~^ ERROR associated type bounds are not allowed within structs, enums, or unions
2223
//~| ERROR the size for values of type `(dyn Iterator<Item = impl Copy> + 'static)`
23-
union U2 { f: Box<dyn Iterator<Item: Copy>> }
24+
union U2 { f: ManuallyDrop<Box<dyn Iterator<Item: Copy>>> }
2425
//~^ ERROR associated type bounds are not allowed within structs, enums, or unions
25-
union U3 { f: dyn Iterator<Item: 'static> }
26+
union U3 { f: ManuallyDrop<dyn Iterator<Item: 'static>> }
2627
//~^ ERROR associated type bounds are not allowed within structs, enums, or unions
2728
//~| ERROR the size for values of type `(dyn Iterator<Item = impl Sized> + 'static)`
2829

0 commit comments

Comments
 (0)