librustc: Check structure constructors against their types.
This breaks code like: struct Point<T> { x: T, y: T, } let pt = Point::<bool> { x: 1, y: 2, }; Change this code to not contain a type error. For example: let pt = Point::<int> { x: 1, y: 2, }; Closes #9620. Closes #15875. [breaking-change]
This commit is contained in:
parent
3550068b53
commit
103d888f65
@ -53,6 +53,7 @@ use middle::const_eval;
|
|||||||
use middle::def;
|
use middle::def;
|
||||||
use middle::lang_items::FnMutTraitLangItem;
|
use middle::lang_items::FnMutTraitLangItem;
|
||||||
use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs};
|
use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs};
|
||||||
|
use middle::subst::{VecPerParamSpace};
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::ty_fold::TypeFolder;
|
use middle::ty_fold::TypeFolder;
|
||||||
use middle::typeck::rscope::{ExplicitRscope, ImpliedSingleRscope};
|
use middle::typeck::rscope::{ExplicitRscope, ImpliedSingleRscope};
|
||||||
@ -299,6 +300,47 @@ pub fn ast_path_to_ty<AC:AstConv,RS:RegionScope>(
|
|||||||
TypeAndSubsts { substs: substs, ty: 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<AC:AstConv,
|
||||||
|
RS:RegionScope>(
|
||||||
|
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_REGIONS: uint = 1;
|
||||||
pub static NO_TPS: uint = 2;
|
pub static NO_TPS: uint = 2;
|
||||||
|
|
||||||
|
@ -3416,10 +3416,11 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
|
|||||||
ast::ExprStruct(ref path, ref fields, base_expr) => {
|
ast::ExprStruct(ref path, ref fields, base_expr) => {
|
||||||
// Resolve the path.
|
// Resolve the path.
|
||||||
let def = tcx.def_map.borrow().find(&id).map(|i| *i);
|
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, _)) => {
|
Some(def::DefVariant(enum_id, variant_id, _)) => {
|
||||||
check_struct_enum_variant(fcx, id, expr.span, enum_id,
|
check_struct_enum_variant(fcx, id, expr.span, enum_id,
|
||||||
variant_id, fields.as_slice());
|
variant_id, fields.as_slice());
|
||||||
|
enum_id
|
||||||
}
|
}
|
||||||
Some(def) => {
|
Some(def) => {
|
||||||
// Verify that this was actually a struct.
|
// Verify that this was actually a struct.
|
||||||
@ -3439,11 +3440,47 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
|
|||||||
pprust::path_to_string(path));
|
pprust::path_to_string(path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def.def_id()
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
tcx.sess.span_bug(path.span,
|
tcx.sess.span_bug(path.span,
|
||||||
"structure constructor wasn't resolved")
|
"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) => {
|
ast::ExprField(ref base, ref field, ref tys) => {
|
||||||
|
56
src/test/compile-fail/structure-constructor-type-mismatch.rs
Normal file
56
src/test/compile-fail/structure-constructor-type-mismatch.rs
Normal file
@ -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 <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.
|
||||||
|
|
||||||
|
struct Point<T> {
|
||||||
|
x: T,
|
||||||
|
y: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
type PointF = Point<f32>;
|
||||||
|
|
||||||
|
struct Pair<T,U> {
|
||||||
|
x: T,
|
||||||
|
y: U,
|
||||||
|
}
|
||||||
|
|
||||||
|
type PairF<U> = Pair<f32,U>;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let pt = PointF {
|
||||||
|
//~^ ERROR expected f32 but found int
|
||||||
|
x: 1i,
|
||||||
|
y: 2i,
|
||||||
|
};
|
||||||
|
|
||||||
|
let pt2 = Point::<f32> {
|
||||||
|
//~^ 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::<int> {
|
||||||
|
//~^ ERROR expected f32 but found int
|
||||||
|
x: 7i,
|
||||||
|
y: 8i,
|
||||||
|
};
|
||||||
|
|
||||||
|
let pt3 = PointF::<int> {
|
||||||
|
//~^ ERROR wrong number of type arguments
|
||||||
|
x: 9i,
|
||||||
|
y: 10i,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user