Skip to content

librustc: Check structure constructors against their types. #15938

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/librustc/middle/typeck/astconv.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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) => {
Expand Down
56 changes: 56 additions & 0 deletions src/test/compile-fail/structure-constructor-type-mismatch.rs
Original file line number Diff line number Diff line change
@@ -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,
};
}