diff options
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, +} |
