clean-up ExprStruct and PatStruct type-checking

This fixes the crazy "transparent aliases" bug, which I hope nobody
relied on.
This commit is contained in:
Ariel Ben-Yehuda 2015-08-15 20:39:28 +03:00
parent b87c292627
commit ba98228b4d
5 changed files with 145 additions and 245 deletions

View File

@ -19,6 +19,7 @@ use check::{check_expr, check_expr_has_type, check_expr_with_expectation};
use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation};
use check::{check_expr_with_lvalue_pref, LvaluePreference};
use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_type};
use TypeAndSubsts;
use require_same_types;
use util::nodemap::FnvHashMap;
@ -526,62 +527,31 @@ pub fn check_pat_struct<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, pat: &'tcx ast::Pat,
etc: bool, expected: Ty<'tcx>) {
let fcx = pcx.fcx;
let tcx = pcx.fcx.ccx.tcx;
let report_nonstruct = || {
let name = pprust::path_to_string(path);
span_err!(tcx.sess, pat.span, E0163,
"`{}` does not name a struct or a struct variant", name);
fcx.write_error(pat.id);
for field in fields {
check_pat(pcx, &field.node.pat, tcx.types.err);
}
};
let def = tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
let (adt_def, variant) = match def {
def::DefTy(did, _) | def::DefStruct(did) => {
match tcx.lookup_item_type(did).ty.sty {
ty::TyStruct(struct_def, _) =>
(struct_def, struct_def.struct_variant()),
_ => {
report_nonstruct();
return;
}
let variant = match fcx.def_struct_variant(def) {
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);
fcx.write_error(pat.id);
for field in fields {
check_pat(pcx, &field.node.pat, tcx.types.err);
}
}
def::DefVariant(eid, vid, true) => {
match tcx.lookup_item_type(vid).ty.sty {
ty::TyEnum(enum_def, _) if enum_def.did == eid => {
(enum_def, enum_def.variant_with_id(vid))
}
_ => tcx.sess.span_bug(pat.span, "variant's type is not its enum")
}
}
_ => {
report_nonstruct();
return;
}
};
instantiate_path(pcx.fcx,
&path.segments,
adt_def.type_scheme(tcx),
&adt_def.predicates(tcx),
None,
def,
pat.span,
pat.id);
let pat_ty = fcx.node_ty(pat.id);
let TypeAndSubsts {
ty: pat_ty, substs: item_substs
} = pcx.fcx.instantiate_type(def.def_id(), path);
demand::eqtype(fcx, pat.span, expected, pat_ty);
let item_substs = fcx
.item_substs()
.get(&pat.id)
.map(|substs| substs.substs.clone())
.unwrap_or_else(|| Substs::empty());
check_struct_pat_fields(pcx, pat.span, fields, variant, &item_substs, etc);
fcx.write_ty(pat.id, pat_ty);
fcx.write_substs(pat.id, ty::ItemSubsts { substs: item_substs });
}
pub fn check_pat_enum<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,

View File

