diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 5fdfa19190b..5c19fa2a66c 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -11,7 +11,6 @@ use hir::def::Def; use rustc::infer::{self, InferOk, TypeOrigin}; use hir::pat_util::EnumerateAndAdjustIterator; -use rustc::ty::subst::Substs; use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference, VariantKind}; use check::{FnCtxt, Expectation}; use lint; @@ -509,11 +508,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.demand_eqtype(pat.span, expected, pat_ty); // Type check subpatterns. - let substs = match pat_ty.sty { - ty::TyStruct(_, substs) | ty::TyUnion(_, substs) | ty::TyEnum(_, substs) => substs, - _ => span_bug!(pat.span, "struct variant is not an ADT") - }; - self.check_struct_pat_fields(pat.span, fields, variant, substs, etc); + self.check_struct_pat_fields(pat_ty, pat.span, variant, fields, etc); } fn check_pat_path(&self, @@ -658,19 +653,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } - /// `path` is the AST path item naming the type of this struct. - /// `fields` is the field patterns of the struct pattern. - /// `struct_fields` describes the type of each field of the struct. - /// `struct_id` is the ID of the struct. - /// `etc` is true if the pattern said '...' and false otherwise. - pub fn check_struct_pat_fields(&self, - span: Span, - fields: &'gcx [Spanned], - variant: ty::VariantDef<'tcx>, - substs: &Substs<'tcx>, - etc: bool) { + fn check_struct_pat_fields(&self, + adt_ty: Ty<'tcx>, + span: Span, + variant: ty::VariantDef<'tcx>, + fields: &'gcx [Spanned], + etc: bool) { let tcx = self.tcx; + let (substs, kind_name) = match adt_ty.sty { + ty::TyEnum(_, substs) => (substs, "variant"), + ty::TyStruct(_, substs) => (substs, "struct"), + ty::TyUnion(_, substs) => (substs, "union"), + _ => span_bug!(span, "struct pattern is not an ADT") + }; + // Index the struct fields' types. let field_map = variant.fields .iter() @@ -700,11 +697,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .map(|f| self.field_ty(span, f, substs)) .unwrap_or_else(|| { struct_span_err!(tcx.sess, span, E0026, - "struct `{}` does not have a field named `{}`", + "{} `{}` does not have a field named `{}`", + kind_name, tcx.item_path_str(variant.did), field.name) .span_label(span, - &format!("struct `{}` does not have field `{}`", + &format!("{} `{}` does not have field `{}`", + kind_name, tcx.item_path_str(variant.did), field.name)) .emit(); @@ -717,8 +716,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.check_pat(&field.pat, field_ty); } - // Report an error if not all the fields were specified. - if !etc { + // Report an error if incorrect number of the fields were specified. + if kind_name == "union" { + if fields.len() > 1 { + tcx.sess.span_err(span, "union patterns can have at most one field"); + } + if fields.is_empty() && !etc { + tcx.sess.span_err(span, "union patterns without `..` \ + should have at least one field"); + } + } else if !etc { for field in variant.fields .iter() .filter(|field| !used_fields.contains_key(&field.name)) { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index e2954cecc9c..f8ee9efee7a 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3109,17 +3109,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ty: Ty<'tcx>, variant: ty::VariantDef<'tcx>, field: &hir::Field, - skip_fields: &[hir::Field]) { + skip_fields: &[hir::Field], + kind_name: &str) { let mut err = self.type_error_struct_with_diag( field.name.span, |actual| if let ty::TyEnum(..) = ty.sty { struct_span_err!(self.tcx.sess, field.name.span, E0559, - "struct variant `{}::{}` has no field named `{}`", - actual, variant.name.as_str(), field.name.node) + "{} `{}::{}` has no field named `{}`", + kind_name, actual, variant.name.as_str(), field.name.node) } else { struct_span_err!(self.tcx.sess, field.name.span, E0560, - "structure `{}` has no field named `{}`", - actual, field.name.node) + "{} `{}` has no field named `{}`", + kind_name, actual, field.name.node) }, ty); // prevent all specified fields from being suggested @@ -3135,8 +3136,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ast_fields: &'gcx [hir::Field], check_completeness: bool) { let tcx = self.tcx; - let substs = match adt_ty.sty { - ty::TyStruct(_, substs) | ty::TyUnion(_, substs) | ty::TyEnum(_, substs) => substs, + let (substs, kind_name) = match adt_ty.sty { + ty::TyEnum(_, substs) => (substs, "variant"), + ty::TyStruct(_, substs) => (substs, "struct"), + ty::TyUnion(_, substs) => (substs, "union"), _ => span_bug!(span, "non-ADT passed to check_expr_struct_fields") }; @@ -3175,7 +3178,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { err.emit(); } else { - self.report_unknown_field(adt_ty, variant, field, ast_fields); + self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name); } } @@ -3184,11 +3187,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.check_expr_coercable_to_type(&field.expr, expected_field_type); } - // Make sure the programmer specified all the fields. - if check_completeness && - !error_happened && - !remaining_fields.is_empty() - { + // Make sure the programmer specified correct number of fields. + if kind_name == "union" { + if ast_fields.len() != 1 { + tcx.sess.span_err(span, "union expressions should have exactly one field"); + } + } else if check_completeness && !error_happened && !remaining_fields.is_empty() { span_err!(tcx.sess, span, E0063, "missing field{} {} in initializer of `{}`", if remaining_fields.len() == 1 {""} else {"s"}, @@ -3198,7 +3202,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { .join(", "), adt_ty); } - } fn check_struct_fields_on_error(&self, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 290a59cf1e5..ec9dc1bae5a 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -5957,8 +5957,10 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } - if self.eat_keyword(keywords::Union) { + if self.check_keyword(keywords::Union) && + self.look_ahead(1, |t| t.is_ident() && !t.is_any_keyword()) { // UNION ITEM + self.bump(); let (ident, item_, extra_attrs) = self.parse_item_union()?; let last_span = self.last_span; let item = self.mk_item(lo, diff --git a/src/test/compile-fail/issue-17800.rs b/src/test/compile-fail/issue-17800.rs index 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 { 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-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/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-empty.rs b/src/test/compile-fail/union-empty.rs new file mode 100644 index 00000000000..ce5bbf60fee --- /dev/null +++ b/src/test/compile-fail/union-empty.rs @@ -0,0 +1,15 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(untagged_unions)] + +union U {} //~ ERROR unions cannot have zero fields + +fn main() {} diff --git a/src/test/compile-fail/union-fields.rs b/src/test/compile-fail/union-fields.rs new file mode 100644 index 00000000000..2bd1b8a7b32 --- /dev/null +++ b/src/test/compile-fail/union-fields.rs @@ -0,0 +1,34 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(untagged_unions)] + +union U { + a: u8, + b: u16, +} + +fn main() { + let u = U {}; //~ ERROR union expressions should have exactly one field + let u = U { a: 0 }; // OK + let u = U { a: 0, b: 1 }; //~ ERROR union expressions should have exactly one field + let u = U { a: 0, b: 1, c: 2 }; //~ ERROR union expressions should have exactly one field + //~^ ERROR union `U` has no field named `c` + let u = U { ..u }; //~ ERROR union expressions should have exactly one field + //~^ ERROR functional record update syntax requires a struct + + let U {} = u; //~ ERROR union patterns without `..` should have at least one field + let U { a } = u; // OK + let U { a, b } = u; //~ ERROR union patterns can have at most one field + let U { a, b, c } = u; //~ ERROR union patterns can have at most one field + //~^ ERROR union `U` does not have a field named `c` + let U { .. } = u; // OK + let U { a, .. } = u; // OK +} diff --git a/src/test/run-pass/union-backcomp.rs b/src/test/run-pass/union-backcomp.rs new file mode 100644 index 00000000000..c1210dd6212 --- /dev/null +++ b/src/test/run-pass/union-backcomp.rs @@ -0,0 +1,23 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(untagged_unions)] + +fn main() { + let union = 10; + + union; + + union as u8; + + union U { + a: u8, + } +} diff --git a/src/test/run-pass/union-basic.rs b/src/test/run-pass/union-basic.rs index 474c8b4b181..dee86b232b4 100644 --- a/src/test/run-pass/union-basic.rs +++ b/src/test/run-pass/union-basic.rs @@ -25,6 +25,12 @@ union W { b: u64, } +#[repr(C)] +union Y { + f1: u16, + f2: [u8; 4], +} + fn main() { assert_eq!(size_of::(), 1); assert_eq!(size_of::(), 8); @@ -32,6 +38,8 @@ fn main() { assert_eq!(align_of::(), 1); assert_eq!(align_of::(), align_of::()); assert_eq!(align_of::(), align_of::()); + assert_eq!(size_of::(), 4); + assert_eq!(align_of::(), 2); let u = U { a: 10 }; assert_eq!(u.a, 10); diff --git a/src/test/run-pass/union-drop.rs b/src/test/run-pass/union-drop.rs new file mode 100644 index 00000000000..467403ff2e1 --- /dev/null +++ b/src/test/run-pass/union-drop.rs @@ -0,0 +1,26 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Drop works for union itself. + +#![feature(untagged_unions)] + +union U { + a: u8 +} + +impl Drop for U { + fn drop(&mut self) {} +} + +fn main() { + // 'unions are not fully implemented', src/librustc_trans/glue.rs:567 + // let u = U { a: 1 }; +} diff --git a/src/test/run-pass/union-pat-refutability.rs b/src/test/run-pass/union-pat-refutability.rs new file mode 100644 index 00000000000..6b39eed7ac9 --- /dev/null +++ b/src/test/run-pass/union-pat-refutability.rs @@ -0,0 +1,62 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(untagged_unions)] + +#[repr(u32)] +enum Tag { I, F } + +#[repr(C)] +union U { + i: i32, + f: f32, +} + +#[repr(C)] +struct Value { + tag: Tag, + u: U, +} + +fn is_zero(v: Value) -> bool { + unsafe { + match v { + Value { tag: Tag::I, u: U { i: 0 } } => true, + Value { tag: Tag::F, u: U { f: 0.0 } } => true, + _ => false, + } + } +} + +union W { + a: u8, + b: u8, +} + +fn refut(w: W) { + match w { + W { a: 10 } => { + panic!(); + } + W { b } => { + assert_eq!(b, 11); + } + } +} + +fn main() { + // ICE + // let v = Value { tag: Tag::I, u: U { i: 1 } }; + // assert_eq!(is_zero(v), false); + + // ICE + // let w = W { a: 11 }; + // refut(w); +}