Resolve partially resolved paths in struct patterns/expressions

Treat Def::Err correctly in struct patterns
Make instantiate_path and instantiate_type a bit closer to each other
This commit is contained in:
Vadim Petrochenkov 2016-06-11 18:47:47 +03:00
parent 49ea3d48a2
commit a397b60ebb
15 changed files with 156 additions and 206 deletions

View File

@ -67,21 +67,6 @@ pub fn pat_is_refutable(dm: &DefMap, pat: &hir::Pat) -> bool {
}
}
pub fn pat_is_variant_or_struct(dm: &DefMap, pat: &hir::Pat) -> bool {
match pat.node {
PatKind::TupleStruct(..) |
PatKind::Path(..) |
PatKind::Struct(..) => {
match dm.get(&pat.id).map(|d| d.full_def()) {
Some(Def::Variant(..)) | Some(Def::Struct(..)) |
Some(Def::TyAlias(..)) | Some(Def::AssociatedTy(..)) => true,
_ => false
}
}
_ => false
}
}
pub fn pat_is_const(dm: &DefMap, pat: &hir::Pat) -> bool {
match pat.node {
PatKind::Path(..) | PatKind::QPath(..) => {

View File

@ -489,39 +489,35 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn check_pat_struct(&self, pat: &'gcx hir::Pat,
path: &hir::Path, fields: &'gcx [Spanned<hir::FieldPat>],
etc: bool, expected: Ty<'tcx>) {
let tcx = self.tcx;
let def = tcx.expect_def(pat.id);
let variant = match self.def_struct_variant(def, path.span) {
Some((_, variant)) => variant,
None => {
let name = pprust::path_to_string(path);
span_err!(tcx.sess, pat.span, E0163,
"`{}` does not name a struct or a struct variant", name);
self.write_error(pat.id);
for field in fields {
self.check_pat(&field.node.pat, tcx.types.err);
}
return;
fn check_pat_struct(&self,
pat: &'gcx hir::Pat,
path: &hir::Path,
fields: &'gcx [Spanned<hir::FieldPat>],
etc: bool,
expected: Ty<'tcx>)
{
// Resolve the path and check the definition for errors.
let def = self.finish_resolving_struct_path(path, pat.id, pat.span);
let variant = if let Some(variant) = self.check_struct_path(def, path, pat.span) {
variant
} else {
self.write_error(pat.id);
for field in fields {
self.check_pat(&field.node.pat, self.tcx.types.err);
}
return;
};
let pat_ty = self.instantiate_type(def.def_id(), path);
let item_substs = match pat_ty.sty {
// Type check the path.
let pat_ty = self.instantiate_type_path(def.def_id(), path, pat.id);
self.demand_eqtype(pat.span, expected, pat_ty);
// Type check subpatterns.
let substs = match pat_ty.sty {
ty::TyStruct(_, substs) | ty::TyEnum(_, substs) => substs,
_ => span_bug!(pat.span, "struct variant is not an ADT")
};
self.demand_eqtype(pat.span, expected, pat_ty);
self.check_struct_pat_fields(pat.span, fields, variant, &item_substs, etc);
self.write_ty(pat.id, pat_ty);
self.write_substs(pat.id, ty::ItemSubsts {
substs: item_substs
});
self.check_struct_pat_fields(pat.span, fields, variant, substs, etc);
}
fn check_pat_path(&self,
@ -539,8 +535,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
};
// Resolve the path and check the definition for errors.
let (def, opt_ty, segments) = self.resolve_ty_and_def_ufcs(tcx.expect_resolution(pat.id),
opt_self_ty, path, pat.span, pat.id);
let (def, opt_ty, segments) = self.resolve_ty_and_def_ufcs(opt_self_ty, path,
pat.id, pat.span);
match def {
Def::Err => {
self.set_tainted_by_errors();
@ -565,8 +561,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Type check the path.
let scheme = tcx.lookup_item_type(def.def_id());
let predicates = tcx.lookup_predicates(def.def_id());
self.instantiate_path(segments, scheme, &predicates, opt_ty, def, pat.span, pat.id);
let pat_ty = self.node_ty(pat.id);
let pat_ty = self.instantiate_value_path(segments, scheme, &predicates,
opt_ty, def, pat.span, pat.id);
self.demand_suptype(pat.span, expected, pat_ty);
}
@ -597,9 +593,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
};
// Resolve the path and check the definition for errors.
let (def, opt_ty, segments) = self.resolve_ty_and_def_ufcs(tcx.expect_resolution(pat.id),
None, path, pat.span, pat.id);
match def {
let (def, opt_ty, segments) = self.resolve_ty_and_def_ufcs(None, path, pat.id, pat.span);
let variant = match def {
Def::Err => {
self.set_tainted_by_errors();
on_error();
@ -609,10 +604,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
report_unexpected_def(false);
return;
}
Def::Variant(..) | Def::Struct(..) => {} // OK
Def::Variant(..) | Def::Struct(..) => {
tcx.expect_variant_def(def)
}
_ => bug!("unexpected pattern definition {:?}", def)
}
let variant = tcx.expect_variant_def(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.
@ -633,20 +629,19 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
scheme
};
let predicates = tcx.lookup_predicates(def.def_id());
self.instantiate_path(segments, scheme, &predicates, opt_ty, def, pat.span, pat.id);
let pat_ty = self.node_ty(pat.id);
let pat_ty = self.instantiate_value_path(segments, scheme, &predicates,
opt_ty, def, pat.span, pat.id);
self.demand_eqtype(pat.span, expected, pat_ty);
// Type check subpatterns.
if subpats.len() == variant.fields.len() ||
subpats.len() < variant.fields.len() && ddpos.is_some() {
let expected_substs = match pat_ty.sty {
ty::TyEnum(_, expected_substs) => expected_substs,
ty::TyStruct(_, expected_substs) => expected_substs,
let substs = match pat_ty.sty {
ty::TyStruct(_, substs) | ty::TyEnum(_, substs) => substs,
ref ty => bug!("unexpected pattern type {:?}", ty),
};
for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) {
let field_ty = self.field_ty(subpat.span, &variant.fields[i], expected_substs);
let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs);
self.check_pat(&subpat, field_ty);
}
} else {

View File

@ -1621,62 +1621,32 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
///
/// Note that this function is only intended to be used with type-paths,
/// not with value-paths.
pub fn instantiate_type(&self,
did: DefId,
path: &hir::Path)
-> Ty<'tcx>
{
debug!("instantiate_type(did={:?}, path={:?})", did, path);
let type_scheme =
self.tcx.lookup_item_type(did);
let type_predicates =
self.tcx.lookup_predicates(did);
pub fn instantiate_type_path(&self,
did: DefId,
path: &hir::Path,
node_id: ast::NodeId)
-> Ty<'tcx> {
debug!("instantiate_type_path(did={:?}, path={:?})", did, path);
let type_scheme = self.tcx.lookup_item_type(did);
let type_predicates = self.tcx.lookup_predicates(did);
let substs = AstConv::ast_path_substs_for_ty(self, self,
path.span,
PathParamMode::Optional,
&type_scheme.generics,
path.segments.last().unwrap());
debug!("instantiate_type: ty={:?} substs={:?}", &type_scheme.ty, &substs);
let bounds =
self.instantiate_bounds(path.span, &substs, &type_predicates);
self.add_obligations_for_parameters(
traits::ObligationCause::new(
path.span,
self.body_id,
traits::ItemObligation(did)),
&bounds);
let substs = self.tcx.mk_substs(substs);
debug!("instantiate_type_path: ty={:?} substs={:?}", &type_scheme.ty, substs);
let bounds = self.instantiate_bounds(path.span, substs, &type_predicates);
let cause = traits::ObligationCause::new(path.span, self.body_id,
traits::ItemObligation(did));
self.add_obligations_for_parameters(cause, &bounds);
self.instantiate_type_scheme(path.span, &substs, &type_scheme.ty)
}
/// Return the dict-like variant corresponding to a given `Def`.
pub fn def_struct_variant(&self,
def: Def,
_span: Span)
-> Option<(ty::AdtDef<'tcx>, ty::VariantDef<'tcx>)>
{
let (adt, variant) = match def {
Def::Variant(enum_id, variant_id) => {
let adt = self.tcx.lookup_adt_def(enum_id);
(adt, adt.variant_with_id(variant_id))
}
Def::Struct(did) | Def::TyAlias(did) => {
let typ = self.tcx.lookup_item_type(did);
if let ty::TyStruct(adt, _) = typ.ty.sty {
(adt, adt.struct_variant())
} else {
return None;
}
}
_ => return None
};
if variant.kind == ty::VariantKind::Struct ||
variant.kind == ty::VariantKind::Unit {
Some((adt, variant))
} else {
None
}
let ty_substituted = self.instantiate_type_scheme(path.span, substs, &type_scheme.ty);
self.write_ty(node_id, ty_substituted);
self.write_substs(node_id, ty::ItemSubsts {
substs: substs
});
ty_substituted
}
pub fn write_nil(&self, node_id: ast::NodeId) {
@ -3151,34 +3121,54 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
}
pub fn check_struct_path(&self,
def: Def,
path: &hir::Path,
span: Span)
-> Option<ty::VariantDef<'tcx>> {
let variant = match def {
Def::Err => {
self.set_tainted_by_errors();
return None;
}
Def::Variant(..) | Def::Struct(..) => {
Some(self.tcx.expect_variant_def(def))
}
Def::TyAlias(did) | Def::AssociatedTy(_, did) => {
if let ty::TyStruct(adt, _) = self.tcx.lookup_item_type(did).ty.sty {
Some(adt.struct_variant())
} else {
None
}
}
_ => None
};
if variant.is_none() || variant.unwrap().kind == ty::VariantKind::Tuple {
// Reject tuple structs for now, braced and unit structs are allowed.
span_err!(self.tcx.sess, span, E0071,
"`{}` does not name a struct or a struct variant",
pprust::path_to_string(path));
return None;
}
variant
}
fn check_expr_struct(&self,
expr: &hir::Expr,
path: &hir::Path,
fields: &'gcx [hir::Field],
base_expr: &'gcx Option<P<hir::Expr>>)
{
let tcx = self.tcx;
// Find the relevant variant
let def = tcx.expect_def(expr.id);
if def == Def::Err {
self.set_tainted_by_errors();
let def = self.finish_resolving_struct_path(path, expr.id, expr.span);
let variant = if let Some(variant) = self.check_struct_path(def, path, expr.span) {
variant
} else {
self.check_struct_fields_on_error(expr.id, fields, base_expr);
return;
}
let variant = match self.def_struct_variant(def, path.span) {
Some((_, variant)) => variant,
None => {
span_err!(self.tcx.sess, path.span, E0071,
"`{}` does not name a structure",
pprust::path_to_string(path));
self.check_struct_fields_on_error(expr.id, fields, base_expr);
return;
}
};
let expr_ty = self.instantiate_type(def.def_id(), path);
self.write_ty(expr.id, expr_ty);
let expr_ty = self.instantiate_type_path(def.def_id(), path, expr.id);
self.check_expr_struct_fields(expr_ty, path.span, variant, fields,
base_expr.is_none());
@ -3190,13 +3180,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expr.id,
adt.struct_variant().fields.iter().map(|f| {
self.normalize_associated_types_in(
expr.span, &f.ty(tcx, substs)
expr.span, &f.ty(self.tcx, substs)
)
}).collect()
);
}
_ => {
span_err!(tcx.sess, base_expr.span, E0436,
span_err!(self.tcx.sess, base_expr.span, E0436,
"functional record update syntax requires a struct");
}
}
@ -3349,12 +3339,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
hir::ExprPath(ref opt_qself, ref path) => {
let opt_self_ty = opt_qself.as_ref().map(|qself| self.to_ty(&qself.ty));
let (def, opt_ty, segments) = self.resolve_ty_and_def_ufcs(tcx.expect_resolution(id),
opt_self_ty, path, expr.span, expr.id);
let (def, opt_ty, segments) = self.resolve_ty_and_def_ufcs(opt_self_ty, path,
expr.id, expr.span);
if def != Def::Err {
let (scheme, predicates) = self.type_scheme_and_predicates_for_def(expr.span,
def);
self.instantiate_path(segments, scheme, &predicates, opt_ty, def, expr.span, id);
self.instantiate_value_path(segments, scheme, &predicates,
opt_ty, def, expr.span, id);
} else {
self.set_tainted_by_errors();
self.write_error(id);
@ -3695,18 +3686,45 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expected);
}
// Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary.
// The newly resolved definition is written into `def_map`.
pub fn finish_resolving_struct_path(&self,
path: &hir::Path,
node_id: ast::NodeId,
span: Span)
-> Def
{
let path_res = self.tcx().expect_resolution(node_id);
if path_res.depth == 0 {
// If fully resolved already, we don't have to do anything.
path_res.base_def
} else {
let base_ty_end = path.segments.len() - path_res.depth;
let (_ty, def) = AstConv::finish_resolving_def_to_ty(self, self, span,
PathParamMode::Optional,
path_res.base_def,
None,
node_id,
&path.segments[..base_ty_end],
&path.segments[base_ty_end..]);
// Write back the new resolution.
self.tcx().def_map.borrow_mut().insert(node_id, PathResolution::new(def));
def
}
}
// Resolve associated value path into a base type and associated constant or method definition.
// The newly resolved definition is written into `def_map`.
pub fn resolve_ty_and_def_ufcs<'b>(&self,
path_res: PathResolution,
opt_self_ty: Option<Ty<'tcx>>,
path: &'b hir::Path,
span: Span,
node_id: ast::NodeId)
node_id: ast::NodeId,
span: Span)
-> (Def, Option<Ty<'tcx>>, &'b [hir::PathSegment])
{
// If fully resolved already, we don't have to do anything.
let path_res = self.tcx().expect_resolution(node_id);
if path_res.depth == 0 {
// If fully resolved already, we don't have to do anything.
(path_res.base_def, opt_self_ty, &path.segments)
} else {
// Try to resolve everything except for the last segment as a type.
@ -3975,15 +3993,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// Instantiates the given path, which must refer to an item with the given
// number of type parameters and type.
pub fn instantiate_path(&self,
segments: &[hir::PathSegment],
type_scheme: TypeScheme<'tcx>,
type_predicates: &ty::GenericPredicates<'tcx>,
opt_self_ty: Option<Ty<'tcx>>,
def: Def,
span: Span,
node_id: ast::NodeId) {
debug!("instantiate_path(path={:?}, def={:?}, node_id={}, type_scheme={:?})",
pub fn instantiate_value_path(&self,
segments: &[hir::PathSegment],
type_scheme: TypeScheme<'tcx>,
type_predicates: &ty::GenericPredicates<'tcx>,
opt_self_ty: Option<Ty<'tcx>>,
def: Def,
span: Span,
node_id: ast::NodeId)
-> Ty<'tcx> {
debug!("instantiate_value_path(path={:?}, def={:?}, node_id={}, type_scheme={:?})",
segments,
def,
node_id,
@ -4012,7 +4031,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// actually pass through this function, but rather the
// `ast_ty_to_ty` function in `astconv`. However, in the case
// of struct patterns (and maybe literals) we do invoke
// `instantiate_path` to get the general type of an instance of
// `instantiate_value_path` to get the general type of an instance of
// a struct. (In these cases, there are actually no type
// parameters permitted at present, but perhaps we will allow
// them in the future.)
@ -4235,20 +4254,21 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
Err(_) => {
span_bug!(span,
"instantiate_path: (UFCS) {:?} was a subtype of {:?} but now is not?",
"instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?",
self_ty,
impl_ty);
}
}
}
debug!("instantiate_path: type of {:?} is {:?}",
debug!("instantiate_value_path: type of {:?} is {:?}",
node_id,
ty_substituted);
self.write_ty(node_id, ty_substituted);
self.write_substs(node_id, ty::ItemSubsts {
substs: substs
});
ty_substituted
}
/// Finds the parameters that the user provided and adds them to `substs`. If too many

View File

@ -1895,33 +1895,6 @@ fn my_start(argc: isize, argv: *const *const u8) -> isize {
```
"##,
E0163: r##"
This error means that an attempt was made to match an enum variant as a
struct type when the variant isn't a struct type:
```compile_fail
enum Foo { B(u32) }
fn bar(foo: Foo) -> u32 {
match foo {
B{i} => i, // error E0163
}
}
```
Try using `()` instead:
```
enum Foo { B(u32) }
fn bar(foo: Foo) -> u32 {
match foo {
Foo::B(i) => i,
}
}
```
"##,
E0164: r##"
This error means that an attempt was made to match a struct type enum
variant as a non-struct type:
@ -4070,6 +4043,7 @@ register_diagnostics! {
// E0129,
// E0141,
// E0159, // use of trait `{}` as struct constructor
// E0163, // merged into E0071
E0167,
// E0168,
// E0173, // manual implementations of unboxed closure traits are experimental

View File

@ -1,20 +0,0 @@
// 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.
enum Foo { B(u32) }
fn bar(foo: Foo) -> u32 {
match foo {
Foo::B { i } => i, //~ ERROR E0163
}
}
fn main() {
}

View File

@ -16,7 +16,7 @@ pub struct GslResult {
impl GslResult {
pub fn new() -> GslResult {
Result { //~ ERROR: `Result` does not name a structure
Result { //~ ERROR: `Result` does not name a struct or a struct variant
val: 0f64,
err: 0f64
}

View File

@ -11,5 +11,5 @@
mod foo {}
fn main() {
let p = foo { x: () }; //~ ERROR `foo` does not name a structure
let p = foo { x: () }; //~ ERROR `foo` does not name a struct or a struct variant
}

View File

@ -15,6 +15,5 @@ enum Foo {
fn main() {
match Foo::Bar(1) {
Foo { i } => () //~ ERROR expected variant, struct or type alias, found enum `Foo`
//~^ ERROR `Foo` does not name a struct or a struct variant
}
}

View File

@ -11,5 +11,5 @@
mod MyMod {}
fn main() {
let myVar = MyMod { T: 0 }; //~ ERROR `MyMod` does not name a structure
let myVar = MyMod { T: 0 }; //~ ERROR `MyMod` does not name a struct or a struct variant
}

View File

@ -12,6 +12,5 @@ fn main() {
match 'a' {
char{ch} => true
//~^ ERROR expected variant, struct or type alias, found builtin type `char`
//~| ERROR `char` does not name a struct or a struct variant
};
}

View File

@ -11,12 +11,10 @@
mod A {}
fn main() {
let u = A { x: 1 }; //~ ERROR `A` does not name a structure
let v = u32 { x: 1 }; //~ ERROR `u32` does not name a structure
let u = A { x: 1 }; //~ ERROR `A` does not name a struct or a struct variant
let v = u32 { x: 1 }; //~ ERROR `u32` does not name a struct or a struct variant
match () {
A { x: 1 } => {} //~ ERROR expected variant, struct or type alias, found module `A`
//~^ ERROR `A` does not name a struct or a struct variant
u32 { x: 1 } => {} //~ ERROR expected variant, struct or type alias, found builtin type `u32
//~^ ERROR `u32` does not name a struct or a struct variant
}
}

View File

@ -18,7 +18,7 @@ enum Enum {
fn main() {
let x = Foo(1);
Foo { ..x }; //~ ERROR `Foo` does not name a structure
Foo { ..x }; //~ ERROR `Foo` does not name a struct or a struct variant
let Foo { .. } = x; //~ ERROR `Foo` does not name a struct
let x = Bar;

View File

@ -11,5 +11,5 @@
struct NonCopyable(());
fn main() {
let z = NonCopyable{ p: () }; //~ ERROR `NonCopyable` does not name a structure
let z = NonCopyable{ p: () }; //~ ERROR `NonCopyable` does not name a struct or a struct variant
}

View File

@ -10,7 +10,7 @@
struct T { i: i32 }
fn f<T>() {
let t = T { i: 0 }; //~ ERROR `T` does not name a structure
let t = T { i: 0 }; //~ ERROR `T` does not name a struct or a struct variant
}
mod Foo {

View File

@ -12,5 +12,5 @@ trait TraitNotAStruct {}
fn main() {
TraitNotAStruct{ value: 0 };
//~^ ERROR: `TraitNotAStruct` does not name a structure [E0071]
//~^ ERROR: `TraitNotAStruct` does not name a struct or a struct variant [E0071]
}