Skip to content

Commit 103d888

Browse files
pcwaltonalexcrichton
authored andcommitted
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]
1 parent 3550068 commit 103d888

File tree

3 files changed

+136
-1
lines changed

3 files changed

+136
-1
lines changed

src/librustc/middle/typeck/astconv.rs

+42
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ use middle::const_eval;
5353
use middle::def;
5454
use middle::lang_items::FnMutTraitLangItem;
5555
use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs};
56+
use middle::subst::{VecPerParamSpace};
5657
use middle::ty;
5758
use middle::ty_fold::TypeFolder;
5859
use middle::typeck::rscope::{ExplicitRscope, ImpliedSingleRscope};
@@ -299,6 +300,47 @@ pub fn ast_path_to_ty<AC:AstConv,RS:RegionScope>(
299300
TypeAndSubsts { substs: substs, ty: ty }
300301
}
301302

303+
/// Returns the type that this AST path refers to. If the path has no type
304+
/// parameters and the corresponding type has type parameters, fresh type
305+
/// and/or region variables are substituted.
306+
///
307+
/// This is used when checking the constructor in struct literals.
308+
pub fn ast_path_to_ty_relaxed<AC:AstConv,
309+
RS:RegionScope>(
310+
this: &AC,
311+
rscope: &RS,
312+
did: ast::DefId,
313+
path: &ast::Path)
314+
-> TypeAndSubsts {
315+
let tcx = this.tcx();
316+
let ty::Polytype {
317+
generics: generics,
318+
ty: decl_ty
319+
} = this.get_item_ty(did);
320+
321+
let substs = if (generics.has_type_params(TypeSpace) ||
322+
generics.has_region_params(TypeSpace)) &&
323+
path.segments.iter().all(|s| {
324+
s.lifetimes.len() == 0 && s.types.len() == 0
325+
}) {
326+
let type_params = Vec::from_fn(generics.types.len(TypeSpace),
327+
|_| this.ty_infer(path.span));
328+
let region_params =
329+
rscope.anon_regions(path.span, generics.regions.len(TypeSpace))
330+
.unwrap();
331+
Substs::new(VecPerParamSpace::params_from_type(type_params),
332+
VecPerParamSpace::params_from_type(region_params))
333+
} else {
334+
ast_path_substs(this, rscope, &generics, None, path)
335+
};
336+
337+
let ty = decl_ty.subst(tcx, &substs);
338+
TypeAndSubsts {
339+
substs: substs,
340+
ty: ty,
341+
}
342+
}
343+
302344
pub static NO_REGIONS: uint = 1;
303345
pub static NO_TPS: uint = 2;
304346

src/librustc/middle/typeck/check/mod.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -3416,10 +3416,11 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
34163416
ast::ExprStruct(ref path, ref fields, base_expr) => {
34173417
// Resolve the path.
34183418
let def = tcx.def_map.borrow().find(&id).map(|i| *i);
3419-
match def {
3419+
let struct_id = match def {
34203420
Some(def::DefVariant(enum_id, variant_id, _)) => {
34213421
check_struct_enum_variant(fcx, id, expr.span, enum_id,
34223422
variant_id, fields.as_slice());
3423+
enum_id
34233424
}
34243425
Some(def) => {
34253426
// Verify that this was actually a struct.
@@ -3439,11 +3440,47 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
34393440
pprust::path_to_string(path));
34403441
}
34413442
}
3443+
3444+
def.def_id()
34423445
}
34433446
_ => {
34443447
tcx.sess.span_bug(path.span,
34453448
"structure constructor wasn't resolved")
34463449
}
3450+
};
3451+
3452+
// Turn the path into a type and verify that that type unifies with
3453+
// the resulting structure type. This is needed to handle type
3454+
// parameters correctly.
3455+
let actual_structure_type = fcx.expr_ty(&*expr);
3456+
if !ty::type_is_error(actual_structure_type) {
3457+
let type_and_substs = astconv::ast_path_to_ty_relaxed(fcx,
3458+
fcx.infcx(),
3459+
struct_id,
3460+
path);
3461+
match fcx.mk_subty(false,
3462+
infer::Misc(path.span),
3463+
actual_structure_type,
3464+
type_and_substs.ty) {
3465+
Ok(()) => {}
3466+
Err(type_error) => {
3467+
let type_error_description =
3468+
ty::type_err_to_str(tcx, &type_error);
3469+
fcx.tcx()
3470+
.sess
3471+
.span_err(path.span,
3472+
format!("structure constructor specifies a \
3473+
structure of type `{}`, but this \
3474+
structure has type `{}`: {}",
3475+
fcx.infcx()
3476+
.ty_to_string(type_and_substs.ty),
3477+
fcx.infcx()
3478+
.ty_to_string(
3479+
actual_structure_type),
3480+
type_error_description).as_slice());
3481+
ty::note_and_explain_type_err(tcx, &type_error);
3482+
}
3483+
}
34473484
}
34483485
}
34493486
ast::ExprField(ref base, ref field, ref tys) => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct Point<T> {
12+
x: T,
13+
y: T,
14+
}
15+
16+
type PointF = Point<f32>;
17+
18+
struct Pair<T,U> {
19+
x: T,
20+
y: U,
21+
}
22+
23+
type PairF<U> = Pair<f32,U>;
24+
25+
fn main() {
26+
let pt = PointF {
27+
//~^ ERROR expected f32 but found int
28+
x: 1i,
29+
y: 2i,
30+
};
31+
32+
let pt2 = Point::<f32> {
33+
//~^ ERROR expected f32 but found int
34+
x: 3i,
35+
y: 4i,
36+
};
37+
38+
let pair = PairF {
39+
//~^ ERROR expected f32 but found int
40+
x: 5i,
41+
y: 6i,
42+
};
43+
44+
let pair2 = PairF::<int> {
45+
//~^ ERROR expected f32 but found int
46+
x: 7i,
47+
y: 8i,
48+
};
49+
50+
let pt3 = PointF::<int> {
51+
//~^ ERROR wrong number of type arguments
52+
x: 9i,
53+
y: 10i,
54+
};
55+
}
56+

0 commit comments

Comments
 (0)