Skip to content

Commit f3b41c1

Browse files
committed
Check fields in union patters/expressions
Make parsing of union items backward compatible Add some tests
1 parent 957971b commit f3b41c1

17 files changed

+227
-47
lines changed

src/librustc_typeck/check/_match.rs

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use hir::def::Def;
1212
use rustc::infer::{self, InferOk, TypeOrigin};
1313
use hir::pat_util::EnumerateAndAdjustIterator;
14-
use rustc::ty::subst::Substs;
1514
use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference, VariantKind};
1615
use check::{FnCtxt, Expectation};
1716
use lint;
@@ -509,11 +508,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
509508
self.demand_eqtype(pat.span, expected, pat_ty);
510509

511510
// Type check subpatterns.
512-
let substs = match pat_ty.sty {
513-
ty::TyStruct(_, substs) | ty::TyUnion(_, substs) | ty::TyEnum(_, substs) => substs,
514-
_ => span_bug!(pat.span, "struct variant is not an ADT")
515-
};
516-
self.check_struct_pat_fields(pat.span, fields, variant, substs, etc);
511+
self.check_struct_pat_fields(pat_ty, pat.span, variant, fields, etc);
517512
}
518513

519514
fn check_pat_path(&self,
@@ -658,19 +653,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
658653
}
659654
}
660655

