about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2016-08-18 18:31:47 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2016-09-03 13:39:34 +0300
commitf3b41c18a8dfbcfec4b2a9e8d9e6a921189e3eea (patch)
tree1d6aaf94ca46fd2e08df59767b8b2fb9c799313a /src
parent957971b63abbc816aebc6654dc68cf9ff15837d7 (diff)
downloadrust-f3b41c18a8dfbcfec4b2a9e8d9e6a921189e3eea.tar.gz
rust-f3b41c18a8dfbcfec4b2a9e8d9e6a921189e3eea.zip
Check fields in union patters/expressions
Make parsing of union items backward compatible
Add some tests
Diffstat (limited to 'src')
-rw-r--r--src/librustc_typeck/check/_match.rs49
-rw-r--r--src/librustc_typeck/check/mod.rs31
-rw-r--r--src/libsyntax/parse/parser.rs4
-rw-r--r--src/test/compile-fail/issue-17800.rs2
-rw-r--r--src/test/compile-fail/issue-19922.rs2
-rw-r--r--src/test/compile-fail/issue-4736.rs2
-rw-r--r--src/test/compile-fail/numeric-fields.rs2
-rw-r--r--src/test/compile-fail/struct-fields-hints-no-dupe.rs2
-rw-r--r--src/test/compile-fail/struct-fields-hints.rs2
-rw-r--r--src/test/compile-fail/struct-fields-too-many.rs2
-rw-r--r--src/test/compile-fail/suggest-private-fields.rs8
-rw-r--r--src/test/compile-fail/union-empty.rs15
-rw-r--r--src/test/compile-fail/union-fields.rs34
-rw-r--r--src/test/run-pass/union-backcomp.rs23
-rw-r--r--src/test/run-pass/union-basic.rs8
-rw-r--r--src/test/run-pass/union-drop.rs26
-rw-r--r--src/test/run-pass/union-pat-refutability.rs62
17 files changed, 227 insertions, 47 deletions
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<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,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<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-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 <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-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 <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 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 <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 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::<U>(), 1);
     assert_eq!(size_of::<U64>(), 8);
@@ -32,6 +38,8 @@ fn main() {
     assert_eq!(align_of::<U>(), 1);
     assert_eq!(align_of::<U64>(), align_of::<u64>());
     assert_eq!(align_of::<W>(), align_of::<u64>());
+    assert_eq!(size_of::<Y>(), 4);
+    assert_eq!(align_of::<Y>(), 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 <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)]
+
+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 <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) {
+    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);
+}