mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-26 14:43:24 +00:00
Check fields in union patters/expressions
Make parsing of union items backward compatible Add some tests
This commit is contained in:
parent
957971b63a
commit
f3b41c18a8
@ -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)) {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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`
|
||||
_ => (),
|
||||
}
|
||||
|
@ -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`
|
||||
}
|
||||
|
@ -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`
|
||||
}
|
||||
|
@ -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`
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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`?
|
||||
};
|
||||
}
|
||||
|
@ -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`
|
||||
};
|
||||
}
|
||||
|
@ -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`?
|
||||
};
|
||||
}
|
||||
|
15
src/test/compile-fail/union-empty.rs
Normal file
15
src/test/compile-fail/union-empty.rs
Normal file
@ -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() {}
|
34
src/test/compile-fail/union-fields.rs
Normal file
34
src/test/compile-fail/union-fields.rs
Normal file
@ -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
|
||||
}
|
23
src/test/run-pass/union-backcomp.rs
Normal file
23
src/test/run-pass/union-backcomp.rs
Normal file
@ -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,
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
26
src/test/run-pass/union-drop.rs
Normal file
26
src/test/run-pass/union-drop.rs
Normal file
@ -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 };
|
||||
}
|
62
src/test/run-pass/union-pat-refutability.rs
Normal file
62
src/test/run-pass/union-pat-refutability.rs
Normal file
@ -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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user