Fix various bugs around empty structs and patterns

This commit is contained in:
Vadim Petrochenkov 2015-10-26 21:09:12 +03:00
parent 3e48b0e380
commit 0f8519c341
14 changed files with 143 additions and 79 deletions

View File

@ -178,7 +178,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
}
ty::TyEnum(adt, substs) => {
match cx.tcx.def_map.borrow()[&self.id].full_def() {
def::DefVariant(enum_id, variant_id, true) => {
def::DefVariant(enum_id, variant_id, _) => {
debug_assert!(adt.did == enum_id);
let index = adt.variant_index_with_id(variant_id);
let field_refs = field_refs(&adt.variants[index], fields);

View File

@ -21,6 +21,7 @@ use check::{check_expr_with_lvalue_pref};
use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type};
use require_same_types;
use util::nodemap::FnvHashMap;
use session::Session;
use std::cmp;
use std::collections::hash_map::Entry::{Occupied, Vacant};
@ -136,6 +137,12 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
}
hir::PatEnum(..) | hir::PatIdent(..)
if pat_is_resolved_const(&tcx.def_map.borrow(), pat) => {
if let hir::PatEnum(ref path, ref subpats) = pat.node {
if !(subpats.is_some() && subpats.as_ref().unwrap().is_empty()) {
bad_struct_kind_err(tcx.sess, pat.span, path);
return;
}
}
let const_did = tcx.def_map.borrow().get(&pat.id).unwrap().def_id();
let const_scheme = tcx.lookup_item_type(const_did);
assert!(const_scheme.generics.is_empty());
@ -192,11 +199,12 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
}
hir::PatIdent(_, ref path, _) => {
let path = hir_util::ident_to_path(path.span, path.node);
check_pat_enum(pcx, pat, &path, Some(&[]), expected);
check_pat_enum(pcx, pat, &path, Some(&[]), expected, false);
}
hir::PatEnum(ref path, ref subpats) => {
let subpats = subpats.as_ref().map(|v| &v[..]);
check_pat_enum(pcx, pat, path, subpats, expected);
let is_tuple_struct_pat = !(subpats.is_some() && subpats.unwrap().is_empty());
check_pat_enum(pcx, pat, path, subpats, expected, is_tuple_struct_pat);
}
hir::PatQPath(ref qself, ref path) => {
let self_ty = fcx.to_ty(&qself.ty);
@ -572,11 +580,19 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx hir::Pat,
fcx.write_substs(pat.id, ty::ItemSubsts { substs: item_substs.clone() });
}
// This function exists due to the warning "diagnostic code E0164 already used"
fn bad_struct_kind_err(sess: &Session, span: Span, path: &hir::Path) {
let name = pprust::path_to_string(path);
span_err!(sess, span, E0164,
"`{}` does not name a tuple variant or a tuple struct", name);
}
pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
pat: &hir::Pat,
path: &hir::Path,
subpats: Option<&'tcx [P<hir::Pat>]>,
expected: Ty<'tcx>)
expected: Ty<'tcx>,
is_tuple_struct_pat: bool)
{
// Typecheck the path.
let fcx = pcx.fcx;
@ -618,25 +634,43 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
path_scheme, &ctor_predicates,
opt_ty, def, pat.span, pat.id);
let report_bad_struct_kind = || {
bad_struct_kind_err(tcx.sess, pat.span, path);
fcx.write_error(pat.id);
if let Some(subpats) = subpats {
for pat in subpats {
check_pat(pcx, &**pat, tcx.types.err);
}
}
};
// If we didn't have a fully resolved path to start with, we had an
// associated const, and we should quit now, since the rest of this
// function uses checks specific to structs and enums.
if path_res.depth != 0 {
let pat_ty = fcx.node_ty(pat.id);
demand::suptype(fcx, pat.span, expected, pat_ty);
if is_tuple_struct_pat {
report_bad_struct_kind();
} else {
let pat_ty = fcx.node_ty(pat.id);
demand::suptype(fcx, pat.span, expected, pat_ty);
}
return;
}
let pat_ty = fcx.node_ty(pat.id);
demand::eqtype(fcx, pat.span, expected, pat_ty);
let real_path_ty = fcx.node_ty(pat.id);
let (arg_tys, kind_name): (Vec<_>, &'static str) = match real_path_ty.sty {
ty::TyEnum(enum_def, expected_substs)
if def == def::DefVariant(enum_def.did, def.def_id(), false) =>
{
let variant = enum_def.variant_of_def(def);
if is_tuple_struct_pat && variant.kind() != ty::VariantKind::Tuple {
report_bad_struct_kind();
return;
}
(variant.fields
.iter()
.map(|f| fcx.instantiate_type_scheme(pat.span,
@ -646,26 +680,21 @@ pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
"variant")
}
ty::TyStruct(struct_def, expected_substs) => {
(struct_def.struct_variant()
.fields
.iter()
.map(|f| fcx.instantiate_type_scheme(pat.span,
expected_substs,
&f.unsubst_ty()))
.collect(),
let variant = struct_def.struct_variant();
if is_tuple_struct_pat && variant.kind() != ty::VariantKind::Tuple {
report_bad_struct_kind();
return;
}
(variant.fields
.iter()
.map(|f| fcx.instantiate_type_scheme(pat.span,
expected_substs,
&f.unsubst_ty()))
.collect(),
"struct")
}
_ => {
let name = pprust::path_to_string(path);
span_err!(tcx.sess, pat.span, E0164,
"`{}` does not name a non-struct variant or a tuple struct", name);
fcx.write_error(pat.id);
if let Some(subpats) = subpats {
for pat in subpats {
check_pat(pcx, &**pat, tcx.types.err);
}
}
report_bad_struct_kind();
return;
}
};

View File

@ -1446,7 +1446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
-> Option<(ty::AdtDef<'tcx>, ty::VariantDef<'tcx>)>
{
let (adt, variant) = match def {
def::DefVariant(enum_id, variant_id, true) => {
def::DefVariant(enum_id, variant_id, _) => {
let adt = self.tcx().lookup_adt_def(enum_id);
(adt, adt.variant_with_id(variant_id))
}

View File

@ -20,8 +20,7 @@ enum E {
fn main() {
let e2: Empty2 = Empty2 {}; //~ ERROR empty structs and enum variants with braces are unstable
let e2: Empty2 = Empty2;
// Issue #28692
// let e5: E = E::Empty5 {}; // ERROR empty structs and enum variants with braces are unstable
let e5: E = E::Empty5 {}; //~ ERROR empty structs and enum variants with braces are unstable
let e5: E = E::Empty5;
match e2 {
@ -33,17 +32,15 @@ fn main() {
match e2 {
Empty2 { .. } => {} //~ ERROR empty structs and enum variants with braces are unstable
}
// Issue #28692
// match e5 {
// E::Empty5 {} => {} // ERROR empty structs and enum variants with braces are unstable
// }
match e5 {
E::Empty5 {} => {} //~ ERROR empty structs and enum variants with braces are unstable
}
match e5 {
E::Empty5 => {}
}
// Issue #28692
// match e5 {
// E::Empty5 { .. } => {} // ERROR empty structs and enum variants with braces are unstable
// }
match e5 {
E::Empty5 { .. } => {} //~ ERROR empty structs and enum variants with braces are unstable
}
let e22 = Empty2 { ..e2 }; //~ ERROR empty structs and enum variants with braces are unstable
}

View File

@ -10,7 +10,6 @@
// Can't use empty braced struct as constant pattern
#![deny(warnings)]
#![feature(braced_empty_structs)]
struct Empty1 {}
@ -23,11 +22,10 @@ fn main() {
let e1 = Empty1 {};
let e2 = E::Empty2 {};
// Issue #28692
// match e1 {
// Empty1 => () // ERROR incorrect error
// }
match e1 {
Empty1 => () // Not an error, `Empty1` is interpreted as a new binding
}
match e2 {
E::Empty2 => () //~ ERROR `E::Empty2` does not name a non-struct variant or a tuple struct
E::Empty2 => () //~ ERROR `E::Empty2` does not name a tuple variant or a tuple struct
}
}

View File

@ -14,13 +14,8 @@
struct Empty1 {}
enum E {
Empty2 {}
}
fn main() {
let e1 = Empty1 {};
let e2 = E::Empty2 {};
// Rejected by parser as yet
// match e1 {
@ -29,11 +24,4 @@ fn main() {
match e1 {
Empty1(..) => () //~ ERROR unresolved enum variant, struct or const `Empty1`
}
// Issue #28692
// match e2 {
// E::Empty2() => () // ERROR unresolved enum variant, struct or const `Empty2`
// }
// match e2 {
// E::Empty2(..) => () // ERROR unresolved enum variant, struct or const `Empty2`
// }
}

View File

@ -0,0 +1,29 @@
// Copyright 2015 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.
// Can't use empty braced struct as enum pattern
#![feature(braced_empty_structs)]
enum E {
Empty2 {}
}
fn main() {
let e2 = E::Empty2 {};
// Rejected by parser as yet
// match e2 {
// E::Empty2() => () // ERROR `E::Empty2` does not name a tuple variant or a tuple struct
// }
match e2 {
E::Empty2(..) => () //~ ERROR `E::Empty2` does not name a tuple variant or a tuple struct
}
}

View File

@ -12,8 +12,6 @@
#![feature(braced_empty_structs)]
FIXME //~ ERROR expected item, found `FIXME`
struct Empty1;
enum E {
@ -24,17 +22,18 @@ fn main() {
let e1 = Empty1;
let e2 = E::Empty2;
// Issue #28692
// Rejected by parser as yet
// match e1 {
// Empty1() => () // ERROR variable `Empty1` should have a snake case name
// }
// match e1 {
// Empty1(..) => () // ERROR variable `Empty1` should have a snake case name
// Empty1() => () // ERROR `Empty1` does not name a tuple variant or a tuple struct
// }
match e1 {
Empty1(..) => () //~ ERROR `Empty1` does not name a tuple variant or a tuple struct
}
// Rejected by parser as yet
// match e2 {
// E::Empty2() => () // ERROR variable `Empty2` should have a snake case name
// }
// match e2 {
// E::Empty2(..) => () // ERROR variable `Empty2` should have a snake case name
// E::Empty2() => () // ERROR `E::Empty2` does not name a tuple variant or a tuple struct
// }
match e2 {
E::Empty2(..) => () //~ ERROR `E::Empty2` does not name a tuple variant or a tuple struct
}
}

View File

@ -18,6 +18,6 @@ fn main() {
let f = FooB { x: 3, y: 4 };
match f {
FooB(a, b) => println!("{} {}", a, b),
//~^ ERROR `FooB` does not name a non-struct variant or a tuple struct
//~^ ERROR `FooB` does not name a tuple variant or a tuple struct
}
}

View File

@ -26,7 +26,7 @@ fn main() {
let Bar { .. } = x; //~ ERROR empty structs and enum variants with braces are unstable
match Enum::Bar {
Enum::Bar { .. } //~ ERROR `Enum::Bar` does not name a struct
Enum::Bar { .. } //~ ERROR empty structs and enum variants with braces are unstable
=> {}
Enum::Foo { .. } //~ ERROR `Enum::Foo` does not name a struct
=> {}

View File

@ -0,0 +1,26 @@
// Copyright 2015 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.
// Can't use constants as tuple struct patterns
#![feature(associated_consts)]
const C1: i32 = 0;
struct S;
impl S {
const C2: i32 = 0;
}
fn main() {
if let C1(..) = 0 {} //~ ERROR `C1` does not name a tuple variant or a tuple struct
if let S::C2(..) = 0 {} //~ ERROR `S::C2` does not name a tuple variant or a tuple struct
}

View File

@ -20,7 +20,7 @@ fn main() {
color::rgb(_, _, _) => { }
color::cmyk(_, _, _, _) => { }
color::no_color(_) => { }
//~^ ERROR this pattern has 1 field, but the corresponding variant has no fields
//~^ ERROR `color::no_color` does not name a tuple variant or a tuple struct
}
}
}

View File

@ -25,7 +25,7 @@ fn f(_c: char) {}
fn main() {
match A::B(1, 2) {
A::B(_, _, _) => (), //~ ERROR this pattern has 3 fields, but
A::D(_) => (), //~ ERROR this pattern has 1 field, but
A::D(_) => (), //~ ERROR `A::D` does not name a tuple variant or a tuple struct
_ => ()
}
match 'c' {

View File

@ -30,7 +30,7 @@ fn main() {
let e3: Empty3 = Empty3 {};
let e3: Empty3 = Empty3;
let e4: E = E::Empty4 {};
// let e5: E = E::Empty5 {}; // Issue #28692
let e5: E = E::Empty5 {};
let e5: E = E::Empty5;
match e1 {
@ -46,11 +46,10 @@ fn main() {
E::Empty4 {} => {}
_ => {}
}
// Issue #28692
// match e5 {
// E::Empty5 {} => {}
// _ => {}
// }
match e5 {
E::Empty5 {} => {}
_ => {}
}
match e1 {
Empty1 { .. } => {}
@ -65,11 +64,10 @@ fn main() {
E::Empty4 { .. } => {}
_ => {}
}
// Issue #28692
// match e5 {
// E::Empty5 { .. } => {}
// _ => {}
// }
match e5 {
E::Empty5 { .. } => {}
_ => {}
}
match e2 {
Empty2 => {}