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:
Patrick Walton 2014-07-23 21:33:33 -07:00 committed by Alex Crichton
parent 3550068b53
commit 103d888f65
3 changed files with 136 additions and 1 deletions

View File

@ -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;

View File

@ -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) => {

View 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,
};
}