@ -1379,65 +1379,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
cause)
}
/// Returns the type of `def_id` with all generics replaced by by fresh type/region variables.
/// Also returns the substitution from the type parameters on `def_id` to the fresh variables.
/// Registers any trait obligations specified on `def_id` at the same time.
/// Instantiates the type in `did` with the generics in `path` and returns
/// it (registering the necessary trait obligations along the way).
///
/// Note that function is only intended to be used with types (notably, not fns). This is
/// because it doesn't do any instantiation of late-bound regions.
/// Note that this function is only intended to be used with type-paths,
/// not with value-paths.
pub fn instantiate_type(&self,
span: Span,
def_id: ast::DefId)
did: ast::DefId,
path: &ast::Path)
-> TypeAndSubsts<'tcx>
{
debug!("instantiate_type(did={:?}, path={:?})", did, path);
let type_scheme =
self.tcx().lookup_item_type(def_id);
self.tcx().lookup_item_type(did);
let type_predicates =
self.tcx().lookup_predicates(def_id);
let substs =
self.infcx().fresh_substs_for_generics(
span,
&type_scheme.generics);
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(span, &substs, &type_predicates);
self.instantiate_bounds(path.span, &substs, &type_predicates);
self.add_obligations_for_parameters(
traits::ObligationCause::new(
span,
path.span,
self.body_id,
traits::ItemObligation(def_id)),
traits::ItemObligation(did)),
&bounds);
let monotype =
self.instantiate_type_scheme(span, &substs, &type_scheme.ty);
TypeAndSubsts {
ty: monotype,
ty: self.instantiate_type_scheme(path.span, &substs, &type_scheme.ty),
substs: substs
}
}
/// Returns the type that this AST path refers to. If the path has no type
/// parameters and the corresponding type has type parameters, fresh type
/// and/or region variables are substituted.
///
/// This is used when checking the constructor in struct literals.
fn instantiate_struct_literal_ty(&self,
struct_ty: ty::TypeScheme<'tcx>,
path: &ast::Path)
-> TypeAndSubsts<'tcx>
pub fn def_struct_variant(&self,
def: def::Def)
-> Option<(ty::AdtDef<'tcx>, ty::VariantDef<'tcx>)>
{
let ty::TypeScheme { generics, ty: decl_ty } = struct_ty;
let substs = astconv::ast_path_substs_for_ty(self, self,
path.span,
PathParamMode::Optional,
&generics,
path.segments.last().unwrap());
let ty = self.instantiate_type_scheme(path.span, &substs, &decl_ty);
TypeAndSubsts { substs: substs, ty: ty }
match def {
def::DefVariant(enum_id, variant_id, true) => {
let adt = self.tcx().lookup_adt_def(enum_id);
Some((adt, adt.variant_with_id(variant_id)))
}
def::DefTy(did, _) | def::DefStruct(did) => {
let typ = self.tcx().lookup_item_type(did);
if let ty::TyStruct(adt, _) = typ.ty.sty {
Some((adt, adt.struct_variant()))
} else {
None
}
}
_ => None
}
}
pub fn write_nil(&self, node_id: ast::NodeId) {
self.write_ty(node_id, self.tcx().mk_nil());
}
@ -3028,18 +3027,17 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
}
fn check_struct_or_variant_fields<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
adt_ty: Ty<'tcx>,
span: Span,
variant_id: ast::DefId,
ast_fields: &'tcx [ast::Field],
check_completeness: bool) -> Result<(),()> {
fn check_expr_struct_fields<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
adt_ty: Ty<'tcx>,
span: Span,
variant: ty::VariantDef<'tcx>,
ast_fields: &'tcx [ast::Field],
check_completeness: bool) {
let tcx = fcx.ccx.tcx;
let (adt_def, substs) = match adt_ty.sty {
ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => (def, substs),
_ => tcx.sess.span_bug(span, "non-ADT passed to check_struct_or_variant_fields")
let substs = match adt_ty.sty {
ty::TyStruct(_, substs) | ty::TyEnum(_, substs) => substs,
_ => tcx.sess.span_bug(span, "non-ADT passed to check_expr_struct_fields")
};
let variant = adt_def.variant_with_id(variant_id);
let mut remaining_fields = FnvHashMap();
for field in &variant.fields {
@ -3076,7 +3074,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
!error_happened &&
!remaining_fields.is_empty()
{
error_happened = true;
span_err!(tcx.sess, span, E0063,
"missing field{}: {}",
if remaining_fields.len() == 1 {""} else {"s"},
@ -3085,68 +3082,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
.collect::<Vec<_>>()
.join(", "));
}
if error_happened { Err(()) } else { Ok(()) }
}
fn check_struct_constructor<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
id: ast::NodeId,
span: codemap::Span,
struct_def: ty::AdtDef<'tcx>,
fields: &'tcx [ast::Field],
base_expr: Option<&'tcx ast::Expr>) {
let tcx = fcx.ccx.tcx;
// Generate the struct type.
let TypeAndSubsts {
ty: mut struct_type,
substs: _
} = fcx.instantiate_type(span, struct_def.did);
// Look up and check the fields.
let res = check_struct_or_variant_fields(fcx,
struct_type,
span,
struct_def.did,
fields,
base_expr.is_none());
if res.is_err() {
struct_type = tcx.types.err;
}
// Check the base expression if necessary.
match base_expr {
None => {}
Some(base_expr) => {
check_expr_has_type(fcx, &*base_expr, struct_type);
}
}
// Write in the resulting type.
fcx.write_ty(id, struct_type);
}
fn check_struct_enum_variant<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
id: ast::NodeId,
span: codemap::Span,
enum_id: ast::DefId,
variant_id: ast::DefId,
fields: &'tcx [ast::Field]) {
// Look up the number of type parameters and the raw type, and
// determine whether the enum is region-parameterized.
let TypeAndSubsts {
ty: enum_type,
substs: _
} = fcx.instantiate_type(span, enum_id);
// Look up and check the enum variant fields.
let _ = check_struct_or_variant_fields(fcx,
enum_type,
span,
variant_id,
fields,
true);
fcx.write_ty(id, enum_type);
}
fn check_struct_fields_on_error<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
@ -3165,16 +3100,42 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
}
}
fn report_exprstruct_on_nondict<'a, 'tcx>(fcx: &FnCtxt<'a,'tcx>,
id: ast::NodeId,
fields: &'tcx [ast::Field],
base_expr: &'tcx Option<P<ast::Expr>>,
path: &ast::Path)
fn check_expr_struct<'a, 'tcx>(fcx: &FnCtxt<'a,'tcx>,
expr: &ast::Expr,
path: &ast::Path,
fields: &'tcx [ast::Field],
base_expr: &'tcx Option<P<ast::Expr>>)
{
span_err!(fcx.tcx().sess, path.span, E0071,
"`{}` does not name a structure",
pprust::path_to_string(path));
check_struct_fields_on_error(fcx, id, fields, base_expr)
let tcx = fcx.tcx();
// Find the relevant variant
let def = lookup_full_def(tcx, path.span, expr.id);
let (adt, variant) = match fcx.def_struct_variant(def) {
Some((adt, variant)) => (adt, variant),
None => {
span_err!(fcx.tcx().sess, path.span, E0071,
"`{}` does not name a structure",
pprust::path_to_string(path));
check_struct_fields_on_error(fcx, expr.id, fields, base_expr);
return;
}
};
let TypeAndSubsts {
ty: expr_ty, ..
} = fcx.instantiate_type(def.def_id(), path);
fcx.write_ty(expr.id, expr_ty);
check_expr_struct_fields(fcx, expr_ty, expr.span, variant, fields,
base_expr.is_none());
if let &Some(ref base_expr) = base_expr {
check_expr_has_type(fcx, base_expr, expr_ty);
if adt.adt_kind() == ty::AdtKind::Enum {
span_err!(tcx.sess, base_expr.span, E0436,
"functional record update syntax requires a struct");
}
}
}
type ExprCheckerWithTy = fn(&FnCtxt, &ast::Expr, Ty);
@ -3625,67 +3586,7 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
}
}
ast::ExprStruct(ref path, ref fields, ref base_expr) => {
// Resolve the path.
let def = lookup_full_def(tcx, path.span, id);
let struct_ty = match def {
def::DefVariant(enum_id, variant_id, true) => {
if let &Some(ref base_expr) = base_expr {
span_err!(tcx.sess, base_expr.span, E0436,
"functional record update syntax requires a struct");
fcx.write_error(base_expr.id);
}
check_struct_enum_variant(fcx, id, expr.span, enum_id,
variant_id, &fields[..]);
Some(tcx.lookup_item_type(enum_id))
}
def::DefTy(did, _) | def::DefStruct(did) => {
// Verify that this was actually a struct.
let typ = tcx.lookup_item_type(did);
if let ty::TyStruct(struct_def, _) = typ.ty.sty {
check_struct_constructor(fcx,
id,
expr.span,
struct_def,
&fields,
base_expr.as_ref().map(|e| &**e));
} else {
report_exprstruct_on_nondict(fcx, id, &fields, base_expr, path);
}
Some(typ)
}
_ => {
report_exprstruct_on_nondict(fcx, id, &fields, base_expr, path);
None
}
};
// Turn the path into a type and verify that that type unifies with
// the resulting structure type. This is needed to handle type
// parameters correctly.
if let Some(struct_ty) = struct_ty {
let expr_ty = fcx.expr_ty(&expr);
let type_and_substs = fcx.instantiate_struct_literal_ty(struct_ty, path);
match fcx.mk_subty(false,
infer::Misc(path.span),
expr_ty,
type_and_substs.ty) {
Ok(()) => {}
Err(type_error) => {
span_err!(fcx.tcx().sess, path.span, E0235,
"structure constructor specifies a \
structure of type `{}`, but this \
structure has type `{}`: {}",
fcx.infcx()
.ty_to_string(type_and_substs.ty),
fcx.infcx()
.ty_to_string(expr_ty),
type_error);
tcx.note_and_explain_type_err(&type_error, path.span);
}
}
}
check_expr_struct(fcx, expr, path, fields, base_expr);
fcx.require_expr_have_sized_type(expr, traits::StructInitializerSized);
}
ast::ExprField(ref base, ref field) => {
@ -4673,6 +4574,9 @@ pub fn instantiate_path<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
}
debug!("instantiate_path: type of {:?} is {:?}",
node_id,
ty_substituted);
fcx.write_ty(node_id, ty_substituted);
fcx.write_substs(node_id, ty::ItemSubsts { substs: substs });
return;

