Check that type parameter bounds are interface types

Issue #1227
This commit is contained in:
Marijn Haverbeke 2011-12-29 11:23:35 +01:00
parent 7ea175f23f
commit 40d5f288c3
6 changed files with 96 additions and 45 deletions

View File

@ -129,11 +129,8 @@ fn item_ty_param_bounds(item: ebml::doc, this_cnum: ast::crate_num,
fn item_ty_param_count(item: ebml::doc) -> uint {
let n = 0u;
ebml::tagged_docs(item, tag_items_data_item_ty_param_bounds) {|p|
for byte in ebml::doc_data(p) {
if byte as char == '.' { n += 1u; }
}
}
ebml::tagged_docs(item, tag_items_data_item_ty_param_bounds,
{|_p| n += 1u; });
n
}

View File

@ -202,9 +202,17 @@ fn parse_ty(st: @pstate, sd: str_def) -> ty::t {
st.pos = st.pos + 1u;
ret ty::mk_tag(st.tcx, def, params);
}
'x' {
assert (next(st) as char == '[');
let def = parse_def(st, sd);
let params: [ty::t] = [];
while peek(st) as char != ']' { params += [parse_ty(st, sd)]; }
st.pos = st.pos + 1u;
ret ty::mk_iface(st.tcx, def, params);
}
'p' {
let bounds = parse_bounds(st, sd);
ret ty::mk_param(st.tcx, parse_int(st) as uint, bounds);
let did = parse_def(st, sd);
ret ty::mk_param(st.tcx, parse_int(st) as uint, did);
}
'@' { ret ty::mk_box(st.tcx, parse_mt(st, sd)); }
'~' { ret ty::mk_uniq(st.tcx, parse_mt(st, sd)); }
@ -401,8 +409,7 @@ fn parse_bounds_data(data: @[u8], crate_num: int, sd: str_def, tcx: ty::ctxt)
fn parse_bounds(st: @pstate, sd: str_def) -> @[ty::param_bound] {
let bounds = [];
while peek(st) as char == '.' {
next(st);
while peek(st) != 0u8 {
bounds += [alt next(st) as char {
'S' { ty::bound_send }
'C' { ty::bound_copy }

View File

@ -126,6 +126,13 @@ fn enc_sty(w: io::writer, cx: @ctxt, st: ty::sty) {
for t: ty::t in tys { enc_ty(w, cx, t); }
w.write_char(']');
}
ty::ty_iface(def, tys) {
w.write_str("x[");
w.write_str(cx.ds(def));
w.write_char('|');
for t: ty::t in tys { enc_ty(w, cx, t); }
w.write_char(']');
}
ty::ty_tup(ts) {
w.write_str("T[");
for t in ts { enc_ty(w, cx, t); }
@ -176,9 +183,10 @@ fn enc_sty(w: io::writer, cx: @ctxt, st: ty::sty) {
w.write_str(cx.ds(def));
w.write_char('|');
}
ty::ty_param(id, bounds) {
ty::ty_param(id, did) {
w.write_char('p');
enc_bounds(w, cx, bounds);
w.write_str(cx.ds(did));
w.write_char('|');
w.write_str(uint::str(id));
}
ty::ty_type. { w.write_char('Y'); }
@ -265,7 +273,6 @@ fn enc_ty_constr(w: io::writer, cx: @ctxt, c: @ty::type_constr) {
fn enc_bounds(w: io::writer, cx: @ctxt, bs: @[ty::param_bound]) {
for bound in *bs {
w.write_char('.');
alt bound {
ty::bound_send. { w.write_char('S'); }
ty::bound_copy. { w.write_char('C'); }

View File

@ -4658,7 +4658,8 @@ fn trans_tag_variant(cx: @local_ctxt, tag_id: ast::node_id,
let ty_param_substs: [ty::t] = [];
i = 0u;
for tp: ast::ty_param in ty_params {
ty_param_substs += [ty::mk_param(ccx.tcx, i, @[])];
ty_param_substs += [ty::mk_param(ccx.tcx, i,
ast_util::local_def(tp.id))];
i += 1u;
}
let arg_tys = arg_tys_of_fn(ccx, variant.node.id);

View File

@ -218,7 +218,7 @@ type ctxt =
needs_drop_cache: hashmap<t, bool>,
kind_cache: hashmap<t, kind>,
ast_ty_to_ty_cache: hashmap<@ast::ty, option::t<t>>,
tag_var_cache: hashmap<ast::def_id, @[variant_info]>,
tag_var_cache: hashmap<def_id, @[variant_info]>,
iface_method_cache: hashmap<def_id, @[method]>,
ty_param_bounds: hashmap<def_id, @[param_bound]>};
@ -268,7 +268,7 @@ tag sty {
ty_tup([t]);
ty_var(int); // type variable
ty_param(uint, @[param_bound]); // fn/tag type param
ty_param(uint, def_id); // fn/tag type param
ty_type; // type_desc*
ty_send_type; // type_desc* that has been cloned into exchange heap
@ -624,7 +624,7 @@ fn mk_res(cx: ctxt, did: ast::def_id, inner: t, tps: [t]) -> t {
fn mk_var(cx: ctxt, v: int) -> t { ret gen_ty(cx, ty_var(v)); }
fn mk_param(cx: ctxt, n: uint, k: @[param_bound]) -> t {
fn mk_param(cx: ctxt, n: uint, k: def_id) -> t {
ret gen_ty(cx, ty_param(n, k));
}
@ -722,7 +722,7 @@ fn walk_ty(cx: ctxt, walker: ty_walk, ty: t) {
tag fold_mode {
fm_var(fn@(int) -> t);
fm_param(fn@(uint, @[param_bound]) -> t);
fm_param(fn@(uint, def_id) -> t);
fm_general(fn@(t) -> t);
}
@ -813,8 +813,8 @@ fn fold_ty(cx: ctxt, fld: fold_mode, ty_0: t) -> t {
ty_var(id) {
alt fld { fm_var(folder) { ty = folder(id); } _ {/* no-op */ } }
}
ty_param(id, k) {
alt fld { fm_param(folder) { ty = folder(id, k); } _ {/* no-op */ } }
ty_param(id, did) {
alt fld { fm_param(folder) { ty = folder(id, did); } _ {/* no-op */ } }
}
}
@ -1083,7 +1083,7 @@ fn type_kind(cx: ctxt, ty: t) -> kind {
}
// Resources are always noncopyable.
ty_res(did, inner, tps) { kind_noncopyable }
ty_param(_, bounds) { param_bounds_to_kind(bounds) }
ty_param(_, did) { param_bounds_to_kind(cx.ty_param_bounds.get(did)) }
ty_constr(t, _) { type_kind(cx, t) }
};
@ -1131,7 +1131,7 @@ fn type_structurally_contains(cx: ctxt, ty: t, test: fn(sty) -> bool) ->
}
}
pure fn type_has_dynamic_size(cx: ctxt, ty: t) -> bool {
pure fn type_has_dynamic_size(cx: ctxt, ty: t) -> bool unchecked {
/* type_structurally_contains can't be declared pure
because it takes a function argument. But it should be
@ -1141,15 +1141,9 @@ pure fn type_has_dynamic_size(cx: ctxt, ty: t) -> bool {
actually checkable. It seems to me like a lot of properties
that the type context tracks about types should be immutable.)
*/
unchecked{
type_structurally_contains(cx, ty,
fn (sty: sty) -> bool {
ret alt sty {
ty_param(_, _) { true }
_ { false }
};
})
}
type_structurally_contains(cx, ty, fn (sty: sty) -> bool {
alt sty { ty_param(_, _) { true } _ { false }}
})
}
// Returns true for noncopyable types and types where a copy of a value can be
@ -2205,7 +2199,14 @@ mod unify {
_ { ret ures_err(terr_mismatch); }
}
}
ty::ty_param(_, _) { ret struct_cmp(cx, expected, actual); }
ty::ty_param(expected_n, _) {
alt struct(cx.tcx, actual) {
ty::ty_param(actual_n, _) when expected_n == actual_n {
ret ures_ok(expected);
}
_ { ret ures_err(terr_mismatch); }
}
}
ty::ty_tag(expected_id, expected_tps) {
alt struct(cx.tcx, actual) {
ty::ty_tag(actual_id, actual_tps) {
@ -2627,8 +2628,7 @@ fn bind_params_in_type(sp: span, cx: ctxt, next_ty_var: fn@() -> int, typ: t,
let i = 0u;
while i < ty_param_count { *param_var_ids += [next_ty_var()]; i += 1u; }
fn binder(sp: span, cx: ctxt, param_var_ids: @mutable [int],
_next_ty_var: fn@() -> int, index: uint,
_bounds: @[param_bound]) -> t {
_next_ty_var: fn@() -> int, index: uint, _did: def_id) -> t {
if index < vec::len(*param_var_ids) {
ret mk_var(cx, param_var_ids[index]);
} else {
@ -2647,9 +2647,8 @@ fn bind_params_in_type(sp: span, cx: ctxt, next_ty_var: fn@() -> int, typ: t,
// substitions.
fn substitute_type_params(cx: ctxt, substs: [ty::t], typ: t) -> t {
if !type_contains_params(cx, typ) { ret typ; }
fn substituter(_cx: ctxt, substs: @[ty::t], idx: uint,
_bounds: @[param_bound])
-> t {
fn substituter(_cx: ctxt, substs: @[ty::t], idx: uint, _did: def_id)
-> t {
// FIXME: bounds check can fail
ret substs[idx];
}

View File

@ -343,7 +343,7 @@ fn ast_ty_to_ty(tcx: ty::ctxt, mode: mode, &&ast_ty: @ast::ty) -> ty::t {
}
some(ast::def_native_ty(id)) { typ = getter(tcx, mode, id).ty; }
some(ast::def_ty_param(id, n)) {
typ = ty::mk_param(tcx, n, tcx.ty_param_bounds.get(id));
typ = ty::mk_param(tcx, n, id);
}
some(_) {
tcx.sess.span_fatal(ast_ty.span,
@ -625,8 +625,8 @@ fn mk_ty_params(tcx: ty::ctxt, atps: [ast::ty_param])
-> {bounds: [@[ty::param_bound]], params: [ty::t]} {
let i = 0u, bounds = ty_param_bounds(tcx, m_collect, atps);
{bounds: bounds,
params: vec::map(atps, {|_atp|
let t = ty::mk_param(tcx, i, bounds[i]);
params: vec::map(atps, {|atp|
let t = ty::mk_param(tcx, i, local_def(atp.id));
i += 1u;
t
})}
@ -2666,13 +2666,16 @@ fn check_method(ccx: @crate_ctxt, method: @ast::method) {
fn check_item(ccx: @crate_ctxt, it: @ast::item) {
alt it.node {
ast::item_const(_, e) { check_const(ccx, it.span, e, it.id); }
ast::item_fn(decl, _, body) {
ast::item_fn(decl, tps, body) {
check_ty_params(ccx, tps);
check_fn(ccx, ast::proto_bare, decl, body, it.id, none);
}
ast::item_res(decl, _, body, dtor_id, _) {
ast::item_res(decl, tps, body, dtor_id, _) {
check_ty_params(ccx, tps);
check_fn(ccx, ast::proto_bare, decl, body, dtor_id, none);
}
ast::item_obj(ob, _, _) {
ast::item_obj(ob, tps, _) {
check_ty_params(ccx, tps);
// We're entering an object, so gather up the info we need.
ccx.self_infos += [self_obj(ob.fields,
ccx.tcx.tcache.get(local_def(it.id)).ty)];
@ -2681,9 +2684,11 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
// Now remove the info from the stack.
vec::pop(ccx.self_infos);
}
ast::item_impl(_, ifce, ty, ms) {
ast::item_impl(tps, ifce, ty, ms) {
check_ty_params(ccx, tps);
ccx.self_infos += [self_impl(ast_ty_to_ty(ccx.tcx, m_check, ty))];
let my_methods = vec::map(ms, {|m|
check_ty_params(ccx, m.tps);
check_method(ccx, m);
ty_of_method(ccx.tcx, m_check, m)
});
@ -2717,10 +2722,43 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
_ {}
}
}
ast::item_iface(tps, _) | ast::item_ty(_, tps) | ast::item_tag(_, tps) {
check_ty_params(ccx, tps);
}
_ {/* nothing to do */ }
}
}
fn check_native_item(ccx: @crate_ctxt, it: @ast::native_item) {
alt it.node {
ast::native_item_fn(_, tps) { check_ty_params(ccx, tps); }
_ {}
}
}
fn check_ty_params(ccx: @crate_ctxt, tps: [ast::ty_param]) {
for tp in tps {
let i = 0u;
for bound in *tp.bounds {
alt bound {
ast::bound_iface(at) {
let tbound = ccx.tcx.ty_param_bounds.get(local_def(tp.id))[i];
let bound_ty = alt tbound { ty::bound_iface(t) { t } };
alt ty::struct(ccx.tcx, bound_ty) {
ty::ty_iface(_, _) {}
_ {
ccx.tcx.sess.span_err(at.span, "type parameter bounds \
must be interface types");
}
}
}
_ {}
}
i += 1u;
}
}
}
fn arg_is_argv_ty(tcx: ty::ctxt, a: ty::arg) -> bool {
alt ty::struct(tcx, a.ty) {
ty::ty_vec(mt) {
@ -2778,8 +2816,10 @@ fn check_crate(tcx: ty::ctxt, impl_map: resolve::impl_map,
method_map: std::map::new_int_hash(),
tcx: tcx};
let visit =
visit::mk_simple_visitor(@{visit_item: bind check_item(ccx, _)
with *visit::default_simple_visitor()});
visit::mk_simple_visitor(@{visit_item: bind check_item(ccx, _),
visit_native_item:
bind check_native_item(ccx, _)
with *visit::default_simple_visitor()});
visit::visit_crate(*crate, (), visit);
check_for_main_fn(tcx, crate);
tcx.sess.abort_if_errors();