about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/hir/check_attr.rs13
-rw-r--r--src/librustc/hir/def.rs6
-rw-r--r--src/librustc/hir/fold.rs4
-rw-r--r--src/librustc/hir/intravisit.rs3
-rw-r--r--src/librustc/hir/lowering.rs5
-rw-r--r--src/librustc/hir/map/def_collector.rs9
-rw-r--r--src/librustc/hir/map/mod.rs1
-rw-r--r--src/librustc/hir/mod.rs3
-rw-r--r--src/librustc/hir/print.rs5
-rw-r--r--src/librustc/infer/error_reporting.rs4
-rw-r--r--src/librustc/infer/freshen.rs1
-rw-r--r--src/librustc/middle/dead.rs20
-rw-r--r--src/librustc/middle/effect.rs24
-rw-r--r--src/librustc/middle/expr_use_visitor.rs8
-rw-r--r--src/librustc/middle/mem_categorization.rs2
-rw-r--r--src/librustc/middle/reachable.rs2
-rw-r--r--src/librustc/middle/resolve_lifetime.rs1
-rw-r--r--src/librustc/middle/stability.rs13
-rw-r--r--src/librustc/mir/repr.rs7
-rw-r--r--src/librustc/mir/tcx.rs2
-rw-r--r--src/librustc/mir/visit.rs3
-rw-r--r--src/librustc/traits/coherence.rs5
-rw-r--r--src/librustc/traits/error_reporting.rs2
-rw-r--r--src/librustc/traits/select.rs7
-rw-r--r--src/librustc/ty/contents.rs3
-rw-r--r--src/librustc/ty/context.rs9
-rw-r--r--src/librustc/ty/error.rs3
-rw-r--r--src/librustc/ty/fast_reject.rs4
-rw-r--r--src/librustc/ty/flags.rs2
-rw-r--r--src/librustc/ty/item_path.rs2
-rw-r--r--src/librustc/ty/layout.rs68
-rw-r--r--src/librustc/ty/mod.rs23
-rw-r--r--src/librustc/ty/outlives.rs1
-rw-r--r--src/librustc/ty/relate.rs7
-rw-r--r--src/librustc/ty/structural_impls.rs2
-rw-r--r--src/librustc/ty/sty.rs15
-rw-r--r--src/librustc/ty/util.rs22
-rw-r--r--src/librustc/ty/walk.rs1
-rw-r--r--src/librustc/ty/wf.rs3
-rw-r--r--src/librustc/util/ppaux.rs5
-rw-r--r--src/librustc_borrowck/borrowck/check_loans.rs4
-rw-r--r--src/librustc_borrowck/borrowck/fragments.rs4
-rw-r--r--src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs2
-rw-r--r--src/librustc_borrowck/borrowck/gather_loans/move_error.rs1
-rw-r--r--src/librustc_borrowck/borrowck/gather_loans/restrictions.rs31
-rw-r--r--src/librustc_borrowck/borrowck/mir/elaborate_drops.rs4
-rw-r--r--src/librustc_borrowck/borrowck/mir/mod.rs2
-rw-r--r--src/librustc_borrowck/borrowck/mod.rs5
-rw-r--r--src/librustc_borrowck/borrowck/move_data.rs57
-rw-r--r--src/librustc_const_eval/check_match.rs7
-rw-r--r--src/librustc_const_eval/eval.rs4
-rw-r--r--src/librustc_driver/test.rs1
-rw-r--r--src/librustc_incremental/calculate_svh/svh_visitor.rs1
-rw-r--r--src/librustc_lint/bad_style.rs2
-rw-r--r--src/librustc_lint/builtin.rs49
-rw-r--r--src/librustc_lint/lib.rs1
-rw-r--r--src/librustc_lint/types.rs34
-rw-r--r--src/librustc_lint/unused.rs1
-rw-r--r--src/librustc_metadata/astencode.rs1
-rw-r--r--src/librustc_metadata/decoder.rs9
-rw-r--r--src/librustc_metadata/encoder.rs34
-rw-r--r--src/librustc_metadata/tydecode.rs8
-rw-r--r--src/librustc_metadata/tyencode.rs5
-rw-r--r--src/librustc_mir/build/expr/as_rvalue.rs9
-rw-r--r--src/librustc_mir/hair/cx/expr.rs4
-rw-r--r--src/librustc_mir/hair/cx/pattern.rs7
-rw-r--r--src/librustc_mir/transform/deaggregator.rs4
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs2
-rw-r--r--src/librustc_mir/transform/type_check.rs4
-rw-r--r--src/librustc_passes/ast_validation.rs10
-rw-r--r--src/librustc_passes/consts.rs1
-rw-r--r--src/librustc_privacy/lib.rs48
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs21
-rw-r--r--src/librustc_resolve/lib.rs5
-rw-r--r--src/librustc_save_analysis/dump_visitor.rs1
-rw-r--r--src/librustc_save_analysis/lib.rs5
-rw-r--r--src/librustc_trans/adt.rs112
-rw-r--r--src/librustc_trans/collector.rs7
-rw-r--r--src/librustc_trans/common.rs6
-rw-r--r--src/librustc_trans/debuginfo/metadata.rs117
-rw-r--r--src/librustc_trans/debuginfo/mod.rs2
-rw-r--r--src/librustc_trans/debuginfo/type_names.rs1
-rw-r--r--src/librustc_trans/glue.rs20
-rw-r--r--src/librustc_trans/mir/constant.rs2
-rw-r--r--src/librustc_trans/mir/rvalue.rs9
-rw-r--r--src/librustc_trans/trans_item.rs1
-rw-r--r--src/librustc_trans/type_of.rs85
-rw-r--r--src/librustc_typeck/astconv.rs2
-rw-r--r--src/librustc_typeck/check/_match.rs48
-rw-r--r--src/librustc_typeck/check/dropck.rs9
-rw-r--r--src/librustc_typeck/check/method/probe.rs3
-rw-r--r--src/librustc_typeck/check/method/suggest.rs52
-rw-r--r--src/librustc_typeck/check/mod.rs108
-rw-r--r--src/librustc_typeck/check/wfcheck.rs33
-rw-r--r--src/librustc_typeck/coherence/mod.rs8
-rw-r--r--src/librustc_typeck/coherence/orphan.rs7
-rw-r--r--src/librustc_typeck/coherence/overlap.rs2
-rw-r--r--src/librustc_typeck/collect.rs24
-rw-r--r--src/librustc_typeck/variance/constraints.rs6
-rw-r--r--src/librustc_typeck/variance/terms.rs3
-rw-r--r--src/librustdoc/clean/inline.rs19
-rw-r--r--src/librustdoc/clean/mod.rs35
-rw-r--r--src/librustdoc/doctree.rs15
-rw-r--r--src/librustdoc/fold.rs7
-rw-r--r--src/librustdoc/html/item_type.rs5
-rw-r--r--src/librustdoc/html/render.rs79
-rw-r--r--src/librustdoc/html/static/main.js3
-rw-r--r--src/librustdoc/passes.rs2
-rw-r--r--src/librustdoc/visit_ast.rs22
-rw-r--r--src/librustdoc/visit_lib.rs1
-rw-r--r--src/libsyntax/ast.rs2
-rw-r--r--src/libsyntax/feature_gate.rs9
-rw-r--r--src/libsyntax/parse/parser.rs33
-rw-r--r--src/libsyntax_ext/deriving/generic/mod.rs11
-rw-r--r--src/rt/rust_test_helpers.c21
-rw-r--r--src/test/compile-fail/attr-usage-repr.rs2
-rw-r--r--src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs44
-rw-r--r--src/test/compile-fail/borrowck/borrowck-union-borrow.rs97
-rw-r--r--src/test/compile-fail/borrowck/borrowck-union-move-assign.rs42
-rw-r--r--src/test/compile-fail/borrowck/borrowck-union-move.rs96
-rw-r--r--src/test/compile-fail/borrowck/borrowck-union-uninitialized.rs30
-rw-r--r--src/test/compile-fail/deriving-non-type.rs18
-rw-r--r--src/test/compile-fail/issue-17800.rs2
-rw-r--r--src/test/compile-fail/issue-19922.rs2
-rw-r--r--src/test/compile-fail/issue-31769.rs2
-rw-r--r--src/test/compile-fail/issue-4736.rs2
-rw-r--r--src/test/compile-fail/numeric-fields.rs2
-rw-r--r--src/test/compile-fail/privacy/union-field-privacy-1.rs30
-rw-r--r--src/test/compile-fail/privacy/union-field-privacy-2.rs28
-rw-r--r--src/test/compile-fail/struct-fields-hints-no-dupe.rs2
-rw-r--r--src/test/compile-fail/struct-fields-hints.rs2
-rw-r--r--src/test/compile-fail/struct-fields-too-many.rs2
-rw-r--r--src/test/compile-fail/suggest-private-fields.rs8
-rw-r--r--src/test/compile-fail/union/union-const-eval.rs26
-rw-r--r--src/test/compile-fail/union/union-const-pat.rs25
-rw-r--r--src/test/compile-fail/union/union-copy.rs26
-rw-r--r--src/test/compile-fail/union/union-derive.rs30
-rw-r--r--src/test/compile-fail/union/union-empty.rs15
-rw-r--r--src/test/compile-fail/union/union-feature-gate.rs15
-rw-r--r--src/test/compile-fail/union/union-fields.rs35
-rw-r--r--src/test/compile-fail/union/union-generic.rs24
-rw-r--r--src/test/compile-fail/union/union-nonrepresentable.rs18
-rw-r--r--src/test/compile-fail/union/union-repr-c.rs29
-rw-r--r--src/test/compile-fail/union/union-suggest-field.rs29
-rw-r--r--src/test/compile-fail/union/union-unsafe.rs24
-rw-r--r--src/test/compile-fail/union/union-unsized.rs23
-rw-r--r--src/test/compile-fail/union/union-with-drop-fields-lint.rs40
-rw-r--r--src/test/debuginfo/union-smoke.rs50
-rw-r--r--src/test/incremental/struct_change_field_name.rs2
-rw-r--r--src/test/run-pass/union/auxiliary/union.rs16
-rw-r--r--src/test/run-pass/union/union-backcomp.rs27
-rw-r--r--src/test/run-pass/union/union-basic.rs68
-rw-r--r--src/test/run-pass/union/union-c-interop.rs47
-rw-r--r--src/test/run-pass/union/union-const-trans.rs27
-rw-r--r--src/test/run-pass/union/union-derive.rs31
-rw-r--r--src/test/run-pass/union/union-drop-assign.rs44
-rw-r--r--src/test/run-pass/union/union-drop.rs65
-rw-r--r--src/test/run-pass/union/union-generic.rs43
-rw-r--r--src/test/run-pass/union/union-inherent-method.rs24
-rw-r--r--src/test/run-pass/union/union-macro.rs33
-rw-r--r--src/test/run-pass/union/union-overwrite.rs80
-rw-r--r--src/test/run-pass/union/union-packed.rs104
-rw-r--r--src/test/run-pass/union/union-pat-refutability.rs62
-rw-r--r--src/test/run-pass/union/union-trait-impl.rs27
-rw-r--r--src/test/run-pass/union/union-transmute.rs40
-rw-r--r--src/test/run-pass/union/union-with-drop-fields-lint.rs42
-rw-r--r--src/test/rustdoc/union.rs20
167 files changed, 2899 insertions, 362 deletions
diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs
index 53624702c84..a2d4239388a 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/def.rs b/src/librustc/hir/def.rs
index d15d51aed09..aa0eac37ecf 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/hir/fold.rs b/src/librustc/hir/fold.rs
index 12bc49c10da..57b5599bd1d 100644
--- a/src/librustc/hir/fold.rs
+++ b/src/librustc/hir/fold.rs
@@ -761,6 +761,10 @@ pub fn noop_fold_item_underscore<T: Folder>(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 bc1dff7c6fc..62157b1ca36 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/lowering.rs b/src/librustc/hir/lowering.rs
index 6739d3f662a..80e034721d6 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/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs
index 77567fc7a46..29fb19fd421 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()),
@@ -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/hir/map/mod.rs b/src/librustc/hir/map/mod.rs
index 5e14bb51ce8..3ffc95e64f5 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 295a49d26d0..e16005558f8 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<A> {x: A}`
     ItemStruct(VariantData, Generics),
+    /// A union definition, e.g. `union Foo<A, B> {x: A, y: B}`
+    ItemUnion(VariantData, Generics),
     /// Represents a Trait Declaration
     ItemTrait(Unsafety, Generics, TyParamBounds, HirVec<TraitItem>),
 
@@ -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 893d6708ead..f236bd4884d 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/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs
index 0cd39882b7c..efce0c8354b 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/infer/freshen.rs b/src/librustc/infer/freshen.rs
index f793d489cab..8aeb0757f5d 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/middle/dead.rs b/src/librustc/middle/dead.rs
index 824383b11af..0b1d9e8d8f6 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/union"),
         }
     }
 
@@ -148,7 +149,7 @@ impl<'a, 'tcx> MarkSymbolVisitor<'a, 'tcx> {
     fn handle_field_pattern_match(&mut self, lhs: &hir::Pat,
                                   pats: &[codemap::Spanned<hir::FieldPat>]) {
         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/effect.rs b/src/librustc/middle/effect.rs
index 250ad80f5af..e52eba68da1 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/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index 798702e6fd6..541aeeb7d8d 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<P<hir::Expr>>) {
         // Consume the expressions supplying values for each field.
@@ -695,7 +694,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
                     with_expr.span,
                     "with expression doesn't evaluate to a struct");
             }
-        };
+        }
 
         // walk the with expression so that complex expressions
         // are properly handled.
@@ -1012,7 +1011,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/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index a74bdb02044..b17411ced57 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/middle/reachable.rs b/src/librustc/middle/reachable.rs
index 1f9738556d9..0625504af88 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 ebe40500221..4d1eed612cf 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/middle/stability.rs b/src/librustc/middle/stability.rs
index c20fcc3fe1d..aea1ee8d824 100644
--- a/src/librustc/middle/stability.rs
+++ b/src/librustc/middle/stability.rs
@@ -561,9 +561,11 @@ 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")
+                               "stability::check_expr: named field access on non-struct/union")
             }
         }
         hir::ExprTupField(ref base_e, ref field) => {
@@ -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 {
@@ -599,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_);
                 }
             }
@@ -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/mir/repr.rs b/src/librustc/mir/repr.rs
index 8145c0aae3f..a2abaa5e12f 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<usize>),
     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 76e5f8598c1..a0ccc72aa1f 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 2771880735c..c2d0b2c686e 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/traits/coherence.rs b/src/librustc/traits/coherence.rs
index a5a415dd27c..0a7d3e6e76d 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"),
@@ -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 6d6d7c2b3ba..e5ebe96932d 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
             }
         }
@@ -173,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/traits/select.rs b/src/librustc/traits/select.rs
index 0573f0c5bba..f8f10d9c265 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 53bf046d6b5..d7d4693c116 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 725bbf6adfd..0fc1641d31f 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());
@@ -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/error.rs b/src/librustc/ty/error.rs
index 3d60d326b2b..0e33e396f7e 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 f7472d611be..f9ca2484d7e 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,6 +67,9 @@ pub fn simplify_type<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
         ty::TyStruct(def, _) => {
             Some(StructSimplifiedType(def.did))
         }
+        ty::TyUnion(def, _) => {
+            Some(UnionSimplifiedType(def.did))
+        }
         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 1afd49ab47f..ce6e4d6516e 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 62bd30e2555..ba8d3328509 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);
@@ -320,6 +321,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
 pub fn characteristic_def_id_of_type(ty: Ty) -> Option<DefId> {
     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 1ede8545e08..9270057b544 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<I>(&mut self, dl: &TargetDataLayout,
+                     fields: I,
+                     scapegoat: Ty<'gcx>)
+                     -> Result<(), LayoutError<'gcx>>
+    where I: Iterator<Item=Result<&'a Layout, LayoutError<'gcx>>> {
+        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,6 +949,15 @@ impl<'a, 'gcx, 'tcx> Layout {
                     non_zero: Some(def.did) == tcx.lang_items.non_zero()
                 }
             }
+            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)
                     .unwrap_or(&attr::ReprAny);
@@ -1115,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,
 
@@ -1149,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, .. } => {
@@ -1188,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, .. } => {
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index e9c01f5bad6..e88f72f2d84 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(..) |
@@ -1421,6 +1423,7 @@ bitflags! {
         const IS_PHANTOM_DATA     = 1 << 3,
         const IS_SIMD             = 1 << 4,
         const IS_FUNDAMENTAL      = 1 << 5,
+        const IS_UNION            = 1 << 6,
     }
 }
 
@@ -1513,7 +1516,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 }
@@ -1544,8 +1547,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,
@@ -1568,6 +1573,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
         }
@@ -1610,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]
     }
 
@@ -1669,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)
         }
     }
@@ -1818,7 +1827,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);
@@ -2408,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/outlives.rs b/src/librustc/ty/outlives.rs
index 4d5b38212f6..a7bb0374b75 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/relate.rs b/src/librustc/ty/relate.rs
index 5c157ff32e7..dfae19487b6 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/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index ad3769605ab..952641f6832 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 0e3f18c4474..d45fde925c5 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`.
@@ -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<T>`; 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
@@ -917,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.
@@ -980,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()
         }
@@ -1199,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
@@ -1207,7 +1213,7 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
 
     pub fn ty_adt_def(&self) -> Option<AdtDef<'tcx>> {
         match self.sty {
-            TyStruct(adt, _) | TyEnum(adt, _) => Some(adt),
+            TyStruct(adt, _) | TyUnion(adt, _) | TyEnum(adt, _) => Some(adt),
             _ => None
         }
     }
@@ -1227,6 +1233,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 77d16287fed..ad209094600 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<DefId>) -> Option<Ty<'tcx>> {
         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<DefId>) -> Option<Ty<'tcx>> {
         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)) => {
@@ -430,6 +432,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 +561,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 +601,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));
 
@@ -660,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,
@@ -677,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
@@ -687,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;
@@ -709,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/ty/walk.rs b/src/librustc/ty/walk.rs
index 409f5a85997..cea3bd6348d 100644
--- a/src/librustc/ty/walk.rs
+++ b/src/librustc/ty/walk.rs
@@ -95,6 +95,7 @@ fn push_subtypes<'tcx>(stack: &mut Vec<Ty<'tcx>>, 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 aef646a7aac..599e2be4db2 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 7e2cc2938ca..d0e02f2e8ac 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_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs
index e86fa9a05f3..b4c6689c24b 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/fragments.rs b/src/librustc_borrowck/borrowck/fragments.rs
index a8993724e67..86f396d8982 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/gather_moves.rs b/src/librustc_borrowck/borrowck/gather_loans/gather_moves.rs
index 9431dcdbcac..3cf02fc85a4 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 fc17633d63b..61c85e393d2 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/gather_loans/restrictions.rs b/src/librustc_borrowck/borrowck/gather_loans/restrictions.rs
index d08f792b30c..c08dc9330b8 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,35 @@ 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()))
+                // 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,
+                        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/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
index 885bbe856c8..c5d10345379 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 887c7deb86b..be408e2db5c 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_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index 67152ed04ec..f5e20285e0c 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 c9822a4fee7..236a1a2835c 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<LoanPath<'tcx>>,
                     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<LoanPath<'tcx>>,
+                       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);
+    }
+
+    fn add_assignment_helper(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                             lp: Rc<LoanPath<'tcx>>,
+                             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_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs
index e71a780dd89..de28cbb7c9c 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 d71add3258f..114b5e1331d 100644
--- a/src/librustc_const_eval/eval.rs
+++ b/src/librustc_const_eval/eval.rs
@@ -271,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_driver/test.rs b/src/librustc_driver/test.rs
index 460a6e68a5c..9f5f82c144c 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 c1158dc2d5f..d4a3ab59f9c 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 0e130c3bb66..1094d0ee12b 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 a103386e2c9..1702c1c0edc 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,13 @@ 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, Substs::empty(cx.tcx)))
+            }
             hir::ItemEnum(_, ref ast_generics) => {
                 if ast_generics.is_parameterized() {
                     return;
@@ -523,7 +533,7 @@ impl LateLintPass for MissingDebugImplementations {
         }
 
         match item.node {
-            hir::ItemStruct(..) | hir::ItemEnum(..) => {},
+            hir::ItemStruct(..) | hir::ItemUnion(..) | hir::ItemEnum(..) => {},
             _ => return,
         }
 
@@ -1154,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 0f0e9cfb357..c3b752d605f 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_lint/types.rs b/src/librustc_lint/types.rs
index 40e78c007cc..54cec3fd7e1 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_lint/unused.rs b/src/librustc_lint/unused.rs
index 924b768958d..44f1cf7b533 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_metadata/astencode.rs b/src/librustc_metadata/astencode.rs
index 0236f9c413d..9d9c6f033a9 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_metadata/decoder.rs b/src/librustc_metadata/decoder.rs
index 9a13be8ade5..aeb95e5670d 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)
@@ -289,7 +291,7 @@ fn maybe_item_name(item: rbml::Doc) -> Option<ast::Name> {
 
 fn family_to_variant_kind<'tcx>(family: Family) -> Option<ty::VariantKind> {
     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),
@@ -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_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index bb4cf70bd3b..35f5eba4160 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');
@@ -1179,6 +1206,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(..) => {
+                self.encode_addl_union_info(def_id);
+            }
             hir::ItemImpl(_, _, _, _, _, ref ast_items) => {
                 self.encode_addl_impl_info(def_id, item.id, ast_items);
             }
@@ -1214,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,
diff --git a/src/librustc_metadata/tydecode.rs b/src/librustc_metadata/tydecode.rs
index f51299226fe..55ff4817683 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 954ca878c01..bef3cf3a194 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<Vec<u8>>, 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_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index dafc53d3c15..6ea1fb50360 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/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 6e8a5771eea..c8f660a2d9c 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,
@@ -579,7 +579,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 0bd22cd2d93..30f79796aaa 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/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs
index cb3010a5cf4..77af02c18c6 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 2fc90ab27a0..6c6a5f7fc74 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_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index 21d4ae595e8..55bd51cd75b 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/ast_validation.rs b/src/librustc_passes/ast_validation.rs
index dde1a4a7595..b8284f5dcf1 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/librustc_passes/consts.rs b/src/librustc_passes/consts.rs
index 2d1b6e1315f..c3749bf4546 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 41b7c51e300..179863c16ff 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);
                 }
@@ -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() {
@@ -320,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 {
@@ -382,10 +383,11 @@ 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))
+            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();
         }
@@ -427,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(..) => {
@@ -942,8 +949,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);
@@ -1067,8 +1074,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_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 3e9b37f0a95..8e97870c21a 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_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 1224c694a4e..db0704db33f 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/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs
index dbe956f021e..f9a20cec42d 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_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs
index db535e22f19..47f3a06de1b 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 2fb7a69d361..15a9d58c9b5 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<Struct<'tcx>>),
@@ -121,6 +123,15 @@ pub struct Struct<'tcx> {
     pub fields: Vec<Ty<'tcx>>,
 }
 
+/// For untagged unions.
+#[derive(Eq, PartialEq, Debug)]
+pub struct Union<'tcx> {
+    pub min_size: u64,
+    pub align: u32,
+    pub packed: bool,
+    pub fields: Vec<Ty<'tcx>>,
+}
+
 #[derive(Copy, Clone)]
 pub struct MaybeSizedValue {
     pub value: ValueRef,
@@ -176,6 +187,13 @@ fn represent_type_uncached<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
 
             Univariant(mk_struct(cx, &ftys[..], packed, t))
         }
+        ty::TyUnion(def, substs) => {
+            let ftys = def.struct_variant().fields.iter().map(|field| {
+                monomorphize::field_ty(cx.tcx(), substs, field)
+            }).collect::<Vec<_>>();
+            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))
         }
@@ -479,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: if packed { 1 } else { align },
+        packed: packed,
+        fields: tys.to_vec(),
+    }
+}
+
 #[derive(Debug)]
 struct IntBounds {
     slo: i64,
@@ -643,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)
@@ -687,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 => {
+                    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);
+                    llty
+                }
+            }
+        }
         General(ity, ref sts) => {
             // We need a representation that has:
             // * The alignment of the most-aligned field
@@ -759,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)
         }
@@ -770,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,
     }
@@ -791,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);
@@ -853,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 { .. } => {
@@ -881,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);
@@ -936,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.
@@ -1097,6 +1176,11 @@ 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(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));
             let contents = build_const_struct(ccx, st, vals);
@@ -1190,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<ValueRef> {
+    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))
 }
@@ -1208,6 +1307,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/collector.rs b/src/librustc_trans/collector.rs
index c82bfa5c91b..4bea5d7e87f 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
     };
@@ -798,6 +799,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,
@@ -1121,8 +1123,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_trans/common.rs b/src/librustc_trans/common.rs
index f4682de7dff..6ae6f8aead7 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 67d4a0e044c..bdfeee37625 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,12 @@ pub fn type_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
                                     unique_type_id,
                                     usage_site_span).finalize(cx)
         }
+        ty::TyUnion(..) => {
+            prepare_union_metadata(cx,
+                                   t,
+                                   unique_type_id,
+                                   usage_site_span).finalize(cx)
+        }
         ty::TyTuple(ref elements) => {
             prepare_tuple_metadata(cx,
                                    t,
@@ -1031,6 +1041,7 @@ enum MemberDescriptionFactory<'tcx> {
     StructMDF(StructMemberDescriptionFactory<'tcx>),
     TupleMDF(TupleMemberDescriptionFactory<'tcx>),
     EnumMDF(EnumMemberDescriptionFactory<'tcx>),
+    UnionMDF(UnionMemberDescriptionFactory<'tcx>),
     VariantMDF(VariantMemberDescriptionFactory<'tcx>)
 }
 
@@ -1047,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)
             }
@@ -1147,7 +1161,6 @@ fn prepare_struct_metadata<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
     )
 }
 
-
 //=-----------------------------------------------------------------------------
 // Tuples
 //=-----------------------------------------------------------------------------
@@ -1202,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<MemberDescription> {
+        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
@@ -1411,7 +1484,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.")
+            }
         }
     }
 }
@@ -1609,7 +1684,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)),
     };
 
@@ -1789,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/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs
index a3a7a79fb58..20a33498475 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/debuginfo/type_names.rs b/src/librustc_trans/debuginfo/type_names.rs
index f757578e695..bd839243e20 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/glue.rs b/src/librustc_trans/glue.rs
index 9e1e415f62a..34c92f334d0 100644
--- a/src/librustc_trans/glue.rs
+++ b/src/librustc_trans/glue.rs
@@ -265,12 +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)
+                                 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;
 
@@ -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) {
@@ -486,7 +489,14 @@ 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_custom_dtor(bcx, t, v0, false)
+        }
+        ty::TyUnion(def, _) => {
+            if def.dtor_kind().is_present() && !skip_dtor {
+                trans_custom_dtor(bcx, t, v0, true)
+            } else {
+                bcx
+            }
         }
         _ => {
             if bcx.fcx.type_needs_drop(t) {
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index ade266a580e..15dc7bb4421 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 13484cb7a4e..21b019d7e24 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/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs
index 8a0f37230c8..deef0b09a17 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 6605d12e2e1..b5565109306 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(..) |
@@ -342,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 6c9cc5f5e13..c445455ef2b 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 f8a2e82edc2..12fce4b928e 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::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<hir::FieldPat>],
-                                   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<hir::FieldPat>],
+                               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,15 @@ 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 should have exactly one field");
+            }
+            if etc {
+                tcx.sess.span_err(span, "`..` cannot be used in union patterns");
+            }
+        } else if !etc {
             for field in variant.fields
                 .iter()
                 .filter(|field| !used_fields.contains_key(&field.name)) {
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 3a607677433..88add66b7dc 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,
@@ -304,7 +305,9 @@ 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 => format!("union {}",
+                                                       tcx.item_path_str(def_id)),
                     };
                     span_note!(
                         &mut err,
@@ -439,7 +442,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() {
@@ -494,7 +497,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 2e2cb2765d9..edee7300868 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 46b3f503b6e..e4ea9bb407d 100644
--- a/src/librustc_typeck/check/method/suggest.rs
+++ b/src/librustc_typeck/check/method/suggest.rs
@@ -164,30 +164,34 @@ 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 +359,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 90a9d9bffe7..f4fea5542b3 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,
@@ -762,6 +764,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(..) => {
+        check_union(ccx, it.id, it.span);
+      }
       hir::ItemTy(_, ref generics) => {
         let pty_ty = ccx.tcx.node_id_to_type(it.id);
         check_bounds_are_used(ccx, generics, pty_ty);
@@ -1171,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
@@ -1274,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> {
@@ -2942,18 +2947,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 +2994,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);
@@ -3098,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
@@ -3124,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::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")
         };
 
@@ -3164,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);
                 }
             }
 
@@ -3173,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"},
@@ -3187,7 +3202,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                       .join(", "),
                       adt_ty);
         }
-
     }
 
     fn check_struct_fields_on_error(&self,
@@ -3217,15 +3231,14 @@ 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) => {
-                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
@@ -4118,6 +4131,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) |
diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs
index 435442bd30a..7cc3be0a890 100644
--- a/src/librustc_typeck/check/wfcheck.rs
+++ b/src/librustc_typeck/check/wfcheck.rs
@@ -136,14 +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(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)
                 });
 
@@ -216,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<F>(&mut self, item: &hir::Item, mut lookup_fields: F) where
-        F: for<'fcx, 'tcx> FnMut(&FnCtxt<'fcx, 'gcx, 'tcx>)
-                                 -> Vec<AdtVariant<'tcx>>
+    fn check_type_defn<F>(&mut self, item: &hir::Item, all_sized: bool, mut lookup_fields: F)
+        where F: for<'fcx, 'tcx> FnMut(&FnCtxt<'fcx, 'gcx, 'tcx>) -> Vec<AdtVariant<'tcx>>
     {
         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/librustc_typeck/coherence/mod.rs b/src/librustc_typeck/coherence/mod.rs
index f743ef21875..fba145efa95 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<DefId> {
         match ty.sty {
             TyEnum(def, _) |
-            TyStruct(def, _) => {
+            TyStruct(def, _) |
+            TyUnion(def, _) => {
                 Some(def.did)
             }
 
@@ -241,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 bcce64cb110..4c38475335c 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 f60fb9583a6..c4d925372f1 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 2b62e513ab2..31f28b3803d 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);
@@ -1069,6 +1070,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<ty::Disr> {
         debug!("disr expr, checking {}", pprust::expr_to_string(e));
@@ -1439,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
                     }
@@ -1576,6 +1588,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(..) |
@@ -1637,7 +1654,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 2cf84b5745a..24eb29f45a5 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
@@ -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!(),
@@ -344,7 +345,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/librustc_typeck/variance/terms.rs b/src/librustc_typeck/variance/terms.rs
index c0b53787177..1238f7cbcb3 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/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 20d4c417655..b7e371e23f3 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).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 e4b6a30d5bc..f8ec5a55e7d 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<bool> {
         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<String>),
     ImportItem(Import),
     StructItem(Struct),
+    UnionItem(Union),
     EnumItem(Enum),
     FunctionItem(Function),
     ModuleItem(Module),
@@ -414,6 +416,7 @@ impl Clean<Item> 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,
@@ -1802,10 +1806,12 @@ impl<'tcx> Clean<Type> for ty::Ty<'tcx> {
                 abi: fty.abi,
             }),
             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);
@@ -1928,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<Item>,
+    pub fields_stripped: bool,
+}
+
 impl Clean<Item> for doctree::Struct {
     fn clean(&self, cx: &DocContext) -> Item {
         Item {
@@ -1948,6 +1962,26 @@ impl Clean<Item> for doctree::Struct {
     }
 }
 
+impl Clean<Item> 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.
@@ -2747,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 04d176c36c8..cc62fcfa0aa 100644
--- a/src/librustdoc/doctree.rs
+++ b/src/librustdoc/doctree.rs
@@ -30,6 +30,7 @@ pub struct Module {
     pub extern_crates: Vec<ExternCrate>,
     pub imports: Vec<Import>,
     pub structs: Vec<Struct>,
+    pub unions: Vec<Union>,
     pub enums: Vec<Enum>,
     pub fns: Vec<Function>,
     pub mods: Vec<Module>,
@@ -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<attr::Stability>,
+    pub depr: Option<attr::Deprecation>,
+    pub id: NodeId,
+    pub struct_type: StructType,
+    pub name: Name,
+    pub generics: hir::Generics,
+    pub attrs: hir::HirVec<ast::Attribute>,
+    pub fields: hir::HirVec<hir::StructField>,
+    pub whence: Span,
+}
+
 pub struct Enum {
     pub vis: hir::Visibility,
     pub stab: Option<attr::Stability>,
diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs
index 5595c749256..8d6ab221c4f 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 be192179284..b93dc17dbdd 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<clean::TypeKind> 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",
@@ -129,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 6d523ff3815..6f66ce88df7 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, "<pre class='rust union'>")?;
+    render_attributes(w, it)?;
+    render_union(w,
+                 it,
+                 Some(&s.generics),
+                 &s.fields,
+                 "",
+                 true)?;
+    write!(w, "</pre>")?;
+
+    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, "<h2 class='fields'>Fields</h2>")?;
+        for (field, ty) in fields {
+            write!(w, "<span id='{shortty}.{name}' class='{shortty}'><code>{name}: {ty}</code>
+                       </span><span class='stab {stab}'></span>",
+                   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, "<pre class='rust enum'>")?;
@@ -2514,6 +2555,38 @@ 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)?;
+        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 de7e4d2483b..9bb7246e7a9 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 b8e40790646..c60e2282496 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 6af36e24d81..16a6e994b5a 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,6 +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(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 f6d89f7c1dc..3af030706b7 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/libsyntax/ast.rs b/src/libsyntax/ast.rs
index fcb99444957..4394fb0e143 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<A, B> { 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<T> { .. }`
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 683d5277359..287d33cc3e5 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 possibly buggy");
+            }
+
             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 92ec0fdb3de..ec9dc1bae5a 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<StructField>> {
         let mut fields = Vec::new();
         if self.eat(&token::OpenDelim(token::Brace)) {
@@ -5938,6 +5957,20 @@ impl<'a> Parser<'a> {
                                     maybe_append(attrs, extra_attrs));
             return Ok(Some(item));
         }
+        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,
+                                    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)
     }
 
diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs
index 5c636d43a71..b37d5332983 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/rt/rust_test_helpers.c b/src/rt/rust_test_helpers.c
index d2ebdcca80c..7a04d377608 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/attr-usage-repr.rs b/src/test/compile-fail/attr-usage-repr.rs
index 9bad6a8389a..b07d3e2f906 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/borrowck/borrowck-union-borrow-nested.rs b/src/test/compile-fail/borrowck/borrowck-union-borrow-nested.rs
new file mode 100644
index 00000000000..19975d79b60
--- /dev/null
+++ b/src/test/compile-fail/borrowck/borrowck-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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/borrowck/borrowck-union-borrow.rs b/src/test/compile-fail/borrowck/borrowck-union-borrow.rs
new file mode 100644
index 00000000000..e8989a3c2d4
--- /dev/null
+++ b/src/test/compile-fail/borrowck/borrowck-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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/borrowck/borrowck-union-move-assign.rs b/src/test/compile-fail/borrowck/borrowck-union-move-assign.rs
new file mode 100644
index 00000000000..d4d7bc6b0f7
--- /dev/null
+++ b/src/test/compile-fail/borrowck/borrowck-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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/borrowck/borrowck-union-move.rs b/src/test/compile-fail/borrowck/borrowck-union-move.rs
new file mode 100644
index 00000000000..5320244cf43
--- /dev/null
+++ b/src/test/compile-fail/borrowck/borrowck-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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/borrowck/borrowck-union-uninitialized.rs b/src/test/compile-fail/borrowck/borrowck-union-uninitialized.rs
new file mode 100644
index 00000000000..36e062f8464
--- /dev/null
+++ b/src/test/compile-fail/borrowck/borrowck-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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/compile-fail/deriving-non-type.rs b/src/test/compile-fail/deriving-non-type.rs
index 5b215f3ccd9..84dd22435b8 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/compile-fail/issue-17800.rs b/src/test/compile-fail/issue-17800.rs
index 58d580a5c1a..d5f1614c14d 100644
--- a/src/test/compile-fail/issue-17800.rs
+++ b/src/test/compile-fail/issue-17800.rs
@@ -18,7 +18,7 @@ enum MyOption<T> {
 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 e3ced302809..a8350fe0986 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-31769.rs b/src/test/compile-fail/issue-31769.rs
index 4b5df7ea53c..7f73d9076ec 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/issue-4736.rs b/src/test/compile-fail/issue-4736.rs
index a8a1b1482fc..c93e75042dd 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 480d2dcdddd..c4aff9471b8 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/privacy/union-field-privacy-1.rs b/src/test/compile-fail/privacy/union-field-privacy-1.rs
new file mode 100644
index 00000000000..4924fabafb0
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 00000000000..7151538f412
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/struct-fields-hints-no-dupe.rs b/src/test/compile-fail/struct-fields-hints-no-dupe.rs
index 8df9ffd6cc7..5f1f8ca856f 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 37001f1e60a..4ba1fd2f7bb 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 9244a9d4f9d..5d16573f2f1 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 9c61f618e69..41bd00a518c 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/union-const-eval.rs b/src/test/compile-fail/union/union-const-eval.rs
new file mode 100644
index 00000000000..b2bf173c59c
--- /dev/null
+++ b/src/test/compile-fail/union/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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/union-const-pat.rs b/src/test/compile-fail/union/union-const-pat.rs
new file mode 100644
index 00000000000..3d168980ed2
--- /dev/null
+++ b/src/test/compile-fail/union/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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/union-copy.rs b/src/test/compile-fail/union/union-copy.rs
new file mode 100644
index 00000000000..6e08ae0074d
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/union-derive.rs b/src/test/compile-fail/union/union-derive.rs
new file mode 100644
index 00000000000..0f78e96f640
--- /dev/null
+++ b/src/test/compile-fail/union/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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/compile-fail/union/union-empty.rs b/src/test/compile-fail/union/union-empty.rs
new file mode 100644
index 00000000000..ce5bbf60fee
--- /dev/null
+++ b/src/test/compile-fail/union/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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/union-feature-gate.rs b/src/test/compile-fail/union/union-feature-gate.rs
new file mode 100644
index 00000000000..abfc4d90921
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/union-fields.rs b/src/test/compile-fail/union/union-fields.rs
new file mode 100644
index 00000000000..a1721dda7de
--- /dev/null
+++ b/src/test/compile-fail/union/union-fields.rs
@@ -0,0 +1,35 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 should have exactly one field
+    let U { a } = u; // OK
+    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; //~ 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 00000000000..e6586b0fb7f
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T: Copy> {
+    a: T
+}
+
+fn main() {
+    let u = U { a: Rc::new(0u32) };
+    //~^ ERROR  the trait bound `std::rc::Rc<u32>: std::marker::Copy` is not satisfied
+    let u = U::<Rc<u32>> { a: Default::default() };
+    //~^ ERROR  the trait bound `std::rc::Rc<u32>: std::marker::Copy` is not satisfied
+}
diff --git a/src/test/compile-fail/union/union-nonrepresentable.rs b/src/test/compile-fail/union/union-nonrepresentable.rs
new file mode 100644
index 00000000000..cb4683c2a0e
--- /dev/null
+++ b/src/test/compile-fail/union/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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/union-repr-c.rs b/src/test/compile-fail/union/union-repr-c.rs
new file mode 100644
index 00000000000..d7dfb126c93
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 00000000000..b05e9b6e273
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/union-unsafe.rs b/src/test/compile-fail/union/union-unsafe.rs
new file mode 100644
index 00000000000..97e1ec2cba8
--- /dev/null
+++ b/src/test/compile-fail/union/union-unsafe.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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 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: 12 } = u {} //~ ERROR matching on union field requires unsafe function or block
+    // let U { .. } = u; // OK
+}
diff --git a/src/test/compile-fail/union/union-unsized.rs b/src/test/compile-fail/union/union-unsized.rs
new file mode 100644
index 00000000000..a238eaf0525
--- /dev/null
+++ b/src/test/compile-fail/union/union-unsized.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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
+    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/union-with-drop-fields-lint.rs b/src/test/compile-fail/union/union-with-drop-fields-lint.rs
new file mode 100644
index 00000000000..87a72efbe08
--- /dev/null
+++ b/src/test/compile-fail/union/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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T> {
+    a: T, //~ ERROR union contains a field with possibly non-trivial drop code
+}
+
+union H<T: Copy> {
+    a: T, // OK, `T` is `Copy`, no destructor
+}
+
+fn main() {}
diff --git a/src/test/debuginfo/union-smoke.rs b/src/test/debuginfo/union-smoke.rs
new file mode 100644
index 00000000000..319927c979b
--- /dev/null
+++ b/src/test/debuginfo/union-smoke.rs
@@ -0,0 +1,50 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 = {__0 = 2 '\002', __1 = 2 '\002'}, b = 514}
+// gdb-command:print union_smoke::SU
+// gdb-check:$2 = {a = {__0 = 1 '\001', __1 = 1 '\001'}, b = 257}
+
+// === LLDB TESTS ==================================================================================
+
+// lldb-command:run
+// lldb-command:print u
+// lldb-check:[...]$0 = { a = ('\x02', '\x02') b = 514 }
+// lldb-command:print union_smoke::SU
+// lldb-check:[...]$1 = 257
+
+#![allow(unused)]
+#![feature(omit_gdb_pretty_printer_section)]
+#![omit_gdb_pretty_printer_section]
+#![feature(untagged_unions)]
+
+union U {
+    a: (u8, u8),
+    b: u16,
+}
+
+static mut SU: U = U { a: (1, 1) };
+
+fn main() {
+    let u = U { b: (2 << 8) + 2 };
+    unsafe { SU = U { a: (1, 1) } }
+
+    zzz(); // #break
+}
+
+fn zzz() {()}
diff --git a/src/test/incremental/struct_change_field_name.rs b/src/test/incremental/struct_change_field_name.rs
index ba469c62002..c27294442e7 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`
 }
diff --git a/src/test/run-pass/union/auxiliary/union.rs b/src/test/run-pass/union/auxiliary/union.rs
new file mode 100644
index 00000000000..0231e38a729
--- /dev/null
+++ b/src/test/run-pass/union/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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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,
+    pub b: u16,
+}
diff --git a/src/test/run-pass/union/union-backcomp.rs b/src/test/run-pass/union/union-backcomp.rs
new file mode 100644
index 00000000000..9394b618ddf
--- /dev/null
+++ b/src/test/run-pass/union/union-backcomp.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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+fn union() {}
+
+fn main() {
+    union();
+
+    let union = 10;
+
+    union;
+
+    union as u8;
+
+    union U {
+        a: u8,
+    }
+}
diff --git a/src/test/run-pass/union/union-basic.rs b/src/test/run-pass/union/union-basic.rs
new file mode 100644
index 00000000000..d23af4b41b7
--- /dev/null
+++ b/src/test/run-pass/union/union-basic.rs
@@ -0,0 +1,68 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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;
+use std::mem::{size_of, align_of, zeroed};
+
+union U {
+    a: u8,
+    b: u16
+}
+
+fn local() {
+    assert_eq!(size_of::<U>(), 2);
+    assert_eq!(align_of::<U>(), 2);
+
+    let u = U { a: 10 };
+    unsafe {
+        assert_eq!(u.a, 10);
+        let U { a } = u;
+        assert_eq!(a, 10);
+    }
+
+    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 xcrate() {
+    assert_eq!(size_of::<union::U>(), 2);
+    assert_eq!(align_of::<union::U>(), 2);
+
+    let u = union::U { a: 10 };
+    unsafe {
+        assert_eq!(u.a, 10);
+        let union::U { a } = u;
+        assert_eq!(a, 10);
+    }
+
+    let mut w = union::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() {
+    local();
+    xcrate();
+}
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 00000000000..a9f97620ebd
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/union-const-trans.rs b/src/test/run-pass/union/union-const-trans.rs
new file mode 100644
index 00000000000..bdae1a0eaf8
--- /dev/null
+++ b/src/test/run-pass/union/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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/union-derive.rs b/src/test/run-pass/union/union-derive.rs
new file mode 100644
index 00000000000..b71c23990a4
--- /dev/null
+++ b/src/test/run-pass/union/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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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();
+}
diff --git a/src/test/run-pass/union/union-drop-assign.rs b/src/test/run-pass/union/union-drop-assign.rs
new file mode 100644
index 00000000000..0da68e43f32
--- /dev/null
+++ b/src/test/run-pass/union/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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/union-drop.rs b/src/test/run-pass/union/union-drop.rs
new file mode 100644
index 00000000000..2ca68dc3b6e
--- /dev/null
+++ b/src/test/run-pass/union/union-drop.rs
@@ -0,0 +1,65 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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: 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) {
+        unsafe { CHECK += 1; }
+    }
+}
+
+impl Drop for W {
+    fn drop(&mut self) {
+        unsafe { CHECK += 1; }
+    }
+}
+
+static mut CHECK: u8 = 0;
+
+fn main() {
+    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
+    }
+}
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 00000000000..9293805edbf
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+union MaybeItem<T: Iterator> {
+    elem: T::Item,
+    none: (),
+}
+
+union U<A, B> {
+    a: A,
+    b: B,
+}
+
+unsafe fn union_transmute<A, B>(a: A) -> B {
+    U { a: a }.b
+}
+
+fn main() {
+    unsafe {
+        let u = U::<String, Vec<u8>> { 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<u8> = vec![1, 2, 3];
+        let mut i = v.iter();
+        i.next();
+        let mi = MaybeItem::<std::slice::Iter<_>> { elem: i.next().unwrap() };
+        assert_eq!(*mi.elem, 2);
+    }
+}
diff --git a/src/test/run-pass/union/union-inherent-method.rs b/src/test/run-pass/union/union-inherent-method.rs
new file mode 100644
index 00000000000..adea27bd254
--- /dev/null
+++ b/src/test/run-pass/union/union-inherent-method.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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(untagged_unions)]
+
+union U {
+    a: u8,
+}
+
+impl U {
+    fn method(&self) -> u8 { unsafe { self.a } }
+}
+
+fn main() {
+    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 00000000000..a23fbc3be9e
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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 00000000000..9389a6237bc
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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>(T, U);
+#[repr(C)]
+struct Triple<T>(T, T, T);
+
+#[repr(C)]
+union U<A, B> {
+    a: Pair<A, A>,
+    b: B,
+}
+
+#[repr(C)]
+union W<A, B> {
+    a: A,
+    b: B,
+}
+
+#[cfg(target_endian = "little")]
+unsafe fn check() {
+    let mut u = U::<u8, u16> { b: 0xDE_DE };
+    u.a.0 = 0xBE;
+    assert_eq!(u.b, 0xDE_BE);
+
+    let mut u = U::<u16, u32> { b: 0xDEAD_DEAD };
+    u.a.0 = 0xBEEF;
+    assert_eq!(u.b, 0xDEAD_BEEF);
+
+    let mut u = U::<u32, u64> { b: 0xDEADBEEF_DEADBEEF };
+    u.a.0 = 0xBAADF00D;
+    assert_eq!(u.b, 0xDEADBEEF_BAADF00D);
+
+    let mut w = W::<Pair<Triple<u8>, u8>, u32> { b: 0xDEAD_DEAD };
+    w.a.0 = Triple(0, 0, 0);
+    assert_eq!(w.b, 0xDE00_0000);
+
+    let mut w = W::<Pair<u8, Triple<u8>>, 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::<u8, u16> { b: 0xDE_DE };
+    u.a.0 = 0xBE;
+    assert_eq!(u.b, 0xBE_DE);
+
+    let mut u = U::<u16, u32> { b: 0xDEAD_DEAD };
+    u.a.0 = 0xBEEF;
+    assert_eq!(u.b, 0xBEEF_DEAD);
+
+    let mut u = U::<u32, u64> { b: 0xDEADBEEF_DEADBEEF };
+    u.a.0 = 0xBAADF00D;
+    assert_eq!(u.b, 0xBAADF00D_DEADBEEF);
+
+    let mut w = W::<Pair<Triple<u8>, u8>, u32> { b: 0xDEAD_DEAD };
+    w.a.0 = Triple(0, 0, 0);
+    assert_eq!(w.b, 0x0000_00AD);
+
+    let mut w = W::<Pair<u8, Triple<u8>>, 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/union-packed.rs b/src/test/run-pass/union/union-packed.rs
new file mode 100644
index 00000000000..6a61280823e
--- /dev/null
+++ b/src/test/run-pass/union/union-packed.rs
@@ -0,0 +1,104 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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::<S>(), 6);
+    assert_eq!(size_of_val(&s), 6);
+    assert_eq!(size_of_val(&CS), 6);
+    assert_eq!(align_of::<S>(), 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::<Sp>(), 5);
+    assert_eq!(size_of_val(&sp), 5);
+    assert_eq!(size_of_val(&CSP), 5);
+    assert_eq!(align_of::<Sp>(), 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::<U>(), 4);
+    assert_eq!(size_of_val(&u), 4);
+    assert_eq!(size_of_val(&CU), 4);
+    assert_eq!(align_of::<U>(), 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::<Up>(), 3);
+    assert_eq!(size_of_val(&up), 3);
+    assert_eq!(size_of_val(&CUP), 3);
+    assert_eq!(align_of::<Up>(), 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::<S1>(), 3);
+        assert_eq!(size_of::<U>(), 3);
+        assert_eq!(size_of::<S2>(), 4);
+    }
+}
diff --git a/src/test/run-pass/union/union-pat-refutability.rs b/src/test/run-pass/union/union-pat-refutability.rs
new file mode 100644
index 00000000000..e6144f35f1d
--- /dev/null
+++ b/src/test/run-pass/union/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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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) {
+    unsafe {
+        match w {
+            W { a: 10 } => {
+                panic!();
+            }
+            W { b } => {
+                assert_eq!(b, 11);
+            }
+        }
+    }
+}
+
+fn main() {
+    let v = Value { tag: Tag::I, u: U { i: 1 } };
+    assert_eq!(is_zero(v), false);
+
+    let w = W { a: 11 };
+    refut(w);
+}
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 00000000000..a5a2be0133a
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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/union-transmute.rs b/src/test/run-pass/union/union-transmute.rs
new file mode 100644
index 00000000000..4eb66268ab8
--- /dev/null
+++ b/src/test/run-pass/union/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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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);
+    }
+}
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 00000000000..5a1424830d0
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T> {
+    a: T, // OK
+}
+
+union H<T: Copy> {
+    a: T, // OK
+}
+
+fn main() {}
diff --git a/src/test/rustdoc/union.rs b/src/test/rustdoc/union.rs
new file mode 100644
index 00000000000..0dcc9098ad7
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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,
+}