View File

@ -746,7 +746,8 @@ enum Foo { FirstValue(i32) };
let u = Foo::FirstValue { value: 0i32 }; // error: Foo::FirstValue
// isn't a structure!
// or even simpler, if the name doesn't refer to a structure at all.
let t = u32 { value: 4 }; // error: `u32` does not name a structure.```
let t = u32 { value: 4 }; // error: `u32` does not name a structure.
```
To fix this, ensure that the name was correctly spelled, and that
the correct form of initializer was used.
@ -2681,7 +2682,7 @@ register_diagnostics! {
E0231, // only named substitution parameters are allowed
E0233,
E0234,
E0235, // structure constructor specifies a structure of type but
// E0235, // structure constructor specifies a structure of type but
E0236, // no lang item for range syntax
E0237, // no lang item for range syntax
E0238, // parenthesized parameters may only be used with a trait

View File

@ -25,7 +25,7 @@ struct Parser<'a> {
impl<'a> Parser<'a> {
pub fn new(lexer: &'a mut Lexer) -> Parser<'a> {
Parser { lexer: lexer }
//~^ ERROR cannot infer an appropriate lifetime for lifetime parameter
//~^ ERROR cannot infer an appropriate lifetime
}
}

View File

@ -24,41 +24,66 @@ type PairF<U> = Pair<f32,U>;
fn main() {
let pt = PointF {
//~^ ERROR structure constructor specifies a structure of type
x: 1,
//~^ ERROR mismatched types
//~| expected f32
//~| found integral variable
x: 1,
y: 2,
//~^ ERROR mismatched types
//~| expected f32
//~| found integral variable
};
let pt2 = Point::<f32> {
//~^ ERROR structure constructor specifies a structure of type
x: 3,
//~^ ERROR mismatched types
//~| expected f32
//~| found integral variable
x: 3,
y: 4,
//~^ ERROR mismatched types
//~| expected f32
//~| found integral variable
};
let pair = PairF {
//~^ ERROR structure constructor specifies a structure of type
x: 5,
//~^ ERROR mismatched types
//~| expected f32
//~| found integral variable
x: 5,
y: 6,
};
let pair2 = PairF::<i32> {
//~^ ERROR structure constructor specifies a structure of type
x: 7,
//~^ ERROR mismatched types
//~| expected f32
//~| found integral variable
x: 7,
y: 8,
};
let pt3 = PointF::<i32> {
//~^ ERROR wrong number of type arguments
//~| ERROR structure constructor specifies a structure of type
x: 9,
y: 10,
let pt3 = PointF::<i32> { //~ ERROR wrong number of type arguments
x: 9, //~ ERROR mismatched types
y: 10, //~ ERROR mismatched types
};
match (Point { x: 1, y: 2 }) {
PointF::<u32> { .. } => {} //~ ERROR wrong number of type arguments
//~^ ERROR mismatched types
}
match (Point { x: 1, y: 2 }) {
PointF { .. } => {} //~ ERROR mismatched types
}
match (Point { x: 1.0, y: 2.0 }) {
PointF { .. } => {} // ok
}
match (Pair { x: 1, y: 2 }) {
PairF::<u32> { .. } => {} //~ ERROR mismatched types
}
match (Pair { x: 1.0, y: 2 }) {
PairF::<u32> { .. } => {} // ok
}
}