Skip to content

Commit

Permalink
Move pattern resolution checks from typeck to resolve
Browse files Browse the repository at this point in the history
Make error messages more precise
  • Loading branch information
petrochenkov committed Oct 4, 2016
1 parent e8ea38e commit c95b280
Show file tree
Hide file tree
Showing 33 changed files with 125 additions and 113 deletions.
10 changes: 7 additions & 3 deletions src/librustc/hir/def.rs
Expand Up @@ -140,12 +140,16 @@ impl Def {
Def::Mod(..) => "module",
Def::Static(..) => "static",
Def::Variant(..) => "variant",
Def::VariantCtor(..) => "variant",
Def::VariantCtor(.., CtorKind::Fn) => "tuple variant",
Def::VariantCtor(.., CtorKind::Const) => "unit variant",
Def::VariantCtor(.., CtorKind::Fictive) => "struct variant",
Def::Enum(..) => "enum",
Def::TyAlias(..) => "type",
Def::TyAlias(..) => "type alias",
Def::AssociatedTy(..) => "associated type",
Def::Struct(..) => "struct",
Def::StructCtor(..) => "struct",
Def::StructCtor(.., CtorKind::Fn) => "tuple struct",
Def::StructCtor(.., CtorKind::Const) => "unit struct",
Def::StructCtor(.., CtorKind::Fictive) => bug!("impossible struct constructor"),
Def::Union(..) => "union",
Def::Trait(..) => "trait",
Def::Method(..) => "method",
Expand Down
31 changes: 19 additions & 12 deletions src/librustc_resolve/lib.rs
Expand Up @@ -485,7 +485,7 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
E0531,
"unresolved {} `{}`",
expected_what,
path.segments.last().unwrap().identifier)
path)
}
ResolutionError::PatPathUnexpected(expected_what, found_what, path) => {
struct_span_err!(resolver.session,
Expand All @@ -494,7 +494,7 @@ fn resolve_struct_error<'b, 'a: 'b, 'c>(resolver: &'b Resolver<'a>,
"expected {}, found {} `{}`",
expected_what,
found_what,
path.segments.last().unwrap().identifier)
path)
}
}
}
Expand Down Expand Up @@ -2376,15 +2376,16 @@ impl<'a> Resolver<'a> {
let always_binding = !pat_src.is_refutable() || opt_pat.is_some() ||
bmode != BindingMode::ByValue(Mutability::Immutable);
match def {
Def::StructCtor(..) | Def::VariantCtor(..) |
Def::Const(..) | Def::AssociatedConst(..) if !always_binding => {
// A constant, unit variant, etc pattern.
Def::StructCtor(_, CtorKind::Const) |
Def::VariantCtor(_, CtorKind::Const) |
Def::Const(..) if !always_binding => {
// A unit struct/variant or constant pattern.
let name = ident.node.name;
self.record_use(name, ValueNS, binding.unwrap(), ident.span);
Some(PathResolution::new(def))
}
Def::StructCtor(..) | Def::VariantCtor(..) |
Def::Const(..) | Def::AssociatedConst(..) | Def::Static(..) => {
Def::Const(..) | Def::Static(..) => {
// A fresh binding that shadows something unacceptable.
resolve_error(
self,
Expand All @@ -2401,7 +2402,7 @@ impl<'a> Resolver<'a> {
}
def => {
span_bug!(ident.span, "unexpected definition for an \
identifier in pattern {:?}", def);
identifier in pattern: {:?}", def);
}
}
}).unwrap_or_else(|| {
Expand All @@ -2411,23 +2412,29 @@ impl<'a> Resolver<'a> {
self.record_def(pat.id, resolution);
}

PatKind::TupleStruct(ref path, ..) => {
PatKind::TupleStruct(ref path, ref pats, ddpos) => {
self.resolve_pattern_path(pat.id, None, path, ValueNS, |def| {
match def {
Def::StructCtor(..) | Def::VariantCtor(..) => true,
Def::StructCtor(_, CtorKind::Fn) |
Def::VariantCtor(_, CtorKind::Fn) => true,
// `UnitVariant(..)` is accepted for backward compatibility.
Def::StructCtor(_, CtorKind::Const) |
Def::VariantCtor(_, CtorKind::Const)
if pats.is_empty() && ddpos.is_some() => true,
_ => false,
}
}, "variant or struct");
}, "tuple struct/variant");
}

PatKind::Path(ref qself, ref path) => {
self.resolve_pattern_path(pat.id, qself.as_ref(), path, ValueNS, |def| {
match def {
Def::StructCtor(..) | Def::VariantCtor(..) |
Def::StructCtor(_, CtorKind::Const) |
Def::VariantCtor(_, CtorKind::Const) |
Def::Const(..) | Def::AssociatedConst(..) => true,
_ => false,
}
}, "variant, struct or constant");
}, "unit struct/variant or constant");
}

PatKind::Struct(ref path, ..) => {
Expand Down
68 changes: 25 additions & 43 deletions src/librustc_typeck/check/_match.rs
Expand Up @@ -8,10 +8,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use hir::def::Def;
use rustc::hir::{self, PatKind};
use rustc::hir::def::{Def, CtorKind};
use rustc::hir::pat_util::EnumerateAndAdjustIterator;
use rustc::infer::{self, InferOk, TypeOrigin};
use hir::pat_util::EnumerateAndAdjustIterator;
use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference, VariantKind};
use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference};
use check::{FnCtxt, Expectation};
use lint;
use util::nodemap::FnvHashMap;
Expand All @@ -23,9 +24,6 @@ use syntax::codemap::Spanned;
use syntax::ptr::P;
use syntax_pos::Span;

use rustc::hir::{self, PatKind};
use rustc::hir::print as pprust;

impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn check_pat(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>) {
let tcx = self.tcx;
Expand Down Expand Up @@ -516,10 +514,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expected: Ty<'tcx>) -> Ty<'tcx>
{
let tcx = self.tcx;
let report_unexpected_def = || {
let report_unexpected_def = |def: Def| {
span_err!(tcx.sess, pat.span, E0533,
"`{}` does not name a unit variant, unit struct or a constant",
pprust::path_to_string(path));
"expected unit struct/variant or constant, found {} `{}`",
def.kind_name(), path);
};

// Resolve the path and check the definition for errors.
Expand All @@ -531,18 +529,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
return tcx.types.err;
}
Def::Method(..) => {
report_unexpected_def();
report_unexpected_def(def);
return tcx.types.err;
}
Def::VariantCtor(..) | Def::StructCtor(..) => {
let variant = tcx.expect_variant_def(def);
if variant.kind != VariantKind::Unit {
report_unexpected_def();
return tcx.types.err;
}
}
Def::VariantCtor(_, CtorKind::Const) |
Def::StructCtor(_, CtorKind::Const) |
Def::Const(..) | Def::AssociatedConst(..) => {} // OK
_ => bug!("unexpected pattern definition {:?}", def)
_ => bug!("unexpected pattern definition: {:?}", def)
}

// Type check the path.
Expand All @@ -564,9 +557,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.check_pat(&pat, tcx.types.err);
}
};
let report_unexpected_def = |is_lint| {
let msg = format!("`{}` does not name a tuple variant or a tuple struct",
pprust::path_to_string(path));
let report_unexpected_def = |def: Def, is_lint| {
let msg = format!("expected tuple struct/variant, found {} `{}`",
def.kind_name(), path);
if is_lint {
tcx.sess.add_lint(lint::builtin::MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
pat.id, pat.span, msg);
Expand All @@ -585,23 +578,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
on_error();
return tcx.types.err;
}
Def::Const(..) | Def::AssociatedConst(..) | Def::Method(..) => {
report_unexpected_def(false);
Def::AssociatedConst(..) | Def::Method(..) => {
report_unexpected_def(def, false);
return tcx.types.err;
}
Def::VariantCtor(..) | Def::StructCtor(..) => {
Def::VariantCtor(_, ctor_kind) | Def::StructCtor(_, ctor_kind) => {
if ctor_kind == CtorKind::Const {
// Matching unit structs with tuple variant patterns (`UnitVariant(..)`)
// is allowed for backward compatibility.
report_unexpected_def(def, true);
}
tcx.expect_variant_def(def)
}
_ => bug!("unexpected pattern definition {:?}", def)
_ => bug!("unexpected pattern definition: {:?}", def)
};
if variant.kind == VariantKind::Unit && subpats.is_empty() && ddpos.is_some() {
// Matching unit structs with tuple variant patterns (`UnitVariant(..)`)
// is allowed for backward compatibility.
report_unexpected_def(true);
} else if variant.kind != VariantKind::Tuple {
report_unexpected_def(false);
return tcx.types.err;
}

// Type check the path.
let pat_ty = self.instantiate_value_path(segments, opt_ty, def, pat.span, pat.id);
Expand All @@ -626,16 +616,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.check_pat(&subpat, field_ty);
}
} else {
let subpats_ending = if subpats.len() == 1 {
""
} else {
"s"
};
let fields_ending = if variant.fields.len() == 1 {
""
} else {
"s"
};
let subpats_ending = if subpats.len() == 1 { "" } else { "s" };
let fields_ending = if variant.fields.len() == 1 { "" } else { "s" };
struct_span_err!(tcx.sess, pat.span, E0023,
"this pattern has {} field{}, but the corresponding {} has {} field{}",
subpats.len(), subpats_ending, def.kind_name(),
Expand Down
8 changes: 7 additions & 1 deletion src/test/compile-fail/E0164.rs
Expand Up @@ -8,7 +8,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

enum Foo { B { i: u32 } }
#![feature(associated_consts)]

enum Foo {}

impl Foo {
const B: u8 = 0;
}

fn bar(foo: Foo) -> u32 {
match foo {
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/blind-item-block-middle.rs
Expand Up @@ -12,6 +12,6 @@ mod foo { pub struct bar; }

fn main() {
let bar = 5;
//~^ ERROR let bindings cannot shadow structs
//~^ ERROR let bindings cannot shadow unit structs
use foo::bar;
}
4 changes: 2 additions & 2 deletions src/test/compile-fail/empty-struct-braces-pat-1.rs
Expand Up @@ -32,13 +32,13 @@ fn main() {
}
match e3 {
E::Empty3 => ()
//~^ ERROR `E::Empty3` does not name a unit variant, unit struct or a constant
//~^ ERROR expected unit struct/variant or constant, found struct variant `E::Empty3`
}
match xe1 {
XEmpty1 => () // Not an error, `XEmpty1` is interpreted as a new binding
}
match xe3 {
XE::XEmpty3 => ()
//~^ ERROR `XE::XEmpty3` does not name a unit variant, unit struct or a constant
//~^ ERROR expected unit struct/variant or constant, found struct variant `XE::XEmpty3`
}
}
8 changes: 4 additions & 4 deletions src/test/compile-fail/empty-struct-braces-pat-2.rs
Expand Up @@ -24,15 +24,15 @@ fn main() {
let xe1 = XEmpty1 {};

match e1 {
Empty1() => () //~ ERROR unresolved variant or struct `Empty1`
Empty1() => () //~ ERROR unresolved tuple struct/variant `Empty1`
}
match xe1 {
XEmpty1() => () //~ ERROR unresolved variant or struct `XEmpty1`
XEmpty1() => () //~ ERROR unresolved tuple struct/variant `XEmpty1`
}
match e1 {
Empty1(..) => () //~ ERROR unresolved variant or struct `Empty1`
Empty1(..) => () //~ ERROR unresolved tuple struct/variant `Empty1`
}
match xe1 {
XEmpty1(..) => () //~ ERROR unresolved variant or struct `XEmpty1`
XEmpty1(..) => () //~ ERROR unresolved tuple struct/variant `XEmpty1`
}
}
12 changes: 8 additions & 4 deletions src/test/compile-fail/empty-struct-braces-pat-3.rs
Expand Up @@ -26,15 +26,19 @@ fn main() {
let xe3 = XE::XEmpty3 {};

match e3 {
E::Empty3() => () //~ ERROR `E::Empty3` does not name a tuple variant or a tuple struct
E::Empty3() => ()
//~^ ERROR expected tuple struct/variant, found struct variant `E::Empty3`
}
match xe3 {
XE::XEmpty3() => () //~ ERROR `XE::XEmpty3` does not name a tuple variant or a tuple struct
XE::XEmpty3() => ()
//~^ ERROR expected tuple struct/variant, found struct variant `XE::XEmpty3`
}
match e3 {
E::Empty3(..) => () //~ ERROR `E::Empty3` does not name a tuple variant or a tuple struct
E::Empty3(..) => ()
//~^ ERROR expected tuple struct/variant, found struct variant `E::Empty3`
}
match xe3 {
XE::XEmpty3(..) => () //~ ERROR `XE::XEmpty3` does not name a tuple variant or a tuple
XE::XEmpty3(..) => ()
//~^ ERROR expected tuple struct/variant, found struct variant `XE::XEmpty3
}
}
10 changes: 6 additions & 4 deletions src/test/compile-fail/empty-struct-tuple-pat.rs
Expand Up @@ -31,17 +31,19 @@ fn main() {
let xe5 = XE::XEmpty5();

match e2 {
Empty2 => () //~ ERROR `Empty2` does not name a unit variant, unit struct or a constant
Empty2 => () //~ ERROR match bindings cannot shadow tuple structs
}
match xe6 {
XEmpty6 => () //~ ERROR `XEmpty6` does not name a unit variant, unit struct or a constant
XEmpty6 => () //~ ERROR match bindings cannot shadow tuple structs
}

match e4 {
E::Empty4 => () //~ ERROR `E::Empty4` does not name a unit variant, unit struct or a
E::Empty4 => ()
//~^ ERROR expected unit struct/variant or constant, found tuple variant `E::Empty4`
}
match xe5 {
XE::XEmpty5 => (), //~ ERROR `XE::XEmpty5` does not name a unit variant, unit struct or a
XE::XEmpty5 => (),
//~^ ERROR expected unit struct/variant or constant, found tuple variant `XE::XEmpty5`
_ => {},
}
}
11 changes: 6 additions & 5 deletions src/test/compile-fail/empty-struct-unit-pat-1.rs
Expand Up @@ -31,21 +31,22 @@ fn main() {
let xe4 = XE::XEmpty4;

match e2 {
Empty2(..) => () //~ ERROR `Empty2` does not name a tuple variant or a tuple struct
Empty2(..) => () //~ ERROR expected tuple struct/variant, found unit struct `Empty2`
//~^ WARNING hard error
}
match xe2 {
XEmpty2(..) => () //~ ERROR `XEmpty2` does not name a tuple variant or a tuple struct
XEmpty2(..) => () //~ ERROR expected tuple struct/variant, found unit struct `XEmpty2`
//~^ WARNING hard error
}

match e4 {
E::Empty4(..) => () //~ ERROR `E::Empty4` does not name a tuple variant or a tuple struct
E::Empty4(..) => () //~ ERROR expected tuple struct/variant, found unit variant `E::Empty4`
//~^ WARNING hard error
}
match xe4 {
XE::XEmpty4(..) => (), //~ ERROR `XE::XEmpty4` does not name a tuple variant or a tuple
//~^ WARNING hard error
XE::XEmpty4(..) => (),
//~^ ERROR expected tuple struct/variant, found unit variant `XE::XEmpty4`
//~| WARNING hard error
_ => {},
}
}
13 changes: 8 additions & 5 deletions src/test/compile-fail/empty-struct-unit-pat-2.rs
Expand Up @@ -23,25 +23,28 @@ enum E {
Empty4
}

// remove attribute after warning cycle and promoting warnings to errors
fn main() {
let e2 = Empty2;
let e4 = E::Empty4;
let xe2 = XEmpty2;
let xe4 = XE::XEmpty4;

match e2 {
Empty2() => () //~ ERROR `Empty2` does not name a tuple variant or a tuple struct
Empty2() => ()
//~^ ERROR expected tuple struct/variant, found unit struct `Empty2`
}
match xe2 {
XEmpty2() => () //~ ERROR `XEmpty2` does not name a tuple variant or a tuple struct
XEmpty2() => ()
//~^ ERROR expected tuple struct/variant, found unit struct `XEmpty2`
}

match e4 {
E::Empty4() => () //~ ERROR `E::Empty4` does not name a tuple variant or a tuple struct
E::Empty4() => ()
//~^ ERROR expected tuple struct/variant, found unit variant `E::Empty4`
}
match xe4 {
XE::XEmpty4() => (), //~ ERROR `XE::XEmpty4` does not name a tuple variant or a tuple
XE::XEmpty4() => (),
//~^ ERROR expected tuple struct/variant, found unit variant `XE::XEmpty4`
_ => {},
}
}

0 comments on commit c95b280

Please sign in to comment.