From 0f8519c341a53a4697f839041bc0a14dd6c6e773 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 26 Oct 2015 21:09:12 +0300 Subject: [PATCH] Fix various bugs around empty structs and patterns --- src/librustc_mir/hair/cx/expr.rs | 2 +- src/librustc_typeck/check/_match.rs | 75 +++++++++++++------ src/librustc_typeck/check/mod.rs | 2 +- .../empty-struct-braces-gate-2.rs | 17 ++--- .../compile-fail/empty-struct-braces-pat-1.rs | 10 +-- .../compile-fail/empty-struct-braces-pat-2.rs | 12 --- .../compile-fail/empty-struct-braces-pat-3.rs | 29 +++++++ .../compile-fail/empty-struct-unit-pat.rs | 21 +++--- src/test/compile-fail/issue-19086.rs | 2 +- src/test/compile-fail/issue-27831.rs | 2 +- src/test/compile-fail/issue-28992-empty.rs | 26 +++++++ .../match-pattern-field-mismatch-2.rs | 2 +- .../compile-fail/pattern-error-continue.rs | 2 +- src/test/run-pass/empty-struct-braces.rs | 20 +++-- 14 files changed, 143 insertions(+), 79 deletions(-) create mode 100644 src/test/compile-fail/empty-struct-braces-pat-3.rs create mode 100644 src/test/compile-fail/issue-28992-empty.rs diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index c546a264be1..3380b3a6c14 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -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); diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index aaaca486006..21958387056 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -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]>, - 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; } }; diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a635c1b047d..bed22aa1e93 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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)) } diff --git a/src/test/compile-fail/empty-struct-braces-gate-2.rs b/src/test/compile-fail/empty-struct-braces-gate-2.rs index c1b73bdc96a..b2d44301eed 100644 --- a/src/test/compile-fail/empty-struct-braces-gate-2.rs +++ b/src/test/compile-fail/empty-struct-braces-gate-2.rs @@ -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 } diff --git a/src/test/compile-fail/empty-struct-braces-pat-1.rs b/src/test/compile-fail/empty-struct-braces-pat-1.rs index e095f69ed7d..6a6c3f16c04 100644 --- a/src/test/compile-fail/empty-struct-braces-pat-1.rs +++ b/src/test/compile-fail/empty-struct-braces-pat-1.rs @@ -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 } } diff --git a/src/test/compile-fail/empty-struct-braces-pat-2.rs b/src/test/compile-fail/empty-struct-braces-pat-2.rs index 0e7152ec89a..d98d64b712a 100644 --- a/src/test/compile-fail/empty-struct-braces-pat-2.rs +++ b/src/test/compile-fail/empty-struct-braces-pat-2.rs @@ -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` - // } } diff --git a/src/test/compile-fail/empty-struct-braces-pat-3.rs b/src/test/compile-fail/empty-struct-braces-pat-3.rs new file mode 100644 index 00000000000..9fae203f389 --- /dev/null +++ b/src/test/compile-fail/empty-struct-braces-pat-3.rs @@ -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 or the MIT license +// , 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 + } +} diff --git a/src/test/compile-fail/empty-struct-unit-pat.rs b/src/test/compile-fail/empty-struct-unit-pat.rs index 966a2780f9f..f9af71527a1 100644 --- a/src/test/compile-fail/empty-struct-unit-pat.rs +++ b/src/test/compile-fail/empty-struct-unit-pat.rs @@ -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 + } } diff --git a/src/test/compile-fail/issue-19086.rs b/src/test/compile-fail/issue-19086.rs index 69201859457..56452449d4e 100644 --- a/src/test/compile-fail/issue-19086.rs +++ b/src/test/compile-fail/issue-19086.rs @@ -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 } } diff --git a/src/test/compile-fail/issue-27831.rs b/src/test/compile-fail/issue-27831.rs index 3cdb370f0e9..ff2846dc705 100644 --- a/src/test/compile-fail/issue-27831.rs +++ b/src/test/compile-fail/issue-27831.rs @@ -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 => {} diff --git a/src/test/compile-fail/issue-28992-empty.rs b/src/test/compile-fail/issue-28992-empty.rs new file mode 100644 index 00000000000..f7d53ba23da --- /dev/null +++ b/src/test/compile-fail/issue-28992-empty.rs @@ -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 or the MIT license +// , 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 +} diff --git a/src/test/compile-fail/match-pattern-field-mismatch-2.rs b/src/test/compile-fail/match-pattern-field-mismatch-2.rs index e63ddf6c7fd..a4ba93ea173 100644 --- a/src/test/compile-fail/match-pattern-field-mismatch-2.rs +++ b/src/test/compile-fail/match-pattern-field-mismatch-2.rs @@ -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 } } } diff --git a/src/test/compile-fail/pattern-error-continue.rs b/src/test/compile-fail/pattern-error-continue.rs index aa7202574ab..891a586423e 100644 --- a/src/test/compile-fail/pattern-error-continue.rs +++ b/src/test/compile-fail/pattern-error-continue.rs @@ -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' { diff --git a/src/test/run-pass/empty-struct-braces.rs b/src/test/run-pass/empty-struct-braces.rs index f2fbf2dd337..80ea1bc3a0e 100644 --- a/src/test/run-pass/empty-struct-braces.rs +++ b/src/test/run-pass/empty-struct-braces.rs @@ -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 => {}