Skip to content

Commit d748fa6

Browse files
authored
Auto merge of #36016 - petrochenkov:union, r=nikomatsakis
Implement untagged unions (RFC 1444) cc #32836 Notes: - The RFC doesn't talk about `#[packed]` unions, this implementation supports them, packing changes union's alignment to 1 and removes trailing padding. - The RFC doesn't talk about dynamically sized unions, this implementation doesn't support them and rejects them during wf-checking (similarly, dynamically sized enums are not supported as well). - The lint for drop fields in unions can't work precisely before monomorphization, so it works pessimistically - non-`Copy` generic fields are reported, types not implementing `Drop` directly, but having non-trivial drop code are reported. ``` struct S(String); // Doesn't implement `Drop` union U<T> { a: S, // Reported b: T, // Reported } ``` - #35764 was indeed helpful and landed timely, I didn't have to implement internal drop flags for unions. - Unions are not permitted in constant patterns, because matching on union fields is unsafe, I didn't want unsafety checker to dig into all constants to uncover this possible unsafety. - The RFC doesn't talk about `#[derive]`, generally trait impls cannot be derived for unions, but some of them can. I implemented only `#[derive(Copy)]` so far. In theory shallow `#[derive(Clone)]` can be derived as well if all union fields are `Copy`, I left it for later though, it requires changing how `Clone` impls are generated. - Moving union fields is implemented as per #32836 (comment). - Testing strategy: union specific behavior is tested, sometimes very basically (e.g. debuginfo), behavior common for all ADTs (e.g. something like coherence checks) is not generally tested. r? @eddyb
2 parents 01b35d8 + 436cfe5 commit d748fa6

File tree

167 files changed

+2899
-362
lines changed

Some content is hidden

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

167 files changed

+2899
-362
lines changed

