Skip to content

Commit

Permalink
librustc: Check structure constructors against their types.
Browse files Browse the repository at this point in the history
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]
  • Loading branch information
pcwalton authored and alexcrichton committed Jul 24, 2014
1 parent 3550068 commit 103d888
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 1 deletion.
42 changes: 42 additions & 0 deletions src/librustc/middle/typeck/astconv.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -299,6 +300,47 @@ pub fn ast_path_to_ty<AC:AstConv,RS:RegionScope>(
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_TPS: uint = 2;

Expand Down
39 changes: 38 additions & 1 deletion src/librustc/middle/typeck/check/mod.rs
Expand Up @@ -3416,10 +3416,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.
Expand All @@ -3439,11 +3440,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) => {
Expand Down
56 changes: 56 additions & 0 deletions 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 <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,
};
}

0 comments on commit 103d888

Please sign in to comment.