diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index e129492dbc2f8..889c38b8ecc81 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -53,6 +53,7 @@ use middle::const_eval; use middle::def; use middle::lang_items::FnMutTraitLangItem; use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs}; +use middle::subst::{VecPerParamSpace}; use middle::ty; use middle::ty_fold::TypeFolder; use middle::typeck::rscope::{ExplicitRscope, ImpliedSingleRscope}; @@ -299,6 +300,47 @@ pub fn ast_path_to_ty( TypeAndSubsts { substs: substs, ty: ty } } +/// 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. +pub fn ast_path_to_ty_relaxed( + this: &AC, + rscope: &RS, + did: ast::DefId, + path: &ast::Path) + -> TypeAndSubsts { + let tcx = this.tcx(); + let ty::Polytype { + generics: generics, + ty: decl_ty + } = this.get_item_ty(did); + + let substs = if (generics.has_type_params(TypeSpace) || + generics.has_region_params(TypeSpace)) && + path.segments.iter().all(|s| { + s.lifetimes.len() == 0 && s.types.len() == 0 + }) { + let type_params = Vec::from_fn(generics.types.len(TypeSpace), + |_| this.ty_infer(path.span)); + let region_params = + rscope.anon_regions(path.span, generics.regions.len(TypeSpace)) + .unwrap(); + Substs::new(VecPerParamSpace::params_from_type(type_params), + VecPerParamSpace::params_from_type(region_params)) + } else { + ast_path_substs(this, rscope, &generics, None, path) + }; + + let ty = decl_ty.subst(tcx, &substs); + TypeAndSubsts { + substs: substs, + ty: ty, + } +} + pub static NO_REGIONS: uint = 1; pub static NO_TPS: uint = 2; diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 557fd522fa918..447154df224e9 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -3409,10 +3409,11 @@ fn check_expr_with_unifier(fcx: &FnCtxt, ast::ExprStruct(ref path, ref fields, base_expr) => { // Resolve the path. let def = tcx.def_map.borrow().find(&id).map(|i| *i); - match def { + let struct_id = match def { Some(def::DefVariant(enum_id, variant_id, _)) => { check_struct_enum_variant(fcx, id, expr.span, enum_id, variant_id, fields.as_slice()); + enum_id } Some(def) => { // Verify that this was actually a struct. @@ -3432,11 +3433,47 @@ fn check_expr_with_unifier(fcx: &FnCtxt, pprust::path_to_string(path)); } } + + def.def_id() } _ => { tcx.sess.span_bug(path.span, "structure constructor wasn't resolved") } + }; + + // 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. + let actual_structure_type = fcx.expr_ty(&*expr); + if !ty::type_is_error(actual_structure_type) { + let type_and_substs = astconv::ast_path_to_ty_relaxed(fcx, + fcx.infcx(), + struct_id, + path); + match fcx.mk_subty(false, + infer::Misc(path.span), + actual_structure_type, + type_and_substs.ty) { + Ok(()) => {} + Err(type_error) => { + let type_error_description = + ty::type_err_to_str(tcx, &type_error); + fcx.tcx() + .sess + .span_err(path.span, + format!("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( + actual_structure_type), + type_error_description).as_slice()); + ty::note_and_explain_type_err(tcx, &type_error); + } + } } } ast::ExprField(ref base, ref field, ref tys) => { diff --git a/src/test/compile-fail/structure-constructor-type-mismatch.rs b/src/test/compile-fail/structure-constructor-type-mismatch.rs new file mode 100644 index 0000000000000..2c8bbee783bd3 --- /dev/null +++ b/src/test/compile-fail/structure-constructor-type-mismatch.rs @@ -0,0 +1,56 @@ +// Copyright 2014 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. + +struct Point { + x: T, + y: T, +} + +type PointF = Point; + +struct Pair { + x: T, + y: U, +} + +type PairF = Pair; + +fn main() { + let pt = PointF { + //~^ ERROR expected f32 but found int + x: 1i, + y: 2i, + }; + + let pt2 = Point:: { + //~^ ERROR expected f32 but found int + x: 3i, + y: 4i, + }; + + let pair = PairF { + //~^ ERROR expected f32 but found int + x: 5i, + y: 6i, + }; + + let pair2 = PairF:: { + //~^ ERROR expected f32 but found int + x: 7i, + y: 8i, + }; + + let pt3 = PointF:: { + //~^ ERROR wrong number of type arguments + x: 9i, + y: 10i, + }; +} +