661-
/// `path` is the AST path item naming the type of this struct.
662-
/// `fields` is the field patterns of the struct pattern.
663-
/// `struct_fields` describes the type of each field of the struct.
664-
/// `struct_id` is the ID of the struct.
665-
/// `etc` is true if the pattern said '...' and false otherwise.
666-
pub fn check_struct_pat_fields(&self,
667-
span: Span,
668-
fields: &'gcx [Spanned<hir::FieldPat>],
669-
variant: ty::VariantDef<'tcx>,
670-
substs: &Substs<'tcx>,
671-
etc: bool) {
656+
fn check_struct_pat_fields(&self,
657+
adt_ty: Ty<'tcx>,
658+
span: Span,
659+
variant: ty::VariantDef<'tcx>,
660+
fields: &'gcx [Spanned<hir::FieldPat>],
661+
etc: bool) {
672662
let tcx = self.tcx;
673663

664+
let (substs, kind_name) = match adt_ty.sty {
665+
ty::TyEnum(_, substs) => (substs, "variant"),
666+
ty::TyStruct(_, substs) => (substs, "struct"),
667+
ty::TyUnion(_, substs) => (substs, "union"),
668+
_ => span_bug!(span, "struct pattern is not an ADT")
669+
};
670+
674671
// Index the struct fields' types.
675672
let field_map = variant.fields
676673
.iter()
@@ -700,11 +697,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
700697
.map(|f| self.field_ty(span, f, substs))
701698
.unwrap_or_else(|| {
702699
struct_span_err!(tcx.sess, span, E0026,
703-
"struct `{}` does not have a field named `{}`",
700+
"{} `{}` does not have a field named `{}`",
701+
kind_name,
704702
tcx.item_path_str(variant.did),
705703
field.name)
706704
.span_label(span,
707-
&format!("struct `{}` does not have field `{}`",
705+
&format!("{} `{}` does not have field `{}`",
706+
kind_name,
708707
tcx.item_path_str(variant.did),
709708
field.name))
710709
.emit();
@@ -717,8 +716,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
717716
self.check_pat(&field.pat, field_ty);
718717
}
719718

720-
// Report an error if not all the fields were specified.
721-
if !etc {
719+
// Report an error if incorrect number of the fields were specified.
720+
if kind_name == "union" {
721+
if fields.len() > 1 {
722+
tcx.sess.span_err(span, "union patterns can have at most one field");
723+
}
724+
if fields.is_empty() && !etc {
725+
tcx.sess.span_err(span, "union patterns without `..` \
726+
should have at least one field");
727+
}
728+
} else if !etc {
722729
for field in variant.fields
723730
.iter()
724731
.filter(|field| !used_fields.contains_key(&field.name)) {

src/librustc_typeck/check/mod.rs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3109,17 +3109,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
31093109
ty: Ty<'tcx>,
31103110
variant: ty::VariantDef<'tcx>,
31113111
field: &hir::Field,
3112-
skip_fields: &[hir::Field]) {
3112+
skip_fields: &[hir::Field],
3113+
kind_name: &str) {
31133114
let mut err = self.type_error_struct_with_diag(
31143115
field.name.span,
31153116
|actual| if let ty::TyEnum(..) = ty.sty {
31163117
struct_span_err!(self.tcx.sess, field.name.span, E0559,
3117-
"struct variant `{}::{}` has no field named `{}`",
3118-
actual, variant.name.as_str(), field.name.node)
3118+
"{} `{}::{}` has no field named `{}`",
3119+
kind_name, actual, variant.name.as_str(), field.name.node)
31193120
} else {
31203121
struct_span_err!(self.tcx.sess, field.name.span, E0560,
3121-
"structure `{}` has no field named `{}`",
3122-
actual, field.name.node)
3122+
"{} `{}` has no field named `{}`",
3123+
kind_name, actual, field.name.node)
31233124
},
31243125
ty);
31253126
// prevent all specified fields from being suggested
@@ -3135,8 +3136,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
31353136
ast_fields: &'gcx [hir::Field],
31363137
check_completeness: bool) {
31373138
let tcx = self.tcx;
3138-
let substs = match adt_ty.sty {
3139-
ty::TyStruct(_, substs) | ty::TyUnion(_, substs) | ty::TyEnum(_, substs) => substs,
3139+
let (substs, kind_name) = match adt_ty.sty {
3140+
ty::TyEnum(_, substs) => (substs, "variant"),
3141+
ty::TyStruct(_, substs) => (substs, "struct"),
3142+
ty::TyUnion(_, substs) => (substs, "union"),
31403143
_ => span_bug!(span, "non-ADT passed to check_expr_struct_fields")
31413144
};
31423145

@@ -3175,7 +3178,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
31753178

31763179
err.emit();
31773180
} else {
3178-
self.report_unknown_field(adt_ty, variant, field, ast_fields);
3181+
self.report_unknown_field(adt_ty, variant, field, ast_fields, kind_name);
31793182
}
31803183
}
31813184

@@ -3184,11 +3187,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
31843187
self.check_expr_coercable_to_type(&field.expr, expected_field_type);
31853188
}
31863189

3187-
// Make sure the programmer specified all the fields.
3188-
if check_completeness &&
3189-
!error_happened &&
3190-
!remaining_fields.is_empty()
3191-
{
3190+
// Make sure the programmer specified correct number of fields.
3191+
if kind_name == "union" {
3192+
if ast_fields.len() != 1 {
3193+
tcx.sess.span_err(span, "union expressions should have exactly one field");
3194+
}
3195+
} else if check_completeness && !error_happened && !remaining_fields.is_empty() {
31923196
span_err!(tcx.sess, span, E0063,
31933197
"missing field{} {} in initializer of `{}`",
31943198
if remaining_fields.len() == 1 {""} else {"s"},
@@ -3198,7 +3202,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
31983202
.join(", "),
31993203
adt_ty);
32003204
}
3201-
32023205
}
32033206

32043207
fn check_struct_fields_on_error(&self,

src/libsyntax/parse/parser.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5957,8 +5957,10 @@ impl<'a> Parser<'a> {
59575957
maybe_append(attrs, extra_attrs));
59585958
return Ok(Some(item));
59595959
}
5960-
if self.eat_keyword(keywords::Union) {
5960+
if self.check_keyword(keywords::Union) &&
5961+
self.look_ahead(1, |t| t.is_ident() && !t.is_any_keyword()) {
59615962
// UNION ITEM
5963+
self.bump();
59625964
let (ident, item_, extra_attrs) = self.parse_item_union()?;
59635965
let last_span = self.last_span;
59645966
let item = self.mk_item(lo,

src/test/compile-fail/issue-17800.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ enum MyOption<T> {
1818
fn main() {
1919
match MyOption::MySome(42) {
2020
MyOption::MySome { x: 42 } => (),
21-
//~^ ERROR struct `MyOption::MySome` does not have a field named `x`
21+
//~^ ERROR variant `MyOption::MySome` does not have a field named `x`
2222
//~| ERROR pattern does not mention field `0`
2323
_ => (),
2424
}

src/test/compile-fail/issue-19922.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ enum Homura {
1414

1515
fn main() {
1616
let homura = Homura::Akemi { kaname: () };
17-
//~^ ERROR struct variant `Homura::Akemi` has no field named `kaname`
17+
//~^ ERROR variant `Homura::Akemi` has no field named `kaname`
1818
}

src/test/compile-fail/issue-4736.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@
1313
struct NonCopyable(());
1414

1515
fn main() {
16-
let z = NonCopyable{ p: () }; //~ ERROR structure `NonCopyable` has no field named `p`
16+
let z = NonCopyable{ p: () }; //~ ERROR struct `NonCopyable` has no field named `p`
1717
}

src/test/compile-fail/numeric-fields.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
struct S(u8, u16);
1414

1515
fn main() {
16-
let s = S{0b1: 10, 0: 11}; //~ ERROR structure `S` has no field named `0b1`
16+
let s = S{0b1: 10, 0: 11}; //~ ERROR struct `S` has no field named `0b1`
1717
match s {
1818
S{0: a, 0x1: b, ..} => {} //~ ERROR does not have a field named `0x1`
1919
}

src/test/compile-fail/struct-fields-hints-no-dupe.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ struct A {
1717
fn main() {
1818
let a = A {
1919
foo : 5,
20-
bar : 42,//~ ERROR structure `A` has no field named `bar`
20+
bar : 42,//~ ERROR struct `A` has no field named `bar`
2121
//~^ HELP did you mean `barr`?
2222
car : 9,
2323
};

src/test/compile-fail/struct-fields-hints.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ struct A {
1717
fn main() {
1818
let a = A {
1919
foo : 5,
20-
bar : 42,//~ ERROR structure `A` has no field named `bar`
20+
bar : 42,//~ ERROR struct `A` has no field named `bar`
2121
//~^ HELP did you mean `car`?
2222
};
2323
}

src/test/compile-fail/struct-fields-too-many.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ struct BuildData {
1515
fn main() {
1616
let foo = BuildData {
1717
foo: 0,
18-
bar: 0 //~ ERROR structure `BuildData` has no field named `bar`
18+
bar: 0 //~ ERROR struct `BuildData` has no field named `bar`
1919
};
2020
}

src/test/compile-fail/suggest-private-fields.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ struct A {
2222
fn main () {
2323
// external crate struct
2424
let k = B {
25-
aa: 20, //~ ERROR structure `xc::B` has no field named `aa`
25+
aa: 20, //~ ERROR struct `xc::B` has no field named `aa`
2626
//~^ HELP did you mean `a`?
27-
bb: 20, //~ ERROR structure `xc::B` has no field named `bb`
27+
bb: 20, //~ ERROR struct `xc::B` has no field named `bb`
2828
//~^ HELP did you mean `a`?
2929
};
3030
// local crate struct
3131
let l = A {
32-
aa: 20, //~ ERROR structure `A` has no field named `aa`
32+
aa: 20, //~ ERROR struct `A` has no field named `aa`
3333
//~^ HELP did you mean `a`?
34-
bb: 20, //~ ERROR structure `A` has no field named `bb`
34+
bb: 20, //~ ERROR struct `A` has no field named `bb`
3535
//~^ HELP did you mean `b`?
3636
};
3737
}

src/test/compile-fail/union-empty.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(untagged_unions)]
12+
13+
union U {} //~ ERROR unions cannot have zero fields
14+
15+
fn main() {}

src/test/compile-fail/union-fields.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(untagged_unions)]
12+
13+
union U {
14+
a: u8,
15+
b: u16,
16+
}
17+
18+
fn main() {
19+
let u = U {}; //~ ERROR union expressions should have exactly one field
20+
let u = U { a: 0 }; // OK
21+
let u = U { a: 0, b: 1 }; //~ ERROR union expressions should have exactly one field
22+
let u = U { a: 0, b: 1, c: 2 }; //~ ERROR union expressions should have exactly one field
23+
//~^ ERROR union `U` has no field named `c`
24+
let u = U { ..u }; //~ ERROR union expressions should have exactly one field
25+
//~^ ERROR functional record update syntax requires a struct
26+
27+
let U {} = u; //~ ERROR union patterns without `..` should have at least one field
28+
let U { a } = u; // OK
29+
let U { a, b } = u; //~ ERROR union patterns can have at most one field
30+
let U { a, b, c } = u; //~ ERROR union patterns can have at most one field
31+
//~^ ERROR union `U` does not have a field named `c`
32+
let U { .. } = u; // OK
33+
let U { a, .. } = u; // OK
34+
}

src/test/run-pass/union-backcomp.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(untagged_unions)]
12+
13+
fn main() {
14+
let union = 10;
15+
16+
union;
17+
18+
union as u8;
19+
20+
union U {
21+
a: u8,
22+
}
23+
}

src/test/run-pass/union-basic.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,21 @@ union W {
2525
b: u64,
2626
}
2727

28+
#[repr(C)]
29+
union Y {
30+
f1: u16,
31+
f2: [u8; 4],
32+
}
33+
2834
fn main() {
2935
assert_eq!(size_of::<U>(), 1);
3036
assert_eq!(size_of::<U64>(), 8);
3137
assert_eq!(size_of::<W>(), 8);
3238
assert_eq!(align_of::<U>(), 1);
3339
assert_eq!(align_of::<U64>(), align_of::<u64>());
3440
assert_eq!(align_of::<W>(), align_of::<u64>());
41+
assert_eq!(size_of::<Y>(), 4);
42+
assert_eq!(align_of::<Y>(), 2);
3543

3644
let u = U { a: 10 };
3745
assert_eq!(u.a, 10);

src/test/run-pass/union-drop.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Drop works for union itself.
12+
13+
#![feature(untagged_unions)]
14+
15+
union U {
16+
a: u8
17+
}
18+
19+
impl Drop for U {
20+
fn drop(&mut self) {}
21+
}
22+
23+
fn main() {
24+
// 'unions are not fully implemented', src/librustc_trans/glue.rs:567
25+
// let u = U { a: 1 };
26+
}

0 commit comments

Comments
 (0)