From 1db878fd38eb00daf794d87ab04cb50c7fa6e4fc Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 17 Jul 2016 00:15:15 +0300 Subject: [PATCH 01/22] Add unions to AST --- src/librustc_passes/ast_validation.rs | 10 ++++++++++ src/libsyntax/ast.rs | 2 +- src/libsyntax/diagnostics/macros.rs | 7 +++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index dde1a4a759563..b8284f5dcf10d 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -196,6 +196,16 @@ impl<'a> Visitor for AstValidator<'a> { // Ensure that `path` attributes on modules are recorded as used (c.f. #35584). attr::first_attr_value_str_by_name(&item.attrs, "path"); } + ItemKind::Union(ref vdata, _) => { + if !vdata.is_struct() { + self.err_handler().span_err(item.span, + "tuple and unit unions are not permitted"); + } + if vdata.fields().len() == 0 { + self.err_handler().span_err(item.span, + "unions cannot have zero fields"); + } + } _ => {} } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index fcb99444957c4..4394fb0e14312 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1886,7 +1886,7 @@ pub enum ItemKind { /// A union definition (`union` or `pub union`). /// /// E.g. `union Foo { x: A, y: B }` - Union(VariantData, Generics), // FIXME: not yet implemented + Union(VariantData, Generics), /// A Trait declaration (`trait` or `pub trait`). /// /// E.g. `trait Foo { .. }` or `trait Foo { .. }` diff --git a/src/libsyntax/diagnostics/macros.rs b/src/libsyntax/diagnostics/macros.rs index 25e0428248df4..e2a7ec0a33de1 100644 --- a/src/libsyntax/diagnostics/macros.rs +++ b/src/libsyntax/diagnostics/macros.rs @@ -107,6 +107,13 @@ macro_rules! help { }) } +#[macro_export] +macro_rules! unimplemented_unions { + () => ({ + panic!("unions are not fully implemented"); + }) +} + #[macro_export] macro_rules! register_diagnostics { ($($code:tt),*) => ( From 4001c039de931f4e5ad5318b12e333ef30c52d1b Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 6 Aug 2016 21:36:28 +0300 Subject: [PATCH 02/22] Add unions to HIR --- src/librustc/hir/fold.rs | 4 ++++ src/librustc/hir/intravisit.rs | 3 ++- src/librustc/hir/map/def_collector.rs | 6 +++--- src/librustc/hir/map/mod.rs | 1 + src/librustc/hir/mod.rs | 3 +++ src/librustc/hir/print.rs | 5 ++++- src/librustc/middle/reachable.rs | 2 +- src/librustc/middle/resolve_lifetime.rs | 1 + src/librustc_metadata/encoder.rs | 3 +++ src/librustc_privacy/lib.rs | 8 +++++--- src/librustc_trans/collector.rs | 5 +++-- src/librustc_typeck/collect.rs | 3 ++- src/librustc_typeck/variance/constraints.rs | 2 +- src/librustc_typeck/variance/terms.rs | 3 ++- src/librustdoc/visit_ast.rs | 2 ++ 15 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/librustc/hir/fold.rs b/src/librustc/hir/fold.rs index 12bc49c10dabc..57b5599bd1d7f 100644 --- a/src/librustc/hir/fold.rs +++ b/src/librustc/hir/fold.rs @@ -761,6 +761,10 @@ pub fn noop_fold_item_underscore(i: Item_, folder: &mut T) -> Item_ { let struct_def = folder.fold_variant_data(struct_def); ItemStruct(struct_def, folder.fold_generics(generics)) } + ItemUnion(struct_def, generics) => { + let struct_def = folder.fold_variant_data(struct_def); + ItemUnion(struct_def, folder.fold_generics(generics)) + } ItemDefaultImpl(unsafety, ref trait_ref) => { ItemDefaultImpl(unsafety, folder.fold_trait_ref((*trait_ref).clone())) } diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index bc1dff7c6fc31..62157b1ca3681 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -348,7 +348,8 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) { visitor.visit_ty(typ); walk_list!(visitor, visit_impl_item, impl_items); } - ItemStruct(ref struct_definition, ref generics) => { + ItemStruct(ref struct_definition, ref generics) | + ItemUnion(ref struct_definition, ref generics) => { visitor.visit_generics(generics); visitor.visit_id(item.id); visitor.visit_variant_data(struct_definition, item.name, generics, item.id, item.span); diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs index 77567fc7a4603..389d0a1d50d56 100644 --- a/src/librustc/hir/map/def_collector.rs +++ b/src/librustc/hir/map/def_collector.rs @@ -302,9 +302,9 @@ impl<'ast> intravisit::Visitor<'ast> for DefCollector<'ast> { let def_data = match i.node { hir::ItemDefaultImpl(..) | hir::ItemImpl(..) => DefPathData::Impl, - hir::ItemEnum(..) | hir::ItemStruct(..) | hir::ItemTrait(..) | - hir::ItemExternCrate(..) | hir::ItemMod(..) | hir::ItemForeignMod(..) | - hir::ItemTy(..) => + hir::ItemEnum(..) | hir::ItemStruct(..) | hir::ItemUnion(..) | + hir::ItemTrait(..) | hir::ItemExternCrate(..) | hir::ItemMod(..) | + hir::ItemForeignMod(..) | hir::ItemTy(..) => DefPathData::TypeNs(i.name.as_str()), hir::ItemStatic(..) | hir::ItemConst(..) | hir::ItemFn(..) => DefPathData::ValueNs(i.name.as_str()), diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs index 5e14bb51ce867..3ffc95e64f5a7 100644 --- a/src/librustc/hir/map/mod.rs +++ b/src/librustc/hir/map/mod.rs @@ -1030,6 +1030,7 @@ fn node_id_to_string(map: &Map, id: NodeId, include_id: bool) -> String { ItemTy(..) => "ty", ItemEnum(..) => "enum", ItemStruct(..) => "struct", + ItemUnion(..) => "union", ItemTrait(..) => "trait", ItemImpl(..) => "impl", ItemDefaultImpl(..) => "default impl", diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 295a49d26d0fe..e16005558f82b 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -1483,6 +1483,8 @@ pub enum Item_ { ItemEnum(EnumDef, Generics), /// A struct definition, e.g. `struct Foo {x: A}` ItemStruct(VariantData, Generics), + /// A union definition, e.g. `union Foo {x: A, y: B}` + ItemUnion(VariantData, Generics), /// Represents a Trait Declaration ItemTrait(Unsafety, Generics, TyParamBounds, HirVec), @@ -1512,6 +1514,7 @@ impl Item_ { ItemTy(..) => "type alias", ItemEnum(..) => "enum", ItemStruct(..) => "struct", + ItemUnion(..) => "union", ItemTrait(..) => "trait", ItemImpl(..) | ItemDefaultImpl(..) => "item", diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 893d6708ead4b..f236bd4884d5b 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -752,7 +752,10 @@ impl<'a> State<'a> { self.head(&visibility_qualified(&item.vis, "struct"))?; self.print_struct(struct_def, generics, item.name, item.span, true)?; } - + hir::ItemUnion(ref struct_def, ref generics) => { + self.head(&visibility_qualified(&item.vis, "union"))?; + self.print_struct(struct_def, generics, item.name, item.span, true)?; + } hir::ItemDefaultImpl(unsafety, ref trait_ref) => { self.head("")?; self.print_visibility(&item.vis)?; diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index 1f9738556d925..0625504af88ea 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -269,7 +269,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { hir::ItemMod(..) | hir::ItemForeignMod(..) | hir::ItemImpl(..) | hir::ItemTrait(..) | hir::ItemStruct(..) | hir::ItemEnum(..) | - hir::ItemDefaultImpl(..) => {} + hir::ItemUnion(..) | hir::ItemDefaultImpl(..) => {} } } ast_map::NodeTraitItem(trait_method) => { diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index ebe4050022153..4d1eed612cfd5 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -156,6 +156,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for LifetimeContext<'a, 'tcx> { hir::ItemTy(_, ref generics) | hir::ItemEnum(_, ref generics) | hir::ItemStruct(_, ref generics) | + hir::ItemUnion(_, ref generics) | hir::ItemTrait(_, ref generics, _, _) | hir::ItemImpl(_, _, ref generics, _, _, _) => { // These kinds of items have only early bound lifetime parameters. diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index bb4cf70bd3b3e..e82742004c39f 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -1179,6 +1179,9 @@ impl<'a, 'tcx, 'encoder> IndexBuilder<'a, 'tcx, 'encoder> { hir::ItemStruct(ref struct_def, _) => { self.encode_addl_struct_info(def_id, struct_def.id(), item); } + hir::ItemUnion(..) => { + unimplemented_unions!(); + } hir::ItemImpl(_, _, _, _, _, ref ast_items) => { self.encode_addl_impl_info(def_id, item.id, ast_items); } diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 41b7c51e30090..4b13d13fb7058 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -234,7 +234,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> { } } // Visit everything except for private fields - hir::ItemStruct(ref struct_def, ref generics) => { + hir::ItemStruct(ref struct_def, ref generics) | + hir::ItemUnion(ref struct_def, ref generics) => { if item_level.is_some() { self.reach().visit_generics(generics); for field in struct_def.fields() { @@ -1067,8 +1068,9 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivateItemsInPublicInterfacesVisitor<'a, 'tc check.visit_foreign_item(foreign_item); } } - // Subitems of structs have their own publicity - hir::ItemStruct(ref struct_def, ref generics) => { + // Subitems of structs and unions have their own publicity + hir::ItemStruct(ref struct_def, ref generics) | + hir::ItemUnion(ref struct_def, ref generics) => { check.required_visibility = item_visibility; check.visit_generics(generics); diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index c82bfa5c91ba1..bea6f30faec30 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -1121,8 +1121,9 @@ impl<'b, 'a, 'v> hir_visit::Visitor<'v> for RootCollector<'b, 'a, 'v> { } } - hir::ItemEnum(_, ref generics) | - hir::ItemStruct(_, ref generics) => { + hir::ItemEnum(_, ref generics) | + hir::ItemStruct(_, ref generics) | + hir::ItemUnion(_, ref generics) => { if !generics.is_parameterized() { let ty = { let tables = self.scx.tcx().tables.borrow(); diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 2b62e513ab27a..f2f3163dde7c7 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1637,7 +1637,8 @@ fn predicates_of_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, hir::ItemFn(_, _, _, _, ref generics, _) | hir::ItemTy(_, ref generics) | hir::ItemEnum(_, ref generics) | - hir::ItemStruct(_, ref generics) => generics, + hir::ItemStruct(_, ref generics) | + hir::ItemUnion(_, ref generics) => generics, _ => &no_generics }; diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index 2cf84b5745af4..888709d257246 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -80,7 +80,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ConstraintContext<'a, 'tcx> { debug!("visit_item item={}", tcx.map.node_to_string(item.id)); match item.node { - hir::ItemEnum(..) | hir::ItemStruct(..) => { + hir::ItemEnum(..) | hir::ItemStruct(..) | hir::ItemUnion(..) => { let scheme = tcx.lookup_item_type(did); // Not entirely obvious: constraints on structs/enums do not diff --git a/src/librustc_typeck/variance/terms.rs b/src/librustc_typeck/variance/terms.rs index c0b53787177d5..1238f7cbcb335 100644 --- a/src/librustc_typeck/variance/terms.rs +++ b/src/librustc_typeck/variance/terms.rs @@ -234,7 +234,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for TermsContext<'a, 'tcx> { match item.node { hir::ItemEnum(_, ref generics) | - hir::ItemStruct(_, ref generics) => { + hir::ItemStruct(_, ref generics) | + hir::ItemUnion(_, ref generics) => { self.add_inferreds_for_item(item.id, false, generics); } hir::ItemTrait(_, ref generics, _, _) => { diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 6af36e24d8156..e70fbf4463a22 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -365,6 +365,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { om.enums.push(self.visit_enum_def(item, name, ed, gen)), hir::ItemStruct(ref sd, ref gen) => om.structs.push(self.visit_variant_data(item, name, sd, gen)), + hir::ItemUnion(..) => + unimplemented_unions!(), hir::ItemFn(ref fd, ref unsafety, constness, ref abi, ref gen, _) => om.fns.push(self.visit_fn(item, name, &**fd, unsafety, constness, abi, gen)), From 35d52a003bf0997d31378796bf59cc66c3ae7683 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 6 Aug 2016 21:56:02 +0300 Subject: [PATCH 03/22] Add unions to definition map --- src/librustc/hir/def.rs | 6 ++++-- src/librustc/middle/mem_categorization.rs | 2 +- src/librustc_metadata/astencode.rs | 1 + src/librustc_resolve/build_reduced_graph.rs | 21 ++++++++++++++++++++- src/librustc_save_analysis/dump_visitor.rs | 1 + src/librustc_typeck/check/mod.rs | 1 + 6 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/librustc/hir/def.rs b/src/librustc/hir/def.rs index d15d51aed09a1..aa0eac37ecff1 100644 --- a/src/librustc/hir/def.rs +++ b/src/librustc/hir/def.rs @@ -41,6 +41,7 @@ pub enum Def { // If Def::Struct lives in value namespace (e.g. tuple struct, unit struct expressions) // it denotes a constructor and its DefId refers to NodeId of the struct's constructor. Struct(DefId), + Union(DefId), Label(ast::NodeId), Method(DefId), Err, @@ -109,7 +110,7 @@ impl Def { Def::Fn(..) | Def::Mod(..) | Def::ForeignMod(..) | Def::Static(..) | Def::Variant(..) | Def::Enum(..) | Def::TyAlias(..) | Def::AssociatedTy(..) | - Def::TyParam(..) | Def::Struct(..) | Def::Trait(..) | + Def::TyParam(..) | Def::Struct(..) | Def::Union(..) | Def::Trait(..) | Def::Method(..) | Def::Const(..) | Def::AssociatedConst(..) | Def::PrimTy(..) | Def::Label(..) | Def::SelfTy(..) | Def::Err => { bug!("attempted .var_id() on invalid {:?}", self) @@ -121,7 +122,7 @@ impl Def { match *self { Def::Fn(id) | Def::Mod(id) | Def::ForeignMod(id) | Def::Static(id, _) | Def::Variant(_, id) | Def::Enum(id) | Def::TyAlias(id) | Def::AssociatedTy(_, id) | - Def::TyParam(id) | Def::Struct(id) | Def::Trait(id) | + Def::TyParam(id) | Def::Struct(id) | Def::Union(id) | Def::Trait(id) | Def::Method(id) | Def::Const(id) | Def::AssociatedConst(id) | Def::Local(id, _) | Def::Upvar(id, _, _, _) => { id @@ -147,6 +148,7 @@ impl Def { Def::TyAlias(..) => "type", Def::AssociatedTy(..) => "associated type", Def::Struct(..) => "struct", + Def::Union(..) => "union", Def::Trait(..) => "trait", Def::Method(..) => "method", Def::Const(..) => "constant", diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index a74bdb02044de..b17411ced57fb 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -572,7 +572,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { id, expr_ty, def); match def { - Def::Struct(..) | Def::Variant(..) | Def::Const(..) | + Def::Struct(..) | Def::Union(..) | Def::Variant(..) | Def::Const(..) | Def::AssociatedConst(..) | Def::Fn(..) | Def::Method(..) => { Ok(self.cat_rvalue_node(id, span, expr_ty)) } diff --git a/src/librustc_metadata/astencode.rs b/src/librustc_metadata/astencode.rs index 0236f9c413ddc..9d9c6f033a960 100644 --- a/src/librustc_metadata/astencode.rs +++ b/src/librustc_metadata/astencode.rs @@ -416,6 +416,7 @@ impl tr for Def { Def::Upvar(did1, nid1, index, nid2) } Def::Struct(did) => Def::Struct(did.tr(dcx)), + Def::Union(did) => Def::Union(did.tr(dcx)), Def::Label(nid) => Def::Label(dcx.tr_id(nid)), Def::Err => Def::Err, } diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 3e9b37f0a95a7..8e97870c21a5b 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -278,7 +278,19 @@ impl<'b> Resolver<'b> { self.structs.insert(item_def_id, field_names); } - ItemKind::Union(..) => panic!("`union` is not yet implemented"), + ItemKind::Union(ref vdata, _) => { + let def = Def::Union(self.definitions.local_def_id(item.id)); + self.define(parent, name, TypeNS, (def, sp, vis)); + + // Record the def ID and fields of this union. + let field_names = vdata.fields().iter().enumerate().map(|(index, field)| { + self.resolve_visibility(&field.vis); + field.ident.map(|ident| ident.name) + .unwrap_or_else(|| token::intern(&index.to_string())) + }).collect(); + let item_def_id = self.definitions.local_def_id(item.id); + self.structs.insert(item_def_id, field_names); + } ItemKind::DefaultImpl(_, _) | ItemKind::Impl(..) => {} @@ -461,6 +473,13 @@ impl<'b> Resolver<'b> { let fields = self.session.cstore.struct_field_names(def_id); self.structs.insert(def_id, fields); } + Def::Union(def_id) => { + let _ = self.try_define(parent, name, TypeNS, (def, DUMMY_SP, vis)); + + // Record the def ID and fields of this union. + let fields = self.session.cstore.struct_field_names(def_id); + self.structs.insert(def_id, fields); + } Def::Struct(..) => {} Def::Local(..) | Def::PrimTy(..) | diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index dbe956f021e4c..f9a20cec42d14 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -300,6 +300,7 @@ impl<'l, 'tcx: 'l, 'll, D: Dump + 'll> DumpVisitor<'l, 'tcx, 'll, D> { }.lower(self.tcx)); } Def::Struct(..) | + Def::Union(..) | Def::Enum(..) | Def::TyAlias(..) | Def::AssociatedTy(..) | diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 90a9d9bffe7dc..be33f836cc6cd 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -4118,6 +4118,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { match def { // Case 1 and 1b. Reference to a *type* or *enum variant*. Def::Struct(def_id) | + Def::Union(def_id) | Def::Variant(_, def_id) | Def::Enum(def_id) | Def::TyAlias(def_id) | From cbd912babab4b8cebe9e90a632117913ca192743 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 7 Aug 2016 01:09:00 +0300 Subject: [PATCH 04/22] Add union types --- src/librustc/infer/freshen.rs | 1 + src/librustc/traits/coherence.rs | 3 +- src/librustc/traits/error_reporting.rs | 1 + src/librustc/traits/select.rs | 7 +- src/librustc/ty/contents.rs | 3 +- src/librustc/ty/context.rs | 4 +- src/librustc/ty/error.rs | 3 + src/librustc/ty/fast_reject.rs | 3 + src/librustc/ty/flags.rs | 2 +- src/librustc/ty/item_path.rs | 1 + src/librustc/ty/layout.rs | 3 + src/librustc/ty/mod.rs | 3 +- src/librustc/ty/outlives.rs | 1 + src/librustc/ty/structural_impls.rs | 2 + src/librustc/ty/sty.rs | 6 ++ src/librustc/ty/util.rs | 5 +- src/librustc/ty/walk.rs | 1 + src/librustc/ty/wf.rs | 3 +- src/librustc/util/ppaux.rs | 5 +- src/librustc_lint/types.rs | 34 ++++++++- src/librustc_metadata/tyencode.rs | 5 ++ src/librustc_trans/collector.rs | 1 + src/librustc_trans/debuginfo/type_names.rs | 1 + src/librustc_trans/trans_item.rs | 1 + src/librustc_trans/type_of.rs | 83 +++++++++------------ src/librustc_typeck/check/dropck.rs | 2 +- src/librustc_typeck/coherence/mod.rs | 5 +- src/librustc_typeck/variance/constraints.rs | 3 +- src/librustdoc/clean/mod.rs | 1 + 29 files changed, 124 insertions(+), 69 deletions(-) diff --git a/src/librustc/infer/freshen.rs b/src/librustc/infer/freshen.rs index f793d489cab06..8aeb0757f5de2 100644 --- a/src/librustc/infer/freshen.rs +++ b/src/librustc/infer/freshen.rs @@ -168,6 +168,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for TypeFreshener<'a, 'gcx, 'tcx> { ty::TyFnPtr(_) | ty::TyTrait(..) | ty::TyStruct(..) | + ty::TyUnion(..) | ty::TyClosure(..) | ty::TyNever | ty::TyTuple(..) | diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs index a5a415dd27c65..f856e110ea2a5 100644 --- a/src/librustc/traits/coherence.rs +++ b/src/librustc/traits/coherence.rs @@ -261,7 +261,8 @@ fn ty_is_local_constructor(tcx: TyCtxt, ty: Ty, infer_is_local: InferIsLocal)-> } ty::TyEnum(def, _) | - ty::TyStruct(def, _) => { + ty::TyStruct(def, _) | + ty::TyUnion(def, _) => { def.did.is_local() } diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 6d6d7c2b3ba0a..95e83d404a74c 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -166,6 +166,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ty::TyParam(..) => Some(14), ty::TyAnon(..) => Some(15), ty::TyNever => Some(16), + ty::TyUnion(..) => Some(17), ty::TyInfer(..) | ty::TyError => None } } diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index 0573f0c5bbaa0..f8f10d9c26541 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -1780,7 +1780,8 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { Where(ty::Binder(tys.last().into_iter().cloned().collect())) } - ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => { + ty::TyStruct(def, substs) | ty::TyUnion(def, substs) | + ty::TyEnum(def, substs) => { let sized_crit = def.sized_constraint(self.tcx()); // (*) binder moved here Where(ty::Binder(match sized_crit.sty { @@ -1836,7 +1837,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { Where(ty::Binder(tys.to_vec())) } - ty::TyStruct(..) | ty::TyEnum(..) | + ty::TyStruct(..) | ty::TyUnion(..) | ty::TyEnum(..) | ty::TyProjection(..) | ty::TyParam(..) | ty::TyAnon(..) => { // Fallback to whatever user-defined impls exist in this case. None @@ -1933,7 +1934,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> { substs.types().collect() } - ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => { + ty::TyStruct(def, substs) | ty::TyUnion(def, substs) | ty::TyEnum(def, substs) => { def.all_fields() .map(|f| f.ty(self.tcx(), substs)) .collect() diff --git a/src/librustc/ty/contents.rs b/src/librustc/ty/contents.rs index 53bf046d6b597..d7d4693c1165f 100644 --- a/src/librustc/ty/contents.rs +++ b/src/librustc/ty/contents.rs @@ -224,7 +224,8 @@ impl<'a, 'tcx> ty::TyS<'tcx> { |ty| tc_ty(tcx, *ty, cache)) } - ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => { + ty::TyStruct(def, substs) | ty::TyUnion(def, substs) | + ty::TyEnum(def, substs) => { let mut res = TypeContents::union(&def.variants, |v| { TypeContents::union(&v.fields, |f| { diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 725bbf6adfd42..8dd7bc562d7c1 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1032,8 +1032,8 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { pub fn print_debug_stats(self) { sty_debug_print!( self, - TyEnum, TyBox, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr, - TyTrait, TyStruct, TyClosure, TyTuple, TyParam, TyInfer, TyProjection, TyAnon); + TyEnum, TyBox, TyArray, TySlice, TyRawPtr, TyRef, TyFnDef, TyFnPtr, TyTrait, + TyStruct, TyUnion, TyClosure, TyTuple, TyParam, TyInfer, TyProjection, TyAnon); println!("Substs interner: #{}", self.interners.substs.borrow().len()); println!("BareFnTy interner: #{}", self.interners.bare_fn.borrow().len()); diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs index 3d60d326b2b0f..0e33e396f7e18 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -247,6 +247,9 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> { ty::TyStruct(def, _) => { format!("struct `{}`", tcx.item_path_str(def.did)) } + ty::TyUnion(def, _) => { + format!("union `{}`", tcx.item_path_str(def.did)) + } ty::TyClosure(..) => "closure".to_string(), ty::TyTuple(_) => "tuple".to_string(), ty::TyInfer(ty::TyVar(_)) => "inferred type".to_string(), diff --git a/src/librustc/ty/fast_reject.rs b/src/librustc/ty/fast_reject.rs index f7472d611befe..23678d1e37742 100644 --- a/src/librustc/ty/fast_reject.rs +++ b/src/librustc/ty/fast_reject.rs @@ -66,6 +66,9 @@ pub fn simplify_type<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, ty::TyStruct(def, _) => { Some(StructSimplifiedType(def.did)) } + ty::TyUnion(..) => { + unimplemented_unions!(); + } ty::TyRef(_, mt) => { // since we introduce auto-refs during method lookup, we // just treat &T and T as equivalent from the point of diff --git a/src/librustc/ty/flags.rs b/src/librustc/ty/flags.rs index 1afd49ab47fbf..ce6e4d6516ec6 100644 --- a/src/librustc/ty/flags.rs +++ b/src/librustc/ty/flags.rs @@ -102,7 +102,7 @@ impl FlagComputation { } } - &ty::TyEnum(_, substs) | &ty::TyStruct(_, substs) => { + &ty::TyEnum(_, substs) | &ty::TyStruct(_, substs) | &ty::TyUnion(_, substs) => { self.add_substs(substs); } diff --git a/src/librustc/ty/item_path.rs b/src/librustc/ty/item_path.rs index 62bd30e255592..42ec2b18fb022 100644 --- a/src/librustc/ty/item_path.rs +++ b/src/librustc/ty/item_path.rs @@ -320,6 +320,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn characteristic_def_id_of_type(ty: Ty) -> Option { match ty.sty { ty::TyStruct(adt_def, _) | + ty::TyUnion(adt_def, _) | ty::TyEnum(adt_def, _) => Some(adt_def.did), ty::TyTrait(ref data) => Some(data.principal.def_id()), diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 1ede8545e08e8..622966ca5aba6 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -896,6 +896,9 @@ impl<'a, 'gcx, 'tcx> Layout { non_zero: Some(def.did) == tcx.lang_items.non_zero() } } + ty::TyUnion(..) => { + unimplemented_unions!(); + } ty::TyEnum(def, substs) => { let hint = *tcx.lookup_repr_hints(def.did).get(0) .unwrap_or(&attr::ReprAny); diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index e9c01f5bad66e..ddf25538ee4d0 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1421,6 +1421,7 @@ bitflags! { const IS_PHANTOM_DATA = 1 << 3, const IS_SIMD = 1 << 4, const IS_FUNDAMENTAL = 1 << 5, + const IS_UNION = 1 << 7, } } @@ -1818,7 +1819,7 @@ impl<'a, 'tcx> AdtDefData<'tcx, 'tcx> { } } - TyEnum(adt, substs) | TyStruct(adt, substs) => { + TyEnum(adt, substs) | TyStruct(adt, substs) | TyUnion(adt, substs) => { // recursive case let adt = tcx.lookup_adt_def_master(adt.did); adt.calculate_sized_constraint_inner(tcx, stack); diff --git a/src/librustc/ty/outlives.rs b/src/librustc/ty/outlives.rs index 4d5b38212f600..a7bb0374b75bf 100644 --- a/src/librustc/ty/outlives.rs +++ b/src/librustc/ty/outlives.rs @@ -174,6 +174,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { ty::TyNever | // ... ty::TyEnum(..) | // OutlivesNominalType ty::TyStruct(..) | // OutlivesNominalType + ty::TyUnion(..) | // OutlivesNominalType ty::TyBox(..) | // OutlivesNominalType (ish) ty::TyAnon(..) | // OutlivesNominalType (ish) ty::TyStr | // OutlivesScalar (ish) diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs index ad3769605abd9..952641f6832f9 100644 --- a/src/librustc/ty/structural_impls.rs +++ b/src/librustc/ty/structural_impls.rs @@ -495,6 +495,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { ty::TyRef(r.fold_with(folder), tm.fold_with(folder)) } ty::TyStruct(did, substs) => ty::TyStruct(did, substs.fold_with(folder)), + ty::TyUnion(did, substs) => ty::TyUnion(did, substs.fold_with(folder)), ty::TyClosure(did, substs) => ty::TyClosure(did, substs.fold_with(folder)), ty::TyProjection(ref data) => ty::TyProjection(data.fold_with(folder)), ty::TyAnon(did, substs) => ty::TyAnon(did, substs.fold_with(folder)), @@ -524,6 +525,7 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { ty::TyFnPtr(ref f) => f.visit_with(visitor), ty::TyRef(r, ref tm) => r.visit_with(visitor) || tm.visit_with(visitor), ty::TyStruct(_did, ref substs) => substs.visit_with(visitor), + ty::TyUnion(_did, ref substs) => substs.visit_with(visitor), ty::TyClosure(_did, ref substs) => substs.visit_with(visitor), ty::TyProjection(ref data) => data.visit_with(visitor), ty::TyAnon(_, ref substs) => substs.visit_with(visitor), diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 0e3f18c4474ea..b023cdad9ee23 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -120,6 +120,11 @@ pub enum TypeVariants<'tcx> { /// See warning about substitutions for enumerated types. TyStruct(AdtDef<'tcx>, &'tcx Substs<'tcx>), + /// A union type, defined with `union`. + /// + /// See warning about substitutions for enumerated types. + TyUnion(AdtDef<'tcx>, &'tcx Substs<'tcx>), + /// `Box`; this is nominally a struct in the documentation, but is /// special-cased internally. For example, it is possible to implicitly /// move the contents of a box out of that box, and methods of any type @@ -1227,6 +1232,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { } TyEnum(_, substs) | TyStruct(_, substs) | + TyUnion(_, substs) | TyAnon(_, substs) => { substs.regions().collect() } diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 77d16287fedc6..971fc2cee804f 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -430,6 +430,7 @@ impl<'a, 'gcx, 'tcx> TypeVisitor<'tcx> for TypeIdHasher<'a, 'gcx, 'tcx> { TyUint(u) => self.hash(u), TyFloat(f) => self.hash(f), TyStruct(d, _) | + TyUnion(d, _) | TyEnum(d, _) => self.def_id(d.did), TyArray(_, n) => self.hash(n), TyRawPtr(m) | @@ -558,7 +559,7 @@ impl<'a, 'tcx> ty::TyS<'tcx> { }) => Some(true), TyArray(..) | TySlice(_) | TyTrait(..) | TyTuple(..) | - TyClosure(..) | TyEnum(..) | TyStruct(..) | TyAnon(..) | + TyClosure(..) | TyEnum(..) | TyStruct(..) | TyUnion(..) | TyAnon(..) | TyProjection(..) | TyParam(..) | TyInfer(..) | TyError => None }.unwrap_or_else(|| !self.impls_bound(tcx, param_env, ty::BoundCopy, span)); @@ -598,7 +599,7 @@ impl<'a, 'tcx> ty::TyS<'tcx> { TyStr | TyTrait(..) | TySlice(_) => Some(false), - TyEnum(..) | TyStruct(..) | TyProjection(..) | TyParam(..) | + TyEnum(..) | TyStruct(..) | TyUnion(..) | TyProjection(..) | TyParam(..) | TyInfer(..) | TyAnon(..) | TyError => None }.unwrap_or_else(|| self.impls_bound(tcx, param_env, ty::BoundSized, span)); diff --git a/src/librustc/ty/walk.rs b/src/librustc/ty/walk.rs index 409f5a85997bd..cea3bd6348dbe 100644 --- a/src/librustc/ty/walk.rs +++ b/src/librustc/ty/walk.rs @@ -95,6 +95,7 @@ fn push_subtypes<'tcx>(stack: &mut Vec>, parent_ty: Ty<'tcx>) { } ty::TyEnum(_, ref substs) | ty::TyStruct(_, ref substs) | + ty::TyUnion(_, ref substs) | ty::TyAnon(_, ref substs) => { stack.extend(substs.types().rev()); } diff --git a/src/librustc/ty/wf.rs b/src/librustc/ty/wf.rs index aef646a7aacaf..599e2be4db247 100644 --- a/src/librustc/ty/wf.rs +++ b/src/librustc/ty/wf.rs @@ -337,7 +337,8 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> { } ty::TyEnum(def, substs) | - ty::TyStruct(def, substs) => { + ty::TyStruct(def, substs) | + ty::TyUnion(def, substs) => { // WfNominalType let obligations = self.nominal_obligations(def.did, substs); self.out.extend(obligations); diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 7e2cc2938ca9e..d0e02f2e8acdd 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -8,11 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - use hir::def_id::DefId; use ty::subst::{self, Subst, Substs}; use ty::{BrAnon, BrEnv, BrFresh, BrNamed}; -use ty::{TyBool, TyChar, TyStruct, TyEnum}; +use ty::{TyBool, TyChar, TyStruct, TyUnion, TyEnum}; use ty::{TyError, TyStr, TyArray, TySlice, TyFloat, TyFnDef, TyFnPtr}; use ty::{TyParam, TyRawPtr, TyRef, TyNever, TyTuple}; use ty::TyClosure; @@ -869,7 +868,7 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> { TyInfer(infer_ty) => write!(f, "{}", infer_ty), TyError => write!(f, "[type error]"), TyParam(ref param_ty) => write!(f, "{}", param_ty), - TyEnum(def, substs) | TyStruct(def, substs) => { + TyEnum(def, substs) | TyStruct(def, substs) | TyUnion(def, substs) => { ty::tls::with(|tcx| { if def.did.is_local() && !tcx.tcache.borrow().contains_key(&def.did) { diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 40e78c007ccfd..54cec3fd7e135 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -377,7 +377,8 @@ enum FfiResult { FfiSafe, FfiUnsafe(&'static str), FfiBadStruct(DefId, &'static str), - FfiBadEnum(DefId, &'static str) + FfiBadUnion(DefId, &'static str), + FfiBadEnum(DefId, &'static str), } /// Check if this enum can be safely exported based on the @@ -452,12 +453,32 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let r = self.check_type_for_ffi(cache, field_ty); match r { FfiSafe => {} - FfiBadStruct(..) | FfiBadEnum(..) => { return r; } + FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { return r; } FfiUnsafe(s) => { return FfiBadStruct(def.did, s); } } } FfiSafe } + ty::TyUnion(def, substs) => { + if !cx.lookup_repr_hints(def.did).contains(&attr::ReprExtern) { + return FfiUnsafe( + "found union without foreign-function-safe \ + representation annotation in foreign module, \ + consider adding a #[repr(C)] attribute to \ + the type"); + } + + for field in &def.struct_variant().fields { + let field_ty = cx.normalize_associated_type(&field.ty(cx, substs)); + let r = self.check_type_for_ffi(cache, field_ty); + match r { + FfiSafe => {} + FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { return r; } + FfiUnsafe(s) => { return FfiBadUnion(def.did, s); } + } + } + FfiSafe + } ty::TyEnum(def, substs) => { if def.variants.is_empty() { // Empty enums are okay... although sort of useless. @@ -507,7 +528,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let r = self.check_type_for_ffi(cache, arg); match r { FfiSafe => {} - FfiBadStruct(..) | FfiBadEnum(..) => { return r; } + FfiBadStruct(..) | FfiBadUnion(..) | FfiBadEnum(..) => { return r; } FfiUnsafe(s) => { return FfiBadEnum(def.did, s); } } } @@ -614,6 +635,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { &format!("found non-foreign-function-safe member in \ struct marked #[repr(C)]: {}", s)); } + FfiResult::FfiBadUnion(_, s) => { + // FIXME: This diagnostic is difficult to read, and doesn't + // point at the relevant field. + self.cx.span_lint(IMPROPER_CTYPES, sp, + &format!("found non-foreign-function-safe member in \ + union marked #[repr(C)]: {}", s)); + } FfiResult::FfiBadEnum(_, s) => { // FIXME: This diagnostic is difficult to read, and doesn't // point at the relevant variant. diff --git a/src/librustc_metadata/tyencode.rs b/src/librustc_metadata/tyencode.rs index 954ca878c01ef..b334a21c07c7a 100644 --- a/src/librustc_metadata/tyencode.rs +++ b/src/librustc_metadata/tyencode.rs @@ -170,6 +170,11 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx enc_substs(w, cx, substs); write!(w, "]"); } + ty::TyUnion(def, substs) => { + write!(w, "u[{}|", (cx.ds)(cx.tcx, def.did)); + enc_substs(w, cx, substs); + write!(w, "]"); + } ty::TyClosure(def, substs) => { write!(w, "k[{}|", (cx.ds)(cx.tcx, def)); enc_substs(w, cx, substs.func_substs); diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index bea6f30faec30..47b3bb36cb930 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -798,6 +798,7 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, /* nothing to do */ } ty::TyStruct(ref adt_def, substs) | + ty::TyUnion(ref adt_def, substs) | ty::TyEnum(ref adt_def, substs) => { for field in adt_def.all_fields() { let field_type = monomorphize::apply_param_substs(scx, diff --git a/src/librustc_trans/debuginfo/type_names.rs b/src/librustc_trans/debuginfo/type_names.rs index f757578e6954d..bd839243e201f 100644 --- a/src/librustc_trans/debuginfo/type_names.rs +++ b/src/librustc_trans/debuginfo/type_names.rs @@ -45,6 +45,7 @@ pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ty::TyUint(uint_ty) => output.push_str(uint_ty.ty_to_string()), ty::TyFloat(float_ty) => output.push_str(float_ty.ty_to_string()), ty::TyStruct(def, substs) | + ty::TyUnion(def, substs) | ty::TyEnum(def, substs) => { push_item_name(cx, def.did, qualified, output); push_type_params(cx, substs, output); diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index 8a0f37230c8df..deef0b09a17b0 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -397,6 +397,7 @@ pub fn push_unique_type_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty::TyFloat(ast::FloatTy::F32) => output.push_str("f32"), ty::TyFloat(ast::FloatTy::F64) => output.push_str("f64"), ty::TyStruct(adt_def, substs) | + ty::TyUnion(adt_def, substs) | ty::TyEnum(adt_def, substs) => { push_item_name(tcx, adt_def.did, output); push_type_params(tcx, substs, &[], output); diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs index 6605d12e2e127..d5d8f049681da 100644 --- a/src/librustc_trans/type_of.rs +++ b/src/librustc_trans/type_of.rs @@ -89,27 +89,23 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ Type::nil(cx) } - ty::TyTuple(..) | ty::TyEnum(..) | ty::TyClosure(..) => { - let repr = adt::represent_type(cx, t); - adt::sizing_type_of(cx, &repr, false) + ty::TyStruct(..) if t.is_simd() => { + let e = t.simd_type(cx.tcx()); + if !e.is_machine() { + cx.sess().fatal(&format!("monomorphising SIMD type `{}` with \ + a non-machine element type `{}`", + t, e)) + } + let llet = type_of(cx, e); + let n = t.simd_size(cx.tcx()) as u64; + ensure_array_fits_in_address_space(cx, llet, n, t); + Type::vector(&llet, n) } - ty::TyStruct(..) => { - if t.is_simd() { - let e = t.simd_type(cx.tcx()); - if !e.is_machine() { - cx.sess().fatal(&format!("monomorphising SIMD type `{}` with \ - a non-machine element type `{}`", - t, e)) - } - let llet = type_of(cx, e); - let n = t.simd_size(cx.tcx()) as u64; - ensure_array_fits_in_address_space(cx, llet, n, t); - Type::vector(&llet, n) - } else { - let repr = adt::represent_type(cx, t); - adt::sizing_type_of(cx, &repr, false) - } + ty::TyTuple(..) | ty::TyStruct(..) | ty::TyUnion(..) | + ty::TyEnum(..) | ty::TyClosure(..) => { + let repr = adt::represent_type(cx, t); + adt::sizing_type_of(cx, &repr, false) } ty::TyProjection(..) | ty::TyInfer(..) | ty::TyParam(..) | @@ -244,15 +240,6 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> ty::TyUint(t) => Type::uint_from_ty(cx, t), ty::TyFloat(t) => Type::float_from_ty(cx, t), ty::TyNever => Type::nil(cx), - ty::TyEnum(def, ref substs) => { - // Only create the named struct, but don't fill it in. We - // fill it in *after* placing it into the type cache. This - // avoids creating more than one copy of the enum when one - // of the enum's variants refers to the enum itself. - let repr = adt::represent_type(cx, t); - let name = llvm_type_name(cx, def.did, substs); - adt::incomplete_type_of(cx, &repr, &name[..]) - } ty::TyClosure(..) => { // Only create the named struct, but don't fill it in. We // fill it in *after* placing it into the type cache. @@ -307,26 +294,28 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> let repr = adt::represent_type(cx, t); adt::type_of(cx, &repr) } - ty::TyStruct(def, ref substs) => { - if t.is_simd() { - let e = t.simd_type(cx.tcx()); - if !e.is_machine() { - cx.sess().fatal(&format!("monomorphising SIMD type `{}` with \ - a non-machine element type `{}`", - t, e)) - } - let llet = in_memory_type_of(cx, e); - let n = t.simd_size(cx.tcx()) as u64; - ensure_array_fits_in_address_space(cx, llet, n, t); - Type::vector(&llet, n) - } else { - // Only create the named struct, but don't fill it in. We fill it - // in *after* placing it into the type cache. This prevents - // infinite recursion with recursive struct types. - let repr = adt::represent_type(cx, t); - let name = llvm_type_name(cx, def.did, substs); - adt::incomplete_type_of(cx, &repr, &name[..]) + ty::TyStruct(..) if t.is_simd() => { + let e = t.simd_type(cx.tcx()); + if !e.is_machine() { + cx.sess().fatal(&format!("monomorphising SIMD type `{}` with \ + a non-machine element type `{}`", + t, e)) } + let llet = in_memory_type_of(cx, e); + let n = t.simd_size(cx.tcx()) as u64; + ensure_array_fits_in_address_space(cx, llet, n, t); + Type::vector(&llet, n) + } + ty::TyStruct(def, ref substs) | + ty::TyUnion(def, ref substs) | + ty::TyEnum(def, ref substs) => { + // Only create the named struct, but don't fill it in. We + // fill it in *after* placing it into the type cache. This + // avoids creating more than one copy of the enum when one + // of the enum's variants refers to the enum itself. + let repr = adt::represent_type(cx, t); + let name = llvm_type_name(cx, def.did, substs); + adt::incomplete_type_of(cx, &repr, &name[..]) } ty::TyInfer(..) | diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index 3a6076774330d..b73b8b9988766 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -439,7 +439,7 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'gcx, 'tcx>( cx, context, ity, depth+1) } - ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => { + ty::TyStruct(def, substs) | ty::TyUnion(def, substs) | ty::TyEnum(def, substs) => { let did = def.did; for variant in &def.variants { for field in variant.fields.iter() { diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index f743ef2187561..a20195bd801d9 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -24,7 +24,7 @@ use rustc::ty::{ImplOrTraitItemId, ConstTraitItemId}; use rustc::ty::{MethodTraitItemId, TypeTraitItemId, ParameterEnvironment}; use rustc::ty::{Ty, TyBool, TyChar, TyEnum, TyError}; use rustc::ty::{TyParam, TyRawPtr}; -use rustc::ty::{TyRef, TyStruct, TyTrait, TyNever, TyTuple}; +use rustc::ty::{TyRef, TyStruct, TyUnion, TyTrait, TyNever, TyTuple}; use rustc::ty::{TyStr, TyArray, TySlice, TyFloat, TyInfer, TyInt}; use rustc::ty::{TyUint, TyClosure, TyBox, TyFnDef, TyFnPtr}; use rustc::ty::{TyProjection, TyAnon}; @@ -70,7 +70,8 @@ impl<'a, 'gcx, 'tcx> CoherenceChecker<'a, 'gcx, 'tcx> { fn get_base_type_def_id(&self, span: Span, ty: Ty<'tcx>) -> Option { match ty.sty { TyEnum(def, _) | - TyStruct(def, _) => { + TyStruct(def, _) | + TyUnion(def, _) => { Some(def.did) } diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index 888709d257246..e20e74be67cd3 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -344,7 +344,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } ty::TyEnum(def, substs) | - ty::TyStruct(def, substs) => { + ty::TyStruct(def, substs) | + ty::TyUnion(def, substs) => { let item_type = self.tcx().lookup_item_type(def.did); // This edge is actually implied by the call to diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index e4b6a30d5bcb3..92bb265ca995a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1801,6 +1801,7 @@ impl<'tcx> Clean for ty::Ty<'tcx> { decl: (cx.map.local_def_id(0), &fty.sig).clean(cx), abi: fty.abi, }), + ty::TyUnion(..) => unimplemented_unions!(), ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => { let did = def.did; From a014323e456c00f93134d03b4af95844b2ed4b95 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 9 Aug 2016 01:18:47 +0300 Subject: [PATCH 05/22] Lower unions from AST to HIR and from HIR to types Parse union items and add a feature for them --- src/librustc/hir/lowering.rs | 5 ++++- src/librustc/ty/context.rs | 5 +++++ src/librustc/ty/mod.rs | 10 +++++++--- src/librustc_privacy/lib.rs | 2 +- src/librustc_typeck/check/dropck.rs | 3 ++- src/librustc_typeck/collect.rs | 10 ++++++++++ src/libsyntax/feature_gate.rs | 9 +++++++++ src/libsyntax/parse/parser.rs | 31 +++++++++++++++++++++++++++++ 8 files changed, 69 insertions(+), 6 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 6739d3f662ac6..80e034721d63f 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -638,7 +638,10 @@ impl<'a> LoweringContext<'a> { let struct_def = self.lower_variant_data(struct_def); hir::ItemStruct(struct_def, self.lower_generics(generics)) } - ItemKind::Union(..) => panic!("`union` is not yet implemented"), + ItemKind::Union(ref vdata, ref generics) => { + let vdata = self.lower_variant_data(vdata); + hir::ItemUnion(vdata, self.lower_generics(generics)) + } ItemKind::DefaultImpl(unsafety, ref trait_ref) => { hir::ItemDefaultImpl(self.lower_unsafety(unsafety), self.lower_trait_ref(trait_ref)) diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 8dd7bc562d7c1..0fc1641d31f70 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -1321,6 +1321,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.mk_ty(TyStruct(def, substs)) } + pub fn mk_union(self, def: AdtDef<'tcx>, substs: &'tcx Substs<'tcx>) -> Ty<'tcx> { + // take a copy of substs so that we own the vectors inside + self.mk_ty(TyUnion(def, substs)) + } + pub fn mk_closure(self, closure_id: DefId, substs: &'tcx Substs<'tcx>, diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index ddf25538ee4d0..4cda74911138e 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1514,7 +1514,7 @@ impl<'tcx> Decodable for AdtDef<'tcx> { #[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum AdtKind { Struct, Enum } +pub enum AdtKind { Struct, Union, Enum } #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] pub enum VariantKind { Struct, Tuple, Unit } @@ -1545,8 +1545,10 @@ impl<'a, 'gcx, 'tcx, 'container> AdtDefData<'gcx, 'container> { if Some(did) == tcx.lang_items.phantom_data() { flags = flags | AdtFlags::IS_PHANTOM_DATA; } - if let AdtKind::Enum = kind { - flags = flags | AdtFlags::IS_ENUM; + match kind { + AdtKind::Enum => flags = flags | AdtFlags::IS_ENUM, + AdtKind::Union => flags = flags | AdtFlags::IS_UNION, + AdtKind::Struct => {} } AdtDefData { did: did, @@ -1569,6 +1571,8 @@ impl<'a, 'gcx, 'tcx, 'container> AdtDefData<'gcx, 'container> { pub fn adt_kind(&self) -> AdtKind { if self.flags.get().intersects(AdtFlags::IS_ENUM) { AdtKind::Enum + } else if self.flags.get().intersects(AdtFlags::IS_UNION) { + AdtKind::Union } else { AdtKind::Struct } diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index 4b13d13fb7058..cd2eb4d2b58bd 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -383,7 +383,7 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> { // Checks that a field is in scope. fn check_field(&mut self, span: Span, def: ty::AdtDef<'tcx>, field: ty::FieldDef<'tcx>) { - if def.adt_kind() == ty::AdtKind::Struct && + if def.adt_kind() != ty::AdtKind::Enum && !field.vis.is_accessible_from(self.curitem, &self.tcx.map) { struct_span_err!(self.tcx.sess, span, E0451, "field `{}` of struct `{}` is private", field.name, self.tcx.item_path_str(def.did)) diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index b73b8b9988766..bd47ff0b00b7b 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -304,7 +304,8 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>( tcx.item_path_str(def_id), variant), ty::AdtKind::Struct => format!("struct {}", - tcx.item_path_str(def_id)) + tcx.item_path_str(def_id)), + ty::AdtKind::Union => unimplemented_unions!(), }; span_note!( &mut err, diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index f2f3163dde7c7..7f9de2becee6e 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1069,6 +1069,16 @@ fn convert_struct_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, adt } +fn convert_union_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, + it: &hir::Item, + def: &hir::VariantData) + -> ty::AdtDefMaster<'tcx> +{ + let did = ccx.tcx.map.local_def_id(it.id); + let variants = vec![convert_struct_variant(ccx, did, it.name, ConstInt::Infer(0), def)]; + ccx.tcx.intern_adt_def(did, ty::AdtKind::Union, variants) +} + fn evaluate_disr_expr(ccx: &CrateCtxt, repr_ty: attr::IntType, e: &hir::Expr) -> Option { debug!("disr expr, checking {}", pprust::expr_to_string(e)); diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 683d5277359e8..b40124bd7741a 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -292,6 +292,9 @@ declare_features! ( // Macros 1.1 (active, rustc_macro, "1.13.0", Some(35900)), + + // Allows untagged unions `union U { ... }` + (active, untagged_unions, "1.13.0", Some(32836)), ); declare_features! ( @@ -953,6 +956,12 @@ impl<'a> Visitor for PostExpansionVisitor<'a> { } } + ast::ItemKind::Union(..) => { + gate_feature_post!(&self, untagged_unions, + i.span, + "unions are unstable and not fully implemented"); + } + ast::ItemKind::DefaultImpl(..) => { gate_feature_post!(&self, optin_builtin_traits, i.span, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 92ec0fdb3de31..290a59cf1e591 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -5102,6 +5102,25 @@ impl<'a> Parser<'a> { Ok((class_name, ItemKind::Struct(vdata, generics), None)) } + /// Parse union Foo { ... } + fn parse_item_union(&mut self) -> PResult<'a, ItemInfo> { + let class_name = self.parse_ident()?; + let mut generics = self.parse_generics()?; + + let vdata = if self.token.is_keyword(keywords::Where) { + generics.where_clause = self.parse_where_clause()?; + VariantData::Struct(self.parse_record_struct_body()?, ast::DUMMY_NODE_ID) + } else if self.token == token::OpenDelim(token::Brace) { + VariantData::Struct(self.parse_record_struct_body()?, ast::DUMMY_NODE_ID) + } else { + let token_str = self.this_token_to_string(); + return Err(self.fatal(&format!("expected `where` or `{{` after union \ + name, found `{}`", token_str))) + }; + + Ok((class_name, ItemKind::Union(vdata, generics), None)) + } + pub fn parse_record_struct_body(&mut self) -> PResult<'a, Vec> { let mut fields = Vec::new(); if self.eat(&token::OpenDelim(token::Brace)) { @@ -5938,6 +5957,18 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } + if self.eat_keyword(keywords::Union) { + // UNION ITEM + let (ident, item_, extra_attrs) = self.parse_item_union()?; + let last_span = self.last_span; + let item = self.mk_item(lo, + last_span.hi, + ident, + item_, + visibility, + maybe_append(attrs, extra_attrs)); + return Ok(Some(item)); + } self.parse_macro_use_or_failure(attrs,macros_allowed,attributes_allowed,lo,visibility) } From 641d8e9e4c12b3753cf4e2a9ac901ad08ea90e00 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 10 Aug 2016 21:00:17 +0300 Subject: [PATCH 06/22] Some better support for unions through the compiler --- src/librustc/hir/check_attr.rs | 13 +++-- src/librustc/hir/map/def_collector.rs | 3 +- src/librustc/infer/error_reporting.rs | 4 +- src/librustc/middle/dead.rs | 20 ++++---- src/librustc/middle/expr_use_visitor.rs | 51 +++++++++++-------- src/librustc/middle/stability.rs | 9 ++-- src/librustc/traits/coherence.rs | 2 +- src/librustc/traits/error_reporting.rs | 1 + src/librustc/ty/item_path.rs | 1 + src/librustc/ty/layout.rs | 3 ++ src/librustc/ty/mod.rs | 10 ++-- src/librustc/ty/relate.rs | 7 +++ src/librustc/ty/sty.rs | 7 +-- src/librustc/ty/util.rs | 17 ++++--- src/librustc_borrowck/borrowck/check_loans.rs | 4 +- .../borrowck/gather_loans/gather_moves.rs | 2 +- .../borrowck/gather_loans/move_error.rs | 1 + .../borrowck/mir/elaborate_drops.rs | 4 +- src/librustc_borrowck/borrowck/mir/mod.rs | 2 +- src/librustc_const_eval/check_match.rs | 7 +-- src/librustc_const_eval/eval.rs | 3 +- src/librustc_driver/test.rs | 1 + .../calculate_svh/svh_visitor.rs | 1 + src/librustc_lint/bad_style.rs | 2 +- src/librustc_lint/builtin.rs | 17 +++++-- src/librustc_lint/unused.rs | 1 + src/librustc_mir/hair/cx/expr.rs | 5 +- src/librustc_mir/hair/cx/pattern.rs | 7 ++- src/librustc_mir/transform/type_check.rs | 4 +- src/librustc_passes/consts.rs | 1 + src/librustc_privacy/lib.rs | 10 ++-- src/librustc_resolve/lib.rs | 5 +- src/librustc_save_analysis/lib.rs | 5 +- src/librustc_trans/adt.rs | 3 ++ src/librustc_trans/collector.rs | 1 + src/librustc_trans/common.rs | 6 +-- src/librustc_trans/debuginfo/metadata.rs | 7 +++ src/librustc_trans/glue.rs | 3 ++ src/librustc_trans/type_of.rs | 2 +- src/librustc_typeck/astconv.rs | 2 +- src/librustc_typeck/check/_match.rs | 2 +- src/librustc_typeck/check/dropck.rs | 3 +- src/librustc_typeck/check/method/probe.rs | 3 +- src/librustc_typeck/check/method/suggest.rs | 51 ++++++++++--------- src/librustc_typeck/check/mod.rs | 45 +++++++++------- src/librustc_typeck/check/wfcheck.rs | 3 ++ src/librustc_typeck/coherence/mod.rs | 3 +- src/librustc_typeck/coherence/orphan.rs | 7 ++- src/librustc_typeck/coherence/overlap.rs | 2 +- src/librustc_typeck/collect.rs | 3 +- src/librustc_typeck/variance/constraints.rs | 1 + 51 files changed, 243 insertions(+), 134 deletions(-) diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs index 53624702c848b..a2d4239388ace 100644 --- a/src/librustc/hir/check_attr.rs +++ b/src/librustc/hir/check_attr.rs @@ -18,6 +18,7 @@ use syntax::visit::Visitor; enum Target { Fn, Struct, + Union, Enum, Other, } @@ -27,6 +28,7 @@ impl Target { match item.node { ast::ItemKind::Fn(..) => Target::Fn, ast::ItemKind::Struct(..) => Target::Struct, + ast::ItemKind::Union(..) => Target::Union, ast::ItemKind::Enum(..) => Target::Enum, _ => Target::Other, } @@ -62,8 +64,10 @@ impl<'a> CheckAttrVisitor<'a> { let message = match &*name { "C" => { conflicting_reprs += 1; - if target != Target::Struct && target != Target::Enum { - "attribute should be applied to struct or enum" + if target != Target::Struct && + target != Target::Union && + target != Target::Enum { + "attribute should be applied to struct, enum or union" } else { continue } @@ -71,8 +75,9 @@ impl<'a> CheckAttrVisitor<'a> { "packed" => { // Do not increment conflicting_reprs here, because "packed" // can be used to modify another repr hint - if target != Target::Struct { - "attribute should be applied to struct" + if target != Target::Struct && + target != Target::Union { + "attribute should be applied to struct or union" } else { continue } diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs index 389d0a1d50d56..29fb19fd42152 100644 --- a/src/librustc/hir/map/def_collector.rs +++ b/src/librustc/hir/map/def_collector.rs @@ -331,7 +331,8 @@ impl<'ast> intravisit::Visitor<'ast> for DefCollector<'ast> { }); } } - hir::ItemStruct(ref struct_def, _) => { + hir::ItemStruct(ref struct_def, _) | + hir::ItemUnion(ref struct_def, _) => { // If this is a tuple-like struct, register the constructor. if !struct_def.is_struct() { this.create_def(struct_def.id(), diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs index 0cd39882b7cde..efce0c8354bac 100644 --- a/src/librustc/infer/error_reporting.rs +++ b/src/librustc/infer/error_reporting.rs @@ -105,6 +105,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { match item.node { hir::ItemImpl(..) => "impl", hir::ItemStruct(..) => "struct", + hir::ItemUnion(..) => "union", hir::ItemEnum(..) => "enum", hir::ItemTrait(..) => "trait", hir::ItemFn(..) => "function body", @@ -1370,7 +1371,8 @@ impl<'a, 'gcx, 'tcx> Rebuilder<'a, 'gcx, 'tcx> { } hir::TyPath(ref maybe_qself, ref path) => { match self.tcx.expect_def(cur_ty.id) { - Def::Enum(did) | Def::TyAlias(did) | Def::Struct(did) => { + Def::Enum(did) | Def::TyAlias(did) | + Def::Struct(did) | Def::Union(did) => { let generics = self.tcx.lookup_generics(did); let expected = diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 824383b11afcb..9a63ad4b9859e 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { } fn lookup_and_handle_definition(&mut self, id: ast::NodeId) { - use ty::TypeVariants::{TyEnum, TyStruct}; + use ty::TypeVariants::{TyEnum, TyStruct, TyUnion}; let def = self.tcx.expect_def(id); @@ -96,7 +96,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { if self.tcx.trait_of_item(def.def_id()).is_some() => { if let Some(substs) = self.tcx.tables.borrow().item_substs.get(&id) { match substs.substs.type_at(0).sty { - TyEnum(tyid, _) | TyStruct(tyid, _) => { + TyEnum(tyid, _) | TyStruct(tyid, _) | TyUnion(tyid, _) => { self.check_def_id(tyid.did) } _ => {} @@ -132,10 +132,11 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { } fn handle_field_access(&mut self, lhs: &hir::Expr, name: ast::Name) { - if let ty::TyStruct(def, _) = self.tcx.expr_ty_adjusted(lhs).sty { - self.insert_def_id(def.struct_variant().field_named(name).did); - } else { - span_bug!(lhs.span, "named field access on non-struct") + match self.tcx.expr_ty_adjusted(lhs).sty { + ty::TyStruct(def, _) | ty::TyUnion(def, _) => { + self.insert_def_id(def.struct_variant().field_named(name).did); + } + _ => span_bug!(lhs.span, "named field access on non-struct"), } } @@ -148,7 +149,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { fn handle_field_pattern_match(&mut self, lhs: &hir::Pat, pats: &[codemap::Spanned]) { let variant = match self.tcx.node_id_to_type(lhs.id).sty { - ty::TyStruct(adt, _) | ty::TyEnum(adt, _) => { + ty::TyStruct(adt, _) | ty::TyUnion(adt, _) | ty::TyEnum(adt, _) => { adt.variant_of_def(self.tcx.expect_def(lhs.id)) } _ => span_bug!(lhs.span, "non-ADT in struct pattern") @@ -185,7 +186,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> { match *node { ast_map::NodeItem(item) => { match item.node { - hir::ItemStruct(..) => { + hir::ItemStruct(..) | hir::ItemUnion(..) => { self.struct_has_extern_repr = item.attrs.iter().any(|attr| { attr::find_repr_attrs(self.tcx.sess.diagnostic(), attr) .contains(&attr::ReprExtern) @@ -423,7 +424,8 @@ impl<'a, 'tcx> DeadVisitor<'a, 'tcx> { | hir::ItemConst(..) | hir::ItemFn(..) | hir::ItemEnum(..) - | hir::ItemStruct(..) => true, + | hir::ItemStruct(..) + | hir::ItemUnion(..) => true, _ => false }; let ctor_id = get_struct_ctor_id(item); diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 798702e6fd657..7214049f6cd64 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -672,30 +672,36 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { // Select just those fields of the `with` // expression that will actually be used - if let ty::TyStruct(def, substs) = with_cmt.ty.sty { - // Consume those fields of the with expression that are needed. - for with_field in &def.struct_variant().fields { - if !contains_field_named(with_field, fields) { - let cmt_field = self.mc.cat_field( - &*with_expr, - with_cmt.clone(), - with_field.name, - with_field.ty(self.tcx(), substs) - ); - self.delegate_consume(with_expr.id, with_expr.span, cmt_field); + match with_cmt.ty.sty { + ty::TyStruct(def, substs) => { + // Consume those fields of the with expression that are needed. + for with_field in &def.struct_variant().fields { + if !contains_field_named(with_field, fields) { + let cmt_field = self.mc.cat_field( + &*with_expr, + with_cmt.clone(), + with_field.name, + with_field.ty(self.tcx(), substs) + ); + self.delegate_consume(with_expr.id, with_expr.span, cmt_field); + } } } - } else { - // the base expression should always evaluate to a - // struct; however, when EUV is run during typeck, it - // may not. This will generate an error earlier in typeck, - // so we can just ignore it. - if !self.tcx().sess.has_errors() { - span_bug!( - with_expr.span, - "with expression doesn't evaluate to a struct"); + ty::TyUnion(..) => { + unimplemented_unions!(); } - }; + _ => { + // the base expression should always evaluate to a + // struct; however, when EUV is run during typeck, it + // may not. This will generate an error earlier in typeck, + // so we can just ignore it. + if !self.tcx().sess.has_errors() { + span_bug!( + with_expr.span, + "with expression doesn't evaluate to a struct"); + } + } + } // walk the with expression so that complex expressions // are properly handled. @@ -1012,7 +1018,8 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { debug!("variant downcast_cmt={:?} pat={:?}", downcast_cmt, pat); delegate.matched_pat(pat, downcast_cmt, match_mode); } - Some(Def::Struct(..)) | Some(Def::TyAlias(..)) | Some(Def::AssociatedTy(..)) => { + Some(Def::Struct(..)) | Some(Def::Union(..)) | + Some(Def::TyAlias(..)) | Some(Def::AssociatedTy(..)) => { debug!("struct cmt_pat={:?} pat={:?}", cmt_pat, pat); delegate.matched_pat(pat, cmt_pat, match_mode); } diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs index c20fcc3fe1dc6..e6979c2545330 100644 --- a/src/librustc/middle/stability.rs +++ b/src/librustc/middle/stability.rs @@ -561,7 +561,9 @@ pub fn check_expr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, e: &hir::Expr, hir::ExprField(ref base_e, ref field) => { span = field.span; match tcx.expr_ty_adjusted(base_e).sty { - ty::TyStruct(def, _) => def.struct_variant().field_named(field.node).did, + ty::TyStruct(def, _) | ty::TyUnion(def, _) => { + def.struct_variant().field_named(field.node).did + } _ => span_bug!(e.span, "stability::check_expr: named field access on non-struct") } @@ -579,7 +581,7 @@ pub fn check_expr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, e: &hir::Expr, hir::ExprStruct(_, ref expr_fields, _) => { let type_ = tcx.expr_ty(e); match type_.sty { - ty::TyStruct(def, _) => { + ty::TyStruct(def, _) | ty::TyUnion(def, _) => { // check the stability of each field that appears // in the construction expression. for field in expr_fields { @@ -647,7 +649,8 @@ pub fn check_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pat: &hir::Pat, if is_internal(tcx, pat.span) { return; } let v = match tcx.pat_ty_opt(pat) { - Some(&ty::TyS { sty: ty::TyStruct(def, _), .. }) => def.struct_variant(), + Some(&ty::TyS { sty: ty::TyStruct(def, _), .. }) | + Some(&ty::TyS { sty: ty::TyUnion(def, _), .. }) => def.struct_variant(), Some(_) | None => return, }; match pat.node { diff --git a/src/librustc/traits/coherence.rs b/src/librustc/traits/coherence.rs index f856e110ea2a5..0a7d3e6e76d8f 100644 --- a/src/librustc/traits/coherence.rs +++ b/src/librustc/traits/coherence.rs @@ -224,7 +224,7 @@ fn fundamental_ty(tcx: TyCtxt, ty: Ty) -> bool { match ty.sty { ty::TyBox(..) | ty::TyRef(..) => true, - ty::TyEnum(def, _) | ty::TyStruct(def, _) => + ty::TyEnum(def, _) | ty::TyStruct(def, _) | ty::TyUnion(def, _) => def.is_fundamental(), ty::TyTrait(ref data) => tcx.has_attr(data.principal.def_id(), "fundamental"), diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 95e83d404a74c..e5ebe96932d4f 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -174,6 +174,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { match (type_category(a), type_category(b)) { (Some(cat_a), Some(cat_b)) => match (&a.sty, &b.sty) { (&ty::TyStruct(def_a, _), &ty::TyStruct(def_b, _)) | + (&ty::TyUnion(def_a, _), &ty::TyUnion(def_b, _)) | (&ty::TyEnum(def_a, _), &ty::TyEnum(def_b, _)) => def_a == def_b, _ => cat_a == cat_b diff --git a/src/librustc/ty/item_path.rs b/src/librustc/ty/item_path.rs index 42ec2b18fb022..ba8d332850925 100644 --- a/src/librustc/ty/item_path.rs +++ b/src/librustc/ty/item_path.rs @@ -263,6 +263,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { // anything other than a simple path. match self_ty.sty { ty::TyStruct(adt_def, substs) | + ty::TyUnion(adt_def, substs) | ty::TyEnum(adt_def, substs) => { if substs.types().next().is_none() { // ignore regions self.push_item_path(buffer, adt_def.did); diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index 622966ca5aba6..ac5e3c6fa7009 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -1256,6 +1256,9 @@ impl<'a, 'gcx, 'tcx> SizeSkeleton<'gcx> { } } + ty::TyUnion(..) => { + unimplemented_unions!(); + } ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => { // Only newtypes and enums w/ nullable pointer optimization. if def.variants.is_empty() || def.variants.len() > 2 { diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 4cda74911138e..ee2188e8e112c 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -948,6 +948,7 @@ impl<'tcx> TraitPredicate<'tcx> { .flat_map(|t| t.walk()) .filter_map(|t| match t.sty { ty::TyStruct(adt_def, _) | + ty::TyUnion(adt_def, _) | ty::TyEnum(adt_def, _) => Some(adt_def.did), _ => @@ -1341,6 +1342,7 @@ impl<'a, 'tcx> ParameterEnvironment<'tcx> { } hir::ItemEnum(..) | hir::ItemStruct(..) | + hir::ItemUnion(..) | hir::ItemTy(..) | hir::ItemImpl(..) | hir::ItemConst(..) | @@ -1615,7 +1617,8 @@ impl<'a, 'gcx, 'tcx, 'container> AdtDefData<'gcx, 'container> { /// Asserts this is a struct and returns the struct's unique /// variant. pub fn struct_variant(&self) -> &VariantDefData<'gcx, 'container> { - assert_eq!(self.adt_kind(), AdtKind::Struct); + let adt_kind = self.adt_kind(); + assert!(adt_kind == AdtKind::Struct || adt_kind == AdtKind::Union); &self.variants[0] } @@ -1674,7 +1677,8 @@ impl<'a, 'gcx, 'tcx, 'container> AdtDefData<'gcx, 'container> { pub fn variant_of_def(&self, def: Def) -> &VariantDefData<'gcx, 'container> { match def { Def::Variant(_, vid) => self.variant_with_id(vid), - Def::Struct(..) | Def::TyAlias(..) | Def::AssociatedTy(..) => self.struct_variant(), + Def::Struct(..) | Def::Union(..) | + Def::TyAlias(..) | Def::AssociatedTy(..) => self.struct_variant(), _ => bug!("unexpected def {:?} in variant_of_def", def) } } @@ -2413,7 +2417,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { Def::Variant(enum_did, did) => { self.lookup_adt_def(enum_did).variant_with_id(did) } - Def::Struct(did) => { + Def::Struct(did) | Def::Union(did) => { self.lookup_adt_def(did).struct_variant() } _ => bug!("expect_variant_def used with unexpected def {:?}", def) diff --git a/src/librustc/ty/relate.rs b/src/librustc/ty/relate.rs index 5c157ff32e7bb..dfae19487b6f0 100644 --- a/src/librustc/ty/relate.rs +++ b/src/librustc/ty/relate.rs @@ -447,6 +447,13 @@ pub fn super_relate_tys<'a, 'gcx, 'tcx, R>(relation: &mut R, Ok(tcx.mk_struct(a_def, substs)) } + (&ty::TyUnion(a_def, a_substs), &ty::TyUnion(b_def, b_substs)) + if a_def == b_def => + { + let substs = relate_item_substs(relation, a_def.did, a_substs, b_substs)?; + Ok(tcx.mk_union(a_def, substs)) + } + (&ty::TyClosure(a_id, a_substs), &ty::TyClosure(b_id, b_substs)) if a_id == b_id => diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index b023cdad9ee23..165f86fbef538 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -922,7 +922,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { // FIXME(#24885): be smarter here, the AdtDefData::is_empty method could easily be made // more complete. match self.sty { - TyEnum(def, _) | TyStruct(def, _) => def.is_empty(), + TyEnum(def, _) | TyStruct(def, _) | TyUnion(def, _) => def.is_empty(), // FIXME(canndrew): There's no reason why these can't be uncommented, they're tested // and they don't break anything. But I'm keeping my changes small for now. @@ -985,7 +985,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { pub fn is_structural(&self) -> bool { match self.sty { - TyStruct(..) | TyTuple(_) | TyEnum(..) | + TyStruct(..) | TyUnion(..) | TyTuple(..) | TyEnum(..) | TyArray(..) | TyClosure(..) => true, _ => self.is_slice() | self.is_trait() } @@ -1204,6 +1204,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { match self.sty { TyTrait(ref tt) => Some(tt.principal.def_id()), TyStruct(def, _) | + TyUnion(def, _) | TyEnum(def, _) => Some(def.did), TyClosure(id, _) => Some(id), _ => None @@ -1212,7 +1213,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> { pub fn ty_adt_def(&self) -> Option> { match self.sty { - TyStruct(adt, _) | TyEnum(adt, _) => Some(adt), + TyStruct(adt, _) | TyUnion(adt, _) | TyEnum(adt, _) => Some(adt), _ => None } } diff --git a/src/librustc/ty/util.rs b/src/librustc/ty/util.rs index 971fc2cee804f..ad209094600ae 100644 --- a/src/librustc/ty/util.rs +++ b/src/librustc/ty/util.rs @@ -138,7 +138,7 @@ impl<'tcx> ParameterEnvironment<'tcx> { // FIXME: (@jroesch) float this code up tcx.infer_ctxt(None, Some(self.clone()), Reveal::ExactMatch).enter(|infcx| { let adt = match self_type.sty { - ty::TyStruct(struct_def, substs) => { + ty::TyStruct(struct_def, substs) | ty::TyUnion(struct_def, substs) => { for field in struct_def.all_fields() { let field_ty = field.ty(tcx, substs); if infcx.type_moves_by_default(field_ty, span) { @@ -183,7 +183,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { pub fn has_error_field(self, ty: Ty<'tcx>) -> bool { match ty.sty { - ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => { + ty::TyStruct(def, substs) | ty::TyUnion(def, substs) | ty::TyEnum(def, substs) => { for field in def.all_fields() { let field_ty = field.ty(self, substs); if let TyError = field_ty.sty { @@ -203,7 +203,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { i: usize, variant: Option) -> Option> { match (&ty.sty, variant) { - (&TyStruct(def, substs), None) => { + (&TyStruct(def, substs), None) | + (&TyUnion(def, substs), None) => { def.struct_variant().fields.get(i).map(|f| f.ty(self, substs)) } (&TyEnum(def, substs), Some(vid)) => { @@ -225,7 +226,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { n: Name, variant: Option) -> Option> { match (&ty.sty, variant) { - (&TyStruct(def, substs), None) => { + (&TyStruct(def, substs), None) | + (&TyUnion(def, substs), None) => { def.struct_variant().find_field_named(n).map(|f| f.ty(self, substs)) } (&TyEnum(def, substs), Some(vid)) => { @@ -661,7 +663,7 @@ impl<'a, 'tcx> ty::TyS<'tcx> { TyArray(ty, _) => { is_type_structurally_recursive(tcx, sp, seen, ty) } - TyStruct(def, substs) | TyEnum(def, substs) => { + TyStruct(def, substs) | TyUnion(def, substs) | TyEnum(def, substs) => { find_nonrepresentable(tcx, sp, seen, @@ -678,7 +680,7 @@ impl<'a, 'tcx> ty::TyS<'tcx> { fn same_struct_or_enum<'tcx>(ty: Ty<'tcx>, def: ty::AdtDef<'tcx>) -> bool { match ty.sty { - TyStruct(ty_def, _) | TyEnum(ty_def, _) => { + TyStruct(ty_def, _) | TyUnion(ty_def, _) | TyEnum(ty_def, _) => { ty_def == def } _ => false @@ -688,6 +690,7 @@ impl<'a, 'tcx> ty::TyS<'tcx> { fn same_type<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { match (&a.sty, &b.sty) { (&TyStruct(did_a, ref substs_a), &TyStruct(did_b, ref substs_b)) | + (&TyUnion(did_a, ref substs_a), &TyUnion(did_b, ref substs_b)) | (&TyEnum(did_a, ref substs_a), &TyEnum(did_b, ref substs_b)) => { if did_a != did_b { return false; @@ -710,7 +713,7 @@ impl<'a, 'tcx> ty::TyS<'tcx> { debug!("is_type_structurally_recursive: {:?}", ty); match ty.sty { - TyStruct(def, _) | TyEnum(def, _) => { + TyStruct(def, _) | TyUnion(def, _) | TyEnum(def, _) => { { // Iterate through stack of previously seen types. let mut iter = seen.iter(); diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index e86fa9a05f372..b4c6689c24b9e 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -796,7 +796,9 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> { } LpExtend(ref lp_base, _, LpInterior(_, InteriorField(_))) => { match lp_base.to_type().sty { - ty::TyStruct(def, _) | ty::TyEnum(def, _) if def.has_dtor() => { + ty::TyStruct(def, _) | + ty::TyUnion(def, _) | + ty::TyEnum(def, _) if def.has_dtor() => { // In the case where the owner implements drop, then // the path must be initialized to prevent a case of // partial reinitialization diff --git a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs index 9431dcdbcac8e..3cf02fc85a463 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs @@ -178,7 +178,7 @@ fn check_and_get_illegal_move_origin<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, Categorization::Interior(ref b, mc::InteriorField(_)) | Categorization::Interior(ref b, mc::InteriorElement(Kind::Pattern, _)) => { match b.ty.sty { - ty::TyStruct(def, _) | ty::TyEnum(def, _) => { + ty::TyStruct(def, _) | ty::TyUnion(def, _) | ty::TyEnum(def, _) => { if def.has_dtor() { Some(cmt.clone()) } else { diff --git a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs index fc17633d63b93..61c85e393d2dd 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/move_error.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/move_error.rs @@ -149,6 +149,7 @@ fn report_cannot_move_out_of<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, Categorization::Interior(ref b, mc::InteriorField(_)) => { match b.ty.sty { ty::TyStruct(def, _) | + ty::TyUnion(def, _) | ty::TyEnum(def, _) if def.has_dtor() => { let mut err = struct_span_err!(bccx, move_from.span, E0509, "cannot move out of type `{}`, \ diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index 885bbe856c8fb..c5d1034537989 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -709,7 +709,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn open_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock { let ty = c.lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); match ty.sty { - ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => { + ty::TyStruct(def, substs) | ty::TyUnion(def, substs) | ty::TyEnum(def, substs) => { self.open_drop_for_adt(c, def, substs) } ty::TyTuple(tys) | ty::TyClosure(_, ty::ClosureSubsts { @@ -893,7 +893,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let ty = c.lvalue.ty(self.mir, self.tcx).to_ty(self.tcx); match ty.sty { - ty::TyStruct(def, _) | ty::TyEnum(def, _) => { + ty::TyStruct(def, _) | ty::TyUnion(def, _) | ty::TyEnum(def, _) => { if def.has_dtor() { self.tcx.sess.span_warn( c.source_info.span, diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index 887c7deb86be0..be408e2db5c34 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -261,7 +261,7 @@ fn lvalue_contents_drop_state_cannot_differ<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx lv, ty); true } - ty::TyStruct(def, _) | ty::TyEnum(def, _) if def.has_dtor() => { + ty::TyStruct(def, _) | ty::TyUnion(def, _) | ty::TyEnum(def, _) if def.has_dtor() => { debug!("lvalue_contents_drop_state_cannot_differ lv: {:?} ty: {:?} Drop => false", lv, ty); true diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index e71a780dd89bc..de28cbb7c9c96 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -566,7 +566,7 @@ fn construct_witness<'a,'tcx>(cx: &MatchCheckCtxt<'a,'tcx>, ctor: &Constructor, let pat = match left_ty.sty { ty::TyTuple(..) => PatKind::Tuple(pats.collect(), None), - ty::TyEnum(adt, _) | ty::TyStruct(adt, _) => { + ty::TyEnum(adt, _) | ty::TyStruct(adt, _) | ty::TyUnion(adt, _) => { let v = ctor.variant_for_adt(adt); match v.kind { VariantKind::Struct => { @@ -792,7 +792,8 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: &Pat, PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => match cx.tcx.expect_def(pat.id) { Def::Variant(_, id) => vec![Variant(id)], - Def::Struct(..) | Def::TyAlias(..) | Def::AssociatedTy(..) => vec![Single], + Def::Struct(..) | Def::Union(..) | + Def::TyAlias(..) | Def::AssociatedTy(..) => vec![Single], Def::Const(..) | Def::AssociatedConst(..) => span_bug!(pat.span, "const pattern should've been rewritten"), def => span_bug!(pat.span, "pat_constructors: unexpected definition {:?}", def), @@ -836,7 +837,7 @@ pub fn constructor_arity(_cx: &MatchCheckCtxt, ctor: &Constructor, ty: Ty) -> us _ => bug!() }, ty::TyRef(..) => 1, - ty::TyEnum(adt, _) | ty::TyStruct(adt, _) => { + ty::TyEnum(adt, _) | ty::TyStruct(adt, _) | ty::TyUnion(adt, _) => { ctor.variant_for_adt(adt).fields.len() } ty::TyArray(_, n) => n, diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index d71add3258fbd..81fe19812ca47 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -258,7 +258,8 @@ pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, format!("floating point constants cannot be used in patterns")); } ty::TyEnum(adt_def, _) | - ty::TyStruct(adt_def, _) => { + ty::TyStruct(adt_def, _) | + ty::TyUnion(adt_def, _) => { if !tcx.has_attr(adt_def.did, "structural_match") { tcx.sess.add_lint( lint::builtin::ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN, diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 460a6e68a5c5a..9f5f82c144ccd 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -220,6 +220,7 @@ impl<'a, 'gcx, 'tcx> Env<'a, 'gcx, 'tcx> { hir::ItemEnum(..) | hir::ItemStruct(..) | + hir::ItemUnion(..) | hir::ItemTrait(..) | hir::ItemImpl(..) | hir::ItemDefaultImpl(..) => { diff --git a/src/librustc_incremental/calculate_svh/svh_visitor.rs b/src/librustc_incremental/calculate_svh/svh_visitor.rs index c1158dc2d5fe9..d4a3ab59f9cb3 100644 --- a/src/librustc_incremental/calculate_svh/svh_visitor.rs +++ b/src/librustc_incremental/calculate_svh/svh_visitor.rs @@ -419,6 +419,7 @@ impl<'a, 'hash, 'tcx> StrictVersionHashVisitor<'a, 'hash, 'tcx> { Def::AssociatedTy(..) | Def::TyParam(..) | Def::Struct(..) | + Def::Union(..) | Def::Trait(..) | Def::Method(..) | Def::Const(..) | diff --git a/src/librustc_lint/bad_style.rs b/src/librustc_lint/bad_style.rs index 0e130c3bb66bf..1094d0ee12bbd 100644 --- a/src/librustc_lint/bad_style.rs +++ b/src/librustc_lint/bad_style.rs @@ -111,7 +111,7 @@ impl LateLintPass for NonCamelCaseTypes { } match it.node { - hir::ItemTy(..) | hir::ItemStruct(..) => { + hir::ItemTy(..) | hir::ItemStruct(..) | hir::ItemUnion(..) => { self.check_case(cx, "type", it.name, it.span) } hir::ItemTrait(..) => { diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index a103386e2c980..571d1222baaa4 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -116,7 +116,8 @@ impl LateLintPass for BoxPointers { hir::ItemFn(..) | hir::ItemTy(..) | hir::ItemEnum(..) | - hir::ItemStruct(..) => + hir::ItemStruct(..) | + hir::ItemUnion(..) => self.check_heap_type(cx, it.span, cx.tcx.node_id_to_type(it.id)), _ => () @@ -124,7 +125,8 @@ impl LateLintPass for BoxPointers { // If it's a struct, we also have to check the fields' types match it.node { - hir::ItemStruct(ref struct_def, _) => { + hir::ItemStruct(ref struct_def, _) | + hir::ItemUnion(ref struct_def, _) => { for struct_field in struct_def.fields() { self.check_heap_type(cx, struct_field.span, cx.tcx.node_id_to_type(struct_field.id)); @@ -348,6 +350,7 @@ impl LateLintPass for MissingDoc { hir::ItemMod(..) => "a module", hir::ItemEnum(..) => "an enum", hir::ItemStruct(..) => "a struct", + hir::ItemUnion(..) => "a union", hir::ItemTrait(_, _, _, ref items) => { // Issue #11592, traits are always considered exported, even when private. if it.vis == hir::Visibility::Inherited { @@ -467,6 +470,14 @@ impl LateLintPass for MissingCopyImplementations { let def = cx.tcx.lookup_adt_def(cx.tcx.map.local_def_id(item.id)); (def, cx.tcx.mk_struct(def, Substs::empty(cx.tcx))) } + hir::ItemUnion(_, ref ast_generics) => { + if ast_generics.is_parameterized() { + return; + } + let def = cx.tcx.lookup_adt_def(cx.tcx.map.local_def_id(item.id)); + (def, cx.tcx.mk_union(def, + cx.tcx.mk_substs(Substs::empty()))) + } hir::ItemEnum(_, ref ast_generics) => { if ast_generics.is_parameterized() { return; @@ -523,7 +534,7 @@ impl LateLintPass for MissingDebugImplementations { } match item.node { - hir::ItemStruct(..) | hir::ItemEnum(..) => {}, + hir::ItemStruct(..) | hir::ItemUnion(..) | hir::ItemEnum(..) => {}, _ => return, } diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 924b768958d69..44f1cf7b53356 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -137,6 +137,7 @@ impl LateLintPass for UnusedResults { ty::TyNever => return, ty::TyBool => return, ty::TyStruct(def, _) | + ty::TyUnion(def, _) | ty::TyEnum(def, _) => { let attrs = cx.tcx.get_attrs(def.did); check_must_use(cx, &attrs[..], s.span) diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 6e8a5771eea94..0469d44de4ba6 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -477,6 +477,9 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, }) } } + ty::TyUnion(..) => { + unimplemented_unions!(); + } ty::TyEnum(adt, substs) => { match cx.tcx.expect_def(expr.id) { Def::Variant(enum_id, variant_id) => { @@ -579,7 +582,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, body: block::to_expr_ref(cx, body) }, hir::ExprField(ref source, name) => { let index = match cx.tcx.expr_ty_adjusted(source).sty { - ty::TyStruct(adt_def, _) => + ty::TyStruct(adt_def, _) | ty::TyUnion(adt_def, _) => adt_def.variants[0].index_of_field_named(name.node), ref ty => span_bug!( diff --git a/src/librustc_mir/hair/cx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs index 0bd22cd2d9308..30f79796aaa6d 100644 --- a/src/librustc_mir/hair/cx/pattern.rs +++ b/src/librustc_mir/hair/cx/pattern.rs @@ -217,7 +217,9 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> { PatKind::Struct(_, ref fields, _) => { let pat_ty = self.cx.tcx.node_id_to_type(pat.id); let adt_def = match pat_ty.sty { - ty::TyStruct(adt_def, _) | ty::TyEnum(adt_def, _) => adt_def, + ty::TyStruct(adt_def, _) | + ty::TyUnion(adt_def, _) | + ty::TyEnum(adt_def, _) => adt_def, _ => { span_bug!( pat.span, @@ -313,7 +315,8 @@ impl<'patcx, 'cx, 'gcx, 'tcx> PatCx<'patcx, 'cx, 'gcx, 'tcx> { } } - Def::Struct(..) | Def::TyAlias(..) | Def::AssociatedTy(..) => { + Def::Struct(..) | Def::Union(..) | + Def::TyAlias(..) | Def::AssociatedTy(..) => { PatternKind::Leaf { subpatterns: subpatterns } } diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 21d4ae595e8ac..55bd51cd75ba9 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -281,7 +281,9 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> { (&adt_def.variants[variant_index], substs) } LvalueTy::Ty { ty } => match ty.sty { - ty::TyStruct(adt_def, substs) | ty::TyEnum(adt_def, substs) + ty::TyStruct(adt_def, substs) | + ty::TyUnion(adt_def, substs) | + ty::TyEnum(adt_def, substs) if adt_def.is_univariant() => { (&adt_def.variants[0], substs) } diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 2d1b6e1315f8b..c3749bf4546f3 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -440,6 +440,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Expr, node_ty: Ty<'tcx>) { match node_ty.sty { ty::TyStruct(def, _) | + ty::TyUnion(def, _) | ty::TyEnum(def, _) if def.has_dtor() => { v.add_qualif(ConstQualif::NEEDS_DROP); } diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index cd2eb4d2b58bd..25601b6bfec7f 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -174,7 +174,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> { self.update(trait_item.id, item_level); } } - hir::ItemStruct(ref def, _) => { + hir::ItemStruct(ref def, _) | hir::ItemUnion(ref def, _) => { if !def.is_struct() { self.update(def.id(), item_level); } @@ -321,8 +321,8 @@ impl<'b, 'a, 'tcx: 'a, 'v> Visitor<'v> for ReachEverythingInTheInterfaceVisitor< if let hir::TyPath(_, ref path) = ty.node { let def = self.ev.tcx.expect_def(ty.id); match def { - Def::Struct(def_id) | Def::Enum(def_id) | Def::TyAlias(def_id) | - Def::Trait(def_id) | Def::AssociatedTy(def_id, _) => { + Def::Struct(def_id) | Def::Union(def_id) | Def::Enum(def_id) | + Def::TyAlias(def_id) | Def::Trait(def_id) | Def::AssociatedTy(def_id, _) => { if let Some(node_id) = self.ev.tcx.map.as_local_node_id(def_id) { let item = self.ev.tcx.map.expect_item(node_id); if let Def::TyAlias(..) = def { @@ -943,8 +943,8 @@ impl<'a, 'tcx: 'a, 'v> Visitor<'v> for SearchInterfaceForPrivateItemsVisitor<'a, // free type aliases, but this isn't done yet. return } - Def::Struct(def_id) | Def::Enum(def_id) | Def::TyAlias(def_id) | - Def::Trait(def_id) | Def::AssociatedTy(def_id, _) => { + Def::Struct(def_id) | Def::Union(def_id) | Def::Enum(def_id) | + Def::TyAlias(def_id) | Def::Trait(def_id) | Def::AssociatedTy(def_id, _) => { // Non-local means public (private items can't leave their crate, modulo bugs) if let Some(node_id) = self.tcx.map.as_local_node_id(def_id) { let item = self.tcx.map.expect_item(node_id); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 1224c694a4e6e..db0704db33fd5 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -2187,6 +2187,7 @@ impl<'a> Resolver<'a> { Def::Trait(_) | Def::Enum(_) | Def::Struct(_) | + Def::Union(_) | Def::TyAlias(_) => true, _ => false, }, @@ -2389,7 +2390,7 @@ impl<'a> Resolver<'a> { PatKind::Struct(ref path, _, _) => { self.resolve_pattern_path(pat.id, None, path, TypeNS, |def| { match def { - Def::Struct(..) | Def::Variant(..) | + Def::Struct(..) | Def::Union(..) | Def::Variant(..) | Def::TyAlias(..) | Def::AssociatedTy(..) => true, _ => false, } @@ -2735,7 +2736,7 @@ impl<'a> Resolver<'a> { // Look for a field with the same name in the current self_type. if let Some(resolution) = self.def_map.get(&node_id) { match resolution.base_def { - Def::Enum(did) | Def::TyAlias(did) | + Def::Enum(did) | Def::TyAlias(did) | Def::Union(did) | Def::Struct(did) | Def::Variant(_, did) if resolution.depth == 0 => { if let Some(fields) = self.structs.get(&did) { if fields.iter().any(|&field_name| name == field_name) { diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index db535e22f194d..47f3a06de1bd1 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -404,7 +404,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } }; match self.tcx.expr_ty_adjusted(&hir_node).sty { - ty::TyStruct(def, _) => { + ty::TyStruct(def, _) | ty::TyUnion(def, _) => { let f = def.struct_variant().field_named(ident.node.name); let sub_span = self.span_utils.span_for_last_ident(expr.span); filter!(self.span_utils, sub_span, expr.span, None); @@ -423,7 +423,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { } ast::ExprKind::Struct(ref path, _, _) => { match self.tcx.expr_ty_adjusted(&hir_node).sty { - ty::TyStruct(def, _) => { + ty::TyStruct(def, _) | ty::TyUnion(def, _) => { let sub_span = self.span_utils.span_for_last_ident(path.span); filter!(self.span_utils, sub_span, path.span, None); Some(Data::TypeRefData(TypeRefData { @@ -487,6 +487,7 @@ impl<'l, 'tcx: 'l> SaveContext<'l, 'tcx> { })) } Def::Struct(def_id) | + Def::Union(def_id) | Def::Enum(def_id) | Def::TyAlias(def_id) | Def::Trait(def_id) | diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs index 2fb7a69d36186..069eef7895c84 100644 --- a/src/librustc_trans/adt.rs +++ b/src/librustc_trans/adt.rs @@ -176,6 +176,9 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, Univariant(mk_struct(cx, &ftys[..], packed, t)) } + ty::TyUnion(..) => { + unimplemented_unions!(); + } ty::TyClosure(_, ref substs) => { Univariant(mk_struct(cx, &substs.upvar_tys, false, t)) } diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 47b3bb36cb930..4bea5d7e87fc9 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -744,6 +744,7 @@ fn find_drop_glue_neighbors<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>, // monomorphized Drop::drop() implementation. let destructor_did = match ty.sty { ty::TyStruct(def, _) | + ty::TyUnion(def, _) | ty::TyEnum(def, _) => def.destructor(), _ => None }; diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index f4682de7dff6a..6ae6f8aead77d 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -88,8 +88,8 @@ pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) - return false; } match ty.sty { - ty::TyStruct(..) | ty::TyEnum(..) | ty::TyTuple(..) | ty::TyArray(_, _) | - ty::TyClosure(..) => { + ty::TyStruct(..) | ty::TyUnion(..) | ty::TyEnum(..) | + ty::TyTuple(..) | ty::TyArray(..) | ty::TyClosure(..) => { let llty = sizing_type_of(ccx, ty); llsize_of_alloc(ccx, llty) <= llsize_of_alloc(ccx, ccx.int_type()) } @@ -205,7 +205,7 @@ impl<'a, 'tcx> VariantInfo<'tcx> { -> Self { match ty.sty { - ty::TyStruct(adt, substs) | ty::TyEnum(adt, substs) => { + ty::TyStruct(adt, substs) | ty::TyUnion(adt, substs) | ty::TyEnum(adt, substs) => { let variant = match opt_def { None => adt.struct_variant(), Some(def) => adt.variant_of_def(def) diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs index 67d4a0e044c9c..f30880ac9beb5 100644 --- a/src/librustc_trans/debuginfo/metadata.rs +++ b/src/librustc_trans/debuginfo/metadata.rs @@ -184,6 +184,10 @@ impl<'tcx> TypeMap<'tcx> { unique_type_id.push_str("struct "); from_def_id_and_substs(self, cx, def.did, substs, &mut unique_type_id); }, + ty::TyUnion(def, substs) => { + unique_type_id.push_str("union "); + from_def_id_and_substs(self, cx, def.did, substs, &mut unique_type_id); + }, ty::TyTuple(component_types) if component_types.is_empty() => { push_debuginfo_type_name(cx, type_, false, &mut unique_type_id); }, @@ -781,6 +785,9 @@ pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, unique_type_id, usage_site_span).finalize(cx) } + ty::TyUnion(..) => { + unimplemented_unions!(); + } ty::TyTuple(ref elements) => { prepare_tuple_metadata(cx, t, diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs index 9e1e415f62a67..5da9ef3646e6b 100644 --- a/src/librustc_trans/glue.rs +++ b/src/librustc_trans/glue.rs @@ -470,6 +470,9 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK trans_exchange_free_ty(bcx, llbox, content_ty, DebugLoc::None) } } + ty::TyUnion(..) => { + unimplemented_unions!(); + } ty::TyTrait(..) => { // No support in vtable for distinguishing destroying with // versus without calling Drop::drop. Assert caller is diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs index d5d8f049681da..b5565109306b4 100644 --- a/src/librustc_trans/type_of.rs +++ b/src/librustc_trans/type_of.rs @@ -331,7 +331,7 @@ pub fn in_memory_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> // If this was an enum or struct, fill in the type now. match t.sty { - ty::TyEnum(..) | ty::TyStruct(..) | ty::TyClosure(..) + ty::TyEnum(..) | ty::TyStruct(..) | ty::TyUnion(..) | ty::TyClosure(..) if !t.is_simd() => { let repr = adt::represent_type(cx, t); adt::finish_type_of(cx, &repr, &mut llty); diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 6c9cc5f5e132a..c445455ef2bc5 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -1476,7 +1476,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { span, partition_bounds(tcx, span, &[])) } - Def::Enum(did) | Def::TyAlias(did) | Def::Struct(did) => { + Def::Enum(did) | Def::TyAlias(did) | Def::Struct(did) | Def::Union(did) => { tcx.prohibit_type_params(base_segments.split_last().unwrap().1); self.ast_path_to_ty(rscope, span, diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index f8a2e82edc2a6..5fdfa19190bdb 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -510,7 +510,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // Type check subpatterns. let substs = match pat_ty.sty { - ty::TyStruct(_, substs) | ty::TyEnum(_, substs) => substs, + ty::TyStruct(_, substs) | ty::TyUnion(_, substs) | ty::TyEnum(_, substs) => substs, _ => span_bug!(pat.span, "struct variant is not an ADT") }; self.check_struct_pat_fields(pat.span, fields, variant, substs, etc); diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index bd47ff0b00b7b..3e37602169a7a 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -45,6 +45,7 @@ pub fn check_drop_impl(ccx: &CrateCtxt, drop_impl_did: DefId) -> Result<(), ()> let dtor_predicates = ccx.tcx.lookup_predicates(drop_impl_did); match dtor_self_type.sty { ty::TyEnum(adt_def, self_to_impl_substs) | + ty::TyUnion(adt_def, self_to_impl_substs) | ty::TyStruct(adt_def, self_to_impl_substs) => { ensure_drop_params_and_item_params_correspond(ccx, drop_impl_did, @@ -495,7 +496,7 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'b, 'gcx, 'tcx>( fn has_dtor_of_interest<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, ty: Ty<'tcx>) -> bool { match ty.sty { - ty::TyEnum(def, _) | ty::TyStruct(def, _) => { + ty::TyEnum(def, _) | ty::TyStruct(def, _) | ty::TyUnion(def, _) => { def.is_dtorck(tcx) } ty::TyTrait(..) | ty::TyProjection(..) | ty::TyAnon(..) => { diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 2e2cb2765d93d..edee730086871 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -293,7 +293,8 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { self.assemble_inherent_impl_candidates_for_type(data.principal.def_id()); } ty::TyEnum(def, _) | - ty::TyStruct(def, _) => { + ty::TyStruct(def, _) | + ty::TyUnion(def, _) => { self.assemble_inherent_impl_candidates_for_type(def.did); } ty::TyBox(_) => { diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 46b3f503b6e76..6d8a73b8a6aa0 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -164,30 +164,33 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // give a helping note that it has to be called as (x.f)(...). if let Some(expr) = rcvr_expr { for (ty, _) in self.autoderef(span, rcvr_ty) { - if let ty::TyStruct(def, substs) = ty.sty { - if let Some(field) = def.struct_variant().find_field_named(item_name) { - let snippet = tcx.sess.codemap().span_to_snippet(expr.span); - let expr_string = match snippet { - Ok(expr_string) => expr_string, - _ => "s".into() // Default to a generic placeholder for the - // expression when we can't generate a - // string snippet - }; - - let field_ty = field.ty(tcx, substs); - - if self.is_fn_ty(&field_ty, span) { - err.span_note(span, &format!( - "use `({0}.{1})(...)` if you meant to call the function \ - stored in the `{1}` field", - expr_string, item_name)); - } else { - err.span_note(span, &format!( - "did you mean to write `{0}.{1}`?", - expr_string, item_name)); + match ty.sty { + ty::TyStruct(def, substs) | ty::TyUnion(def, substs) => { + if let Some(field) = def.struct_variant().find_field_named(item_name) { + let snippet = tcx.sess.codemap().span_to_snippet(expr.span); + let expr_string = match snippet { + Ok(expr_string) => expr_string, + _ => "s".into() // Default to a generic placeholder for the + // expression when we can't generate a + // string snippet + }; + + let field_ty = field.ty(tcx, substs); + + if self.is_fn_ty(&field_ty, span) { + err.span_note(span, &format!( + "use `({0}.{1})(...)` if you meant to call the function \ + stored in the `{1}` field", + expr_string, item_name)); + } else { + err.span_note(span, &format!( + "did you mean to write `{0}.{1}`?", + expr_string, item_name)); + } + break; } - break; } + _ => {} } } } @@ -355,7 +358,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { rcvr_expr: Option<&hir::Expr>) -> bool { fn is_local(ty: Ty) -> bool { match ty.sty { - ty::TyEnum(def, _) | ty::TyStruct(def, _) => def.did.is_local(), + ty::TyEnum(def, _) | ty::TyStruct(def, _) | ty::TyUnion(def, _) => { + def.did.is_local() + } ty::TyTrait(ref tr) => tr.principal.def_id().is_local(), diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index be33f836cc6cd..679ced1987e6c 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -762,6 +762,9 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) { hir::ItemStruct(..) => { check_struct(ccx, it.id, it.span); } + hir::ItemUnion(..) => { + unimplemented_unions!(); + } hir::ItemTy(_, ref generics) => { let pty_ty = ccx.tcx.node_id_to_type(it.id); check_bounds_are_used(ccx, generics, pty_ty); @@ -2942,18 +2945,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { let mut private_candidate = None; let mut autoderef = self.autoderef(expr.span, expr_t); while let Some((base_t, autoderefs)) = autoderef.next() { - if let ty::TyStruct(base_def, substs) = base_t.sty { - debug!("struct named {:?}", base_t); - if let Some(field) = base_def.struct_variant().find_field_named(field.node) { - let field_ty = self.field_ty(expr.span, field, substs); - if field.vis.is_accessible_from(self.body_id, &self.tcx().map) { - autoderef.finalize(lvalue_pref, Some(base)); - self.write_ty(expr.id, field_ty); - self.write_autoderef_adjustment(base.id, autoderefs); - return; + match base_t.sty { + ty::TyStruct(base_def, substs) | ty::TyUnion(base_def, substs) => { + debug!("struct named {:?}", base_t); + if let Some(field) = base_def.struct_variant().find_field_named(field.node) { + let field_ty = self.field_ty(expr.span, field, substs); + if field.vis.is_accessible_from(self.body_id, &self.tcx().map) { + autoderef.finalize(lvalue_pref, Some(base)); + self.write_ty(expr.id, field_ty); + self.write_autoderef_adjustment(base.id, autoderefs); + return; + } + private_candidate = Some((base_def.did, field_ty)); } - private_candidate = Some((base_def.did, field_ty)); } + _ => {} } } autoderef.unambiguous_final_ty(); @@ -2986,12 +2992,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { but no field with that name was found", field.node, actual) }, expr_t); - if let ty::TyRawPtr(..) = expr_t.sty { - err.note(&format!("`{0}` is a native pointer; perhaps you need to deref with \ - `(*{0}).{1}`", pprust::expr_to_string(base), field.node)); - } - if let ty::TyStruct(def, _) = expr_t.sty { - Self::suggest_field_names(&mut err, def.struct_variant(), field, vec![]); + match expr_t.sty { + ty::TyStruct(def, _) | ty::TyUnion(def, _) => { + Self::suggest_field_names(&mut err, def.struct_variant(), field, vec![]); + } + ty::TyRawPtr(..) => { + err.note(&format!("`{0}` is a native pointer; perhaps you need to deref with \ + `(*{0}).{1}`", pprust::expr_to_string(base), field.node)); + } + _ => {} } err.emit(); self.write_error(expr.id); @@ -3125,7 +3134,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { check_completeness: bool) { let tcx = self.tcx; let substs = match adt_ty.sty { - ty::TyStruct(_, substs) | ty::TyEnum(_, substs) => substs, + ty::TyStruct(_, substs) | ty::TyUnion(_, substs) | ty::TyEnum(_, substs) => substs, _ => span_bug!(span, "non-ADT passed to check_expr_struct_fields") }; @@ -3217,7 +3226,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.set_tainted_by_errors(); return None; } - Def::Variant(type_did, _) | Def::Struct(type_did) => { + Def::Variant(type_did, _) | Def::Struct(type_did) | Def::Union(type_did) => { Some((type_did, self.tcx.expect_variant_def(def))) } Def::TyAlias(did) => { diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 435442bd30a65..19bee13b6f603 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -142,6 +142,9 @@ impl<'ccx, 'gcx> CheckTypeWellFormedVisitor<'ccx, 'gcx> { self.check_variances_for_type_defn(item, ast_generics); } + hir::ItemUnion(..) => { + unimplemented_unions!(); + } hir::ItemEnum(ref enum_def, ref ast_generics) => { self.check_type_defn(item, |fcx| { fcx.enum_variants(enum_def) diff --git a/src/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs index a20195bd801d9..fba145efa9507 100644 --- a/src/librustc_typeck/coherence/mod.rs +++ b/src/librustc_typeck/coherence/mod.rs @@ -242,7 +242,8 @@ impl<'a, 'gcx, 'tcx> CoherenceChecker<'a, 'gcx, 'tcx> { let self_type = tcx.lookup_item_type(impl_did); match self_type.ty.sty { ty::TyEnum(type_def, _) | - ty::TyStruct(type_def, _) => { + ty::TyStruct(type_def, _) | + ty::TyUnion(type_def, _) => { type_def.set_destructor(method_def_id.def_id()); } _ => { diff --git a/src/librustc_typeck/coherence/orphan.rs b/src/librustc_typeck/coherence/orphan.rs index bcce64cb110c6..4c38475335ce8 100644 --- a/src/librustc_typeck/coherence/orphan.rs +++ b/src/librustc_typeck/coherence/orphan.rs @@ -76,7 +76,8 @@ impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> { let self_ty = self.tcx.lookup_item_type(def_id).ty; match self_ty.sty { ty::TyEnum(def, _) | - ty::TyStruct(def, _) => { + ty::TyStruct(def, _) | + ty::TyUnion(def, _) => { self.check_def_id(item, def.did); } ty::TyTrait(ref data) => { @@ -293,7 +294,9 @@ impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> { { let self_ty = trait_ref.self_ty(); let opt_self_def_id = match self_ty.sty { - ty::TyStruct(self_def, _) | ty::TyEnum(self_def, _) => + ty::TyStruct(self_def, _) | + ty::TyUnion(self_def, _) | + ty::TyEnum(self_def, _) => Some(self_def.did), ty::TyBox(..) => self.tcx.lang_items.owned_box(), diff --git a/src/librustc_typeck/coherence/overlap.rs b/src/librustc_typeck/coherence/overlap.rs index f60fb9583a661..c4d925372f18d 100644 --- a/src/librustc_typeck/coherence/overlap.rs +++ b/src/librustc_typeck/coherence/overlap.rs @@ -97,7 +97,7 @@ impl<'cx, 'tcx> OverlapChecker<'cx, 'tcx> { impl<'cx, 'tcx,'v> intravisit::Visitor<'v> for OverlapChecker<'cx, 'tcx> { fn visit_item(&mut self, item: &'v hir::Item) { match item.node { - hir::ItemEnum(..) | hir::ItemStruct(..) => { + hir::ItemEnum(..) | hir::ItemStruct(..) | hir::ItemUnion(..) => { let type_def_id = self.tcx.map.local_def_id(item.id); self.check_for_overlapping_inherent_impls(type_def_id); } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 7f9de2becee6e..1d260b9dbb290 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -931,7 +931,8 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) { tcx.trait_item_def_ids.borrow_mut().insert(ccx.tcx.map.local_def_id(it.id), trait_item_def_ids); }, - hir::ItemStruct(ref struct_def, _) => { + hir::ItemStruct(ref struct_def, _) | + hir::ItemUnion(ref struct_def, _) => { let def_id = ccx.tcx.map.local_def_id(it.id); let scheme = type_scheme_of_def_id(ccx, def_id); let predicates = predicates_of_item(ccx, it); diff --git a/src/librustc_typeck/variance/constraints.rs b/src/librustc_typeck/variance/constraints.rs index e20e74be67cd3..24eb29f45a5e5 100644 --- a/src/librustc_typeck/variance/constraints.rs +++ b/src/librustc_typeck/variance/constraints.rs @@ -185,6 +185,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { hir::ItemTy(..) | hir::ItemEnum(..) | hir::ItemStruct(..) | + hir::ItemUnion(..) | hir::ItemTrait(..) => is_inferred = true, hir::ItemFn(..) => is_inferred = false, _ => cannot_happen!(), From 6792bd99febc646f22814b6759e31e622850f405 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 10 Aug 2016 21:00:17 +0300 Subject: [PATCH 07/22] Support unions in rustdoc --- src/librustdoc/clean/inline.rs | 19 +++++++ src/librustdoc/clean/mod.rs | 36 ++++++++++++- src/librustdoc/doctree.rs | 15 ++++++ src/librustdoc/fold.rs | 7 +++ src/librustdoc/html/item_type.rs | 4 ++ src/librustdoc/html/render.rs | 81 ++++++++++++++++++++++++++++-- src/librustdoc/html/static/main.js | 3 +- src/librustdoc/passes.rs | 2 +- src/librustdoc/visit_ast.rs | 24 ++++++++- src/librustdoc/visit_lib.rs | 1 + src/test/rustdoc/union.rs | 20 ++++++++ 11 files changed, 204 insertions(+), 8 deletions(-) create mode 100644 src/test/rustdoc/union.rs diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 20d4c41765540..4250979a24033 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -88,6 +88,11 @@ fn try_inline_def<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, ret.extend(build_impls(cx, tcx, did)); clean::StructItem(build_struct(cx, tcx, did)) } + Def::Union(did) => { + record_extern_fqn(cx, did, clean::TypeUnion); + ret.extend(build_impls(cx, tcx, did)); + clean::UnionItem(build_union(cx, tcx, did)) + } Def::TyAlias(did) => { record_extern_fqn(cx, did, clean::TypeTypedef); ret.extend(build_impls(cx, tcx, did)); @@ -214,6 +219,20 @@ fn build_struct<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, } } +fn build_union<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, + did: DefId) -> clean::Union { + let t = tcx.lookup_item_type(did); + let predicates = tcx.lookup_predicates(did); + let variant = tcx.lookup_adt_def(did).struct_variant(); + + clean::Union { + struct_type: doctree::Plain, + generics: (&t.generics, &predicates, subst::TypeSpace).clean(cx), + fields: variant.fields.clean(cx), + fields_stripped: false, + } +} + fn build_type<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>, did: DefId) -> clean::ItemEnum { let t = tcx.lookup_item_type(did); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 92bb265ca995a..f8ec5a55e7d4c 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -321,6 +321,7 @@ impl Item { pub fn has_stripped_fields(&self) -> Option { match self.inner { StructItem(ref _struct) => Some(_struct.fields_stripped), + UnionItem(ref union) => Some(union.fields_stripped), VariantItem(Variant { kind: StructVariant(ref vstruct)} ) => { Some(vstruct.fields_stripped) }, @@ -351,6 +352,7 @@ pub enum ItemEnum { ExternCrateItem(String, Option), ImportItem(Import), StructItem(Struct), + UnionItem(Union), EnumItem(Enum), FunctionItem(Function), ModuleItem(Module), @@ -414,6 +416,7 @@ impl Clean for doctree::Module { items.extend(self.extern_crates.iter().map(|x| x.clean(cx))); items.extend(self.imports.iter().flat_map(|x| x.clean(cx))); items.extend(self.structs.iter().map(|x| x.clean(cx))); + items.extend(self.unions.iter().map(|x| x.clean(cx))); items.extend(self.enums.iter().map(|x| x.clean(cx))); items.extend(self.fns.iter().map(|x| x.clean(cx))); items.extend(self.foreigns.iter().flat_map(|x| x.clean(cx))); @@ -1464,6 +1467,7 @@ pub enum TypeKind { TypeConst, TypeStatic, TypeStruct, + TypeUnion, TypeTrait, TypeVariant, TypeTypedef, @@ -1801,12 +1805,13 @@ impl<'tcx> Clean for ty::Ty<'tcx> { decl: (cx.map.local_def_id(0), &fty.sig).clean(cx), abi: fty.abi, }), - ty::TyUnion(..) => unimplemented_unions!(), ty::TyStruct(def, substs) | + ty::TyUnion(def, substs) | ty::TyEnum(def, substs) => { let did = def.did; let kind = match self.sty { ty::TyStruct(..) => TypeStruct, + ty::TyUnion(..) => TypeUnion, _ => TypeEnum, }; inline::record_extern_fqn(cx, did, kind); @@ -1929,6 +1934,14 @@ pub struct Struct { pub fields_stripped: bool, } +#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +pub struct Union { + pub struct_type: doctree::StructType, + pub generics: Generics, + pub fields: Vec, + pub fields_stripped: bool, +} + impl Clean for doctree::Struct { fn clean(&self, cx: &DocContext) -> Item { Item { @@ -1949,6 +1962,26 @@ impl Clean for doctree::Struct { } } +impl Clean for doctree::Union { + fn clean(&self, cx: &DocContext) -> Item { + Item { + name: Some(self.name.clean(cx)), + attrs: self.attrs.clean(cx), + source: self.whence.clean(cx), + def_id: cx.map.local_def_id(self.id), + visibility: self.vis.clean(cx), + stability: self.stab.clean(cx), + deprecation: self.depr.clean(cx), + inner: UnionItem(Union { + struct_type: self.struct_type, + generics: self.generics.clean(cx), + fields: self.fields.clean(cx), + fields_stripped: false, + }), + } + } +} + /// This is a more limited form of the standard Struct, different in that /// it lacks the things most items have (name, id, parameterization). Found /// only as a variant in an enum. @@ -2748,6 +2781,7 @@ fn register_def(cx: &DocContext, def: Def) -> DefId { Def::Enum(i) => (i, TypeEnum), Def::Trait(i) => (i, TypeTrait), Def::Struct(i) => (i, TypeStruct), + Def::Union(i) => (i, TypeUnion), Def::Mod(i) => (i, TypeModule), Def::Static(i, _) => (i, TypeStatic), Def::Variant(i, _) => (i, TypeEnum), diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index 04d176c36c8cf..cc62fcfa0aa8b 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -30,6 +30,7 @@ pub struct Module { pub extern_crates: Vec, pub imports: Vec, pub structs: Vec, + pub unions: Vec, pub enums: Vec, pub fns: Vec, pub mods: Vec, @@ -62,6 +63,7 @@ impl Module { extern_crates: Vec::new(), imports : Vec::new(), structs : Vec::new(), + unions : Vec::new(), enums : Vec::new(), fns : Vec::new(), mods : Vec::new(), @@ -108,6 +110,19 @@ pub struct Struct { pub whence: Span, } +pub struct Union { + pub vis: hir::Visibility, + pub stab: Option, + pub depr: Option, + pub id: NodeId, + pub struct_type: StructType, + pub name: Name, + pub generics: hir::Generics, + pub attrs: hir::HirVec, + pub fields: hir::HirVec, + pub whence: Span, +} + pub struct Enum { pub vis: hir::Visibility, pub stab: Option, diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index 5595c749256df..8d6ab221c4fce 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -49,6 +49,13 @@ pub trait DocFolder : Sized { i.fields.iter().any(|f| f.is_stripped()); StructItem(i) }, + UnionItem(mut i) => { + let num_fields = i.fields.len(); + i.fields = i.fields.into_iter().filter_map(|x| self.fold_item(x)).collect(); + i.fields_stripped |= num_fields != i.fields.len() || + i.fields.iter().any(|f| f.is_stripped()); + UnionItem(i) + }, EnumItem(mut i) => { let num_variants = i.variants.len(); i.variants = i.variants.into_iter().filter_map(|x| self.fold_item(x)).collect(); diff --git a/src/librustdoc/html/item_type.rs b/src/librustdoc/html/item_type.rs index be19217928467..9ce3c79e2f19b 100644 --- a/src/librustdoc/html/item_type.rs +++ b/src/librustdoc/html/item_type.rs @@ -40,6 +40,7 @@ pub enum ItemType { AssociatedType = 16, Constant = 17, AssociatedConst = 18, + Union = 19, } @@ -62,6 +63,7 @@ impl<'a> From<&'a clean::Item> for ItemType { clean::ExternCrateItem(..) => ItemType::ExternCrate, clean::ImportItem(..) => ItemType::Import, clean::StructItem(..) => ItemType::Struct, + clean::UnionItem(..) => ItemType::Union, clean::EnumItem(..) => ItemType::Enum, clean::FunctionItem(..) => ItemType::Function, clean::TypedefItem(..) => ItemType::Typedef, @@ -89,6 +91,7 @@ impl From for ItemType { fn from(kind: clean::TypeKind) -> ItemType { match kind { clean::TypeStruct => ItemType::Struct, + clean::TypeUnion => ItemType::Union, clean::TypeEnum => ItemType::Enum, clean::TypeFunction => ItemType::Function, clean::TypeTrait => ItemType::Trait, @@ -108,6 +111,7 @@ impl ItemType { ItemType::ExternCrate => "externcrate", ItemType::Import => "import", ItemType::Struct => "struct", + ItemType::Union => "union", ItemType::Enum => "enum", ItemType::Function => "fn", ItemType::Typedef => "type", diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 6d523ff381556..c1bb9d9f633bc 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1053,6 +1053,7 @@ impl DocFolder for Cache { // information if present. Some(&(ref fqp, ItemType::Trait)) | Some(&(ref fqp, ItemType::Struct)) | + Some(&(ref fqp, ItemType::Union)) | Some(&(ref fqp, ItemType::Enum)) => Some(&fqp[..fqp.len() - 1]), Some(..) => Some(&*self.stack), @@ -1106,7 +1107,8 @@ impl DocFolder for Cache { clean::TypedefItem(..) | clean::TraitItem(..) | clean::FunctionItem(..) | clean::ModuleItem(..) | clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) | - clean::ConstantItem(..) | clean::StaticItem(..) + clean::ConstantItem(..) | clean::StaticItem(..) | + clean::UnionItem(..) if !self.stripped_mod => { // Reexported items mean that the same id can show up twice // in the rustdoc ast that we're looking at. We know, @@ -1141,7 +1143,8 @@ impl DocFolder for Cache { // Maintain the parent stack let orig_parent_is_trait_impl = self.parent_is_trait_impl; let parent_pushed = match item.inner { - clean::TraitItem(..) | clean::EnumItem(..) | clean::StructItem(..) => { + clean::TraitItem(..) | clean::EnumItem(..) | + clean::StructItem(..) | clean::UnionItem(..) => { self.parent_stack.push(item.def_id); self.parent_is_trait_impl = false; true @@ -1557,6 +1560,7 @@ impl<'a> fmt::Display for Item<'a> { clean::FunctionItem(..) => write!(fmt, "Function ")?, clean::TraitItem(..) => write!(fmt, "Trait ")?, clean::StructItem(..) => write!(fmt, "Struct ")?, + clean::UnionItem(..) => write!(fmt, "Union ")?, clean::EnumItem(..) => write!(fmt, "Enum ")?, clean::PrimitiveItem(..) => write!(fmt, "Primitive Type ")?, _ => {} @@ -1613,6 +1617,7 @@ impl<'a> fmt::Display for Item<'a> { item_function(fmt, self.cx, self.item, f), clean::TraitItem(ref t) => item_trait(fmt, self.cx, self.item, t), clean::StructItem(ref s) => item_struct(fmt, self.cx, self.item, s), + clean::UnionItem(ref s) => item_union(fmt, self.cx, self.item, s), clean::EnumItem(ref e) => item_enum(fmt, self.cx, self.item, e), clean::TypedefItem(ref t, _) => item_typedef(fmt, self.cx, self.item, t), clean::MacroItem(ref m) => item_macro(fmt, self.cx, self.item, m), @@ -1715,7 +1720,8 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, ItemType::Trait => 9, ItemType::Function => 10, ItemType::Typedef => 12, - _ => 13 + ty as u8, + ItemType::Union => 13, + _ => 14 + ty as u8, } } @@ -1759,6 +1765,7 @@ fn item_module(w: &mut fmt::Formatter, cx: &Context, ItemType::Import => ("reexports", "Reexports"), ItemType::Module => ("modules", "Modules"), ItemType::Struct => ("structs", "Structs"), + ItemType::Union => ("unions", "Unions"), ItemType::Enum => ("enums", "Enums"), ItemType::Function => ("functions", "Functions"), ItemType::Typedef => ("types", "Type Definitions"), @@ -2312,6 +2319,40 @@ fn item_struct(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) } +fn item_union(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, + s: &clean::Union) -> fmt::Result { + write!(w, "
")?;
+    render_attributes(w, it)?;
+    render_union(w,
+                 it,
+                 Some(&s.generics),
+                 &s.fields,
+                 "",
+                 true)?;
+    write!(w, "
")?; + + document(w, cx, it)?; + let mut fields = s.fields.iter().filter_map(|f| { + match f.inner { + clean::StructFieldItem(ref ty) => Some((f, ty)), + _ => None, + } + }).peekable(); + if fields.peek().is_some() { + write!(w, "

Fields

")?; + for (field, ty) in fields { + write!(w, "{name}: {ty} + ", + shortty = ItemType::StructField, + stab = field.stability_class(), + name = field.name.as_ref().unwrap(), + ty = ty)?; + document(w, cx, field)?; + } + } + render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) +} + fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, e: &clean::Enum) -> fmt::Result { write!(w, "
")?;
@@ -2514,6 +2555,40 @@ fn render_struct(w: &mut fmt::Formatter, it: &clean::Item,
     Ok(())
 }
 
+fn render_union(w: &mut fmt::Formatter, it: &clean::Item,
+                g: Option<&clean::Generics>,
+                fields: &[clean::Item],
+                tab: &str,
+                structhead: bool) -> fmt::Result {
+    write!(w, "{}{}{}",
+           VisSpace(&it.visibility),
+           if structhead {"union "} else {""},
+           it.name.as_ref().unwrap())?;
+    if let Some(g) = g {
+        write!(w, "{}", g)?
+    }
+    if let Some(g) = g {
+        write!(w, "{}", WhereClause(g))?
+    }
+
+    write!(w, " {{\n{}", tab)?;
+    for field in fields {
+        if let clean::StructFieldItem(ref ty) = field.inner {
+            write!(w, "    {}{}: {},\n{}",
+                   VisSpace(&field.visibility),
+                   field.name.as_ref().unwrap(),
+                   *ty,
+                   tab)?;
+        }
+    }
+
+    if it.has_stripped_fields().unwrap() {
+        write!(w, "    // some fields omitted\n{}", tab)?;
+    }
+    write!(w, "}}")?;
+    Ok(())
+}
+
 #[derive(Copy, Clone)]
 enum AssocItemLink<'a> {
     Anchor(Option<&'a str>),
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index de7e4d2483b43..9bb7246e7a92e 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -34,7 +34,8 @@
                      "primitive",
                      "associatedtype",
                      "constant",
-                     "associatedconstant"];
+                     "associatedconstant",
+                     "union"];
 
     // used for special search precedence
     var TY_PRIMITIVE = itemTypes.indexOf("primitive");
diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs
index b8e40790646a7..c60e22824965f 100644
--- a/src/librustdoc/passes.rs
+++ b/src/librustdoc/passes.rs
@@ -113,7 +113,7 @@ impl<'a> fold::DocFolder for Stripper<'a> {
             clean::TraitItem(..) | clean::FunctionItem(..) |
             clean::VariantItem(..) | clean::MethodItem(..) |
             clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) |
-            clean::ConstantItem(..) => {
+            clean::ConstantItem(..) | clean::UnionItem(..) => {
                 if i.def_id.is_local() {
                     if !self.access_levels.is_exported(i.def_id) {
                         return None;
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index e70fbf4463a22..16a6e994b5a0b 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -108,6 +108,25 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         }
     }
 
+    pub fn visit_union_data(&mut self, item: &hir::Item,
+                            name: ast::Name, sd: &hir::VariantData,
+                            generics: &hir::Generics) -> Union {
+        debug!("Visiting union");
+        let struct_type = struct_type_from_def(&*sd);
+        Union {
+            id: item.id,
+            struct_type: struct_type,
+            name: name,
+            vis: item.vis.clone(),
+            stab: self.stability(item.id),
+            depr: self.deprecation(item.id),
+            attrs: item.attrs.clone(),
+            generics: generics.clone(),
+            fields: sd.fields().iter().cloned().collect(),
+            whence: item.span
+        }
+    }
+
     pub fn visit_enum_def(&mut self, it: &hir::Item,
                           name: ast::Name, def: &hir::EnumDef,
                           params: &hir::Generics) -> Enum {
@@ -258,6 +277,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             match def {
                 Def::Trait(did) |
                 Def::Struct(did) |
+                Def::Union(did) |
                 Def::Enum(did) |
                 Def::TyAlias(did) if !self_is_hidden => {
                     self.cx.access_levels.borrow_mut().map.insert(did, AccessLevel::Public);
@@ -365,8 +385,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                 om.enums.push(self.visit_enum_def(item, name, ed, gen)),
             hir::ItemStruct(ref sd, ref gen) =>
                 om.structs.push(self.visit_variant_data(item, name, sd, gen)),
-            hir::ItemUnion(..) =>
-                unimplemented_unions!(),
+            hir::ItemUnion(ref sd, ref gen) =>
+                om.unions.push(self.visit_union_data(item, name, sd, gen)),
             hir::ItemFn(ref fd, ref unsafety, constness, ref abi, ref gen, _) =>
                 om.fns.push(self.visit_fn(item, name, &**fd, unsafety,
                                           constness, abi, gen)),
diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs
index f6d89f7c1dc89..3af030706b739 100644
--- a/src/librustdoc/visit_lib.rs
+++ b/src/librustdoc/visit_lib.rs
@@ -73,6 +73,7 @@ impl<'a, 'b, 'tcx> LibEmbargoVisitor<'a, 'b, 'tcx> {
                     Def::ForeignMod(did) |
                     Def::Trait(did) |
                     Def::Struct(did) |
+                    Def::Union(did) |
                     Def::Enum(did) |
                     Def::TyAlias(did) |
                     Def::Fn(did) |
diff --git a/src/test/rustdoc/union.rs b/src/test/rustdoc/union.rs
new file mode 100644
index 0000000000000..0dcc9098ad75c
--- /dev/null
+++ b/src/test/rustdoc/union.rs
@@ -0,0 +1,20 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+// @has union/union.U.html
+pub union U {
+    // @has - //pre "pub a: u8"
+    pub a: u8,
+    // @has - //pre "// some fields omitted"
+    // @!has - //pre "b: u16"
+    b: u16,
+}

From 5f9ef3c8b28c9a25be74bc2e41399a4c684f07b1 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Mon, 15 Aug 2016 16:10:58 +0300
Subject: [PATCH 08/22] Implement encoding/decoding unions in metadata

Add well-formedness check
Implement some more missing code
---
 src/librustc/middle/expr_use_visitor.rs       |  6 +---
 src/librustc/ty/fast_reject.rs                |  5 +--
 src/librustc_metadata/decoder.rs              |  7 ++++
 src/librustc_typeck/check/dropck.rs           |  3 +-
 src/librustc_typeck/check/mod.rs              | 26 +++++++-------
 src/librustc_typeck/check/wfcheck.rs          | 34 ++++++++++---------
 src/test/compile-fail/attr-usage-repr.rs      |  2 +-
 src/test/compile-fail/issue-31769.rs          |  2 +-
 .../compile-fail/union-nonrepresentable.rs    | 18 ++++++++++
 src/test/compile-fail/union-unsized.rs        | 17 ++++++++++
 src/test/run-pass/auxiliary/union.rs          | 16 +++++++++
 src/test/run-pass/union-xcrate.rs             | 21 ++++++++++++
 12 files changed, 119 insertions(+), 38 deletions(-)
 create mode 100644 src/test/compile-fail/union-nonrepresentable.rs
 create mode 100644 src/test/compile-fail/union-unsized.rs
 create mode 100644 src/test/run-pass/auxiliary/union.rs
 create mode 100644 src/test/run-pass/union-xcrate.rs

diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 7214049f6cd64..a6835802f1cf9 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -414,7 +414,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
             }
 
             hir::ExprStruct(_, ref fields, ref opt_with) => {
-                self.walk_struct_expr(expr, fields, opt_with);
+                self.walk_struct_expr(fields, opt_with);
             }
 
             hir::ExprTup(ref exprs) => {
@@ -655,7 +655,6 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
     }
 
     fn walk_struct_expr(&mut self,
-                        _expr: &hir::Expr,
                         fields: &[hir::Field],
                         opt_with: &Option>) {
         // Consume the expressions supplying values for each field.
@@ -687,9 +686,6 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
                     }
                 }
             }
-            ty::TyUnion(..) => {
-                unimplemented_unions!();
-            }
             _ => {
                 // the base expression should always evaluate to a
                 // struct; however, when EUV is run during typeck, it
diff --git a/src/librustc/ty/fast_reject.rs b/src/librustc/ty/fast_reject.rs
index 23678d1e37742..f9ca2484d7efc 100644
--- a/src/librustc/ty/fast_reject.rs
+++ b/src/librustc/ty/fast_reject.rs
@@ -30,6 +30,7 @@ pub enum SimplifiedType {
     TupleSimplifiedType(usize),
     TraitSimplifiedType(DefId),
     StructSimplifiedType(DefId),
+    UnionSimplifiedType(DefId),
     ClosureSimplifiedType(DefId),
     AnonSimplifiedType(DefId),
     FunctionSimplifiedType(usize),
@@ -66,8 +67,8 @@ pub fn simplify_type<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
         ty::TyStruct(def, _) => {
             Some(StructSimplifiedType(def.did))
         }
-        ty::TyUnion(..) => {
-            unimplemented_unions!();
+        ty::TyUnion(def, _) => {
+            Some(UnionSimplifiedType(def.did))
         }
         ty::TyRef(_, mt) => {
             // since we introduce auto-refs during method lookup, we
diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index 9a13be8ade52a..8775f58d0b22b 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -133,6 +133,7 @@ enum Family {
     DefaultImpl,           // d
     Trait,                 // I
     Struct(VariantKind),   // S, s, u
+    Union,                 // U
     PublicField,           // g
     InheritedField,        // N
     Constant,              // C
@@ -160,6 +161,7 @@ fn item_family(item: rbml::Doc) -> Family {
       'S' => Struct(VariantKind::Struct),
       's' => Struct(VariantKind::Tuple),
       'u' => Struct(VariantKind::Unit),
+      'U' => Union,
       'g' => PublicField,
       'N' => InheritedField,
        c => bug!("unexpected family char: {}", c)
@@ -317,6 +319,7 @@ fn item_to_def_like(cdata: Cmd, item: rbml::Doc, did: DefId) -> DefLike {
         ImmStatic => DlDef(Def::Static(did, false)),
         MutStatic => DlDef(Def::Static(did, true)),
         Struct(..) => DlDef(Def::Struct(did)),
+        Union => DlDef(Def::Union(did)),
         Fn        => DlDef(Def::Fn(did)),
         Method | StaticMethod => {
             DlDef(Def::Method(did))
@@ -461,6 +464,10 @@ pub fn get_adt_def<'a, 'tcx>(cdata: Cmd,
             (ty::AdtKind::Struct,
              vec![get_struct_variant(cdata, doc, ctor_did.unwrap_or(did))])
         }
+        Union => {
+            (ty::AdtKind::Union,
+             vec![get_struct_variant(cdata, doc, did)])
+        }
         _ => bug!("get_adt_def called on a non-ADT {:?} - {:?}",
                   item_family(doc), did)
     };
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 3e37602169a7a..88add66b7dcb0 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -306,7 +306,8 @@ pub fn check_safety_of_destructor_if_necessary<'a, 'gcx, 'tcx>(
                                                      variant),
                         ty::AdtKind::Struct => format!("struct {}",
                                                        tcx.item_path_str(def_id)),
-                        ty::AdtKind::Union => unimplemented_unions!(),
+                        ty::AdtKind::Union => format!("union {}",
+                                                       tcx.item_path_str(def_id)),
                     };
                     span_note!(
                         &mut err,
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 679ced1987e6c..e2954cecc9c46 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -713,16 +713,18 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
     fcx
 }
 
-pub fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
-    let tcx = ccx.tcx;
-
-    check_representable(tcx, span, id, "struct");
+fn check_struct(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
+    check_representable(ccx.tcx, span, id);
 
-    if tcx.lookup_simd(ccx.tcx.map.local_def_id(id)) {
-        check_simd(tcx, span, id);
+    if ccx.tcx.lookup_simd(ccx.tcx.map.local_def_id(id)) {
+        check_simd(ccx.tcx, span, id);
     }
 }
 
+fn check_union(ccx: &CrateCtxt, id: ast::NodeId, span: Span) {
+    check_representable(ccx.tcx, span, id);
+}
+
 pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) {
     debug!("check_item_type(it.id={}, it.name={})",
            it.id,
@@ -763,7 +765,7 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) {
         check_struct(ccx, it.id, it.span);
       }
       hir::ItemUnion(..) => {
-        unimplemented_unions!();
+        check_union(ccx, it.id, it.span);
       }
       hir::ItemTy(_, ref generics) => {
         let pty_ty = ccx.tcx.node_id_to_type(it.id);
@@ -1174,10 +1176,10 @@ fn check_const<'a, 'tcx>(ccx: &CrateCtxt<'a,'tcx>,
 /// Checks whether a type can be represented in memory. In particular, it
 /// identifies types that contain themselves without indirection through a
 /// pointer, which would mean their size is unbounded.
-pub fn check_representable<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                     sp: Span,
-                                     item_id: ast::NodeId,
-                                     _designation: &str) -> bool {
+fn check_representable<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                 sp: Span,
+                                 item_id: ast::NodeId)
+                                 -> bool {
     let rty = tcx.node_id_to_type(item_id);
 
     // Check that it is possible to represent this type. This call identifies
@@ -1277,7 +1279,7 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
         disr_vals.push(current_disr_val);
     }
 
-    check_representable(ccx.tcx, sp, id, "enum");
+    check_representable(ccx.tcx, sp, id);
 }
 
 impl<'a, 'gcx, 'tcx> AstConv<'gcx, 'tcx> for FnCtxt<'a, 'gcx, 'tcx> {
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index 19bee13b6f603..7cc3be0a89062 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -136,17 +136,21 @@ impl<'ccx, 'gcx> CheckTypeWellFormedVisitor<'ccx, 'gcx> {
                 self.check_item_type(item);
             }
             hir::ItemStruct(ref struct_def, ref ast_generics) => {
-                self.check_type_defn(item, |fcx| {
+                self.check_type_defn(item, false, |fcx| {
                     vec![fcx.struct_variant(struct_def)]
                 });
 
                 self.check_variances_for_type_defn(item, ast_generics);
             }
-            hir::ItemUnion(..) => {
-                unimplemented_unions!();
+            hir::ItemUnion(ref struct_def, ref ast_generics) => {
+                self.check_type_defn(item, true, |fcx| {
+                    vec![fcx.struct_variant(struct_def)]
+                });
+
+                self.check_variances_for_type_defn(item, ast_generics);
             }
             hir::ItemEnum(ref enum_def, ref ast_generics) => {
-                self.check_type_defn(item, |fcx| {
+                self.check_type_defn(item, false, |fcx| {
                     fcx.enum_variants(enum_def)
                 });
 
@@ -219,24 +223,22 @@ impl<'ccx, 'gcx> CheckTypeWellFormedVisitor<'ccx, 'gcx> {
     }
 
     /// In a type definition, we check that to ensure that the types of the fields are well-formed.
-    fn check_type_defn(&mut self, item: &hir::Item, mut lookup_fields: F) where
-        F: for<'fcx, 'tcx> FnMut(&FnCtxt<'fcx, 'gcx, 'tcx>)
-                                 -> Vec>
+    fn check_type_defn(&mut self, item: &hir::Item, all_sized: bool, mut lookup_fields: F)
+        where F: for<'fcx, 'tcx> FnMut(&FnCtxt<'fcx, 'gcx, 'tcx>) -> Vec>
     {
         self.for_item(item).with_fcx(|fcx, this| {
             let variants = lookup_fields(fcx);
 
             for variant in &variants {
                 // For DST, all intermediate types must be sized.
-                if let Some((_, fields)) = variant.fields.split_last() {
-                    for field in fields {
-                        fcx.register_builtin_bound(
-                            field.ty,
-                            ty::BoundSized,
-                            traits::ObligationCause::new(field.span,
-                                                         fcx.body_id,
-                                                         traits::FieldSized));
-                    }
+                let unsized_len = if all_sized || variant.fields.is_empty() { 0 } else { 1 };
+                for field in &variant.fields[..variant.fields.len() - unsized_len] {
+                    fcx.register_builtin_bound(
+                        field.ty,
+                        ty::BoundSized,
+                        traits::ObligationCause::new(field.span,
+                                                     fcx.body_id,
+                                                     traits::FieldSized));
                 }
 
                 // All field types must be well-formed.
diff --git a/src/test/compile-fail/attr-usage-repr.rs b/src/test/compile-fail/attr-usage-repr.rs
index 9bad6a8389a5d..b07d3e2f90675 100644
--- a/src/test/compile-fail/attr-usage-repr.rs
+++ b/src/test/compile-fail/attr-usage-repr.rs
@@ -11,7 +11,7 @@
 #![allow(dead_code)]
 #![feature(repr_simd)]
 
-#[repr(C)] //~ ERROR: attribute should be applied to struct or enum
+#[repr(C)] //~ ERROR: attribute should be applied to struct, enum or union
 fn f() {}
 
 #[repr(C)]
diff --git a/src/test/compile-fail/issue-31769.rs b/src/test/compile-fail/issue-31769.rs
index 4b5df7ea53ca1..7f73d9076ec99 100644
--- a/src/test/compile-fail/issue-31769.rs
+++ b/src/test/compile-fail/issue-31769.rs
@@ -10,5 +10,5 @@
 
 fn main() {
     #[inline] struct Foo;  //~ ERROR attribute should be applied to function
-    #[repr(C)] fn foo() {} //~ ERROR attribute should be applied to struct or enum
+    #[repr(C)] fn foo() {} //~ ERROR attribute should be applied to struct, enum or union
 }
diff --git a/src/test/compile-fail/union-nonrepresentable.rs b/src/test/compile-fail/union-nonrepresentable.rs
new file mode 100644
index 0000000000000..cb4683c2a0e12
--- /dev/null
+++ b/src/test/compile-fail/union-nonrepresentable.rs
@@ -0,0 +1,18 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+union U { //~ ERROR recursive type `U` has infinite size
+    a: u8,
+    b: U,
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/union-unsized.rs b/src/test/compile-fail/union-unsized.rs
new file mode 100644
index 0000000000000..381122406d71f
--- /dev/null
+++ b/src/test/compile-fail/union-unsized.rs
@@ -0,0 +1,17 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+union U {
+    a: str, //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied
+}
+
+fn main() {}
diff --git a/src/test/run-pass/auxiliary/union.rs b/src/test/run-pass/auxiliary/union.rs
new file mode 100644
index 0000000000000..dc0ca7c81c009
--- /dev/null
+++ b/src/test/run-pass/auxiliary/union.rs
@@ -0,0 +1,16 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+pub union U {
+    pub a: u8,
+    b: u16,
+}
diff --git a/src/test/run-pass/union-xcrate.rs b/src/test/run-pass/union-xcrate.rs
new file mode 100644
index 0000000000000..2a76c96ef25f5
--- /dev/null
+++ b/src/test/run-pass/union-xcrate.rs
@@ -0,0 +1,21 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:union.rs
+
+// #![feature(untagged_unions)]
+
+extern crate union;
+
+type A = union::U;
+
+fn main() {
+    assert_eq!(std::mem::size_of::(), 8);
+}

From c2ca1530dbcde745175569b9f5434fa6643e5766 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Thu, 18 Aug 2016 15:43:24 +0300
Subject: [PATCH 09/22] Fix rebase + address comments

---
 src/librustc/middle/dead.rs                 | 2 +-
 src/librustc/middle/stability.rs            | 4 ++--
 src/librustc/ty/sty.rs                      | 2 +-
 src/librustc_lint/builtin.rs                | 3 +--
 src/librustc_typeck/check/method/suggest.rs | 7 ++++---
 src/librustc_typeck/collect.rs              | 5 +++++
 src/librustdoc/clean/inline.rs              | 2 +-
 src/librustdoc/html/item_type.rs            | 1 +
 src/librustdoc/html/render.rs               | 6 ++----
 9 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs
index 9a63ad4b9859e..0b1d9e8d8f69e 100644
--- a/src/librustc/middle/dead.rs
+++ b/src/librustc/middle/dead.rs
@@ -136,7 +136,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
             ty::TyStruct(def, _) | ty::TyUnion(def, _) => {
                 self.insert_def_id(def.struct_variant().field_named(name).did);
             }
-            _ => span_bug!(lhs.span, "named field access on non-struct"),
+            _ => span_bug!(lhs.span, "named field access on non-struct/union"),
         }
     }
 
diff --git a/src/librustc/middle/stability.rs b/src/librustc/middle/stability.rs
index e6979c2545330..aea1ee8d82401 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -565,7 +565,7 @@ pub fn check_expr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, e: &hir::Expr,
                     def.struct_variant().field_named(field.node).did
                 }
                 _ => span_bug!(e.span,
-                               "stability::check_expr: named field access on non-struct")
+                               "stability::check_expr: named field access on non-struct/union")
             }
         }
         hir::ExprTupField(ref base_e, ref field) => {
@@ -601,7 +601,7 @@ pub fn check_expr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, e: &hir::Expr,
                 _ => {
                     span_bug!(e.span,
                               "stability::check_expr: struct construction \
-                               of non-struct, type {:?}",
+                               of non-struct/union, type {:?}",
                               type_);
                 }
             }
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 165f86fbef538..d45fde925c511 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -112,7 +112,7 @@ pub enum TypeVariants<'tcx> {
     /// That is, even after substitution it is possible that there are type
     /// variables. This happens when the `TyEnum` corresponds to an enum
     /// definition and not a concrete use of it. This is true for `TyStruct`
-    /// as well.
+    /// and `TyUnion` as well.
     TyEnum(AdtDef<'tcx>, &'tcx Substs<'tcx>),
 
     /// A structure type, defined with `struct`.
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 571d1222baaa4..b55cad58e2fe4 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -475,8 +475,7 @@ impl LateLintPass for MissingCopyImplementations {
                     return;
                 }
                 let def = cx.tcx.lookup_adt_def(cx.tcx.map.local_def_id(item.id));
-                (def, cx.tcx.mk_union(def,
-                                      cx.tcx.mk_substs(Substs::empty())))
+                (def, cx.tcx.mk_union(def, Substs::empty(cx.tcx)))
             }
             hir::ItemEnum(_, ref ast_generics) => {
                 if ast_generics.is_parameterized() {
diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs
index 6d8a73b8a6aa0..e4ea9bb407d96 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -166,7 +166,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     for (ty, _) in self.autoderef(span, rcvr_ty) {
                         match ty.sty {
                             ty::TyStruct(def, substs) | ty::TyUnion(def, substs) => {
-                                if let Some(field) = def.struct_variant().find_field_named(item_name) {
+                                if let Some(field) = def.struct_variant().
+                                                         find_field_named(item_name) {
                                     let snippet = tcx.sess.codemap().span_to_snippet(expr.span);
                                     let expr_string = match snippet {
                                         Ok(expr_string) => expr_string,
@@ -179,8 +180,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
                                     if self.is_fn_ty(&field_ty, span) {
                                         err.span_note(span, &format!(
-                                            "use `({0}.{1})(...)` if you meant to call the function \
-                                             stored in the `{1}` field",
+                                            "use `({0}.{1})(...)` if you meant to call the \
+                                             function stored in the `{1}` field",
                                             expr_string, item_name));
                                     } else {
                                         err.span_note(span, &format!(
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 1d260b9dbb290..a100c919d6f4e 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1587,6 +1587,11 @@ fn type_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
                         let substs = mk_item_substs(&ccx.icx(generics), item.span, def_id);
                         ccx.tcx.mk_struct(def, substs)
                     }
+                    ItemUnion(ref un, ref generics) => {
+                        let def = convert_union_def(ccx, item, un);
+                        let substs = mk_item_substs(&ccx.icx(generics), item.span, def_id);
+                        ccx.tcx.mk_union(def, substs)
+                    }
                     ItemDefaultImpl(..) |
                     ItemTrait(..) |
                     ItemImpl(..) |
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 4250979a24033..b7e371e23f323 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -227,7 +227,7 @@ fn build_union<'a, 'tcx>(cx: &DocContext, tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
     clean::Union {
         struct_type: doctree::Plain,
-        generics: (&t.generics, &predicates, subst::TypeSpace).clean(cx),
+        generics: (t.generics, &predicates).clean(cx),
         fields: variant.fields.clean(cx),
         fields_stripped: false,
     }
diff --git a/src/librustdoc/html/item_type.rs b/src/librustdoc/html/item_type.rs
index 9ce3c79e2f19b..b93dc17dbdd7d 100644
--- a/src/librustdoc/html/item_type.rs
+++ b/src/librustdoc/html/item_type.rs
@@ -133,6 +133,7 @@ impl ItemType {
     pub fn name_space(&self) -> NameSpace {
         match *self {
             ItemType::Struct |
+            ItemType::Union |
             ItemType::Enum |
             ItemType::Module |
             ItemType::Typedef |
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index c1bb9d9f633bc..6f66ce88df7a5 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -2565,10 +2565,8 @@ fn render_union(w: &mut fmt::Formatter, it: &clean::Item,
            if structhead {"union "} else {""},
            it.name.as_ref().unwrap())?;
     if let Some(g) = g {
-        write!(w, "{}", g)?
-    }
-    if let Some(g) = g {
-        write!(w, "{}", WhereClause(g))?
+        write!(w, "{}", g)?;
+        write!(w, "{}", WhereClause(g))?;
     }
 
     write!(w, " {{\n{}", tab)?;

From 957971b63abbc816aebc6654dc68cf9ff15837d7 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Thu, 18 Aug 2016 15:44:00 +0300
Subject: [PATCH 10/22] Implement layout calculation and add more trans stubs

---
 src/librustc/ty/layout.rs                | 72 ++++++++++++++++--
 src/librustc_mir/hair/cx/expr.rs         |  5 +-
 src/librustc_trans/adt.rs                | 96 ++++++++++++++++++++++--
 src/librustc_trans/debuginfo/metadata.rs |  5 +-
 src/test/run-pass/union-basic.rs         | 47 ++++++++++++
 5 files changed, 205 insertions(+), 20 deletions(-)
 create mode 100644 src/test/run-pass/union-basic.rs

diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs
index ac5e3c6fa7009..9270057b54415 100644
--- a/src/librustc/ty/layout.rs
+++ b/src/librustc/ty/layout.rs
@@ -488,7 +488,7 @@ impl<'a, 'gcx, 'tcx> Struct {
 
         for field in fields {
             if !self.sized {
-                bug!("Struct::compute: field #{} of `{}` comes after unsized field",
+                bug!("Struct::extend: field #{} of `{}` comes after unsized field",
                      self.offset_after_field.len(), scapegoat);
             }
 
@@ -623,6 +623,54 @@ impl<'a, 'gcx, 'tcx> Struct {
     }
 }
 
+/// An untagged union.
+#[derive(PartialEq, Eq, Hash, Debug)]
+pub struct Union {
+    pub align: Align,
+
+    pub min_size: Size,
+
+    /// If true, no alignment padding is used.
+    pub packed: bool,
+}
+
+impl<'a, 'gcx, 'tcx> Union {
+    pub fn new(dl: &TargetDataLayout, packed: bool) -> Union {
+        Union {
+            align: if packed { dl.i8_align } else { dl.aggregate_align },
+            min_size: Size::from_bytes(0),
+            packed: packed,
+        }
+    }
+
+    /// Extend the Struct with more fields.
+    pub fn extend(&mut self, dl: &TargetDataLayout,
+                     fields: I,
+                     scapegoat: Ty<'gcx>)
+                     -> Result<(), LayoutError<'gcx>>
+    where I: Iterator>> {
+        for (index, field) in fields.enumerate() {
+            let field = field?;
+            if field.is_unsized() {
+                bug!("Union::extend: field #{} of `{}` is unsized",
+                     index, scapegoat);
+            }
+
+            if !self.packed {
+                self.align = self.align.max(field.align(dl));
+            }
+            self.min_size = cmp::max(self.min_size, field.size(dl));
+        }
+
+        Ok(())
+    }
+
+    /// Get the size with trailing aligment padding.
+    pub fn stride(&self) -> Size {
+        self.min_size.abi_align(self.align)
+    }
+}
+
 /// The first half of a fat pointer.
 /// - For a trait object, this is the address of the box.
 /// - For a slice, this is the base address.
@@ -690,6 +738,11 @@ pub enum Layout {
         non_zero: bool
     },
 
+    /// Untagged unions.
+    UntaggedUnion {
+        variants: Union,
+    },
+
     /// General-case enums: for each case there is a struct, and they
     /// all start with a field for the discriminant.
     General {
@@ -896,8 +949,14 @@ impl<'a, 'gcx, 'tcx> Layout {
                     non_zero: Some(def.did) == tcx.lang_items.non_zero()
                 }
             }
-            ty::TyUnion(..) => {
-                unimplemented_unions!();
+            ty::TyUnion(def, substs) => {
+                let fields = def.struct_variant().fields.iter().map(|field| {
+                    field.ty(tcx, substs).layout(infcx)
+                });
+                let packed = tcx.lookup_packed(def.did);
+                let mut un = Union::new(dl, packed);
+                un.extend(dl, fields, ty)?;
+                UntaggedUnion { variants: un }
             }
             ty::TyEnum(def, substs) => {
                 let hint = *tcx.lookup_repr_hints(def.did).get(0)
@@ -1118,7 +1177,7 @@ impl<'a, 'gcx, 'tcx> Layout {
     pub fn is_unsized(&self) -> bool {
         match *self {
             Scalar {..} | Vector {..} | FatPointer {..} |
-            CEnum {..} | General {..} |
+            CEnum {..} | UntaggedUnion {..} | General {..} |
             RawNullablePointer {..} |
             StructWrappedNullablePointer {..} => false,
 
@@ -1152,6 +1211,7 @@ impl<'a, 'gcx, 'tcx> Layout {
 
             CEnum { discr, .. } => Int(discr).size(dl),
             Array { size, .. } | General { size, .. } => size,
+            UntaggedUnion { ref variants } => variants.stride(),
 
             Univariant { ref variant, .. } |
             StructWrappedNullablePointer { nonnull: ref variant, .. } => {
@@ -1191,6 +1251,7 @@ impl<'a, 'gcx, 'tcx> Layout {
 
             CEnum { discr, .. } => Int(discr).align(dl),
             Array { align, .. } | General { align, .. } => align,
+            UntaggedUnion { ref variants } => variants.align,
 
             Univariant { ref variant, .. } |
             StructWrappedNullablePointer { nonnull: ref variant, .. } => {
@@ -1256,9 +1317,6 @@ impl<'a, 'gcx, 'tcx> SizeSkeleton<'gcx> {
                 }
             }
 
-            ty::TyUnion(..) => {
-                unimplemented_unions!();
-            }
             ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => {
                 // Only newtypes and enums w/ nullable pointer optimization.
                 if def.variants.is_empty() || def.variants.len() > 2 {
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 0469d44de4ba6..c8f660a2d9c7c 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -459,7 +459,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
 
         hir::ExprStruct(_, ref fields, ref base) => {
             match expr_ty.sty {
-                ty::TyStruct(adt, substs) => {
+                ty::TyStruct(adt, substs) | ty::TyUnion(adt, substs) => {
                     let field_refs = field_refs(&adt.variants[0], fields);
                     ExprKind::Adt {
                         adt_def: adt,
@@ -477,9 +477,6 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                         })
                     }
                 }
-                ty::TyUnion(..) => {
-                    unimplemented_unions!();
-                }
                 ty::TyEnum(adt, substs) => {
                     match cx.tcx.expect_def(expr.id) {
                         Def::Variant(enum_id, variant_id) => {
diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs
index 069eef7895c84..abbb9a5d4dbe5 100644
--- a/src/librustc_trans/adt.rs
+++ b/src/librustc_trans/adt.rs
@@ -79,6 +79,8 @@ pub enum Repr<'tcx> {
     CEnum(IntType, Disr, Disr), // discriminant range (signedness based on the IntType)
     /// Single-case variants, and structs/tuples/records.
     Univariant(Struct<'tcx>),
+    /// Untagged unions.
+    UntaggedUnion(Union<'tcx>),
     /// General-case enums: for each case there is a struct, and they
     /// all start with a field for the discriminant.
     General(IntType, Vec>),
@@ -121,6 +123,15 @@ pub struct Struct<'tcx> {
     pub fields: Vec>,
 }
 
+/// For untagged unions.
+#[derive(Eq, PartialEq, Debug)]
+pub struct Union<'tcx> {
+    pub min_size: u64,
+    pub align: u32,
+    pub packed: bool,
+    pub fields: Vec>,
+}
+
 #[derive(Copy, Clone)]
 pub struct MaybeSizedValue {
     pub value: ValueRef,
@@ -176,8 +187,12 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
             Univariant(mk_struct(cx, &ftys[..], packed, t))
         }
-        ty::TyUnion(..) => {
-            unimplemented_unions!();
+        ty::TyUnion(def, substs) => {
+            let ftys = def.struct_variant().fields.iter().map(|field| {
+                monomorphize::field_ty(cx.tcx(), substs, field)
+            }).collect::>();
+            let packed = cx.tcx().lookup_packed(def.did);
+            UntaggedUnion(mk_union(cx, &ftys[..], packed, t))
         }
         ty::TyClosure(_, ref substs) => {
             Univariant(mk_struct(cx, &substs.upvar_tys, false, t))
@@ -482,6 +497,31 @@ fn mk_struct<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     }
 }
 
+fn mk_union<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
+                      tys: &[Ty<'tcx>], packed: bool,
+                      _scapegoat: Ty<'tcx>)
+                      -> Union<'tcx> {
+    let mut min_size = 0;
+    let mut align = 0;
+    for llty in tys.iter().map(|&ty| type_of::sizing_type_of(cx, ty)) {
+        let field_size = machine::llsize_of_alloc(cx, llty);
+        if min_size < field_size {
+            min_size = field_size;
+        }
+        let field_align = machine::llalign_of_min(cx, llty);
+        if align < field_align {
+            align = field_align;
+        }
+    }
+
+    Union {
+        min_size: min_size,
+        align: align,
+        packed: packed,
+        fields: tys.to_vec(),
+    }
+}
+
 #[derive(Debug)]
 struct IntBounds {
     slo: i64,
@@ -646,7 +686,7 @@ pub fn incomplete_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 pub fn finish_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                 r: &Repr<'tcx>, llty: &mut Type) {
     match *r {
-        CEnum(..) | General(..) | RawNullablePointer { .. } => { }
+        CEnum(..) | General(..) | UntaggedUnion(..) | RawNullablePointer { .. } => { }
         Univariant(ref st) | StructWrappedNullablePointer { nonnull: ref st, .. } =>
             llty.set_struct_body(&struct_llfields(cx, st, false, false),
                                  st.packed)
@@ -690,6 +730,34 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 }
             }
         }
+        UntaggedUnion(ref un) => {
+            // Use alignment-sized ints to fill all the union storage.
+            let (size, align) = (roundup(un.min_size, un.align), un.align);
+
+            let align_s = align as u64;
+            assert_eq!(size % align_s, 0); // Ensure division in align_units comes out evenly
+            let align_units = size / align_s;
+            let fill_ty = match align_s {
+                1 => Type::array(&Type::i8(cx), align_units),
+                2 => Type::array(&Type::i16(cx), align_units),
+                4 => Type::array(&Type::i32(cx), align_units),
+                8 if machine::llalign_of_min(cx, Type::i64(cx)) == 8 =>
+                                 Type::array(&Type::i64(cx), align_units),
+                a if a.count_ones() == 1 => Type::array(&Type::vector(&Type::i32(cx), a / 4),
+                                                              align_units),
+                _ => bug!("unsupported union alignment: {}", align)
+            };
+            match name {
+                None => {
+                    TypeContext::direct(Type::struct_(cx, &[fill_ty], un.packed))
+                }
+                Some(name) => {
+                    let mut llty = Type::named_struct(cx, name);
+                    llty.set_struct_body(&[fill_ty], un.packed);
+                    TypeContext::direct(llty)
+                }
+            }
+        }
         General(ity, ref sts) => {
             // We need a representation that has:
             // * The alignment of the most-aligned field
@@ -762,7 +830,7 @@ pub fn trans_switch<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
         RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => {
             (BranchKind::Switch, Some(trans_get_discr(bcx, r, scrutinee, None, range_assert)))
         }
-        Univariant(..) => {
+        Univariant(..) | UntaggedUnion(..) => {
             // N.B.: Univariant means <= 1 enum variants (*not* == 1 variants).
             (BranchKind::Single, None)
         }
@@ -773,7 +841,7 @@ pub fn is_discr_signed<'tcx>(r: &Repr<'tcx>) -> bool {
     match *r {
         CEnum(ity, _, _) => ity.is_signed(),
         General(ity, _) => ity.is_signed(),
-        Univariant(..) => false,
+        Univariant(..) | UntaggedUnion(..) => false,
         RawNullablePointer { .. } => false,
         StructWrappedNullablePointer { .. } => false,
     }
@@ -794,7 +862,7 @@ pub fn trans_get_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
             load_discr(bcx, ity, ptr, Disr(0), Disr(cases.len() as u64 - 1),
                        range_assert)
         }
-        Univariant(..) => C_u8(bcx.ccx(), 0),
+        Univariant(..) | UntaggedUnion(..) => C_u8(bcx.ccx(), 0),
         RawNullablePointer { nndiscr, nnty, .. } =>  {
             let cmp = if nndiscr == Disr(0) { IntEQ } else { IntNE };
             let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty);
@@ -856,8 +924,8 @@ pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr)
         General(ity, _) => {
             C_integral(ll_inttype(bcx.ccx(), ity), discr.0, true)
         }
-        Univariant(..) => {
-            bug!("no cases for univariants or structs")
+        Univariant(..) | UntaggedUnion(..) => {
+            bug!("no cases for univariants, structs or unions")
         }
         RawNullablePointer { .. } |
         StructWrappedNullablePointer { .. } => {
@@ -884,6 +952,9 @@ pub fn trans_set_discr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr<'tcx>,
         Univariant(_) => {
             assert_eq!(discr, Disr(0));
         }
+        UntaggedUnion(..) => {
+            assert_eq!(discr, Disr(0));
+        }
         RawNullablePointer { nndiscr, nnty, ..} => {
             if discr != nndiscr {
                 let llptrty = type_of::sizing_type_of(bcx.ccx(), nnty);
@@ -939,6 +1010,11 @@ pub fn trans_field_ptr_builder<'blk, 'tcx>(bcx: &BlockAndBuilder<'blk, 'tcx>,
         General(_, ref cases) => {
             struct_field_ptr(bcx, &cases[discr.0 as usize], val, ix + 1, true)
         }
+        UntaggedUnion(ref un) => {
+            let ty = type_of::in_memory_type_of(bcx.ccx(), un.fields[ix]);
+            if bcx.is_unreachable() { return C_undef(ty.ptr_to()); }
+            bcx.pointercast(val.value, ty.ptr_to())
+        }
         RawNullablePointer { nndiscr, ref nullfields, .. } |
         StructWrappedNullablePointer { nndiscr, ref nullfields, .. } if discr != nndiscr => {
             // The unit-like case might have a nonzero number of unit-like fields.
@@ -1100,6 +1176,9 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, discr
             contents.extend_from_slice(&[padding(ccx, max_sz - case.size)]);
             C_struct(ccx, &contents[..], false)
         }
+        UntaggedUnion(..) => {
+            unimplemented_unions!();
+        }
         Univariant(ref st) => {
             assert_eq!(discr, Disr(0));
             let contents = build_const_struct(ccx, st, vals);
@@ -1211,6 +1290,7 @@ pub fn const_get_field(r: &Repr, val: ValueRef, _discr: Disr,
     match *r {
         CEnum(..) => bug!("element access in C-like enum const"),
         Univariant(..) => const_struct_field(val, ix),
+        UntaggedUnion(..) => const_struct_field(val, 0),
         General(..) => const_struct_field(val, ix + 1),
         RawNullablePointer { .. } => {
             assert_eq!(ix, 0);
diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs
index f30880ac9beb5..bd67a215d65eb 100644
--- a/src/librustc_trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/debuginfo/metadata.rs
@@ -1302,6 +1302,9 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                     ]
                 }
             }
+            adt::UntaggedUnion(..) => {
+                unimplemented_unions!();
+            }
             adt::RawNullablePointer { nndiscr: non_null_variant_index, nnty, .. } => {
                 // As far as debuginfo is concerned, the pointer this enum
                 // represents is still wrapped in a struct. This is to make the
@@ -1616,7 +1619,7 @@ fn prepare_enum_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
         },
         adt::RawNullablePointer { .. }           |
         adt::StructWrappedNullablePointer { .. } |
-        adt::Univariant(..)                      => None,
+        adt::Univariant(..) | adt::UntaggedUnion(..) => None,
         adt::General(inttype, _) => Some(discriminant_type_metadata(inttype)),
     };
 
diff --git a/src/test/run-pass/union-basic.rs b/src/test/run-pass/union-basic.rs
new file mode 100644
index 0000000000000..474c8b4b18103
--- /dev/null
+++ b/src/test/run-pass/union-basic.rs
@@ -0,0 +1,47 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+use std::mem::{size_of, align_of, zeroed};
+
+union U {
+    a: u8,
+}
+
+union U64 {
+    a: u64,
+}
+
+union W {
+    a: u8,
+    b: u64,
+}
+
+fn main() {
+    assert_eq!(size_of::(), 1);
+    assert_eq!(size_of::(), 8);
+    assert_eq!(size_of::(), 8);
+    assert_eq!(align_of::(), 1);
+    assert_eq!(align_of::(), align_of::());
+    assert_eq!(align_of::(), align_of::());
+
+    let u = U { a: 10 };
+    assert_eq!(u.a, 10);
+    let U { a } = u;
+    assert_eq!(a, 10);
+
+    let mut w: W = unsafe { zeroed() };
+    assert_eq!(w.a, 0);
+    assert_eq!(w.b, 0);
+    // w.a = 1;
+    // assert_eq!(w.a, 0);
+    // assert_eq!(w.b, 0);
+}

From f3b41c18a8dfbcfec4b2a9e8d9e6a921189e3eea Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Thu, 18 Aug 2016 18:31:47 +0300
Subject: [PATCH 11/22] Check fields in union patters/expressions

Make parsing of union items backward compatible
Add some tests
---
 src/librustc_typeck/check/_match.rs           | 49 ++++++++-------
 src/librustc_typeck/check/mod.rs              | 31 +++++-----
 src/libsyntax/parse/parser.rs                 |  4 +-
 src/test/compile-fail/issue-17800.rs          |  2 +-
 src/test/compile-fail/issue-19922.rs          |  2 +-
 src/test/compile-fail/issue-4736.rs           |  2 +-
 src/test/compile-fail/numeric-fields.rs       |  2 +-
 .../struct-fields-hints-no-dupe.rs            |  2 +-
 src/test/compile-fail/struct-fields-hints.rs  |  2 +-
 .../compile-fail/struct-fields-too-many.rs    |  2 +-
 .../compile-fail/suggest-private-fields.rs    |  8 +--
 src/test/compile-fail/union-empty.rs          | 15 +++++
 src/test/compile-fail/union-fields.rs         | 34 ++++++++++
 src/test/run-pass/union-backcomp.rs           | 23 +++++++
 src/test/run-pass/union-basic.rs              |  8 +++
 src/test/run-pass/union-drop.rs               | 26 ++++++++
 src/test/run-pass/union-pat-refutability.rs   | 62 +++++++++++++++++++
 17 files changed, 227 insertions(+), 47 deletions(-)
 create mode 100644 src/test/compile-fail/union-empty.rs
 create mode 100644 src/test/compile-fail/union-fields.rs
 create mode 100644 src/test/run-pass/union-backcomp.rs
 create mode 100644 src/test/run-pass/union-drop.rs
 create mode 100644 src/test/run-pass/union-pat-refutability.rs

diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 5fdfa19190bdb..5c19fa2a66cfc 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -11,7 +11,6 @@
 use hir::def::Def;
 use rustc::infer::{self, InferOk, TypeOrigin};
 use hir::pat_util::EnumerateAndAdjustIterator;
-use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference, VariantKind};
 use check::{FnCtxt, Expectation};
 use lint;
@@ -509,11 +508,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         self.demand_eqtype(pat.span, expected, pat_ty);
 
         // Type check subpatterns.
-        let substs = match pat_ty.sty {
-            ty::TyStruct(_, substs) | ty::TyUnion(_, substs) | ty::TyEnum(_, substs) => substs,
-            _ => span_bug!(pat.span, "struct variant is not an ADT")
-        };
-        self.check_struct_pat_fields(pat.span, fields, variant, substs, etc);
+        self.check_struct_pat_fields(pat_ty, pat.span, variant, fields, etc);
     }
 
     fn check_pat_path(&self,
@@ -658,19 +653,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
-    /// `path` is the AST path item naming the type of this struct.
-    /// `fields` is the field patterns of the struct pattern.
-    /// `struct_fields` describes the type of each field of the struct.
-    /// `struct_id` is the ID of the struct.
-    /// `etc` is true if the pattern said '...' and false otherwise.
-    pub fn check_struct_pat_fields(&self,
-                                   span: Span,
-                                   fields: &'gcx [Spanned],
-                                   variant: ty::VariantDef<'tcx>,
-                                   substs: &Substs<'tcx>,
-                                   etc: bool) {
+    fn check_struct_pat_fields(&self,
+                               adt_ty: Ty<'tcx>,
+                               span: Span,
+                               variant: ty::VariantDef<'tcx>,
+                               fields: &'gcx [Spanned],
+                               etc: bool) {
         let tcx = self.tcx;
 
+        let (substs, kind_name) = match adt_ty.sty {
+            ty::TyEnum(_, substs) => (substs, "variant"),
+            ty::TyStruct(_, substs) => (substs, "struct"),
+            ty::TyUnion(_, substs) => (substs, "union"),
+            _ => span_bug!(span, "struct pattern is not an ADT")
+        };
+
         // Index the struct fields' types.
         let field_map = variant.fields
             .iter()
@@ -700,11 +697,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                         .map(|f| self.field_ty(span, f, substs))
                         .unwrap_or_else(|| {
                             struct_span_err!(tcx.sess, span, E0026,
-                                             "struct `{}` does not have a field named `{}`",
+                                             "{} `{}` does not have a field named `{}`",
+                                             kind_name,
                                              tcx.item_path_str(variant.did),
                                              field.name)
                                 .span_label(span,
-                                            &format!("struct `{}` does not have field `{}`",
+                                            &format!("{} `{}` does not have field `{}`",
+                                                     kind_name,
                                                      tcx.item_path_str(variant.did),
                                                      field.name))
                                 .emit();
@@ -717,8 +716,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             self.check_pat(&field.pat, field_ty);
         }
 
-        // Report an error if not all the fields were specified.
-        if !etc {
+        // Report an error if incorrect number of the fields were specified.
+        if kind_name == "union" {
+            if fields.len() > 1 {
+                tcx.sess.span_err(span, "union patterns can have at most one field");
+            }
+            if fields.is_empty() && !etc {
+                tcx.sess.span_err(span, "union patterns without `..` \
+                                         should have at least one field");
+            }
+        } else if !etc {
             for field in variant.fields
                 .iter()
                 .filter(|field| !used_fields.contains_key(&field.name)) {
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index e2954cecc9c46..f8ee9efee7a5a 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3109,17 +3109,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                             ty: Ty<'tcx>,
                             variant: ty::VariantDef<'tcx>,
                             field: &hir::Field,
-                            skip_fields: &[hir::Field]) {
+                            skip_fields: &[hir::Field],
+                            kind_name: &str) {
         let mut err = self.type_error_struct_with_diag(
             field.name.span,
             |actual| if let ty::TyEnum(..) = ty.sty {
                 struct_span_err!(self.tcx.sess, field.name.span, E0559,
-                                 "struct variant `{}::{}` has no field named `{}`",
-                                 actual, variant.name.as_str(), field.name.node)
+                                 "{} `{}::{}` has no field named `{}`",
+                                 kind_name, actual, variant.name.as_str(), field.name.node)
             } else {
                 struct_span_err!(self.tcx.sess, field.name.span, E0560,
-                                 "structure `{}` has no field named `{}`",
-                                 actual, field.name.node)
+                                 "{} `{}` has no field named `{}`",
+                                 kind_name, actual, field.name.node)
             },
             ty);
         // prevent all specified fields from being suggested
@@ -3135,8 +3136,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                 ast_fields: &'gcx [hir::Field],
                                 check_completeness: bool) {
         let tcx = self.tcx;
-        let substs = match adt_ty.sty {
-            ty::TyStruct(_, substs) | ty::TyUnion(_, substs) | ty::TyEnum(_, substs) => substs,
+        let (substs, kind_name) = match adt_ty.sty {
+            ty::TyEnum(_, substs) => (substs, "variant"),
+            ty::TyStruct(_, substs) => (substs, "struct"),
+            ty::TyUnion(_, substs) => (substs, "union"),
             _ => span_bug!(span, "non-ADT passed to check_expr_struct_fields")
         };
 
@@ -3175,7 +3178,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
                     err.emit();
                 } else {
-                    self.report_unknown_field(adt_ty, variant, field, ast_fields);
+                    self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name);
                 }
             }
 
@@ -3184,11 +3187,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             self.check_expr_coercable_to_type(&field.expr, expected_field_type);
         }
 
-            // Make sure the programmer specified all the fields.
-        if check_completeness &&
-            !error_happened &&
-            !remaining_fields.is_empty()
-        {
+        // Make sure the programmer specified correct number of fields.
+        if kind_name == "union" {
+            if ast_fields.len() != 1 {
+                tcx.sess.span_err(span, "union expressions should have exactly one field");
+            }
+        } else if check_completeness && !error_happened && !remaining_fields.is_empty() {
             span_err!(tcx.sess, span, E0063,
                       "missing field{} {} in initializer of `{}`",
                       if remaining_fields.len() == 1 {""} else {"s"},
@@ -3198,7 +3202,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                       .join(", "),
                       adt_ty);
         }
-
     }
 
     fn check_struct_fields_on_error(&self,
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 290a59cf1e591..ec9dc1bae5ad9 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -5957,8 +5957,10 @@ impl<'a> Parser<'a> {
                                     maybe_append(attrs, extra_attrs));
             return Ok(Some(item));
         }
-        if self.eat_keyword(keywords::Union) {
+        if self.check_keyword(keywords::Union) &&
+                self.look_ahead(1, |t| t.is_ident() && !t.is_any_keyword()) {
             // UNION ITEM
+            self.bump();
             let (ident, item_, extra_attrs) = self.parse_item_union()?;
             let last_span = self.last_span;
             let item = self.mk_item(lo,
diff --git a/src/test/compile-fail/issue-17800.rs b/src/test/compile-fail/issue-17800.rs
index 58d580a5c1a1c..d5f1614c14d2d 100644
--- a/src/test/compile-fail/issue-17800.rs
+++ b/src/test/compile-fail/issue-17800.rs
@@ -18,7 +18,7 @@ enum MyOption {
 fn main() {
     match MyOption::MySome(42) {
         MyOption::MySome { x: 42 } => (),
-        //~^ ERROR struct `MyOption::MySome` does not have a field named `x`
+        //~^ ERROR variant `MyOption::MySome` does not have a field named `x`
         //~| ERROR pattern does not mention field `0`
         _ => (),
     }
diff --git a/src/test/compile-fail/issue-19922.rs b/src/test/compile-fail/issue-19922.rs
index e3ced3028098b..a8350fe0986c0 100644
--- a/src/test/compile-fail/issue-19922.rs
+++ b/src/test/compile-fail/issue-19922.rs
@@ -14,5 +14,5 @@ enum Homura {
 
 fn main() {
     let homura = Homura::Akemi { kaname: () };
-    //~^ ERROR struct variant `Homura::Akemi` has no field named `kaname`
+    //~^ ERROR variant `Homura::Akemi` has no field named `kaname`
 }
diff --git a/src/test/compile-fail/issue-4736.rs b/src/test/compile-fail/issue-4736.rs
index a8a1b1482fc08..c93e75042dd17 100644
--- a/src/test/compile-fail/issue-4736.rs
+++ b/src/test/compile-fail/issue-4736.rs
@@ -13,5 +13,5 @@
 struct NonCopyable(());
 
 fn main() {
-    let z = NonCopyable{ p: () }; //~ ERROR structure `NonCopyable` has no field named `p`
+    let z = NonCopyable{ p: () }; //~ ERROR struct `NonCopyable` has no field named `p`
 }
diff --git a/src/test/compile-fail/numeric-fields.rs b/src/test/compile-fail/numeric-fields.rs
index 480d2dcddddd4..c4aff9471b8a1 100644
--- a/src/test/compile-fail/numeric-fields.rs
+++ b/src/test/compile-fail/numeric-fields.rs
@@ -13,7 +13,7 @@
 struct S(u8, u16);
 
 fn main() {
-    let s = S{0b1: 10, 0: 11}; //~ ERROR structure `S` has no field named `0b1`
+    let s = S{0b1: 10, 0: 11}; //~ ERROR struct `S` has no field named `0b1`
     match s {
         S{0: a, 0x1: b, ..} => {} //~ ERROR does not have a field named `0x1`
     }
diff --git a/src/test/compile-fail/struct-fields-hints-no-dupe.rs b/src/test/compile-fail/struct-fields-hints-no-dupe.rs
index 8df9ffd6cc73f..5f1f8ca856f9c 100644
--- a/src/test/compile-fail/struct-fields-hints-no-dupe.rs
+++ b/src/test/compile-fail/struct-fields-hints-no-dupe.rs
@@ -17,7 +17,7 @@ struct A {
 fn main() {
     let a = A {
         foo : 5,
-        bar : 42,//~ ERROR structure `A` has no field named `bar`
+        bar : 42,//~ ERROR struct `A` has no field named `bar`
         //~^ HELP did you mean `barr`?
         car : 9,
     };
diff --git a/src/test/compile-fail/struct-fields-hints.rs b/src/test/compile-fail/struct-fields-hints.rs
index 37001f1e60a0f..4ba1fd2f7bb33 100644
--- a/src/test/compile-fail/struct-fields-hints.rs
+++ b/src/test/compile-fail/struct-fields-hints.rs
@@ -17,7 +17,7 @@ struct A {
 fn main() {
     let a = A {
         foo : 5,
-        bar : 42,//~ ERROR structure `A` has no field named `bar`
+        bar : 42,//~ ERROR struct `A` has no field named `bar`
         //~^ HELP did you mean `car`?
     };
 }
diff --git a/src/test/compile-fail/struct-fields-too-many.rs b/src/test/compile-fail/struct-fields-too-many.rs
index 9244a9d4f9d0f..5d16573f2f1e3 100644
--- a/src/test/compile-fail/struct-fields-too-many.rs
+++ b/src/test/compile-fail/struct-fields-too-many.rs
@@ -15,6 +15,6 @@ struct BuildData {
 fn main() {
     let foo = BuildData {
         foo: 0,
-        bar: 0 //~ ERROR structure `BuildData` has no field named `bar`
+        bar: 0 //~ ERROR struct `BuildData` has no field named `bar`
     };
 }
diff --git a/src/test/compile-fail/suggest-private-fields.rs b/src/test/compile-fail/suggest-private-fields.rs
index 9c61f618e690f..41bd00a518c5c 100644
--- a/src/test/compile-fail/suggest-private-fields.rs
+++ b/src/test/compile-fail/suggest-private-fields.rs
@@ -22,16 +22,16 @@ struct A {
 fn main () {
     // external crate struct
     let k = B {
-        aa: 20, //~ ERROR structure `xc::B` has no field named `aa`
+        aa: 20, //~ ERROR struct `xc::B` has no field named `aa`
         //~^ HELP did you mean `a`?
-        bb: 20, //~ ERROR structure `xc::B` has no field named `bb`
+        bb: 20, //~ ERROR struct `xc::B` has no field named `bb`
         //~^ HELP did you mean `a`?
     };
     // local crate struct
     let l = A {
-        aa: 20, //~ ERROR structure `A` has no field named `aa`
+        aa: 20, //~ ERROR struct `A` has no field named `aa`
         //~^ HELP did you mean `a`?
-        bb: 20, //~ ERROR structure `A` has no field named `bb`
+        bb: 20, //~ ERROR struct `A` has no field named `bb`
         //~^ HELP did you mean `b`?
     };
 }
diff --git a/src/test/compile-fail/union-empty.rs b/src/test/compile-fail/union-empty.rs
new file mode 100644
index 0000000000000..ce5bbf60fee25
--- /dev/null
+++ b/src/test/compile-fail/union-empty.rs
@@ -0,0 +1,15 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+union U {} //~ ERROR unions cannot have zero fields
+
+fn main() {}
diff --git a/src/test/compile-fail/union-fields.rs b/src/test/compile-fail/union-fields.rs
new file mode 100644
index 0000000000000..2bd1b8a7b32c2
--- /dev/null
+++ b/src/test/compile-fail/union-fields.rs
@@ -0,0 +1,34 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+union U {
+    a: u8,
+    b: u16,
+}
+
+fn main() {
+    let u = U {}; //~ ERROR union expressions should have exactly one field
+    let u = U { a: 0 }; // OK
+    let u = U { a: 0, b: 1 }; //~ ERROR union expressions should have exactly one field
+    let u = U { a: 0, b: 1, c: 2 }; //~ ERROR union expressions should have exactly one field
+                                    //~^ ERROR union `U` has no field named `c`
+    let u = U { ..u }; //~ ERROR union expressions should have exactly one field
+                       //~^ ERROR functional record update syntax requires a struct
+
+    let U {} = u; //~ ERROR union patterns without `..` should have at least one field
+    let U { a } = u; // OK
+    let U { a, b } = u; //~ ERROR union patterns can have at most one field
+    let U { a, b, c } = u; //~ ERROR union patterns can have at most one field
+                           //~^ ERROR union `U` does not have a field named `c`
+    let U { .. } = u; // OK
+    let U { a, .. } = u; // OK
+}
diff --git a/src/test/run-pass/union-backcomp.rs b/src/test/run-pass/union-backcomp.rs
new file mode 100644
index 0000000000000..c1210dd621210
--- /dev/null
+++ b/src/test/run-pass/union-backcomp.rs
@@ -0,0 +1,23 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+fn main() {
+    let union = 10;
+
+    union;
+
+    union as u8;
+
+    union U {
+        a: u8,
+    }
+}
diff --git a/src/test/run-pass/union-basic.rs b/src/test/run-pass/union-basic.rs
index 474c8b4b18103..dee86b232b485 100644
--- a/src/test/run-pass/union-basic.rs
+++ b/src/test/run-pass/union-basic.rs
@@ -25,6 +25,12 @@ union W {
     b: u64,
 }
 
+#[repr(C)]
+union Y {
+    f1: u16,
+    f2: [u8; 4],
+}
+
 fn main() {
     assert_eq!(size_of::(), 1);
     assert_eq!(size_of::(), 8);
@@ -32,6 +38,8 @@ fn main() {
     assert_eq!(align_of::(), 1);
     assert_eq!(align_of::(), align_of::());
     assert_eq!(align_of::(), align_of::());
+    assert_eq!(size_of::(), 4);
+    assert_eq!(align_of::(), 2);
 
     let u = U { a: 10 };
     assert_eq!(u.a, 10);
diff --git a/src/test/run-pass/union-drop.rs b/src/test/run-pass/union-drop.rs
new file mode 100644
index 0000000000000..467403ff2e126
--- /dev/null
+++ b/src/test/run-pass/union-drop.rs
@@ -0,0 +1,26 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Drop works for union itself.
+
+#![feature(untagged_unions)]
+
+union U {
+    a: u8
+}
+
+impl Drop for U {
+    fn drop(&mut self) {}
+}
+
+fn main() {
+    // 'unions are not fully implemented', src/librustc_trans/glue.rs:567
+    // let u = U { a: 1 };
+}
diff --git a/src/test/run-pass/union-pat-refutability.rs b/src/test/run-pass/union-pat-refutability.rs
new file mode 100644
index 0000000000000..6b39eed7ac942
--- /dev/null
+++ b/src/test/run-pass/union-pat-refutability.rs
@@ -0,0 +1,62 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+#[repr(u32)]
+enum Tag { I, F }
+
+#[repr(C)]
+union U {
+    i: i32,
+    f: f32,
+}
+
+#[repr(C)]
+struct Value {
+    tag: Tag,
+    u: U,
+}
+
+fn is_zero(v: Value) -> bool {
+    unsafe {
+        match v {
+            Value { tag: Tag::I, u: U { i: 0 } } => true,
+            Value { tag: Tag::F, u: U { f: 0.0 } } => true,
+            _ => false,
+        }
+    }
+}
+
+union W {
+    a: u8,
+    b: u8,
+}
+
+fn refut(w: W) {
+    match w {
+        W { a: 10 } => {
+            panic!();
+        }
+        W { b } => {
+            assert_eq!(b, 11);
+        }
+    }
+}
+
+fn main() {
+    // ICE
+    // let v = Value { tag: Tag::I, u: U { i: 1 } };
+    // assert_eq!(is_zero(v), false);
+
+    // ICE
+    // let w = W { a: 11 };
+    // refut(w);
+}

From e88d4ca0e1bb7c3b0a446788ea0c010aaea65ffc Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Thu, 18 Aug 2016 20:12:28 +0300
Subject: [PATCH 12/22] Make accesses to union fields unsafe

---
 src/librustc/middle/effect.rs               | 24 +++++++++++++++++----
 src/test/compile-fail/union-unsafe.rs       | 23 ++++++++++++++++++++
 src/test/run-pass/union-basic.rs            | 20 ++++++++++-------
 src/test/run-pass/union-pat-refutability.rs | 14 ++++++------
 4 files changed, 63 insertions(+), 18 deletions(-)
 create mode 100644 src/test/compile-fail/union-unsafe.rs

diff --git a/src/librustc/middle/effect.rs b/src/librustc/middle/effect.rs
index 250ad80f5af6c..e52eba68da198 100644
--- a/src/librustc/middle/effect.rs
+++ b/src/librustc/middle/effect.rs
@@ -13,15 +13,14 @@
 use self::RootUnsafeContext::*;
 
 use dep_graph::DepNode;
-use hir::def::Def;
 use ty::{self, Ty, TyCtxt};
 use ty::MethodCall;
 
 use syntax::ast;
 use syntax_pos::Span;
-use hir;
-use hir::intravisit;
-use hir::intravisit::{FnKind, Visitor};
+use hir::{self, PatKind};
+use hir::def::Def;
+use hir::intravisit::{self, FnKind, Visitor};
 
 #[derive(Copy, Clone)]
 struct UnsafeContext {
@@ -178,11 +177,28 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EffectCheckVisitor<'a, 'tcx> {
                     self.require_unsafe(expr.span, "use of mutable static");
                 }
             }
+            hir::ExprField(ref base_expr, field) => {
+                if let ty::TyUnion(..) = self.tcx.expr_ty_adjusted(base_expr).sty {
+                    self.require_unsafe(field.span, "access to union field");
+                }
+            }
             _ => {}
         }
 
         intravisit::walk_expr(self, expr);
     }
+
+    fn visit_pat(&mut self, pat: &hir::Pat) {
+        if let PatKind::Struct(_, ref fields, _) = pat.node {
+            if let ty::TyUnion(..) = self.tcx.pat_ty(pat).sty {
+                for field in fields {
+                    self.require_unsafe(field.span, "matching on union field");
+                }
+            }
+        }
+
+        intravisit::walk_pat(self, pat);
+    }
 }
 
 pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
diff --git a/src/test/compile-fail/union-unsafe.rs b/src/test/compile-fail/union-unsafe.rs
new file mode 100644
index 0000000000000..762ac5d875137
--- /dev/null
+++ b/src/test/compile-fail/union-unsafe.rs
@@ -0,0 +1,23 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+union U {
+    a: u8
+}
+
+fn main() {
+    let u = U { a: 10 }; // OK
+    let a = u.a; //~ ERROR access to union field requires unsafe function or block
+    let U { a } = u; //~ ERROR matching on union field requires unsafe function or block
+    if let U { a: 11 } = u {} //~ ERROR matching on union field requires unsafe function or block
+    let U { .. } = u; // OK
+}
diff --git a/src/test/run-pass/union-basic.rs b/src/test/run-pass/union-basic.rs
index dee86b232b485..afbfe5bf92be7 100644
--- a/src/test/run-pass/union-basic.rs
+++ b/src/test/run-pass/union-basic.rs
@@ -42,14 +42,18 @@ fn main() {
     assert_eq!(align_of::(), 2);
 
     let u = U { a: 10 };
-    assert_eq!(u.a, 10);
-    let U { a } = u;
-    assert_eq!(a, 10);
+    unsafe {
+        assert_eq!(u.a, 10);
+        let U { a } = u;
+        assert_eq!(a, 10);
+    }
 
     let mut w: W = unsafe { zeroed() };
-    assert_eq!(w.a, 0);
-    assert_eq!(w.b, 0);
-    // w.a = 1;
-    // assert_eq!(w.a, 0);
-    // assert_eq!(w.b, 0);
+    unsafe {
+        assert_eq!(w.a, 0);
+        assert_eq!(w.b, 0);
+        // w.a = 1;
+        // assert_eq!(w.a, 0);
+        // assert_eq!(w.b, 0);
+    }
 }
diff --git a/src/test/run-pass/union-pat-refutability.rs b/src/test/run-pass/union-pat-refutability.rs
index 6b39eed7ac942..a57c1103a9b4a 100644
--- a/src/test/run-pass/union-pat-refutability.rs
+++ b/src/test/run-pass/union-pat-refutability.rs
@@ -41,12 +41,14 @@ union W {
 }
 
 fn refut(w: W) {
-    match w {
-        W { a: 10 } => {
-            panic!();
-        }
-        W { b } => {
-            assert_eq!(b, 11);
+    unsafe {
+        match w {
+            W { a: 10 } => {
+                panic!();
+            }
+            W { b } => {
+                assert_eq!(b, 11);
+            }
         }
     }
 }

From bea0b15935a8cb5811eb80e3220e9ab786feb782 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Fri, 19 Aug 2016 19:20:30 +0300
Subject: [PATCH 13/22] Implement drop translation and add lint for unions with
 drop fields

Fix some typeck bugs blocking drop tests
---
 src/librustc_lint/builtin.rs                  | 33 ++++++++++++++
 src/librustc_lint/lib.rs                      |  1 +
 src/librustc_trans/glue.rs                    | 17 ++++---
 src/librustc_typeck/check/mod.rs              |  9 ++--
 src/librustc_typeck/collect.rs                |  3 +-
 .../union-with-drop-fields-lint.rs            | 40 +++++++++++++++++
 src/test/run-pass/union-drop.rs               | 45 +++++++++++++++++--
 7 files changed, 134 insertions(+), 14 deletions(-)
 create mode 100644 src/test/compile-fail/union-with-drop-fields-lint.rs

diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index b55cad58e2fe4..1702c1c0edc9a 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -1164,3 +1164,36 @@ impl LateLintPass for UnstableFeatures {
         }
     }
 }
+
+/// Lint for unions that contain fields with possibly non-trivial destructors.
+pub struct UnionsWithDropFields;
+
+declare_lint! {
+    UNIONS_WITH_DROP_FIELDS,
+    Warn,
+    "use of unions that contain fields with possibly non-trivial drop code"
+}
+
+impl LintPass for UnionsWithDropFields {
+    fn get_lints(&self) -> LintArray {
+        lint_array!(UNIONS_WITH_DROP_FIELDS)
+    }
+}
+
+impl LateLintPass for UnionsWithDropFields {
+    fn check_item(&mut self, ctx: &LateContext, item: &hir::Item) {
+        if let hir::ItemUnion(ref vdata, _) = item.node {
+            let param_env = &ty::ParameterEnvironment::for_item(ctx.tcx, item.id);
+            for field in vdata.fields() {
+                let field_ty = ctx.tcx.node_id_to_type(field.id);
+                if ctx.tcx.type_needs_drop_given_env(field_ty, param_env) {
+                    ctx.span_lint(UNIONS_WITH_DROP_FIELDS,
+                                  field.span,
+                                  "union contains a field with possibly non-trivial drop code, \
+                                   drop code of union fields is ignored when dropping the union");
+                    return;
+                }
+            }
+        }
+    }
+}
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index 0f0e9cfb35773..c3b752d605f97 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -128,6 +128,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
                  InvalidNoMangleItems,
                  PluginAsLibrary,
                  MutableTransmutes,
+                 UnionsWithDropFields,
                  );
 
     add_builtin_with_new!(sess,
diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs
index 5da9ef3646e6b..0d62a63b89fc9 100644
--- a/src/librustc_trans/glue.rs
+++ b/src/librustc_trans/glue.rs
@@ -267,7 +267,8 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
 
 fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                  t: Ty<'tcx>,
-                                 v0: ValueRef)
+                                 v0: ValueRef,
+                                 shallow_drop: bool)
                                  -> Block<'blk, 'tcx>
 {
     debug!("trans_struct_drop t: {}", t);
@@ -286,7 +287,9 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
     // Issue #23611: schedule cleanup of contents, re-inspecting the
     // discriminant (if any) in case of variant swap in drop code.
-    bcx.fcx.schedule_drop_adt_contents(contents_scope, v0, t);
+    if !shallow_drop {
+        bcx.fcx.schedule_drop_adt_contents(contents_scope, v0, t);
+    }
 
     let (sized_args, unsized_args);
     let args: &[ValueRef] = if type_is_sized(tcx, t) {
@@ -470,9 +473,6 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK
                 trans_exchange_free_ty(bcx, llbox, content_ty, DebugLoc::None)
             }
         }
-        ty::TyUnion(..) => {
-            unimplemented_unions!();
-        }
         ty::TyTrait(..) => {
             // No support in vtable for distinguishing destroying with
             // versus without calling Drop::drop. Assert caller is
@@ -491,6 +491,13 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK
                 if def.dtor_kind().is_present() && !skip_dtor => {
             trans_struct_drop(bcx, t, v0)
         }
+        ty::TyUnion(def, _) => {
+            if def.dtor_kind().is_present() && !skip_dtor {
+                trans_struct_drop(bcx, t, v0, true)
+            } else {
+                bcx
+            }
+        }
         _ => {
             if bcx.fcx.type_needs_drop(t) {
                 drop_structural_ty(bcx, v0, t)
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index f8ee9efee7a5a..f4fea5542b3de 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3235,11 +3235,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 Some((type_did, self.tcx.expect_variant_def(def)))
             }
             Def::TyAlias(did) => {
-                if let Some(&ty::TyStruct(adt, _)) = self.tcx.opt_lookup_item_type(did)
-                                                             .map(|scheme| &scheme.ty.sty) {
-                    Some((did, adt.struct_variant()))
-                } else {
-                    None
+                match self.tcx.opt_lookup_item_type(did).map(|scheme| &scheme.ty.sty) {
+                    Some(&ty::TyStruct(adt, _)) |
+                    Some(&ty::TyUnion(adt, _)) => Some((did, adt.struct_variant())),
+                    _ => None,
                 }
             }
             _ => None
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index a100c919d6f4e..31f28b3803d66 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1450,7 +1450,8 @@ fn generics_of_def_id<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
 
                     ItemTy(_, ref generics) |
                     ItemEnum(_, ref generics) |
-                    ItemStruct(_, ref generics) => {
+                    ItemStruct(_, ref generics) |
+                    ItemUnion(_, ref generics) => {
                         allow_defaults = true;
                         generics
                     }
diff --git a/src/test/compile-fail/union-with-drop-fields-lint.rs b/src/test/compile-fail/union-with-drop-fields-lint.rs
new file mode 100644
index 0000000000000..87a72efbe08e5
--- /dev/null
+++ b/src/test/compile-fail/union-with-drop-fields-lint.rs
@@ -0,0 +1,40 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+#![allow(dead_code)]
+#![deny(unions_with_drop_fields)]
+
+union U {
+    a: u8, // OK
+}
+
+union W {
+    a: String, //~ ERROR union contains a field with possibly non-trivial drop code
+    b: String, // OK, only one field is reported
+}
+
+struct S(String);
+
+// `S` doesn't implement `Drop` trait, but still has non-trivial destructor
+union Y {
+    a: S, //~ ERROR union contains a field with possibly non-trivial drop code
+}
+
+// We don't know if `T` is trivially-destructable or not until trans
+union J {
+    a: T, //~ ERROR union contains a field with possibly non-trivial drop code
+}
+
+union H {
+    a: T, // OK, `T` is `Copy`, no destructor
+}
+
+fn main() {}
diff --git a/src/test/run-pass/union-drop.rs b/src/test/run-pass/union-drop.rs
index 467403ff2e126..2ca68dc3b6e39 100644
--- a/src/test/run-pass/union-drop.rs
+++ b/src/test/run-pass/union-drop.rs
@@ -12,15 +12,54 @@
 
 #![feature(untagged_unions)]
 
+struct S;
+
 union U {
     a: u8
 }
 
+union W {
+    a: S,
+}
+
+union Y {
+    a: S,
+}
+
+impl Drop for S {
+    fn drop(&mut self) {
+        unsafe { CHECK += 10; }
+    }
+}
+
 impl Drop for U {
-    fn drop(&mut self) {}
+    fn drop(&mut self) {
+        unsafe { CHECK += 1; }
+    }
 }
 
+impl Drop for W {
+    fn drop(&mut self) {
+        unsafe { CHECK += 1; }
+    }
+}
+
+static mut CHECK: u8 = 0;
+
 fn main() {
-    // 'unions are not fully implemented', src/librustc_trans/glue.rs:567
-    // let u = U { a: 1 };
+    unsafe {
+        assert_eq!(CHECK, 0);
+        {
+            let u = U { a: 1 };
+        }
+        assert_eq!(CHECK, 1); // 1, dtor of U is called
+        {
+            let w = W { a: S };
+        }
+        assert_eq!(CHECK, 2); // 2, not 11, dtor of S is not called
+        {
+            let y = Y { a: S };
+        }
+        assert_eq!(CHECK, 2); // 2, not 12, dtor of S is not called
+    }
 }

From 0cb19389b060c4d34c8e0654aabce8d39304ccca Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Fri, 19 Aug 2016 19:20:30 +0300
Subject: [PATCH 14/22] Fix buggy field access translation

---
 src/librustc/mir/repr.rs                     |  7 +++++--
 src/librustc/mir/tcx.rs                      |  2 +-
 src/librustc/mir/visit.rs                    |  3 ++-
 src/librustc_mir/build/expr/as_rvalue.rs     |  9 ++++++---
 src/librustc_mir/transform/deaggregator.rs   |  4 ++--
 src/librustc_mir/transform/qualify_consts.rs |  2 +-
 src/librustc_trans/mir/constant.rs           |  2 +-
 src/librustc_trans/mir/rvalue.rs             |  9 +++++----
 src/test/run-pass/union-basic.rs             |  6 +++---
 src/test/run-pass/union-pat-refutability.rs  | 10 ++++------
 10 files changed, 30 insertions(+), 24 deletions(-)

diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs
index 8145c0aae3f74..a2abaa5e12f55 100644
--- a/src/librustc/mir/repr.rs
+++ b/src/librustc/mir/repr.rs
@@ -962,7 +962,10 @@ pub enum CastKind {
 pub enum AggregateKind<'tcx> {
     Vec,
     Tuple,
-    Adt(AdtDef<'tcx>, usize, &'tcx Substs<'tcx>),
+    /// The second field is variant number (discriminant), it's equal to 0
+    /// for struct and union expressions. The fourth field is active field
+    /// number and is present only for union expressions.
+    Adt(AdtDef<'tcx>, usize, &'tcx Substs<'tcx>, Option),
     Closure(DefId, ClosureSubsts<'tcx>),
 }
 
@@ -1069,7 +1072,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
                         }
                     }
 
-                    Adt(adt_def, variant, substs) => {
+                    Adt(adt_def, variant, substs, _) => {
                         let variant_def = &adt_def.variants[variant];
 
                         ppaux::parameterized(fmt, substs, variant_def.did,
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index 76e5f8598c1c5..a0ccc72aa1fce 100644
--- a/src/librustc/mir/tcx.rs
+++ b/src/librustc/mir/tcx.rs
@@ -187,7 +187,7 @@ impl<'tcx> Rvalue<'tcx> {
                             ops.iter().map(|op| op.ty(mir, tcx)).collect()
                         ))
                     }
-                    AggregateKind::Adt(def, _, substs) => {
+                    AggregateKind::Adt(def, _, substs, _) => {
                         Some(tcx.lookup_item_type(def.did).ty.subst(tcx, substs))
                     }
                     AggregateKind::Closure(did, substs) => {
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index 2771880735c27..c2d0b2c686e77 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -536,7 +536,8 @@ macro_rules! make_mir_visitor {
                             }
                             AggregateKind::Adt(_adt_def,
                                                _variant_index,
-                                               ref $($mutability)* substs) => {
+                                               ref $($mutability)* substs,
+                                               _active_field_index) => {
                                 self.visit_substs(substs);
                             }
                             AggregateKind::Closure(ref $($mutability)* def_id,
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index dafc53d3c1542..6ea1fb5036065 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -181,6 +181,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             ExprKind::Adt {
                 adt_def, variant_index, substs, fields, base
             } => { // see (*) above
+                let is_union = adt_def.adt_kind() == ty::AdtKind::Union;
+                let active_field_index = if is_union { Some(fields[0].name.index()) } else { None };
+
                 // first process the set of fields that were provided
                 // (evaluating them in order given by user)
                 let fields_map: FnvHashMap<_, _> =
@@ -204,11 +207,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         })
                         .collect()
                 } else {
-                    field_names.iter().map(|n| fields_map[n].clone()).collect()
+                    field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect()
                 };
 
-                block.and(Rvalue::Aggregate(AggregateKind::Adt(adt_def, variant_index, substs),
-                                            fields))
+                let adt = AggregateKind::Adt(adt_def, variant_index, substs, active_field_index);
+                block.and(Rvalue::Aggregate(adt, fields))
             }
             ExprKind::Assign { .. } |
             ExprKind::AssignOp { .. } => {
diff --git a/src/librustc_mir/transform/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs
index cb3010a5cf43d..77af02c18c60e 100644
--- a/src/librustc_mir/transform/deaggregator.rs
+++ b/src/librustc_mir/transform/deaggregator.rs
@@ -57,7 +57,7 @@ impl<'tcx> MirPass<'tcx> for Deaggregator {
                 _ => span_bug!(src_info.span, "expected aggregate, not {:?}", rhs),
             };
             let (adt_def, variant, substs) = match agg_kind {
-                &AggregateKind::Adt(adt_def, variant, substs) => (adt_def, variant, substs),
+                &AggregateKind::Adt(adt_def, variant, substs, None) => (adt_def, variant, substs),
                 _ => span_bug!(src_info.span, "expected struct, not {:?}", rhs),
             };
             let n = bb.statements.len();
@@ -120,7 +120,7 @@ fn get_aggregate_statement_index<'a, 'tcx, 'b>(start: usize,
             _ => continue,
         };
         let (adt_def, variant) = match kind {
-            &AggregateKind::Adt(adt_def, variant, _) => (adt_def, variant),
+            &AggregateKind::Adt(adt_def, variant, _, None) => (adt_def, variant),
             _ => continue,
         };
         if operands.len() == 0 {
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 2fc90ab27a085..6c6a5f7fc74b0 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -705,7 +705,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
             }
 
             Rvalue::Aggregate(ref kind, _) => {
-                if let AggregateKind::Adt(def, _, _) = *kind {
+                if let AggregateKind::Adt(def, _, _, _) = *kind {
                     if def.has_dtor() {
                         self.add(Qualif::NEEDS_DROP);
                         self.deny_drop();
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index ade266a580e78..15dc7bb4421c1 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -547,7 +547,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                                                         self.monomorphize(&substs));
                 }
 
-                let val = if let mir::AggregateKind::Adt(adt_def, index, _) = *kind {
+                let val = if let mir::AggregateKind::Adt(adt_def, index, _, _) = *kind {
                     let repr = adt::represent_type(self.ccx, dest_ty);
                     let disr = Disr::from(adt_def.variants[index].disr_val);
                     adt::trans_const(self.ccx, &repr, disr, &fields)
diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs
index 13484cb7a4ece..21b019d7e24df 100644
--- a/src/librustc_trans/mir/rvalue.rs
+++ b/src/librustc_trans/mir/rvalue.rs
@@ -110,9 +110,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 
             mir::Rvalue::Aggregate(ref kind, ref operands) => {
                 match *kind {
-                    mir::AggregateKind::Adt(adt_def, index, _) => {
+                    mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => {
                         let repr = adt::represent_type(bcx.ccx(), dest.ty.to_ty(bcx.tcx()));
-                        let disr = Disr::from(adt_def.variants[index].disr_val);
+                        let disr = Disr::from(adt_def.variants[variant_index].disr_val);
                         bcx.with_block(|bcx| {
                             adt::trans_set_discr(bcx, &repr, dest.llval, Disr::from(disr));
                         });
@@ -121,8 +121,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                             // Do not generate stores and GEPis for zero-sized fields.
                             if !common::type_is_zero_size(bcx.ccx(), op.ty) {
                                 let val = adt::MaybeSizedValue::sized(dest.llval);
-                                let lldest_i = adt::trans_field_ptr_builder(&bcx, &repr,
-                                                                            val, disr, i);
+                                let field_index = active_field_index.unwrap_or(i);
+                                let lldest_i = adt::trans_field_ptr_builder(&bcx, &repr, val,
+                                                                            disr, field_index);
                                 self.store_operand(&bcx, lldest_i, op);
                             }
                         }
diff --git a/src/test/run-pass/union-basic.rs b/src/test/run-pass/union-basic.rs
index afbfe5bf92be7..a00bd73115a1d 100644
--- a/src/test/run-pass/union-basic.rs
+++ b/src/test/run-pass/union-basic.rs
@@ -48,12 +48,12 @@ fn main() {
         assert_eq!(a, 10);
     }
 
-    let mut w: W = unsafe { zeroed() };
+    let mut w = W { b: 0 };
     unsafe {
         assert_eq!(w.a, 0);
         assert_eq!(w.b, 0);
         // w.a = 1;
-        // assert_eq!(w.a, 0);
-        // assert_eq!(w.b, 0);
+        assert_eq!(w.a, 0);
+        assert_eq!(w.b, 0);
     }
 }
diff --git a/src/test/run-pass/union-pat-refutability.rs b/src/test/run-pass/union-pat-refutability.rs
index a57c1103a9b4a..e6144f35f1d54 100644
--- a/src/test/run-pass/union-pat-refutability.rs
+++ b/src/test/run-pass/union-pat-refutability.rs
@@ -54,11 +54,9 @@ fn refut(w: W) {
 }
 
 fn main() {
-    // ICE
-    // let v = Value { tag: Tag::I, u: U { i: 1 } };
-    // assert_eq!(is_zero(v), false);
+    let v = Value { tag: Tag::I, u: U { i: 1 } };
+    assert_eq!(is_zero(v), false);
 
-    // ICE
-    // let w = W { a: 11 };
-    // refut(w);
+    let w = W { a: 11 };
+    refut(w);
 }

From 2dc2fc5fc528aab7ba138f1a351df6f846dfec1d Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Mon, 22 Aug 2016 15:17:05 +0300
Subject: [PATCH 15/22] Fix rebase

---
 src/librustc_metadata/encoder.rs | 33 +++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index e82742004c39f..35f5eba4160d9 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -1029,6 +1029,33 @@ impl<'a, 'tcx, 'encoder> ItemContentBuilder<'a, 'tcx, 'encoder> {
                                               def_to_u64(ctor_did));
                 }
             }
+            hir::ItemUnion(..) => {
+                let def = ecx.tcx.lookup_adt_def(def_id);
+                let variant = def.struct_variant();
+
+                encode_def_id_and_key(ecx, self.rbml_w, def_id);
+                encode_family(self.rbml_w, 'U');
+                self.encode_bounds_and_type_for_item(item.id);
+
+                encode_item_variances(self.rbml_w, ecx, item.id);
+                encode_name(self.rbml_w, item.name);
+                encode_attributes(self.rbml_w, &item.attrs);
+                encode_stability(self.rbml_w, stab);
+                encode_deprecation(self.rbml_w, depr);
+                self.encode_visibility(vis);
+                self.encode_repr_attrs(&item.attrs);
+
+                /* Encode def_ids for each field and method
+                for methods, write all the stuff get_trait_method
+                needs to know*/
+                self.encode_struct_fields(variant);
+
+                encode_inlined_item(ecx, self.rbml_w, InlinedItemRef::Item(def_id, item));
+                self.encode_mir(item.id);
+
+                // Encode inherent implementations for self union.
+                encode_inherent_implementations(ecx, self.rbml_w, def_id);
+            }
             hir::ItemDefaultImpl(unsafety, _) => {
                 encode_def_id_and_key(ecx, self.rbml_w, def_id);
                 encode_family(self.rbml_w, 'd');
@@ -1180,7 +1207,7 @@ impl<'a, 'tcx, 'encoder> IndexBuilder<'a, 'tcx, 'encoder> {
                 self.encode_addl_struct_info(def_id, struct_def.id(), item);
             }
             hir::ItemUnion(..) => {
-                unimplemented_unions!();
+                self.encode_addl_union_info(def_id);
             }
             hir::ItemImpl(_, _, _, _, _, ref ast_items) => {
                 self.encode_addl_impl_info(def_id, item.id, ast_items);
@@ -1217,6 +1244,10 @@ impl<'a, 'tcx, 'encoder> IndexBuilder<'a, 'tcx, 'encoder> {
         }
     }
 
+    fn encode_addl_union_info(&mut self, def_id: DefId) {
+        self.encode_fields(def_id);
+    }
+
     fn encode_addl_impl_info(&mut self,
                              def_id: DefId,
                              impl_id: ast::NodeId,

From d9b332bd69770cb716233b6998b11d345f6f184b Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Mon, 22 Aug 2016 19:56:14 +0300
Subject: [PATCH 16/22] Translate union constants

Fix alignment for packed unions
Add some missing privacy test
Get rid of `unimplemented_unions` macro
---
 src/librustc_const_eval/eval.rs              |  7 +-
 src/librustc_privacy/lib.rs                  |  5 +-
 src/librustc_trans/adt.rs                    | 23 +++++-
 src/librustc_trans/debuginfo/metadata.rs     |  9 ++-
 src/libsyntax/diagnostics/macros.rs          |  7 --
 src/libsyntax/feature_gate.rs                |  2 +-
 src/test/compile-fail/union-const-eval.rs    | 26 +++++++
 src/test/compile-fail/union-const-pat.rs     | 25 +++++++
 src/test/compile-fail/union-field-privacy.rs | 21 ++++++
 src/test/run-pass/union-const-trans.rs       | 27 +++++++
 src/test/run-pass/union-packed.rs            | 74 ++++++++++++++++++++
 11 files changed, 206 insertions(+), 20 deletions(-)
 create mode 100644 src/test/compile-fail/union-const-eval.rs
 create mode 100644 src/test/compile-fail/union-const-pat.rs
 create mode 100644 src/test/compile-fail/union-field-privacy.rs
 create mode 100644 src/test/run-pass/union-const-trans.rs
 create mode 100644 src/test/run-pass/union-packed.rs

diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs
index 81fe19812ca47..114b5e1331de2 100644
--- a/src/librustc_const_eval/eval.rs
+++ b/src/librustc_const_eval/eval.rs
@@ -258,8 +258,7 @@ pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 format!("floating point constants cannot be used in patterns"));
         }
         ty::TyEnum(adt_def, _) |
-        ty::TyStruct(adt_def, _) |
-        ty::TyUnion(adt_def, _) => {
+        ty::TyStruct(adt_def, _) => {
             if !tcx.has_attr(adt_def.did, "structural_match") {
                 tcx.sess.add_lint(
                     lint::builtin::ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN,
@@ -272,6 +271,10 @@ pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                             tcx.item_path_str(adt_def.did)));
             }
         }
+        ty::TyUnion(..) => {
+            // Matching on union fields is unsafe, we can't hide it in constants
+            tcx.sess.span_err(span, "cannot use unions in constant patterns");
+        }
         _ => { }
     }
     let pat = match expr.node {
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index 25601b6bfec7f..6b291c6930722 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -385,8 +385,9 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
     fn check_field(&mut self, span: Span, def: ty::AdtDef<'tcx>, field: ty::FieldDef<'tcx>) {
         if def.adt_kind() != ty::AdtKind::Enum &&
            !field.vis.is_accessible_from(self.curitem, &self.tcx.map) {
-            struct_span_err!(self.tcx.sess, span, E0451, "field `{}` of struct `{}` is private",
-                      field.name, self.tcx.item_path_str(def.did))
+            let kind_descr = if def.adt_kind() == ty::AdtKind::Union { "union" } else { "struct" };
+            struct_span_err!(self.tcx.sess, span, E0451, "field `{}` of {} `{}` is private",
+                      field.name, kind_descr, self.tcx.item_path_str(def.did))
                 .span_label(span, &format!("field `{}` is private", field.name))
                 .emit();
         }
diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs
index abbb9a5d4dbe5..7fd02ab82a3f8 100644
--- a/src/librustc_trans/adt.rs
+++ b/src/librustc_trans/adt.rs
@@ -516,7 +516,7 @@ fn mk_union<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
     Union {
         min_size: min_size,
-        align: align,
+        align: if packed { 1 } else { align },
         packed: packed,
         fields: tys.to_vec(),
     }
@@ -1176,8 +1176,10 @@ pub fn trans_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, r: &Repr<'tcx>, discr
             contents.extend_from_slice(&[padding(ccx, max_sz - case.size)]);
             C_struct(ccx, &contents[..], false)
         }
-        UntaggedUnion(..) => {
-            unimplemented_unions!();
+        UntaggedUnion(ref un) => {
+            assert_eq!(discr, Disr(0));
+            let contents = build_const_union(ccx, un, vals[0]);
+            C_struct(ccx, &contents, un.packed)
         }
         Univariant(ref st) => {
             assert_eq!(discr, Disr(0));
@@ -1272,6 +1274,21 @@ fn build_const_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     cfields
 }
 
+fn build_const_union<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
+                               un: &Union<'tcx>,
+                               field_val: ValueRef)
+                               -> Vec {
+    let mut cfields = vec![field_val];
+
+    let offset = machine::llsize_of_alloc(ccx, val_ty(field_val));
+    let size = roundup(un.min_size, un.align);
+    if offset != size {
+        cfields.push(padding(ccx, size - offset));
+    }
+
+    cfields
+}
+
 fn padding(ccx: &CrateContext, size: u64) -> ValueRef {
     C_undef(Type::array(&Type::i8(ccx), size))
 }
diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs
index bd67a215d65eb..00493b018c10e 100644
--- a/src/librustc_trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/debuginfo/metadata.rs
@@ -786,7 +786,7 @@ pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                     usage_site_span).finalize(cx)
         }
         ty::TyUnion(..) => {
-            unimplemented_unions!();
+            unimplemented!();
         }
         ty::TyTuple(ref elements) => {
             prepare_tuple_metadata(cx,
@@ -1302,9 +1302,6 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                     ]
                 }
             }
-            adt::UntaggedUnion(..) => {
-                unimplemented_unions!();
-            }
             adt::RawNullablePointer { nndiscr: non_null_variant_index, nnty, .. } => {
                 // As far as debuginfo is concerned, the pointer this enum
                 // represents is still wrapped in a struct. This is to make the
@@ -1421,7 +1418,9 @@ impl<'tcx> EnumMemberDescriptionFactory<'tcx> {
                     }
                 ]
             },
-            adt::CEnum(..) => span_bug!(self.span, "This should be unreachable.")
+            adt::CEnum(..) | adt::UntaggedUnion(..) => {
+                span_bug!(self.span, "This should be unreachable.")
+            }
         }
     }
 }
diff --git a/src/libsyntax/diagnostics/macros.rs b/src/libsyntax/diagnostics/macros.rs
index e2a7ec0a33de1..25e0428248df4 100644
--- a/src/libsyntax/diagnostics/macros.rs
+++ b/src/libsyntax/diagnostics/macros.rs
@@ -107,13 +107,6 @@ macro_rules! help {
     })
 }
 
-#[macro_export]
-macro_rules! unimplemented_unions {
-    () => ({
-        panic!("unions are not fully implemented");
-    })
-}
-
 #[macro_export]
 macro_rules! register_diagnostics {
     ($($code:tt),*) => (
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index b40124bd7741a..287d33cc3e5b2 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -959,7 +959,7 @@ impl<'a> Visitor for PostExpansionVisitor<'a> {
             ast::ItemKind::Union(..) => {
                 gate_feature_post!(&self, untagged_unions,
                                    i.span,
-                                   "unions are unstable and not fully implemented");
+                                   "unions are unstable and possibly buggy");
             }
 
             ast::ItemKind::DefaultImpl(..) => {
diff --git a/src/test/compile-fail/union-const-eval.rs b/src/test/compile-fail/union-const-eval.rs
new file mode 100644
index 0000000000000..b2bf173c59c86
--- /dev/null
+++ b/src/test/compile-fail/union-const-eval.rs
@@ -0,0 +1,26 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+union U {
+    a: usize,
+    b: usize,
+}
+
+const C: U = U { a: 10 };
+
+fn main() {
+    unsafe {
+        let a: [u8; C.a]; // OK
+        let b: [u8; C.b]; //~ ERROR constant evaluation error
+                          //~^ NOTE nonexistent struct field
+    }
+}
diff --git a/src/test/compile-fail/union-const-pat.rs b/src/test/compile-fail/union-const-pat.rs
new file mode 100644
index 0000000000000..3d168980ed246
--- /dev/null
+++ b/src/test/compile-fail/union-const-pat.rs
@@ -0,0 +1,25 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+union U {
+    a: usize,
+    b: usize,
+}
+
+const C: U = U { a: 10 };
+
+fn main() {
+    match C {
+        C => {} //~ ERROR cannot use unions in constant patterns
+        _ => {}
+    }
+}
diff --git a/src/test/compile-fail/union-field-privacy.rs b/src/test/compile-fail/union-field-privacy.rs
new file mode 100644
index 0000000000000..d1f2bbbc3d03c
--- /dev/null
+++ b/src/test/compile-fail/union-field-privacy.rs
@@ -0,0 +1,21 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+mod m {
+    pub union U {
+        a: u8
+    }
+}
+
+fn main() {
+    let u = m::U { a: 0 }; //~ ERROR field `a` of union `m::U` is private
+}
diff --git a/src/test/run-pass/union-const-trans.rs b/src/test/run-pass/union-const-trans.rs
new file mode 100644
index 0000000000000..bdae1a0eaf88f
--- /dev/null
+++ b/src/test/run-pass/union-const-trans.rs
@@ -0,0 +1,27 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+union U {
+    a: u64,
+    b: u64,
+}
+
+const C: U = U { b: 10 };
+
+fn main() {
+    unsafe {
+        let a = C.a;
+        let b = C.b;
+        assert_eq!(a, 10);
+        assert_eq!(b, 10);
+     }
+}
diff --git a/src/test/run-pass/union-packed.rs b/src/test/run-pass/union-packed.rs
new file mode 100644
index 0000000000000..b1650ae3a7c15
--- /dev/null
+++ b/src/test/run-pass/union-packed.rs
@@ -0,0 +1,74 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+use std::mem::{size_of, size_of_val, align_of, align_of_val};
+
+struct S {
+    a: u16,
+    b: [u8; 3],
+}
+
+#[repr(packed)]
+struct Sp {
+    a: u16,
+    b: [u8; 3],
+}
+
+union U {
+    a: u16,
+    b: [u8; 3],
+}
+
+#[repr(packed)]
+union Up {
+    a: u16,
+    b: [u8; 3],
+}
+
+const CS: S = S { a: 0, b: [0, 0, 0] };
+const CSP: Sp = Sp { a: 0, b: [0, 0, 0] };
+const CU: U = U { b: [0, 0, 0] };
+const CUP: Up = Up { b: [0, 0, 0] };
+
+fn main() {
+    let s = S { a: 0, b: [0, 0, 0] };
+    assert_eq!(size_of::(), 6);
+    assert_eq!(size_of_val(&s), 6);
+    assert_eq!(size_of_val(&CS), 6);
+    assert_eq!(align_of::(), 2);
+    assert_eq!(align_of_val(&s), 2);
+    assert_eq!(align_of_val(&CS), 2);
+
+    let sp = Sp { a: 0, b: [0, 0, 0] };
+    assert_eq!(size_of::(), 5);
+    assert_eq!(size_of_val(&sp), 5);
+    assert_eq!(size_of_val(&CSP), 5);
+    assert_eq!(align_of::(), 1);
+    assert_eq!(align_of_val(&sp), 1);
+    assert_eq!(align_of_val(&CSP), 1);
+
+    let u = U { b: [0, 0, 0] };
+    assert_eq!(size_of::(), 4);
+    assert_eq!(size_of_val(&u), 4);
+    assert_eq!(size_of_val(&CU), 4);
+    assert_eq!(align_of::(), 2);
+    assert_eq!(align_of_val(&u), 2);
+    assert_eq!(align_of_val(&CU), 2);
+
+    let up = Up { b: [0, 0, 0] };
+    assert_eq!(size_of::(), 3);
+    assert_eq!(size_of_val(&up), 3);
+    assert_eq!(size_of_val(&CUP), 3);
+    assert_eq!(align_of::(), 1);
+    assert_eq!(align_of_val(&up), 1);
+    assert_eq!(align_of_val(&CUP), 1);
+}

From 079c390d5089735b5eaa8b06ddb3beedcddbee7d Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Mon, 22 Aug 2016 21:11:22 +0300
Subject: [PATCH 17/22] Generate debuginfo for unions

---
 src/librustc_trans/debuginfo/metadata.rs | 106 ++++++++++++++++++++++-
 src/test/debuginfo/union-smoke.rs        |  49 +++++++++++
 2 files changed, 153 insertions(+), 2 deletions(-)
 create mode 100644 src/test/debuginfo/union-smoke.rs

diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs
index 00493b018c10e..bdfeee37625e8 100644
--- a/src/librustc_trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/debuginfo/metadata.rs
@@ -786,7 +786,10 @@ pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                     usage_site_span).finalize(cx)
         }
         ty::TyUnion(..) => {
-            unimplemented!();
+            prepare_union_metadata(cx,
+                                   t,
+                                   unique_type_id,
+                                   usage_site_span).finalize(cx)
         }
         ty::TyTuple(ref elements) => {
             prepare_tuple_metadata(cx,
@@ -1038,6 +1041,7 @@ enum MemberDescriptionFactory<'tcx> {
     StructMDF(StructMemberDescriptionFactory<'tcx>),
     TupleMDF(TupleMemberDescriptionFactory<'tcx>),
     EnumMDF(EnumMemberDescriptionFactory<'tcx>),
+    UnionMDF(UnionMemberDescriptionFactory<'tcx>),
     VariantMDF(VariantMemberDescriptionFactory<'tcx>)
 }
 
@@ -1054,6 +1058,9 @@ impl<'tcx> MemberDescriptionFactory<'tcx> {
             EnumMDF(ref this) => {
                 this.create_member_descriptions(cx)
             }
+            UnionMDF(ref this) => {
+                this.create_member_descriptions(cx)
+            }
             VariantMDF(ref this) => {
                 this.create_member_descriptions(cx)
             }
@@ -1154,7 +1161,6 @@ fn prepare_struct_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     )
 }
 
-
 //=-----------------------------------------------------------------------------
 // Tuples
 //=-----------------------------------------------------------------------------
@@ -1209,6 +1215,66 @@ fn prepare_tuple_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     )
 }
 
+//=-----------------------------------------------------------------------------
+// Unions
+//=-----------------------------------------------------------------------------
+
+struct UnionMemberDescriptionFactory<'tcx> {
+    variant: ty::VariantDef<'tcx>,
+    substs: &'tcx Substs<'tcx>,
+    span: Span,
+}
+
+impl<'tcx> UnionMemberDescriptionFactory<'tcx> {
+    fn create_member_descriptions<'a>(&self, cx: &CrateContext<'a, 'tcx>)
+                                      -> Vec {
+        self.variant.fields.iter().map(|field| {
+            let fty = monomorphize::field_ty(cx.tcx(), self.substs, field);
+            MemberDescription {
+                name: field.name.to_string(),
+                llvm_type: type_of::type_of(cx, fty),
+                type_metadata: type_metadata(cx, fty, self.span),
+                offset: FixedMemberOffset { bytes: 0 },
+                flags: FLAGS_NONE,
+            }
+        }).collect()
+    }
+}
+
+fn prepare_union_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
+                                    union_type: Ty<'tcx>,
+                                    unique_type_id: UniqueTypeId,
+                                    span: Span)
+                                    -> RecursiveTypeDescription<'tcx> {
+    let union_name = compute_debuginfo_type_name(cx, union_type, false);
+    let union_llvm_type = type_of::in_memory_type_of(cx, union_type);
+
+    let (union_def_id, variant, substs) = match union_type.sty {
+        ty::TyUnion(def, substs) => (def.did, def.struct_variant(), substs),
+        _ => bug!("prepare_union_metadata on a non-union")
+    };
+
+    let (containing_scope, _) = get_namespace_and_span_for_item(cx, union_def_id);
+
+    let union_metadata_stub = create_union_stub(cx,
+                                                union_llvm_type,
+                                                &union_name,
+                                                unique_type_id,
+                                                containing_scope);
+
+    create_and_register_recursive_type_forward_declaration(
+        cx,
+        union_type,
+        unique_type_id,
+        union_metadata_stub,
+        union_llvm_type,
+        UnionMDF(UnionMemberDescriptionFactory {
+            variant: variant,
+            substs: substs,
+            span: span,
+        })
+    )
+}
 
 //=-----------------------------------------------------------------------------
 // Enums
@@ -1798,6 +1864,42 @@ fn create_struct_stub(cx: &CrateContext,
     return metadata_stub;
 }
 
+fn create_union_stub(cx: &CrateContext,
+                     union_llvm_type: Type,
+                     union_type_name: &str,
+                     unique_type_id: UniqueTypeId,
+                     containing_scope: DIScope)
+                   -> DICompositeType {
+    let (union_size, union_align) = size_and_align_of(cx, union_llvm_type);
+
+    let unique_type_id_str = debug_context(cx).type_map
+                                              .borrow()
+                                              .get_unique_type_id_as_string(unique_type_id);
+    let name = CString::new(union_type_name).unwrap();
+    let unique_type_id = CString::new(unique_type_id_str.as_bytes()).unwrap();
+    let metadata_stub = unsafe {
+        // LLVMRustDIBuilderCreateUnionType() wants an empty array. A null
+        // pointer will lead to hard to trace and debug LLVM assertions
+        // later on in llvm/lib/IR/Value.cpp.
+        let empty_array = create_DIArray(DIB(cx), &[]);
+
+        llvm::LLVMRustDIBuilderCreateUnionType(
+            DIB(cx),
+            containing_scope,
+            name.as_ptr(),
+            unknown_file_metadata(cx),
+            UNKNOWN_LINE_NUMBER,
+            bytes_to_bits(union_size),
+            bytes_to_bits(union_align),
+            0, // Flags
+            empty_array,
+            0, // RuntimeLang
+            unique_type_id.as_ptr())
+    };
+
+    return metadata_stub;
+}
+
 /// Creates debug information for the given global variable.
 ///
 /// Adds the created metadata nodes directly to the crate's IR.
diff --git a/src/test/debuginfo/union-smoke.rs b/src/test/debuginfo/union-smoke.rs
new file mode 100644
index 0000000000000..11ee5031ca771
--- /dev/null
+++ b/src/test/debuginfo/union-smoke.rs
@@ -0,0 +1,49 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// min-lldb-version: 310
+
+// compile-flags:-g
+
+// === GDB TESTS ===================================================================================
+
+// gdb-command:run
+// gdb-command:print u
+// gdb-check:$1 = {a = 11 '\v', b = 11}
+// gdb-command:print union_smoke::SU
+// gdb-check:$2 = {a = 10 '\n', b = 10}
+
+// === LLDB TESTS ==================================================================================
+
+// lldb-command:run
+// lldb-command:print a
+// lldb-check:[...]$0 = {a = 11 '\v', b = 11}
+// lldb-command:print union_smoke::SU
+// lldb-check:[...]$1 = {a = 10 '\n', b = 10}
+
+#![allow(unused)]
+#![feature(omit_gdb_pretty_printer_section)]
+#![omit_gdb_pretty_printer_section]
+#![feature(untagged_unions)]
+
+union U {
+    a: u8,
+    b: u64,
+}
+
+static SU: U = U { a: 10 };
+
+fn main() {
+    let u = U { b: 11 };
+
+    zzz(); // #break
+}
+
+fn zzz() {()}

From 59ccb7b6dbaf3a590cf3a234661aa7dcc2188aed Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Wed, 24 Aug 2016 21:10:19 +0300
Subject: [PATCH 18/22] Support deriving some traits for unions

---
 src/libsyntax_ext/deriving/generic/mod.rs | 11 +++++++-
 src/test/compile-fail/union-derive.rs     | 30 ++++++++++++++++++++++
 src/test/run-pass/union-derive.rs         | 31 +++++++++++++++++++++++
 3 files changed, 71 insertions(+), 1 deletion(-)
 create mode 100644 src/test/compile-fail/union-derive.rs
 create mode 100644 src/test/run-pass/union-derive.rs

diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs
index 5c636d43a7142..b37d533298399 100644
--- a/src/libsyntax_ext/deriving/generic/mod.rs
+++ b/src/libsyntax_ext/deriving/generic/mod.rs
@@ -410,9 +410,18 @@ impl<'a> TraitDef<'a> {
                     ast::ItemKind::Enum(ref enum_def, ref generics) => {
                         self.expand_enum_def(cx, enum_def, &item.attrs, item.ident, generics)
                     }
+                    ast::ItemKind::Union(ref struct_def, ref generics) => {
+                        if self.supports_unions {
+                            self.expand_struct_def(cx, &struct_def, item.ident, generics)
+                        } else {
+                            cx.span_err(mitem.span,
+                                        "this trait cannot be derived for unions");
+                            return;
+                        }
+                    }
                     _ => {
                         cx.span_err(mitem.span,
-                                    "`derive` may only be applied to structs and enums");
+                                    "`derive` may only be applied to structs, enums and unions");
                         return;
                     }
                 };
diff --git a/src/test/compile-fail/union-derive.rs b/src/test/compile-fail/union-derive.rs
new file mode 100644
index 0000000000000..0f78e96f640c7
--- /dev/null
+++ b/src/test/compile-fail/union-derive.rs
@@ -0,0 +1,30 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Most traits cannot be derived for unions.
+
+#![feature(untagged_unions)]
+
+#[derive(
+    Clone, //~ ERROR this trait cannot be derived for unions
+    PartialEq, //~ ERROR this trait cannot be derived for unions
+    Eq, //~ ERROR this trait cannot be derived for unions
+    PartialOrd, //~ ERROR this trait cannot be derived for unions
+    Ord, //~ ERROR this trait cannot be derived for unions
+    Hash, //~ ERROR this trait cannot be derived for unions
+    Default, //~ ERROR this trait cannot be derived for unions
+    Debug, //~ ERROR this trait cannot be derived for unions
+)]
+union U {
+    a: u8,
+    b: u16,
+}
+
+fn main() {}
diff --git a/src/test/run-pass/union-derive.rs b/src/test/run-pass/union-derive.rs
new file mode 100644
index 0000000000000..b71c23990a474
--- /dev/null
+++ b/src/test/run-pass/union-derive.rs
@@ -0,0 +1,31 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Some traits can be derived for unions.
+
+#![feature(untagged_unions)]
+
+#[derive(
+    Copy,
+)]
+union U {
+    a: u8,
+    b: u16,
+}
+
+impl Clone for U {
+    fn clone(&self) -> Self { *self }
+}
+
+fn main() {
+    let u = U { b: 0 };
+    let u1 = u;
+    let u2 = u.clone();
+}

From 5f975e969b46278669940aa60e5aea50ba588531 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Fri, 26 Aug 2016 16:54:58 +0300
Subject: [PATCH 19/22] Support unions in borrow checker

Add some more tests
---
 src/librustc_borrowck/borrowck/fragments.rs   |  4 +
 .../borrowck/gather_loans/restrictions.rs     | 30 +++++-
 src/librustc_borrowck/borrowck/mod.rs         |  5 -
 src/librustc_borrowck/borrowck/move_data.rs   | 57 ++++++++++-
 src/test/compile-fail/union-borrow-nested.rs  | 44 +++++++++
 src/test/compile-fail/union-borrow.rs         | 97 +++++++++++++++++++
 src/test/compile-fail/union-move-assign.rs    | 42 ++++++++
 src/test/compile-fail/union-move.rs           | 96 ++++++++++++++++++
 src/test/compile-fail/union-uninitialized.rs  | 30 ++++++
 src/test/run-pass/union-basic.rs              |  6 +-
 src/test/run-pass/union-drop-assign.rs        | 44 +++++++++
 src/test/run-pass/union-transmute.rs          | 40 ++++++++
 12 files changed, 484 insertions(+), 11 deletions(-)
 create mode 100644 src/test/compile-fail/union-borrow-nested.rs
 create mode 100644 src/test/compile-fail/union-borrow.rs
 create mode 100644 src/test/compile-fail/union-move-assign.rs
 create mode 100644 src/test/compile-fail/union-move.rs
 create mode 100644 src/test/compile-fail/union-uninitialized.rs
 create mode 100644 src/test/run-pass/union-drop-assign.rs
 create mode 100644 src/test/run-pass/union-transmute.rs

diff --git a/src/librustc_borrowck/borrowck/fragments.rs b/src/librustc_borrowck/borrowck/fragments.rs
index a8993724e6706..86f396d8982b0 100644
--- a/src/librustc_borrowck/borrowck/fragments.rs
+++ b/src/librustc_borrowck/borrowck/fragments.rs
@@ -461,6 +461,10 @@ fn add_fragment_siblings_for_extension<'a, 'tcx>(this: &MoveData<'tcx>,
             }
         }
 
+        (&ty::TyUnion(..), None) => {
+            // Do nothing, all union fields are moved/assigned together.
+        }
+
         (&ty::TyEnum(def, _), ref enum_variant_info) => {
             let variant = match *enum_variant_info {
                 Some((vid, ref _lp2)) => def.variant_with_id(vid),
diff --git a/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs b/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
index d08f792b30c14..6193157fa7b36 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
@@ -89,7 +89,7 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
                 self.restrict(cmt_base)
             }
 
-            Categorization::Interior(cmt_base, i) => {
+            Categorization::Interior(cmt_base, interior) => {
                 // R-Field
                 //
                 // Overwriting the base would not change the type of
@@ -99,8 +99,34 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
                     Categorization::Downcast(_, variant_id) => Some(variant_id),
                     _ => None
                 };
+                let interior = interior.cleaned();
+                let base_ty = cmt_base.ty;
                 let result = self.restrict(cmt_base);
-                self.extend(result, &cmt, LpInterior(opt_variant_id, i.cleaned()))
+                if let ty::TyUnion(ref adt_def, _) = base_ty.sty {
+                    match result {
+                        RestrictionResult::Safe => RestrictionResult::Safe,
+                        RestrictionResult::SafeIf(base_lp, mut base_vec) => {
+                            for field in &adt_def.struct_variant().fields {
+                                let field = InteriorKind::InteriorField(mc::NamedField(field.name));
+                                let field_ty = if field == interior {
+                                    cmt.ty
+                                } else {
+                                    self.bccx.tcx.types.err // Doesn't matter
+                                };
+                                let sibling_lp_kind = LpExtend(base_lp.clone(), cmt.mutbl,
+                                                               LpInterior(opt_variant_id, field));
+                                let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, field_ty));
+                                base_vec.push(sibling_lp);
+                            }
+
+                            let lp = new_lp(LpExtend(base_lp, cmt.mutbl,
+                                                     LpInterior(opt_variant_id, interior)));
+                            RestrictionResult::SafeIf(lp, base_vec)
+                        }
+                    }
+                } else {
+                    self.extend(result, &cmt, LpInterior(opt_variant_id, interior))
+                }
             }
 
             Categorization::StaticItem => {
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index 67152ed04ec1d..f5e20285e0c1b 100644
--- a/src/librustc_borrowck/borrowck/mod.rs
+++ b/src/librustc_borrowck/borrowck/mod.rs
@@ -477,8 +477,6 @@ impl<'a, 'tcx> LoanPath<'tcx> {
                     base.common(&base2).map(|x| {
                         let xd = x.depth();
                         if base.depth() == xd && base2.depth() == xd {
-                            assert_eq!(base.ty, base2.ty);
-                            assert_eq!(self.ty, other.ty);
                             LoanPath {
                                 kind: LpExtend(Rc::new(x), a, LpInterior(opt_variant_id, id)),
                                 ty: self.ty,
@@ -495,7 +493,6 @@ impl<'a, 'tcx> LoanPath<'tcx> {
             (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&other),
             (&LpVar(id), &LpVar(id2)) => {
                 if id == id2 {
-                    assert_eq!(self.ty, other.ty);
                     Some(LoanPath { kind: LpVar(id), ty: self.ty })
                 } else {
                     None
@@ -503,7 +500,6 @@ impl<'a, 'tcx> LoanPath<'tcx> {
             }
             (&LpUpvar(id), &LpUpvar(id2)) => {
                 if id == id2 {
-                    assert_eq!(self.ty, other.ty);
                     Some(LoanPath { kind: LpUpvar(id), ty: self.ty })
                 } else {
                     None
@@ -1136,7 +1132,6 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
                 out.push(')');
             }
 
-
             LpExtend(ref lp_base, _, LpInterior(_, InteriorField(fname))) => {
                 self.append_autoderefd_loan_path_to_string(&lp_base, out);
                 match fname {
diff --git a/src/librustc_borrowck/borrowck/move_data.rs b/src/librustc_borrowck/borrowck/move_data.rs
index c9822a4fee749..b13291b8419d4 100644
--- a/src/librustc_borrowck/borrowck/move_data.rs
+++ b/src/librustc_borrowck/borrowck/move_data.rs
@@ -21,7 +21,8 @@ use rustc::middle::dataflow::DataFlowOperator;
 use rustc::middle::dataflow::KillFrom;
 use rustc::middle::expr_use_visitor as euv;
 use rustc::middle::expr_use_visitor::MutateMode;
-use rustc::ty::TyCtxt;
+use rustc::middle::mem_categorization as mc;
+use rustc::ty::{self, TyCtxt};
 use rustc::util::nodemap::{FnvHashMap, NodeSet};
 
 use std::cell::RefCell;
@@ -364,6 +365,32 @@ impl<'a, 'tcx> MoveData<'tcx> {
                     lp: Rc>,
                     id: ast::NodeId,
                     kind: MoveKind) {
+        // Moving one union field automatically moves all its fields.
+        if let LpExtend(ref base_lp, mutbl, LpInterior(opt_variant_id, interior)) = lp.kind {
+            if let ty::TyUnion(ref adt_def, _) = base_lp.ty.sty {
+                for field in &adt_def.struct_variant().fields {
+                    let field = InteriorKind::InteriorField(mc::NamedField(field.name));
+                    let field_ty = if field == interior {
+                        lp.ty
+                    } else {
+                        tcx.types.err // Doesn't matter
+                    };
+                    let sibling_lp_kind = LpExtend(base_lp.clone(), mutbl,
+                                                   LpInterior(opt_variant_id, field));
+                    let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, field_ty));
+                    self.add_move_helper(tcx, sibling_lp, id, kind);
+                }
+                return;
+            }
+        }
+
+        self.add_move_helper(tcx, lp.clone(), id, kind);
+    }
+
+    fn add_move_helper(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                       lp: Rc>,
+                       id: ast::NodeId,
+                       kind: MoveKind) {
         debug!("add_move(lp={:?}, id={}, kind={:?})",
                lp,
                id,
@@ -393,6 +420,34 @@ impl<'a, 'tcx> MoveData<'tcx> {
                           span: Span,
                           assignee_id: ast::NodeId,
                           mode: euv::MutateMode) {
+        // Assigning to one union field automatically assigns to all its fields.
+        if let LpExtend(ref base_lp, mutbl, LpInterior(opt_variant_id, interior)) = lp.kind {
+            if let ty::TyUnion(ref adt_def, _) = base_lp.ty.sty {
+                for field in &adt_def.struct_variant().fields {
+                    let field = InteriorKind::InteriorField(mc::NamedField(field.name));
+                    let field_ty = if field == interior {
+                        lp.ty
+                    } else {
+                        tcx.types.err // Doesn't matter
+                    };
+                    let sibling_lp_kind = LpExtend(base_lp.clone(), mutbl,
+                                                   LpInterior(opt_variant_id, field));
+                    let sibling_lp = Rc::new(LoanPath::new(sibling_lp_kind, field_ty));
+                    self.add_assignment_helper(tcx, sibling_lp, assign_id, span, assignee_id, mode);
+                }
+                return;
+            }
+        }
+
+        self.add_assignment_helper(tcx, lp.clone(), assign_id, span, assignee_id, mode);
+    }
+
+    pub fn add_assignment_helper(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                 lp: Rc>,
+                                 assign_id: ast::NodeId,
+                                 span: Span,
+                                 assignee_id: ast::NodeId,
+                                 mode: euv::MutateMode) {
         debug!("add_assignment(lp={:?}, assign_id={}, assignee_id={}",
                lp, assign_id, assignee_id);
 
diff --git a/src/test/compile-fail/union-borrow-nested.rs b/src/test/compile-fail/union-borrow-nested.rs
new file mode 100644
index 0000000000000..19975d79b60be
--- /dev/null
+++ b/src/test/compile-fail/union-borrow-nested.rs
@@ -0,0 +1,44 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-tidy-linelength
+
+#![feature(untagged_unions)]
+
+#[derive(Clone, Copy)]
+struct S {
+    a: u8,
+    b: u16,
+}
+
+union U {
+    s: S,
+    c: u32,
+}
+
+impl Clone for U {
+    fn clone(&self) -> Self { *self }
+}
+impl Copy for U {}
+
+fn main() {
+    unsafe {
+        {
+            let mut u = U { s: S { a: 0, b: 1 } };
+            let ra = &mut u.s.a;
+            let b = u.s.b; // OK
+        }
+        {
+            let mut u = U { s: S { a: 0, b: 1 } };
+            let ra = &mut u.s.a;
+            let b = u.c; //~ ERROR cannot use `u.c` because it was mutably borrowed
+        }
+    }
+}
diff --git a/src/test/compile-fail/union-borrow.rs b/src/test/compile-fail/union-borrow.rs
new file mode 100644
index 0000000000000..e8989a3c2d499
--- /dev/null
+++ b/src/test/compile-fail/union-borrow.rs
@@ -0,0 +1,97 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-tidy-linelength
+
+#![feature(untagged_unions)]
+
+union U {
+    a: u8,
+    b: u64,
+}
+
+impl Clone for U {
+    fn clone(&self) -> Self { *self }
+}
+impl Copy for U {}
+
+fn main() {
+    unsafe {
+        let mut u = U { b: 0 };
+        // Imm borrow, same field
+        {
+            let ra = &u.a;
+            let ra2 = &u.a; // OK
+        }
+        {
+            let ra = &u.a;
+            let a = u.a; // OK
+        }
+        {
+            let ra = &u.a;
+            let rma = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable
+        }
+        {
+            let ra = &u.a;
+            u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed
+        }
+        // Imm borrow, other field
+        {
+            let ra = &u.a;
+            let rb = &u.b; // OK
+        }
+        {
+            let ra = &u.a;
+            let b = u.b; // OK
+        }
+        {
+            let ra = &u.a;
+            let rmb = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`)
+        }
+        {
+            let ra = &u.a;
+            u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed
+        }
+        // Mut borrow, same field
+        {
+            let rma = &mut u.a;
+            let ra = &u.a; //~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable
+        }
+        {
+            let ra = &mut u.a;
+            let a = u.a; //~ ERROR cannot use `u.a` because it was mutably borrowed
+        }
+        {
+            let rma = &mut u.a;
+            let rma2 = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable more than once at a time
+        }
+        {
+            let rma = &mut u.a;
+            u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed
+        }
+        // Mut borrow, other field
+        {
+            let rma = &mut u.a;
+            let rb = &u.b; //~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`)
+        }
+        {
+            let ra = &mut u.a;
+            let b = u.b; //~ ERROR cannot use `u.b` because it was mutably borrowed
+        }
+        {
+            let rma = &mut u.a;
+            let rmb2 = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time
+        }
+        {
+            let rma = &mut u.a;
+            u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed
+        }
+    }
+}
diff --git a/src/test/compile-fail/union-move-assign.rs b/src/test/compile-fail/union-move-assign.rs
new file mode 100644
index 0000000000000..d4d7bc6b0f7c5
--- /dev/null
+++ b/src/test/compile-fail/union-move-assign.rs
@@ -0,0 +1,42 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+// Non-copy
+struct A;
+struct B;
+
+union U {
+    a: A,
+    b: B,
+}
+
+fn main() {
+    unsafe {
+        {
+            let mut u = U { a: A };
+            let a = u.a;
+            let a = u.a; //~ ERROR use of moved value: `u.a`
+        }
+        {
+            let mut u = U { a: A };
+            let a = u.a;
+            u.a = A;
+            let a = u.a; // OK
+        }
+        {
+            let mut u = U { a: A };
+            let a = u.a;
+            u.b = B;
+            let a = u.a; // OK
+        }
+    }
+}
diff --git a/src/test/compile-fail/union-move.rs b/src/test/compile-fail/union-move.rs
new file mode 100644
index 0000000000000..5320244cf43b3
--- /dev/null
+++ b/src/test/compile-fail/union-move.rs
@@ -0,0 +1,96 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+#[derive(Clone, Copy)]
+struct Copy;
+struct NonCopy;
+
+union Unn {
+    n1: NonCopy,
+    n2: NonCopy,
+}
+union Ucc {
+    c1: Copy,
+    c2: Copy,
+}
+union Ucn {
+    c: Copy,
+    n: NonCopy,
+}
+
+fn main() {
+    unsafe {
+        // 2 NonCopy
+        {
+            let mut u = Unn { n1: NonCopy };
+            let a = u.n1;
+            let a = u.n1; //~ ERROR use of moved value: `u.n1`
+        }
+        {
+            let mut u = Unn { n1: NonCopy };
+            let a = u.n1;
+            let a = u; //~ ERROR use of partially moved value: `u`
+        }
+        {
+            let mut u = Unn { n1: NonCopy };
+            let a = u.n1;
+            let a = u.n2; //~ ERROR use of moved value: `u.n2`
+        }
+        // 2 Copy
+        {
+            let mut u = Ucc { c1: Copy };
+            let a = u.c1;
+            let a = u.c1; // OK
+        }
+        {
+            let mut u = Ucc { c1: Copy };
+            let a = u.c1;
+            let a = u; // OK
+        }
+        {
+            let mut u = Ucc { c1: Copy };
+            let a = u.c1;
+            let a = u.c2; // OK
+        }
+        // 1 Copy, 1 NonCopy
+        {
+            let mut u = Ucn { c: Copy };
+            let a = u.c;
+            let a = u.c; // OK
+        }
+        {
+            let mut u = Ucn { c: Copy };
+            let a = u.n;
+            let a = u.n; //~ ERROR use of moved value: `u.n`
+        }
+        {
+            let mut u = Ucn { c: Copy };
+            let a = u.n;
+            let a = u.c; //~ ERROR use of moved value: `u.c`
+        }
+        {
+            let mut u = Ucn { c: Copy };
+            let a = u.c;
+            let a = u.n; // OK
+        }
+        {
+            let mut u = Ucn { c: Copy };
+            let a = u.c;
+            let a = u; // OK
+        }
+        {
+            let mut u = Ucn { c: Copy };
+            let a = u.n;
+            let a = u; //~ ERROR use of partially moved value: `u`
+        }
+    }
+}
diff --git a/src/test/compile-fail/union-uninitialized.rs b/src/test/compile-fail/union-uninitialized.rs
new file mode 100644
index 0000000000000..36e062f8464e9
--- /dev/null
+++ b/src/test/compile-fail/union-uninitialized.rs
@@ -0,0 +1,30 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+struct S {
+    a: u8,
+}
+
+union U {
+    a: u8,
+}
+
+fn main() {
+    unsafe {
+        let mut s: S;
+        let mut u: U;
+        s.a = 0;
+        u.a = 0;
+        let sa = s.a; //~ ERROR use of possibly uninitialized variable: `s.a`
+        let ua = u.a; //~ ERROR use of possibly uninitialized variable: `u.a`
+    }
+}
diff --git a/src/test/run-pass/union-basic.rs b/src/test/run-pass/union-basic.rs
index a00bd73115a1d..1651aa901b966 100644
--- a/src/test/run-pass/union-basic.rs
+++ b/src/test/run-pass/union-basic.rs
@@ -52,8 +52,8 @@ fn main() {
     unsafe {
         assert_eq!(w.a, 0);
         assert_eq!(w.b, 0);
-        // w.a = 1;
-        assert_eq!(w.a, 0);
-        assert_eq!(w.b, 0);
+        w.a = 1;
+        assert_eq!(w.a, 1);
+        assert_eq!(w.b, 1);
     }
 }
diff --git a/src/test/run-pass/union-drop-assign.rs b/src/test/run-pass/union-drop-assign.rs
new file mode 100644
index 0000000000000..0da68e43f32a1
--- /dev/null
+++ b/src/test/run-pass/union-drop-assign.rs
@@ -0,0 +1,44 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Drop works for union itself.
+
+#![feature(untagged_unions)]
+
+struct S;
+
+union U {
+    a: S
+}
+
+impl Drop for S {
+    fn drop(&mut self) {
+        unsafe { CHECK += 10; }
+    }
+}
+
+impl Drop for U {
+    fn drop(&mut self) {
+        unsafe { CHECK += 1; }
+    }
+}
+
+static mut CHECK: u8 = 0;
+
+fn main() {
+    unsafe {
+        let mut u = U { a: S };
+        assert_eq!(CHECK, 0);
+        u = U { a: S };
+        assert_eq!(CHECK, 1); // union itself is assigned, union is dropped, field is not dropped
+        u.a = S;
+        assert_eq!(CHECK, 11); // union field is assigned, field is dropped
+    }
+}
diff --git a/src/test/run-pass/union-transmute.rs b/src/test/run-pass/union-transmute.rs
new file mode 100644
index 0000000000000..4eb66268ab8ea
--- /dev/null
+++ b/src/test/run-pass/union-transmute.rs
@@ -0,0 +1,40 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(core_float)]
+#![feature(float_extras)]
+#![feature(untagged_unions)]
+
+extern crate core;
+use core::num::Float;
+
+union U {
+    a: (u8, u8),
+    b: u16,
+}
+
+union W {
+    a: u32,
+    b: f32,
+}
+
+fn main() {
+    unsafe {
+        let mut u = U { a: (1, 1) };
+        assert_eq!(u.b, (1 << 8) + 1);
+        u.b = (2 << 8) + 2;
+        assert_eq!(u.a, (2, 2));
+
+        let mut w = W { a: 0b0_11111111_00000000000000000000000 };
+        assert_eq!(w.b, f32::infinity());
+        w.b = f32::neg_infinity();
+        assert_eq!(w.a, 0b1_11111111_00000000000000000000000);
+    }
+}

From e67c2282afa3c527da49618b928280564e92868f Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Fri, 26 Aug 2016 19:23:42 +0300
Subject: [PATCH 20/22] Fix rebase, fix some tests

---
 src/librustc_trans/adt.rs                      |  4 ++--
 src/librustc_trans/debuginfo/mod.rs            |  2 +-
 src/librustc_trans/glue.rs                     |  2 +-
 src/test/compile-fail/deriving-non-type.rs     | 18 +++++++++---------
 src/test/debuginfo/union-smoke.rs              | 17 +++++++++--------
 .../incremental/struct_change_field_name.rs    |  2 +-
 6 files changed, 23 insertions(+), 22 deletions(-)

diff --git a/src/librustc_trans/adt.rs b/src/librustc_trans/adt.rs
index 7fd02ab82a3f8..15a9d58c9b574 100644
--- a/src/librustc_trans/adt.rs
+++ b/src/librustc_trans/adt.rs
@@ -749,12 +749,12 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
             };
             match name {
                 None => {
-                    TypeContext::direct(Type::struct_(cx, &[fill_ty], un.packed))
+                    Type::struct_(cx, &[fill_ty], un.packed)
                 }
                 Some(name) => {
                     let mut llty = Type::named_struct(cx, name);
                     llty.set_struct_body(&[fill_ty], un.packed);
-                    TypeContext::direct(llty)
+                    llty
                 }
             }
         }
diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs
index a3a7a79fb58be..20a33498475a2 100644
--- a/src/librustc_trans/debuginfo/mod.rs
+++ b/src/librustc_trans/debuginfo/mod.rs
@@ -421,7 +421,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                 // Only "class" methods are generally understood by LLVM,
                 // so avoid methods on other types (e.g. `<*mut T>::null`).
                 match impl_self_ty.sty {
-                    ty::TyStruct(..) | ty::TyEnum(..) => {
+                    ty::TyStruct(..) | ty::TyUnion(..) | ty::TyEnum(..) => {
                         Some(type_metadata(cx, impl_self_ty, syntax_pos::DUMMY_SP))
                     }
                     _ => None
diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs
index 0d62a63b89fc9..bb77db8fd6947 100644
--- a/src/librustc_trans/glue.rs
+++ b/src/librustc_trans/glue.rs
@@ -489,7 +489,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK
         }
         ty::TyStruct(def, _) | ty::TyEnum(def, _)
                 if def.dtor_kind().is_present() && !skip_dtor => {
-            trans_struct_drop(bcx, t, v0)
+            trans_struct_drop(bcx, t, v0, false)
         }
         ty::TyUnion(def, _) => {
             if def.dtor_kind().is_present() && !skip_dtor {
diff --git a/src/test/compile-fail/deriving-non-type.rs b/src/test/compile-fail/deriving-non-type.rs
index 5b215f3ccd961..84dd22435b888 100644
--- a/src/test/compile-fail/deriving-non-type.rs
+++ b/src/test/compile-fail/deriving-non-type.rs
@@ -12,29 +12,29 @@
 
 struct S;
 
-#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs and enums
+#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs, enums and unions
 trait T { }
 
-#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs and enums
+#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs, enums and unions
 impl S { }
 
-#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs and enums
+#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs, enums and unions
 impl T for S { }
 
-#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs and enums
+#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs, enums and unions
 static s: usize = 0;
 
-#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs and enums
+#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs, enums and unions
 const c: usize = 0;
 
-#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs and enums
+#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs, enums and unions
 mod m { }
 
-#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs and enums
+#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs, enums and unions
 extern "C" { }
 
-#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs and enums
+#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs, enums and unions
 type A = usize;
 
-#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs and enums
+#[derive(PartialEq)] //~ ERROR: `derive` may only be applied to structs, enums and unions
 fn main() { }
diff --git a/src/test/debuginfo/union-smoke.rs b/src/test/debuginfo/union-smoke.rs
index 11ee5031ca771..17dea300ff9d6 100644
--- a/src/test/debuginfo/union-smoke.rs
+++ b/src/test/debuginfo/union-smoke.rs
@@ -16,17 +16,17 @@
 
 // gdb-command:run
 // gdb-command:print u
-// gdb-check:$1 = {a = 11 '\v', b = 11}
+// gdb-check:$1 = {a = {__0 = 2 '\002', __1 = 2 '\002'}, b = 514}
 // gdb-command:print union_smoke::SU
-// gdb-check:$2 = {a = 10 '\n', b = 10}
+// gdb-check:$2 = {a = {__0 = 1 '\001', __1 = 1 '\001'}, b = 257}
 
 // === LLDB TESTS ==================================================================================
 
 // lldb-command:run
 // lldb-command:print a
-// lldb-check:[...]$0 = {a = 11 '\v', b = 11}
+// lldb-check:[...]$0 = {a = {__0 = 2 '\002', __1 = 2 '\002'}, b = 514}
 // lldb-command:print union_smoke::SU
-// lldb-check:[...]$1 = {a = 10 '\n', b = 10}
+// lldb-check:[...]$1 = {a = {__0 = 1 '\001', __1 = 1 '\001'}, b = 257}
 
 #![allow(unused)]
 #![feature(omit_gdb_pretty_printer_section)]
@@ -34,14 +34,15 @@
 #![feature(untagged_unions)]
 
 union U {
-    a: u8,
-    b: u64,
+    a: (u8, u8),
+    b: u16,
 }
 
-static SU: U = U { a: 10 };
+static mut SU: U = U { a: (1, 1) };
 
 fn main() {
-    let u = U { b: 11 };
+    let u = U { b: (2 << 8) + 2 };
+    unsafe { SU = U { a: (1, 1) } }
 
     zzz(); // #break
 }
diff --git a/src/test/incremental/struct_change_field_name.rs b/src/test/incremental/struct_change_field_name.rs
index ba469c62002e4..c27294442e7a4 100644
--- a/src/test/incremental/struct_change_field_name.rs
+++ b/src/test/incremental/struct_change_field_name.rs
@@ -37,7 +37,7 @@ pub struct Y {
 #[rustc_dirty(label="TypeckItemBody", cfg="cfail2")]
 pub fn use_X() -> u32 {
     let x: X = X { x: 22 };
-    //[cfail2]~^ ERROR structure `X` has no field named `x`
+    //[cfail2]~^ ERROR struct `X` has no field named `x`
     x.x as u32
     //[cfail2]~^ ERROR attempted access of field `x`
 }

From 93067ca089ea570e4e2bdfc456958c81a4d1e092 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Fri, 26 Aug 2016 19:23:42 +0300
Subject: [PATCH 21/22] Address comments and add requested tests

---
 src/librustc/middle/expr_use_visitor.rs       | 43 +++++-----
 src/librustc/ty/mod.rs                        |  2 +-
 .../borrowck/gather_loans/restrictions.rs     |  1 +
 src/librustc_borrowck/borrowck/move_data.rs   | 12 +--
 src/librustc_privacy/lib.rs                   | 23 +++---
 src/librustc_trans/glue.rs                    |  8 +-
 src/librustc_typeck/check/_match.rs           |  9 +--
 src/rt/rust_test_helpers.c                    | 21 +++++
 .../borrowck-union-borrow-nested.rs}          |  0
 .../borrowck-union-borrow.rs}                 |  0
 .../borrowck-union-move-assign.rs}            |  0
 .../borrowck-union-move.rs}                   |  0
 .../borrowck-union-uninitialized.rs}          |  0
 .../privacy/union-field-privacy-1.rs          | 30 +++++++
 .../privacy/union-field-privacy-2.rs          | 28 +++++++
 .../{ => union}/union-const-eval.rs           |  0
 .../{ => union}/union-const-pat.rs            |  0
 src/test/compile-fail/union/union-copy.rs     | 26 ++++++
 .../compile-fail/{ => union}/union-derive.rs  |  0
 .../compile-fail/{ => union}/union-empty.rs   |  0
 .../compile-fail/union/union-feature-gate.rs  | 15 ++++
 .../compile-fail/{ => union}/union-fields.rs  | 11 +--
 src/test/compile-fail/union/union-generic.rs  | 24 ++++++
 .../{ => union}/union-nonrepresentable.rs     |  0
 src/test/compile-fail/union/union-repr-c.rs   | 29 +++++++
 .../compile-fail/union/union-suggest-field.rs | 29 +++++++
 .../compile-fail/{ => union}/union-unsafe.rs  |  7 +-
 .../compile-fail/{ => union}/union-unsized.rs |  6 ++
 .../union-with-drop-fields-lint.rs            |  0
 .../run-pass/{ => union}/auxiliary/union.rs   |  0
 .../run-pass/{ => union}/union-backcomp.rs    |  4 +
 src/test/run-pass/{ => union}/union-basic.rs  |  0
 src/test/run-pass/union/union-c-interop.rs    | 47 +++++++++++
 .../run-pass/{ => union}/union-const-trans.rs |  0
 src/test/run-pass/{ => union}/union-derive.rs |  0
 .../run-pass/{ => union}/union-drop-assign.rs |  0
 src/test/run-pass/{ => union}/union-drop.rs   |  0
 src/test/run-pass/union/union-generic.rs      | 43 ++++++++++
 .../union/union-inherent-method.rs}           | 13 +--
 src/test/run-pass/union/union-macro.rs        | 33 ++++++++
 src/test/run-pass/union/union-overwrite.rs    | 80 +++++++++++++++++++
 src/test/run-pass/{ => union}/union-packed.rs | 30 +++++++
 .../{ => union}/union-pat-refutability.rs     |  0
 src/test/run-pass/union/union-trait-impl.rs   | 27 +++++++
 .../run-pass/{ => union}/union-transmute.rs   |  0
 .../union/union-with-drop-fields-lint.rs      | 42 ++++++++++
 src/test/run-pass/{ => union}/union-xcrate.rs |  0
 47 files changed, 582 insertions(+), 61 deletions(-)
 rename src/test/compile-fail/{union-borrow-nested.rs => borrowck/borrowck-union-borrow-nested.rs} (100%)
 rename src/test/compile-fail/{union-borrow.rs => borrowck/borrowck-union-borrow.rs} (100%)
 rename src/test/compile-fail/{union-move-assign.rs => borrowck/borrowck-union-move-assign.rs} (100%)
 rename src/test/compile-fail/{union-move.rs => borrowck/borrowck-union-move.rs} (100%)
 rename src/test/compile-fail/{union-uninitialized.rs => borrowck/borrowck-union-uninitialized.rs} (100%)
 create mode 100644 src/test/compile-fail/privacy/union-field-privacy-1.rs
 create mode 100644 src/test/compile-fail/privacy/union-field-privacy-2.rs
 rename src/test/compile-fail/{ => union}/union-const-eval.rs (100%)
 rename src/test/compile-fail/{ => union}/union-const-pat.rs (100%)
 create mode 100644 src/test/compile-fail/union/union-copy.rs
 rename src/test/compile-fail/{ => union}/union-derive.rs (100%)
 rename src/test/compile-fail/{ => union}/union-empty.rs (100%)
 create mode 100644 src/test/compile-fail/union/union-feature-gate.rs
 rename src/test/compile-fail/{ => union}/union-fields.rs (72%)
 create mode 100644 src/test/compile-fail/union/union-generic.rs
 rename src/test/compile-fail/{ => union}/union-nonrepresentable.rs (100%)
 create mode 100644 src/test/compile-fail/union/union-repr-c.rs
 create mode 100644 src/test/compile-fail/union/union-suggest-field.rs
 rename src/test/compile-fail/{ => union}/union-unsafe.rs (77%)
 rename src/test/compile-fail/{ => union}/union-unsized.rs (83%)
 rename src/test/compile-fail/{ => union}/union-with-drop-fields-lint.rs (100%)
 rename src/test/run-pass/{ => union}/auxiliary/union.rs (100%)
 rename src/test/run-pass/{ => union}/union-backcomp.rs (95%)
 rename src/test/run-pass/{ => union}/union-basic.rs (100%)
 create mode 100644 src/test/run-pass/union/union-c-interop.rs
 rename src/test/run-pass/{ => union}/union-const-trans.rs (100%)
 rename src/test/run-pass/{ => union}/union-derive.rs (100%)
 rename src/test/run-pass/{ => union}/union-drop-assign.rs (100%)
 rename src/test/run-pass/{ => union}/union-drop.rs (100%)
 create mode 100644 src/test/run-pass/union/union-generic.rs
 rename src/test/{compile-fail/union-field-privacy.rs => run-pass/union/union-inherent-method.rs} (78%)
 create mode 100644 src/test/run-pass/union/union-macro.rs
 create mode 100644 src/test/run-pass/union/union-overwrite.rs
 rename src/test/run-pass/{ => union}/union-packed.rs (81%)
 rename src/test/run-pass/{ => union}/union-pat-refutability.rs (100%)
 create mode 100644 src/test/run-pass/union/union-trait-impl.rs
 rename src/test/run-pass/{ => union}/union-transmute.rs (100%)
 create mode 100644 src/test/run-pass/union/union-with-drop-fields-lint.rs
 rename src/test/run-pass/{ => union}/union-xcrate.rs (100%)

diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index a6835802f1cf9..541aeeb7d8dd7 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -671,31 +671,28 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
 
         // Select just those fields of the `with`
         // expression that will actually be used
-        match with_cmt.ty.sty {
-            ty::TyStruct(def, substs) => {
-                // Consume those fields of the with expression that are needed.
-                for with_field in &def.struct_variant().fields {
-                    if !contains_field_named(with_field, fields) {
-                        let cmt_field = self.mc.cat_field(
-                            &*with_expr,
-                            with_cmt.clone(),
-                            with_field.name,
-                            with_field.ty(self.tcx(), substs)
-                        );
-                        self.delegate_consume(with_expr.id, with_expr.span, cmt_field);
-                    }
+        if let ty::TyStruct(def, substs) = with_cmt.ty.sty {
+            // Consume those fields of the with expression that are needed.
+            for with_field in &def.struct_variant().fields {
+                if !contains_field_named(with_field, fields) {
+                    let cmt_field = self.mc.cat_field(
+                        &*with_expr,
+                        with_cmt.clone(),
+                        with_field.name,
+                        with_field.ty(self.tcx(), substs)
+                    );
+                    self.delegate_consume(with_expr.id, with_expr.span, cmt_field);
                 }
             }
-            _ => {
-                // the base expression should always evaluate to a
-                // struct; however, when EUV is run during typeck, it
-                // may not. This will generate an error earlier in typeck,
-                // so we can just ignore it.
-                if !self.tcx().sess.has_errors() {
-                    span_bug!(
-                        with_expr.span,
-                        "with expression doesn't evaluate to a struct");
-                }
+        } else {
+            // the base expression should always evaluate to a
+            // struct; however, when EUV is run during typeck, it
+            // may not. This will generate an error earlier in typeck,
+            // so we can just ignore it.
+            if !self.tcx().sess.has_errors() {
+                span_bug!(
+                    with_expr.span,
+                    "with expression doesn't evaluate to a struct");
             }
         }
 
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index ee2188e8e112c..e88f72f2d84bc 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -1423,7 +1423,7 @@ bitflags! {
         const IS_PHANTOM_DATA     = 1 << 3,
         const IS_SIMD             = 1 << 4,
         const IS_FUNDAMENTAL      = 1 << 5,
-        const IS_UNION            = 1 << 7,
+        const IS_UNION            = 1 << 6,
     }
 }
 
diff --git a/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs b/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
index 6193157fa7b36..c08dc9330b8fd 100644
--- a/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
+++ b/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
@@ -102,6 +102,7 @@ impl<'a, 'tcx> RestrictionsContext<'a, 'tcx> {
                 let interior = interior.cleaned();
                 let base_ty = cmt_base.ty;
                 let result = self.restrict(cmt_base);
+                // Borrowing one union field automatically borrows all its fields.
                 if let ty::TyUnion(ref adt_def, _) = base_ty.sty {
                     match result {
                         RestrictionResult::Safe => RestrictionResult::Safe,
diff --git a/src/librustc_borrowck/borrowck/move_data.rs b/src/librustc_borrowck/borrowck/move_data.rs
index b13291b8419d4..236a1a2835c2a 100644
--- a/src/librustc_borrowck/borrowck/move_data.rs
+++ b/src/librustc_borrowck/borrowck/move_data.rs
@@ -442,12 +442,12 @@ impl<'a, 'tcx> MoveData<'tcx> {
         self.add_assignment_helper(tcx, lp.clone(), assign_id, span, assignee_id, mode);
     }
 
-    pub fn add_assignment_helper(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                 lp: Rc>,
-                                 assign_id: ast::NodeId,
-                                 span: Span,
-                                 assignee_id: ast::NodeId,
-                                 mode: euv::MutateMode) {
+    fn add_assignment_helper(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                             lp: Rc>,
+                             assign_id: ast::NodeId,
+                             span: Span,
+                             assignee_id: ast::NodeId,
+                             mode: euv::MutateMode) {
         debug!("add_assignment(lp={:?}, assign_id={}, assignee_id={}",
                lp, assign_id, assignee_id);
 
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index 6b291c6930722..179863c16fff2 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -429,19 +429,24 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivacyVisitor<'a, 'tcx> {
                 let method = self.tcx.tables.borrow().method_map[&method_call];
                 self.check_method(expr.span, method.def_id);
             }
-            hir::ExprStruct(_, ref fields, _) => {
+            hir::ExprStruct(_, ref expr_fields, _) => {
                 let adt = self.tcx.expr_ty(expr).ty_adt_def().unwrap();
                 let variant = adt.variant_of_def(self.tcx.expect_def(expr.id));
                 // RFC 736: ensure all unmentioned fields are visible.
                 // Rather than computing the set of unmentioned fields
-                // (i.e. `all_fields - fields`), just check them all.
-                for field in variant.fields.iter() {
-                    let span = if let Some(f) = fields.iter().find(|f| f.name.node == field.name) {
-                        f.span
-                    } else {
-                        expr.span
-                    };
-                    self.check_field(span, adt, field);
+                // (i.e. `all_fields - fields`), just check them all,
+                // unless the ADT is a union, then unmentioned fields
+                // are not checked.
+                if adt.adt_kind() == ty::AdtKind::Union {
+                    for expr_field in expr_fields {
+                        self.check_field(expr.span, adt, variant.field_named(expr_field.name.node));
+                    }
+                } else {
+                    for field in &variant.fields {
+                        let expr_field = expr_fields.iter().find(|f| f.name.node == field.name);
+                        let span = if let Some(f) = expr_field { f.span } else { expr.span };
+                        self.check_field(span, adt, field);
+                    }
                 }
             }
             hir::ExprPath(..) => {
diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs
index bb77db8fd6947..34c92f334d0ac 100644
--- a/src/librustc_trans/glue.rs
+++ b/src/librustc_trans/glue.rs
@@ -265,13 +265,13 @@ pub fn implement_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
     fcx.finish(bcx, DebugLoc::None);
 }
 
-fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
+fn trans_custom_dtor<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
                                  t: Ty<'tcx>,
                                  v0: ValueRef,
                                  shallow_drop: bool)
                                  -> Block<'blk, 'tcx>
 {
-    debug!("trans_struct_drop t: {}", t);
+    debug!("trans_custom_dtor t: {}", t);
     let tcx = bcx.tcx();
     let mut bcx = bcx;
 
@@ -489,11 +489,11 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, g: DropGlueK
         }
         ty::TyStruct(def, _) | ty::TyEnum(def, _)
                 if def.dtor_kind().is_present() && !skip_dtor => {
-            trans_struct_drop(bcx, t, v0, false)
+            trans_custom_dtor(bcx, t, v0, false)
         }
         ty::TyUnion(def, _) => {
             if def.dtor_kind().is_present() && !skip_dtor {
-                trans_struct_drop(bcx, t, v0, true)
+                trans_custom_dtor(bcx, t, v0, true)
             } else {
                 bcx
             }
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 5c19fa2a66cfc..12fce4b928e08 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -718,12 +718,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
         // Report an error if incorrect number of the fields were specified.
         if kind_name == "union" {
-            if fields.len() > 1 {
-                tcx.sess.span_err(span, "union patterns can have at most one field");
+            if fields.len() != 1 {
+                tcx.sess.span_err(span, "union patterns should have exactly one field");
             }
-            if fields.is_empty() && !etc {
-                tcx.sess.span_err(span, "union patterns without `..` \
-                                         should have at least one field");
+            if etc {
+                tcx.sess.span_err(span, "`..` cannot be used in union patterns");
             }
         } else if !etc {
             for field in variant.fields
diff --git a/src/rt/rust_test_helpers.c b/src/rt/rust_test_helpers.c
index d2ebdcca80cf0..7a04d377608d4 100644
--- a/src/rt/rust_test_helpers.c
+++ b/src/rt/rust_test_helpers.c
@@ -247,3 +247,24 @@ double rust_interesting_average(uint64_t n, ...) {
 int32_t rust_int8_to_int32(int8_t x) {
     return (int32_t)x;
 }
+
+typedef union LARGE_INTEGER {
+  struct {
+    uint32_t LowPart;
+    uint32_t HighPart;
+  };
+  struct {
+    uint32_t LowPart;
+    uint32_t HighPart;
+  } u;
+  uint64_t QuadPart;
+} LARGE_INTEGER;
+
+LARGE_INTEGER increment_all_parts(LARGE_INTEGER li) {
+    li.LowPart += 1;
+    li.HighPart += 1;
+    li.u.LowPart += 1;
+    li.u.HighPart += 1;
+    li.QuadPart += 1;
+    return li;
+}
diff --git a/src/test/compile-fail/union-borrow-nested.rs b/src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs
similarity index 100%
rename from src/test/compile-fail/union-borrow-nested.rs
rename to src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs
diff --git a/src/test/compile-fail/union-borrow.rs b/src/test/compile-fail/borrowck/borrowck-union-borrow.rs
similarity index 100%
rename from src/test/compile-fail/union-borrow.rs
rename to src/test/compile-fail/borrowck/borrowck-union-borrow.rs
diff --git a/src/test/compile-fail/union-move-assign.rs b/src/test/compile-fail/borrowck/borrowck-union-move-assign.rs
similarity index 100%
rename from src/test/compile-fail/union-move-assign.rs
rename to src/test/compile-fail/borrowck/borrowck-union-move-assign.rs
diff --git a/src/test/compile-fail/union-move.rs b/src/test/compile-fail/borrowck/borrowck-union-move.rs
similarity index 100%
rename from src/test/compile-fail/union-move.rs
rename to src/test/compile-fail/borrowck/borrowck-union-move.rs
diff --git a/src/test/compile-fail/union-uninitialized.rs b/src/test/compile-fail/borrowck/borrowck-union-uninitialized.rs
similarity index 100%
rename from src/test/compile-fail/union-uninitialized.rs
rename to src/test/compile-fail/borrowck/borrowck-union-uninitialized.rs
diff --git a/src/test/compile-fail/privacy/union-field-privacy-1.rs b/src/test/compile-fail/privacy/union-field-privacy-1.rs
new file mode 100644
index 0000000000000..4924fabafb0a0
--- /dev/null
+++ b/src/test/compile-fail/privacy/union-field-privacy-1.rs
@@ -0,0 +1,30 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(pub_restricted)]
+#![feature(untagged_unions)]
+
+mod m {
+    pub union U {
+        pub a: u8,
+        pub(super) b: u8,
+        c: u8,
+    }
+}
+
+fn main() {
+    let u = m::U { a: 0 }; // OK
+    let u = m::U { b: 0 }; // OK
+    let u = m::U { c: 0 }; //~ ERROR field `c` of union `m::U` is private
+
+    let m::U { a } = u; // OK
+    let m::U { b } = u; // OK
+    let m::U { c } = u; //~ ERROR field `c` of union `m::U` is private
+}
diff --git a/src/test/compile-fail/privacy/union-field-privacy-2.rs b/src/test/compile-fail/privacy/union-field-privacy-2.rs
new file mode 100644
index 0000000000000..7151538f41256
--- /dev/null
+++ b/src/test/compile-fail/privacy/union-field-privacy-2.rs
@@ -0,0 +1,28 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(pub_restricted)]
+#![feature(untagged_unions)]
+
+mod m {
+    pub union U {
+        pub a: u8,
+        pub(super) b: u8,
+        c: u8,
+    }
+}
+
+fn main() {
+    let u = m::U { a: 10 };
+
+    let a = u.a; // OK
+    let b = u.b; // OK
+    let c = u.c; //~ ERROR field `c` of struct `m::U` is private
+}
diff --git a/src/test/compile-fail/union-const-eval.rs b/src/test/compile-fail/union/union-const-eval.rs
similarity index 100%
rename from src/test/compile-fail/union-const-eval.rs
rename to src/test/compile-fail/union/union-const-eval.rs
diff --git a/src/test/compile-fail/union-const-pat.rs b/src/test/compile-fail/union/union-const-pat.rs
similarity index 100%
rename from src/test/compile-fail/union-const-pat.rs
rename to src/test/compile-fail/union/union-const-pat.rs
diff --git a/src/test/compile-fail/union/union-copy.rs b/src/test/compile-fail/union/union-copy.rs
new file mode 100644
index 0000000000000..6e08ae0074d48
--- /dev/null
+++ b/src/test/compile-fail/union/union-copy.rs
@@ -0,0 +1,26 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+union U {
+    a: u8
+}
+
+union W {
+    a: String
+}
+
+impl Clone for U { fn clone(&self) { panic!(); } }
+impl Clone for W { fn clone(&self) { panic!(); } }
+impl Copy for U {} // OK
+impl Copy for W {} //~ ERROR the trait `Copy` may not be implemented for this type
+
+fn main() {}
diff --git a/src/test/compile-fail/union-derive.rs b/src/test/compile-fail/union/union-derive.rs
similarity index 100%
rename from src/test/compile-fail/union-derive.rs
rename to src/test/compile-fail/union/union-derive.rs
diff --git a/src/test/compile-fail/union-empty.rs b/src/test/compile-fail/union/union-empty.rs
similarity index 100%
rename from src/test/compile-fail/union-empty.rs
rename to src/test/compile-fail/union/union-empty.rs
diff --git a/src/test/compile-fail/union/union-feature-gate.rs b/src/test/compile-fail/union/union-feature-gate.rs
new file mode 100644
index 0000000000000..abfc4d909218b
--- /dev/null
+++ b/src/test/compile-fail/union/union-feature-gate.rs
@@ -0,0 +1,15 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+union U { //~ ERROR unions are unstable and possibly buggy
+    a: u8,
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/union-fields.rs b/src/test/compile-fail/union/union-fields.rs
similarity index 72%
rename from src/test/compile-fail/union-fields.rs
rename to src/test/compile-fail/union/union-fields.rs
index 2bd1b8a7b32c2..a1721dda7decb 100644
--- a/src/test/compile-fail/union-fields.rs
+++ b/src/test/compile-fail/union/union-fields.rs
@@ -24,11 +24,12 @@ fn main() {
     let u = U { ..u }; //~ ERROR union expressions should have exactly one field
                        //~^ ERROR functional record update syntax requires a struct
 
-    let U {} = u; //~ ERROR union patterns without `..` should have at least one field
+    let U {} = u; //~ ERROR union patterns should have exactly one field
     let U { a } = u; // OK
-    let U { a, b } = u; //~ ERROR union patterns can have at most one field
-    let U { a, b, c } = u; //~ ERROR union patterns can have at most one field
+    let U { a, b } = u; //~ ERROR union patterns should have exactly one field
+    let U { a, b, c } = u; //~ ERROR union patterns should have exactly one field
                            //~^ ERROR union `U` does not have a field named `c`
-    let U { .. } = u; // OK
-    let U { a, .. } = u; // OK
+    let U { .. } = u; //~ ERROR union patterns should have exactly one field
+                      //~^ ERROR `..` cannot be used in union patterns
+    let U { a, .. } = u; //~ ERROR `..` cannot be used in union patterns
 }
diff --git a/src/test/compile-fail/union/union-generic.rs b/src/test/compile-fail/union/union-generic.rs
new file mode 100644
index 0000000000000..e6586b0fb7f6a
--- /dev/null
+++ b/src/test/compile-fail/union/union-generic.rs
@@ -0,0 +1,24 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+use std::rc::Rc;
+
+union U {
+    a: T
+}
+
+fn main() {
+    let u = U { a: Rc::new(0u32) };
+    //~^ ERROR  the trait bound `std::rc::Rc: std::marker::Copy` is not satisfied
+    let u = U::> { a: Default::default() };
+    //~^ ERROR  the trait bound `std::rc::Rc: std::marker::Copy` is not satisfied
+}
diff --git a/src/test/compile-fail/union-nonrepresentable.rs b/src/test/compile-fail/union/union-nonrepresentable.rs
similarity index 100%
rename from src/test/compile-fail/union-nonrepresentable.rs
rename to src/test/compile-fail/union/union-nonrepresentable.rs
diff --git a/src/test/compile-fail/union/union-repr-c.rs b/src/test/compile-fail/union/union-repr-c.rs
new file mode 100644
index 0000000000000..d7dfb126c9324
--- /dev/null
+++ b/src/test/compile-fail/union/union-repr-c.rs
@@ -0,0 +1,29 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+#![allow(unused)]
+#![deny(improper_ctypes)]
+
+#[repr(C)]
+union U {
+    a: u8,
+}
+
+union W {
+    a: u8,
+}
+
+extern "C" {
+    static FOREIGN1: U; // OK
+    static FOREIGN2: W; //~ ERROR found union without foreign-function-safe representation
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/union/union-suggest-field.rs b/src/test/compile-fail/union/union-suggest-field.rs
new file mode 100644
index 0000000000000..b05e9b6e27334
--- /dev/null
+++ b/src/test/compile-fail/union/union-suggest-field.rs
@@ -0,0 +1,29 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+union U {
+    principal: u8,
+}
+
+impl U {
+    fn calculate(&self) {}
+}
+
+fn main() {
+    let u = U { principle: 0 }; //~ ERROR union `U` has no field named `principle`
+                                //~^ HELP did you mean `principal`?
+    let w = u.principial; //~ ERROR attempted access of field `principial` on type `U`
+                          //~^ HELP did you mean `principal`?
+
+    let y = u.calculate; //~ ERROR attempted to take value of method `calculate` on type `U`
+                         //~^ HELP maybe a `()` to call it is missing?
+}
diff --git a/src/test/compile-fail/union-unsafe.rs b/src/test/compile-fail/union/union-unsafe.rs
similarity index 77%
rename from src/test/compile-fail/union-unsafe.rs
rename to src/test/compile-fail/union/union-unsafe.rs
index 762ac5d875137..97e1ec2cba865 100644
--- a/src/test/compile-fail/union-unsafe.rs
+++ b/src/test/compile-fail/union/union-unsafe.rs
@@ -15,9 +15,10 @@ union U {
 }
 
 fn main() {
-    let u = U { a: 10 }; // OK
+    let mut u = U { a: 10 }; // OK
     let a = u.a; //~ ERROR access to union field requires unsafe function or block
+    u.a = 11; //~ ERROR access to union field requires unsafe function or block
     let U { a } = u; //~ ERROR matching on union field requires unsafe function or block
-    if let U { a: 11 } = u {} //~ ERROR matching on union field requires unsafe function or block
-    let U { .. } = u; // OK
+    if let U { a: 12 } = u {} //~ ERROR matching on union field requires unsafe function or block
+    // let U { .. } = u; // OK
 }
diff --git a/src/test/compile-fail/union-unsized.rs b/src/test/compile-fail/union/union-unsized.rs
similarity index 83%
rename from src/test/compile-fail/union-unsized.rs
rename to src/test/compile-fail/union/union-unsized.rs
index 381122406d71f..a238eaf052508 100644
--- a/src/test/compile-fail/union-unsized.rs
+++ b/src/test/compile-fail/union/union-unsized.rs
@@ -12,6 +12,12 @@
 
 union U {
     a: str, //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied
+    b: u8,
+}
+
+union W {
+    a: u8,
+    b: str, //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied
 }
 
 fn main() {}
diff --git a/src/test/compile-fail/union-with-drop-fields-lint.rs b/src/test/compile-fail/union/union-with-drop-fields-lint.rs
similarity index 100%
rename from src/test/compile-fail/union-with-drop-fields-lint.rs
rename to src/test/compile-fail/union/union-with-drop-fields-lint.rs
diff --git a/src/test/run-pass/auxiliary/union.rs b/src/test/run-pass/union/auxiliary/union.rs
similarity index 100%
rename from src/test/run-pass/auxiliary/union.rs
rename to src/test/run-pass/union/auxiliary/union.rs
diff --git a/src/test/run-pass/union-backcomp.rs b/src/test/run-pass/union/union-backcomp.rs
similarity index 95%
rename from src/test/run-pass/union-backcomp.rs
rename to src/test/run-pass/union/union-backcomp.rs
index c1210dd621210..9394b618ddf25 100644
--- a/src/test/run-pass/union-backcomp.rs
+++ b/src/test/run-pass/union/union-backcomp.rs
@@ -10,7 +10,11 @@
 
 #![feature(untagged_unions)]
 
+fn union() {}
+
 fn main() {
+    union();
+
     let union = 10;
 
     union;
diff --git a/src/test/run-pass/union-basic.rs b/src/test/run-pass/union/union-basic.rs
similarity index 100%
rename from src/test/run-pass/union-basic.rs
rename to src/test/run-pass/union/union-basic.rs
diff --git a/src/test/run-pass/union/union-c-interop.rs b/src/test/run-pass/union/union-c-interop.rs
new file mode 100644
index 0000000000000..a9f97620ebd46
--- /dev/null
+++ b/src/test/run-pass/union/union-c-interop.rs
@@ -0,0 +1,47 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+#[derive(Copy)]
+#[repr(C)]
+struct LARGE_INTEGER_U {
+    LowPart: u32,
+    HighPart: u32,
+}
+
+#[derive(Copy)]
+#[repr(C)]
+union LARGE_INTEGER {
+  __unnamed__: LARGE_INTEGER_U,
+  u: LARGE_INTEGER_U,
+  QuadPart: u64,
+}
+
+impl Clone for LARGE_INTEGER_U { fn clone(&self) -> Self { *self } }
+impl Clone for LARGE_INTEGER { fn clone(&self) -> Self { *self } }
+
+#[link(name = "rust_test_helpers")]
+extern "C" {
+    fn increment_all_parts(_: LARGE_INTEGER) -> LARGE_INTEGER;
+}
+
+fn main() {
+    unsafe {
+        let mut li = LARGE_INTEGER { QuadPart: 0 };
+        let li_c = increment_all_parts(li);
+        li.__unnamed__.LowPart += 1;
+        li.__unnamed__.HighPart += 1;
+        li.u.LowPart += 1;
+        li.u.HighPart += 1;
+        li.QuadPart += 1;
+        assert_eq!(li.QuadPart, li_c.QuadPart);
+    }
+}
diff --git a/src/test/run-pass/union-const-trans.rs b/src/test/run-pass/union/union-const-trans.rs
similarity index 100%
rename from src/test/run-pass/union-const-trans.rs
rename to src/test/run-pass/union/union-const-trans.rs
diff --git a/src/test/run-pass/union-derive.rs b/src/test/run-pass/union/union-derive.rs
similarity index 100%
rename from src/test/run-pass/union-derive.rs
rename to src/test/run-pass/union/union-derive.rs
diff --git a/src/test/run-pass/union-drop-assign.rs b/src/test/run-pass/union/union-drop-assign.rs
similarity index 100%
rename from src/test/run-pass/union-drop-assign.rs
rename to src/test/run-pass/union/union-drop-assign.rs
diff --git a/src/test/run-pass/union-drop.rs b/src/test/run-pass/union/union-drop.rs
similarity index 100%
rename from src/test/run-pass/union-drop.rs
rename to src/test/run-pass/union/union-drop.rs
diff --git a/src/test/run-pass/union/union-generic.rs b/src/test/run-pass/union/union-generic.rs
new file mode 100644
index 0000000000000..9293805edbf83
--- /dev/null
+++ b/src/test/run-pass/union/union-generic.rs
@@ -0,0 +1,43 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+union MaybeItem {
+    elem: T::Item,
+    none: (),
+}
+
+union U {
+    a: A,
+    b: B,
+}
+
+unsafe fn union_transmute(a: A) -> B {
+    U { a: a }.b
+}
+
+fn main() {
+    unsafe {
+        let u = U::> { a: String::from("abcd") };
+
+        assert_eq!(u.b.len(), 4);
+        assert_eq!(u.b[0], b'a');
+
+        let b = union_transmute::<(u8, u8), u16>((1, 1));
+        assert_eq!(b, (1 << 8) + 1);
+
+        let v: Vec = vec![1, 2, 3];
+        let mut i = v.iter();
+        i.next();
+        let mi = MaybeItem::> { elem: i.next().unwrap() };
+        assert_eq!(*mi.elem, 2);
+    }
+}
diff --git a/src/test/compile-fail/union-field-privacy.rs b/src/test/run-pass/union/union-inherent-method.rs
similarity index 78%
rename from src/test/compile-fail/union-field-privacy.rs
rename to src/test/run-pass/union/union-inherent-method.rs
index d1f2bbbc3d03c..adea27bd25462 100644
--- a/src/test/compile-fail/union-field-privacy.rs
+++ b/src/test/run-pass/union/union-inherent-method.rs
@@ -10,12 +10,15 @@
 
 #![feature(untagged_unions)]
 
-mod m {
-    pub union U {
-        a: u8
-    }
+union U {
+    a: u8,
+}
+
+impl U {
+    fn method(&self) -> u8 { unsafe { self.a } }
 }
 
 fn main() {
-    let u = m::U { a: 0 }; //~ ERROR field `a` of union `m::U` is private
+    let u = U { a: 10 };
+    assert_eq!(u.method(), 10);
 }
diff --git a/src/test/run-pass/union/union-macro.rs b/src/test/run-pass/union/union-macro.rs
new file mode 100644
index 0000000000000..a23fbc3be9e2c
--- /dev/null
+++ b/src/test/run-pass/union/union-macro.rs
@@ -0,0 +1,33 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+macro_rules! duplicate {
+   ($i: item) => {
+        mod m1 {
+            $i
+        }
+        mod m2 {
+            $i
+        }
+   }
+}
+
+duplicate! {
+    pub union U {
+        pub a: u8
+    }
+}
+
+fn main() {
+    let u1 = m1::U { a: 0 };
+    let u2 = m2::U { a: 0 };
+}
diff --git a/src/test/run-pass/union/union-overwrite.rs b/src/test/run-pass/union/union-overwrite.rs
new file mode 100644
index 0000000000000..9389a6237bca6
--- /dev/null
+++ b/src/test/run-pass/union/union-overwrite.rs
@@ -0,0 +1,80 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+#[repr(C)]
+struct Pair(T, U);
+#[repr(C)]
+struct Triple(T, T, T);
+
+#[repr(C)]
+union U {
+    a: Pair,
+    b: B,
+}
+
+#[repr(C)]
+union W {
+    a: A,
+    b: B,
+}
+
+#[cfg(target_endian = "little")]
+unsafe fn check() {
+    let mut u = U:: { b: 0xDE_DE };
+    u.a.0 = 0xBE;
+    assert_eq!(u.b, 0xDE_BE);
+
+    let mut u = U:: { b: 0xDEAD_DEAD };
+    u.a.0 = 0xBEEF;
+    assert_eq!(u.b, 0xDEAD_BEEF);
+
+    let mut u = U:: { b: 0xDEADBEEF_DEADBEEF };
+    u.a.0 = 0xBAADF00D;
+    assert_eq!(u.b, 0xDEADBEEF_BAADF00D);
+
+    let mut w = W::, u8>, u32> { b: 0xDEAD_DEAD };
+    w.a.0 = Triple(0, 0, 0);
+    assert_eq!(w.b, 0xDE00_0000);
+
+    let mut w = W::>, u32> { b: 0xDEAD_DEAD };
+    w.a.1 = Triple(0, 0, 0);
+    assert_eq!(w.b, 0x0000_00AD);
+}
+
+#[cfg(target_endian = "big")]
+unsafe fn check() {
+    let mut u = U:: { b: 0xDE_DE };
+    u.a.0 = 0xBE;
+    assert_eq!(u.b, 0xBE_DE);
+
+    let mut u = U:: { b: 0xDEAD_DEAD };
+    u.a.0 = 0xBEEF;
+    assert_eq!(u.b, 0xBEEF_DEAD);
+
+    let mut u = U:: { b: 0xDEADBEEF_DEADBEEF };
+    u.a.0 = 0xBAADF00D;
+    assert_eq!(u.b, 0xBAADF00D_DEADBEEF);
+
+    let mut w = W::, u8>, u32> { b: 0xDEAD_DEAD };
+    w.a.0 = Triple(0, 0, 0);
+    assert_eq!(w.b, 0x0000_00AD);
+
+    let mut w = W::>, u32> { b: 0xDEAD_DEAD };
+    w.a.1 = Triple(0, 0, 0);
+    assert_eq!(w.b, 0xDE00_0000);
+}
+
+fn main() {
+    unsafe {
+        check();
+    }
+}
diff --git a/src/test/run-pass/union-packed.rs b/src/test/run-pass/union/union-packed.rs
similarity index 81%
rename from src/test/run-pass/union-packed.rs
rename to src/test/run-pass/union/union-packed.rs
index b1650ae3a7c15..6a61280823e50 100644
--- a/src/test/run-pass/union-packed.rs
+++ b/src/test/run-pass/union/union-packed.rs
@@ -71,4 +71,34 @@ fn main() {
     assert_eq!(align_of::(), 1);
     assert_eq!(align_of_val(&up), 1);
     assert_eq!(align_of_val(&CUP), 1);
+
+    hybrid::check_hybrid();
+}
+
+mod hybrid {
+    use std::mem::size_of;
+
+    #[repr(packed)]
+    struct S1 {
+        a: u16,
+        b: u8,
+    }
+
+    #[repr(packed)]
+    union U {
+        s: S1,
+        c: u16,
+    }
+
+    #[repr(packed)]
+    struct S2 {
+        d: u8,
+        u: U,
+    }
+
+    pub fn check_hybrid() {
+        assert_eq!(size_of::(), 3);
+        assert_eq!(size_of::(), 3);
+        assert_eq!(size_of::(), 4);
+    }
 }
diff --git a/src/test/run-pass/union-pat-refutability.rs b/src/test/run-pass/union/union-pat-refutability.rs
similarity index 100%
rename from src/test/run-pass/union-pat-refutability.rs
rename to src/test/run-pass/union/union-pat-refutability.rs
diff --git a/src/test/run-pass/union/union-trait-impl.rs b/src/test/run-pass/union/union-trait-impl.rs
new file mode 100644
index 0000000000000..a5a2be0133aba
--- /dev/null
+++ b/src/test/run-pass/union/union-trait-impl.rs
@@ -0,0 +1,27 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+use std::fmt;
+
+union U {
+    a: u8
+}
+
+impl fmt::Display for U {
+    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        unsafe { write!(f, "Oh hai {}", self.a) }
+    }
+}
+
+fn main() {
+    assert_eq!(U { a: 2 }.to_string(), "Oh hai 2");
+}
diff --git a/src/test/run-pass/union-transmute.rs b/src/test/run-pass/union/union-transmute.rs
similarity index 100%
rename from src/test/run-pass/union-transmute.rs
rename to src/test/run-pass/union/union-transmute.rs
diff --git a/src/test/run-pass/union/union-with-drop-fields-lint.rs b/src/test/run-pass/union/union-with-drop-fields-lint.rs
new file mode 100644
index 0000000000000..5a1424830d074
--- /dev/null
+++ b/src/test/run-pass/union/union-with-drop-fields-lint.rs
@@ -0,0 +1,42 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0  or the MIT license
+// , at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-pretty
+
+#![feature(untagged_unions)]
+#![allow(dead_code)]
+#![allow(unions_with_drop_fields)]
+
+union U {
+    a: u8, // OK
+}
+
+union W {
+    a: String, // OK
+    b: String, // OK
+}
+
+struct S(String);
+
+// `S` doesn't implement `Drop` trait, but still has non-trivial destructor
+union Y {
+    a: S, // OK
+}
+
+// We don't know if `T` is trivially-destructable or not until trans
+union J {
+    a: T, // OK
+}
+
+union H {
+    a: T, // OK
+}
+
+fn main() {}
diff --git a/src/test/run-pass/union-xcrate.rs b/src/test/run-pass/union/union-xcrate.rs
similarity index 100%
rename from src/test/run-pass/union-xcrate.rs
rename to src/test/run-pass/union/union-xcrate.rs

From 436cfe56534b405786816d4bbcccd11ed7571981 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov 
Date: Fri, 26 Aug 2016 19:23:42 +0300
Subject: [PATCH 22/22] Fix type encoding/decoding for unions

Fix union debuginfo test on lldb
---
 src/librustc_metadata/decoder.rs           |  2 +-
 src/librustc_metadata/tydecode.rs          |  8 ++++
 src/librustc_metadata/tyencode.rs          |  2 +-
 src/test/debuginfo/union-smoke.rs          |  6 +--
 src/test/run-pass/union/auxiliary/union.rs |  2 +-
 src/test/run-pass/union/union-basic.rs     | 55 +++++++++++++---------
 src/test/run-pass/union/union-xcrate.rs    | 21 ---------
 7 files changed, 46 insertions(+), 50 deletions(-)
 delete mode 100644 src/test/run-pass/union/union-xcrate.rs

diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index 8775f58d0b22b..aeb95e5670d6a 100644
--- a/src/librustc_metadata/decoder.rs
+++ b/src/librustc_metadata/decoder.rs
@@ -291,7 +291,7 @@ fn maybe_item_name(item: rbml::Doc) -> Option {
 
 fn family_to_variant_kind<'tcx>(family: Family) -> Option {
     match family {
-        Struct(VariantKind::Struct) | Variant(VariantKind::Struct) =>
+        Struct(VariantKind::Struct) | Variant(VariantKind::Struct) | Union =>
             Some(ty::VariantKind::Struct),
         Struct(VariantKind::Tuple) | Variant(VariantKind::Tuple) =>
             Some(ty::VariantKind::Tuple),
diff --git a/src/librustc_metadata/tydecode.rs b/src/librustc_metadata/tydecode.rs
index f51299226fe7d..55ff4817683de 100644
--- a/src/librustc_metadata/tydecode.rs
+++ b/src/librustc_metadata/tydecode.rs
@@ -472,6 +472,14 @@ impl<'a,'tcx> TyDecoder<'a,'tcx> {
                 let def = self.tcx.lookup_adt_def(did);
                 return self.tcx.mk_struct(def, substs);
             }
+            'U' => {
+                assert_eq!(self.next(), '[');
+                let did = self.parse_def();
+                let substs = self.parse_substs();
+                assert_eq!(self.next(), ']');
+                let def = self.tcx.lookup_adt_def(did);
+                return self.tcx.mk_union(def, substs);
+            }
             'k' => {
                 assert_eq!(self.next(), '[');
                 let did = self.parse_def();
diff --git a/src/librustc_metadata/tyencode.rs b/src/librustc_metadata/tyencode.rs
index b334a21c07c7a..bef3cf3a1940f 100644
--- a/src/librustc_metadata/tyencode.rs
+++ b/src/librustc_metadata/tyencode.rs
@@ -171,7 +171,7 @@ pub fn enc_ty<'a, 'tcx>(w: &mut Cursor>, cx: &ctxt<'a, 'tcx>, t: Ty<'tcx
             write!(w, "]");
         }
         ty::TyUnion(def, substs) => {
-            write!(w, "u[{}|", (cx.ds)(cx.tcx, def.did));
+            write!(w, "U[{}|", (cx.ds)(cx.tcx, def.did));
             enc_substs(w, cx, substs);
             write!(w, "]");
         }
diff --git a/src/test/debuginfo/union-smoke.rs b/src/test/debuginfo/union-smoke.rs
index 17dea300ff9d6..319927c979bf8 100644
--- a/src/test/debuginfo/union-smoke.rs
+++ b/src/test/debuginfo/union-smoke.rs
@@ -23,10 +23,10 @@
 // === LLDB TESTS ==================================================================================
 
 // lldb-command:run
-// lldb-command:print a
-// lldb-check:[...]$0 = {a = {__0 = 2 '\002', __1 = 2 '\002'}, b = 514}
+// lldb-command:print u
+// lldb-check:[...]$0 = { a = ('\x02', '\x02') b = 514 }
 // lldb-command:print union_smoke::SU
-// lldb-check:[...]$1 = {a = {__0 = 1 '\001', __1 = 1 '\001'}, b = 257}
+// lldb-check:[...]$1 = 257
 
 #![allow(unused)]
 #![feature(omit_gdb_pretty_printer_section)]
diff --git a/src/test/run-pass/union/auxiliary/union.rs b/src/test/run-pass/union/auxiliary/union.rs
index dc0ca7c81c009..0231e38a729b7 100644
--- a/src/test/run-pass/union/auxiliary/union.rs
+++ b/src/test/run-pass/union/auxiliary/union.rs
@@ -12,5 +12,5 @@
 
 pub union U {
     pub a: u8,
-    b: u16,
+    pub b: u16,
 }
diff --git a/src/test/run-pass/union/union-basic.rs b/src/test/run-pass/union/union-basic.rs
index 1651aa901b966..d23af4b41b73f 100644
--- a/src/test/run-pass/union/union-basic.rs
+++ b/src/test/run-pass/union/union-basic.rs
@@ -8,47 +8,51 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// aux-build:union.rs
+
 #![feature(untagged_unions)]
 
+extern crate union;
 use std::mem::{size_of, align_of, zeroed};
 
 union U {
     a: u8,
+    b: u16
 }
 
-union U64 {
-    a: u64,
-}
+fn local() {
+    assert_eq!(size_of::(), 2);
+    assert_eq!(align_of::(), 2);
 
-union W {
-    a: u8,
-    b: u64,
-}
+    let u = U { a: 10 };
+    unsafe {
+        assert_eq!(u.a, 10);
+        let U { a } = u;
+        assert_eq!(a, 10);
+    }
 
-#[repr(C)]
-union Y {
-    f1: u16,
-    f2: [u8; 4],
+    let mut w = U { b: 0 };
+    unsafe {
+        assert_eq!(w.a, 0);
+        assert_eq!(w.b, 0);
+        w.a = 1;
+        assert_eq!(w.a, 1);
+        assert_eq!(w.b, 1);
+    }
 }
 
-fn main() {
-    assert_eq!(size_of::(), 1);
-    assert_eq!(size_of::(), 8);
-    assert_eq!(size_of::(), 8);
-    assert_eq!(align_of::(), 1);
-    assert_eq!(align_of::(), align_of::());
-    assert_eq!(align_of::(), align_of::());
-    assert_eq!(size_of::(), 4);
-    assert_eq!(align_of::(), 2);
+fn xcrate() {
+    assert_eq!(size_of::(), 2);
+    assert_eq!(align_of::(), 2);
 
-    let u = U { a: 10 };
+    let u = union::U { a: 10 };
     unsafe {
         assert_eq!(u.a, 10);
-        let U { a } = u;
+        let union::U { a } = u;
         assert_eq!(a, 10);
     }
 
-    let mut w = W { b: 0 };
+    let mut w = union::U { b: 0 };
     unsafe {
         assert_eq!(w.a, 0);
         assert_eq!(w.b, 0);
@@ -57,3 +61,8 @@ fn main() {
         assert_eq!(w.b, 1);
     }
 }
+
+fn main() {
+    local();
+    xcrate();
+}
diff --git a/src/test/run-pass/union/union-xcrate.rs b/src/test/run-pass/union/union-xcrate.rs
deleted file mode 100644
index 2a76c96ef25f5..0000000000000
--- a/src/test/run-pass/union/union-xcrate.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0  or the MIT license
-// , at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// aux-build:union.rs
-
-// #![feature(untagged_unions)]
-
-extern crate union;
-
-type A = union::U;
-
-fn main() {
-    assert_eq!(std::mem::size_of::(), 8);
-}