src/librustc/hir/check_attr.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use syntax::visit::Visitor;
1818
enum Target {
1919
Fn,
2020
Struct,
21+
Union,
2122
Enum,
2223
Other,
2324
}
@@ -27,6 +28,7 @@ impl Target {
2728
match item.node {
2829
ast::ItemKind::Fn(..) => Target::Fn,
2930
ast::ItemKind::Struct(..) => Target::Struct,
31+
ast::ItemKind::Union(..) => Target::Union,
3032
ast::ItemKind::Enum(..) => Target::Enum,
3133
_ => Target::Other,
3234
}
@@ -62,17 +64,20 @@ impl<'a> CheckAttrVisitor<'a> {
6264
let message = match &*name {
6365
"C" => {
6466
conflicting_reprs += 1;
65-
if target != Target::Struct && target != Target::Enum {
66-
"attribute should be applied to struct or enum"
67+
if target != Target::Struct &&
68+
target != Target::Union &&
69+
target != Target::Enum {
70+
"attribute should be applied to struct, enum or union"
6771
} else {
6872
continue
6973
}
7074
}
7175
"packed" => {
7276
// Do not increment conflicting_reprs here, because "packed"
7377
// can be used to modify another repr hint
74-
if target != Target::Struct {
75-
"attribute should be applied to struct"
78+
if target != Target::Struct &&
79+
target != Target::Union {
80+
"attribute should be applied to struct or union"
7681
} else {
7782
continue
7883
}

src/librustc/hir/def.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub enum Def {
4141
// If Def::Struct lives in value namespace (e.g. tuple struct, unit struct expressions)
4242
// it denotes a constructor and its DefId refers to NodeId of the struct's constructor.
4343
Struct(DefId),
44+
Union(DefId),
4445
Label(ast::NodeId),
4546
Method(DefId),
4647
Err,
@@ -109,7 +110,7 @@ impl Def {
109110

110111
Def::Fn(..) | Def::Mod(..) | Def::ForeignMod(..) | Def::Static(..) |
111112
Def::Variant(..) | Def::Enum(..) | Def::TyAlias(..) | Def::AssociatedTy(..) |
112-
Def::TyParam(..) | Def::Struct(..) | Def::Trait(..) |
113+
Def::TyParam(..) | Def::Struct(..) | Def::Union(..) | Def::Trait(..) |
113114
Def::Method(..) | Def::Const(..) | Def::AssociatedConst(..) |
114115
Def::PrimTy(..) | Def::Label(..) | Def::SelfTy(..) | Def::Err => {
115116
bug!("attempted .var_id() on invalid {:?}", self)
@@ -121,7 +122,7 @@ impl Def {
121122
match *self {
122123
Def::Fn(id) | Def::Mod(id) | Def::ForeignMod(id) | Def::Static(id, _) |
123124
Def::Variant(_, id) | Def::Enum(id) | Def::TyAlias(id) | Def::AssociatedTy(_, id) |
124-
Def::TyParam(id) | Def::Struct(id) | Def::Trait(id) |
125+
Def::TyParam(id) | Def::Struct(id) | Def::Union(id) | Def::Trait(id) |
125126
Def::Method(id) | Def::Const(id) | Def::AssociatedConst(id) |
126127
Def::Local(id, _) | Def::Upvar(id, _, _, _) => {
127128
id
@@ -147,6 +148,7 @@ impl Def {
147148
Def::TyAlias(..) => "type",
148149
Def::AssociatedTy(..) => "associated type",
149150
Def::Struct(..) => "struct",
151+
Def::Union(..) => "union",
150152
Def::Trait(..) => "trait",
151153
Def::Method(..) => "method",
152154
Def::Const(..) => "constant",

src/librustc/hir/fold.rs

+4
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,10 @@ pub fn noop_fold_item_underscore<T: Folder>(i: Item_, folder: &mut T) -> Item_ {
761761
let struct_def = folder.fold_variant_data(struct_def);
762762
ItemStruct(struct_def, folder.fold_generics(generics))
763763
}
764+
ItemUnion(struct_def, generics) => {
765+
let struct_def = folder.fold_variant_data(struct_def);
766+
ItemUnion(struct_def, folder.fold_generics(generics))
767+
}
764768
ItemDefaultImpl(unsafety, ref trait_ref) => {
765769
ItemDefaultImpl(unsafety, folder.fold_trait_ref((*trait_ref).clone()))
766770
}

src/librustc/hir/intravisit.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,8 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) {
348348
visitor.visit_ty(typ);
349349
walk_list!(visitor, visit_impl_item, impl_items);
350350
}
351-
ItemStruct(ref struct_definition, ref generics) => {
351+
ItemStruct(ref struct_definition, ref generics) |
352+
ItemUnion(ref struct_definition, ref generics) => {
352353
visitor.visit_generics(generics);
353354
visitor.visit_id(item.id);
354355
visitor.visit_variant_data(struct_definition, item.name, generics, item.id, item.span);

src/librustc/hir/lowering.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,10 @@ impl<'a> LoweringContext<'a> {
638638
let struct_def = self.lower_variant_data(struct_def);
639639
hir::ItemStruct(struct_def, self.lower_generics(generics))
640640
}
641-
ItemKind::Union(..) => panic!("`union` is not yet implemented"),
641+
ItemKind::Union(ref vdata, ref generics) => {
642+
let vdata = self.lower_variant_data(vdata);
643+
hir::ItemUnion(vdata, self.lower_generics(generics))
644+
}
642645
ItemKind::DefaultImpl(unsafety, ref trait_ref) => {
643646
hir::ItemDefaultImpl(self.lower_unsafety(unsafety),
644647
self.lower_trait_ref(trait_ref))

src/librustc/hir/map/def_collector.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -302,9 +302,9 @@ impl<'ast> intravisit::Visitor<'ast> for DefCollector<'ast> {
302302
let def_data = match i.node {
303303
hir::ItemDefaultImpl(..) | hir::ItemImpl(..) =>
304304
DefPathData::Impl,
305-
hir::ItemEnum(..) | hir::ItemStruct(..) | hir::ItemTrait(..) |
306-
hir::ItemExternCrate(..) | hir::ItemMod(..) | hir::ItemForeignMod(..) |
307-
hir::ItemTy(..) =>
305+
hir::ItemEnum(..) | hir::ItemStruct(..) | hir::ItemUnion(..) |
306+
hir::ItemTrait(..) | hir::ItemExternCrate(..) | hir::ItemMod(..) |
307+
hir::ItemForeignMod(..) | hir::ItemTy(..) =>
308308
DefPathData::TypeNs(i.name.as_str()),
309309
hir::ItemStatic(..) | hir::ItemConst(..) | hir::ItemFn(..) =>
310310
DefPathData::ValueNs(i.name.as_str()),
@@ -331,7 +331,8 @@ impl<'ast> intravisit::Visitor<'ast> for DefCollector<'ast> {
331331
});
332332
}
333333
}
334-
hir::ItemStruct(ref struct_def, _) => {
334+
hir::ItemStruct(ref struct_def, _) |
335+
hir::ItemUnion(ref struct_def, _) => {
335336
// If this is a tuple-like struct, register the constructor.
336337
if !struct_def.is_struct() {
337338
this.create_def(struct_def.id(),

src/librustc/hir/map/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,7 @@ fn node_id_to_string(map: &Map, id: NodeId, include_id: bool) -> String {
10301030
ItemTy(..) => "ty",
10311031
ItemEnum(..) => "enum",
10321032
ItemStruct(..) => "struct",
1033+
ItemUnion(..) => "union",
10331034
ItemTrait(..) => "trait",
10341035
ItemImpl(..) => "impl",
10351036
ItemDefaultImpl(..) => "default impl",

src/librustc/hir/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1483,6 +1483,8 @@ pub enum Item_ {
14831483
ItemEnum(EnumDef, Generics),
14841484
/// A struct definition, e.g. `struct Foo<A> {x: A}`
14851485
ItemStruct(VariantData, Generics),
1486+
/// A union definition, e.g. `union Foo<A, B> {x: A, y: B}`
1487+
ItemUnion(VariantData, Generics),
14861488
/// Represents a Trait Declaration
14871489
ItemTrait(Unsafety, Generics, TyParamBounds, HirVec<TraitItem>),
14881490

@@ -1512,6 +1514,7 @@ impl Item_ {
15121514
ItemTy(..) => "type alias",
15131515
ItemEnum(..) => "enum",
15141516
ItemStruct(..) => "struct",
1517+
ItemUnion(..) => "union",
15151518
ItemTrait(..) => "trait",
15161519
ItemImpl(..) |
15171520
ItemDefaultImpl(..) => "item",

src/librustc/hir/print.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,10 @@ impl<'a> State<'a> {
752752
self.head(&visibility_qualified(&item.vis, "struct"))?;
753753
self.print_struct(struct_def, generics, item.name, item.span, true)?;
754754
}
755-
755+
hir::ItemUnion(ref struct_def, ref generics) => {
756+
self.head(&visibility_qualified(&item.vis, "union"))?;
757+
self.print_struct(struct_def, generics, item.name, item.span, true)?;
758+
}
756759
hir::ItemDefaultImpl(unsafety, ref trait_ref) => {
757760
self.head("")?;
758761
self.print_visibility(&item.vis)?;

src/librustc/infer/error_reporting.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
105105
match item.node {
106106
hir::ItemImpl(..) => "impl",
107107
hir::ItemStruct(..) => "struct",
108+
hir::ItemUnion(..) => "union",
108109
hir::ItemEnum(..) => "enum",
109110
hir::ItemTrait(..) => "trait",
110111
hir::ItemFn(..) => "function body",
@@ -1370,7 +1371,8 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> {
13701371
}
13711372
hir::TyPath(ref maybe_qself, ref path) => {
13721373
match self.tcx.expect_def(cur_ty.id) {
1373-
Def::Enum(did) | Def::TyAlias(did) | Def::Struct(did) => {
1374+
Def::Enum(did) | Def::TyAlias(did) |
1375+
Def::Struct(did) | Def::Union(did) => {
13741376
let generics = self.tcx.lookup_generics(did);
13751377

13761378
let expected =

src/librustc/infer/freshen.rs

+1
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> {
168168
ty::TyFnPtr(_) |
169169
ty::TyTrait(..) |
170170
ty::TyStruct(..) |
171+
ty::TyUnion(..) |
171172
ty::TyClosure(..) |
172173
ty::TyNever |
173174
ty::TyTuple(..) |

src/librustc/middle/dead.rs

+11-9
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
8686
}
8787

8888
fn lookup_and_handle_definition(&mut self, id: ast::NodeId) {
89-
use ty::TypeVariants::{TyEnum, TyStruct};
89+
use ty::TypeVariants::{TyEnum, TyStruct, TyUnion};
9090

9191
let def = self.tcx.expect_def(id);
9292

@@ -96,7 +96,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
9696
if self.tcx.trait_of_item(def.def_id()).is_some() => {
9797
if let Some(substs) = self.tcx.tables.borrow().item_substs.get(&id) {
9898
match substs.substs.type_at(0).sty {
99-
TyEnum(tyid, _) | TyStruct(tyid, _) => {
99+
TyEnum(tyid, _) | TyStruct(tyid, _) | TyUnion(tyid, _) => {
100100
self.check_def_id(tyid.did)
101101
}
102102
_ => {}
@@ -132,10 +132,11 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
132132
}
133133

134134
fn handle_field_access(&mut self, lhs: &hir::Expr, name: ast::Name) {
135-
if let ty::TyStruct(def, _) = self.tcx.expr_ty_adjusted(lhs).sty {
136-
self.insert_def_id(def.struct_variant().field_named(name).did);
137-
} else {
138-
span_bug!(lhs.span, "named field access on non-struct")
135+
match self.tcx.expr_ty_adjusted(lhs).sty {
136+
ty::TyStruct(def, _) | ty::TyUnion(def, _) => {
137+
self.insert_def_id(def.struct_variant().field_named(name).did);
138+
}
139+
_ => span_bug!(lhs.span, "named field access on non-struct/union"),
139140
}
140141
}
141142

@@ -148,7 +149,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
148149
fn handle_field_pattern_match(&mut self, lhs: &hir::Pat,
149150
pats: &[codemap::Spanned<hir::FieldPat>]) {
150151
let variant = match self.tcx.node_id_to_type(lhs.id).sty {
151-
ty::TyStruct(adt, _) | ty::TyEnum(adt, _) => {
152+
ty::TyStruct(adt, _) | ty::TyUnion(adt, _) | ty::TyEnum(adt, _) => {
152153
adt.variant_of_def(self.tcx.expect_def(lhs.id))
153154
}
154155
_ => span_bug!(lhs.span, "non-ADT in struct pattern")
@@ -185,7 +186,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
185186
match *node {
186187
ast_map::NodeItem(item) => {
187188
match item.node {
188-
hir::ItemStruct(..) => {
189+
hir::ItemStruct(..) | hir::ItemUnion(..) => {
189190
self.struct_has_extern_repr = item.attrs.iter().any(|attr| {
190191
attr::find_repr_attrs(self.tcx.sess.diagnostic(), attr)
191192
.contains(&attr::ReprExtern)
@@ -423,7 +424,8 @@ impl<'a, 'tcx> DeadVisitor<'a, 'tcx> {
423424
| hir::ItemConst(..)
424425
| hir::ItemFn(..)
425426
| hir::ItemEnum(..)
426-
| hir::ItemStruct(..) => true,
427+
| hir::ItemStruct(..)
428+
| hir::ItemUnion(..) => true,
427429
_ => false
428430
};
429431
let ctor_id = get_struct_ctor_id(item);

src/librustc/middle/effect.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,14 @@
1313
use self::RootUnsafeContext::*;
1414

1515
use dep_graph::DepNode;
16-
use hir::def::Def;
1716
use ty::{self, Ty, TyCtxt};
1817
use ty::MethodCall;
1918

2019
use syntax::ast;
2120
use syntax_pos::Span;
22-
use hir;
23-
use hir::intravisit;
24-
use hir::intravisit::{FnKind, Visitor};
21+
use hir::{self, PatKind};
22+
use hir::def::Def;
23+
use hir::intravisit::{self, FnKind, Visitor};
2524

2625
#[derive(Copy, Clone)]
2726
struct UnsafeContext {
@@ -178,11 +177,28 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
178177
self.require_unsafe(expr.span, "use of mutable static");
179178
}
180179
}
180+
hir::ExprField(ref base_expr, field) => {
181+
if let ty::TyUnion(..) = self.tcx.expr_ty_adjusted(base_expr).sty {
182+
self.require_unsafe(field.span, "access to union field");
183+
}
184+
}
181185
_ => {}
182186
}
183187

184188
intravisit::walk_expr(self, expr);
185189
}
190+
191+
fn visit_pat(&mut self, pat: &hir::Pat) {
192+
if let PatKind::Struct(_, ref fields, _) = pat.node {
193+
if let ty::TyUnion(..) = self.tcx.pat_ty(pat).sty {
194+
for field in fields {
195+
self.require_unsafe(field.span, "matching on union field");
196+
}
197+
}
198+
}
199+
200+
intravisit::walk_pat(self, pat);
201+
}
186202
}
187203

188204
pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {

src/librustc/middle/expr_use_visitor.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
414414
}
415415

416416
hir::ExprStruct(_, ref fields, ref opt_with) => {
417-
self.walk_struct_expr(expr, fields, opt_with);
417+
self.walk_struct_expr(fields, opt_with);
418418
}
419419

420420
hir::ExprTup(ref exprs) => {
@@ -655,7 +655,6 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
655655
}
656656

657657
fn walk_struct_expr(&mut self,
658-
_expr: &hir::Expr,
659658
fields: &[hir::Field],
660659
opt_with: &Option<P<hir::Expr>>) {
661660
// Consume the expressions supplying values for each field.
@@ -695,7 +694,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
695694
with_expr.span,
696695
"with expression doesn't evaluate to a struct");
697696
}
698-
};
697+
}
699698

700699
// walk the with expression so that complex expressions
701700
// are properly handled.
@@ -1012,7 +1011,8 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
10121011
debug!("variant downcast_cmt={:?} pat={:?}", downcast_cmt, pat);
10131012
delegate.matched_pat(pat, downcast_cmt, match_mode);
10141013
}
1015-
Some(Def::Struct(..)) | Some(Def::TyAlias(..)) | Some(Def::AssociatedTy(..)) => {
1014+
Some(Def::Struct(..)) | Some(Def::Union(..)) |
1015+
Some(Def::TyAlias(..)) | Some(Def::AssociatedTy(..)) => {
10161016
debug!("struct cmt_pat={:?} pat={:?}", cmt_pat, pat);
10171017
delegate.matched_pat(pat, cmt_pat, match_mode);
10181018
}

src/librustc/middle/mem_categorization.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
572572
id, expr_ty, def);
573573

574574
match def {
575-
Def::Struct(..) | Def::Variant(..) | Def::Const(..) |
575+
Def::Struct(..) | Def::Union(..) | Def::Variant(..) | Def::Const(..) |
576576
Def::AssociatedConst(..) | Def::Fn(..) | Def::Method(..) => {
577577
Ok(self.cat_rvalue_node(id, span, expr_ty))
578578
}

src/librustc/middle/reachable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> {
269269
hir::ItemMod(..) | hir::ItemForeignMod(..) |
270270
hir::ItemImpl(..) | hir::ItemTrait(..) |
271271
hir::ItemStruct(..) | hir::ItemEnum(..) |
272-
hir::ItemDefaultImpl(..) => {}
272+
hir::ItemUnion(..) | hir::ItemDefaultImpl(..) => {}
273273
}
274274
}
275275
ast_map::NodeTraitItem(trait_method) => {

src/librustc/middle/resolve_lifetime.rs

+1
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for LifetimeContext<'a, 'tcx> {
156156
hir::ItemTy(_, ref generics) |
157157
hir::ItemEnum(_, ref generics) |
158158
hir::ItemStruct(_, ref generics) |
159+
hir::ItemUnion(_, ref generics) |
159160
hir::ItemTrait(_, ref generics, _, _) |
160161
hir::ItemImpl(_, _, ref generics, _, _, _) => {
161162
// These kinds of items have only early bound lifetime parameters.

0 commit comments

Comments
 (0)