1
Fork 0

Implement implicit self type parameters for ifaces

Closes #1661
This commit is contained in:
Marijn Haverbeke 2012-01-25 16:44:51 +01:00
parent 76aabbe99d
commit 2d4d8e8bdb
8 changed files with 102 additions and 72 deletions

View file

@ -128,14 +128,15 @@ fn item_impl_iface(item: ebml::doc, tcx: ty::ctxt, cdata: cmd)
result result
} }
fn item_ty_param_bounds(item: ebml::doc, tcx: ty::ctxt, cdata: cmd) fn item_ty_param_bounds(item: ebml::doc, tcx: ty::ctxt, cdata: cmd,
-> @[ty::param_bounds] { skip: bool) -> @[ty::param_bounds] {
let bounds = []; let bounds = [], skip = skip;
ebml::tagged_docs(item, tag_items_data_item_ty_param_bounds) {|p| ebml::tagged_docs(item, tag_items_data_item_ty_param_bounds) {|p|
let bd = parse_bounds_data(p.data, p.start, cdata.cnum, tcx, {|did| let bd = parse_bounds_data(p.data, p.start, cdata.cnum, tcx, {|did|
translate_def_id(cdata, did) translate_def_id(cdata, did)
}); });
bounds += [bd]; if skip { skip = false; }
else { bounds += [bd]; }
} }
@bounds @bounds
} }
@ -218,8 +219,9 @@ fn get_type(cdata: cmd, id: ast::node_id, tcx: ty::ctxt)
-> ty::ty_param_bounds_and_ty { -> ty::ty_param_bounds_and_ty {
let item = lookup_item(id, cdata.data); let item = lookup_item(id, cdata.data);
let t = item_type(item, tcx, cdata); let t = item_type(item, tcx, cdata);
let tp_bounds = if family_has_type_params(item_family(item)) { let family = item_family(item);
item_ty_param_bounds(item, tcx, cdata) let tp_bounds = if family_has_type_params(family) {
item_ty_param_bounds(item, tcx, cdata, family == ('I' as u8))
} else { @[] }; } else { @[] };
ret {bounds: tp_bounds, ty: t}; ret {bounds: tp_bounds, ty: t};
} }
@ -302,7 +304,7 @@ fn get_iface_methods(cdata: cmd, id: ast::node_id, tcx: ty::ctxt)
let data = cdata.data; let data = cdata.data;
let item = lookup_item(id, data), result = []; let item = lookup_item(id, data), result = [];
ebml::tagged_docs(item, tag_item_method) {|mth| ebml::tagged_docs(item, tag_item_method) {|mth|
let bounds = item_ty_param_bounds(mth, tcx, cdata); let bounds = item_ty_param_bounds(mth, tcx, cdata, false);
let name = item_name(mth); let name = item_name(mth);
let ty = doc_type(mth, tcx, cdata); let ty = doc_type(mth, tcx, cdata);
let fty = alt ty::struct(tcx, ty) { ty::ty_fn(f) { f } }; let fty = alt ty::struct(tcx, ty) { ty::ty_fn(f) { f } };

View file

@ -1014,7 +1014,7 @@ fn lookup_in_ty_params(e: env, name: ident, ty_params: [ast::ty_param])
} { ret some(ast::def_ty_param(local_def(tp.id), n)); } } { ret some(ast::def_ty_param(local_def(tp.id), n)); }
n += 1u; n += 1u;
} }
ret none::<def>; ret none;
} }
fn lookup_in_pat(e: env, name: ident, pat: @ast::pat) -> option::t<def_id> { fn lookup_in_pat(e: env, name: ident, pat: @ast::pat) -> option::t<def_id> {

View file

@ -892,7 +892,7 @@ fn linearize_ty_params(cx: @block_ctxt, t: ty::t) ->
} }
let x = @{cx: cx, mutable vals: param_vals, mutable defs: param_defs}; let x = @{cx: cx, mutable vals: param_vals, mutable defs: param_defs};
let f = bind linearizer(x, _); let f = bind linearizer(x, _);
ty::walk_ty(bcx_tcx(cx), f, t); ty::walk_ty(bcx_tcx(cx), t, f);
ret {params: x.defs, descs: x.vals}; ret {params: x.defs, descs: x.vals};
} }

View file

@ -673,41 +673,37 @@ pure fn ty_name(cx: ctxt, typ: t) -> option::t<@str> {
} }
} }
fn walk_ty(cx: ctxt, ty: t, walker: fn(t)) {
// Type folds
type ty_walk = fn@(t);
fn walk_ty(cx: ctxt, walker: ty_walk, ty: t) {
alt struct(cx, ty) { alt struct(cx, ty) {
ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
ty_str | ty_send_type | ty_type | ty_native(_) | ty_str | ty_send_type | ty_type | ty_native(_) |
ty_opaque_closure_ptr(_) { ty_opaque_closure_ptr(_) {
/* no-op */ /* no-op */
} }
ty_box(tm) | ty_vec(tm) | ty_ptr(tm) { walk_ty(cx, walker, tm.ty); } ty_box(tm) | ty_vec(tm) | ty_ptr(tm) { walk_ty(cx, tm.ty, walker); }
ty_enum(_, subtys) | ty_iface(_, subtys) { ty_enum(_, subtys) | ty_iface(_, subtys) {
for subty: t in subtys { walk_ty(cx, walker, subty); } for subty: t in subtys { walk_ty(cx, subty, walker); }
} }
ty_rec(fields) { ty_rec(fields) {
for fl: field in fields { walk_ty(cx, walker, fl.mt.ty); } for fl: field in fields { walk_ty(cx, fl.mt.ty, walker); }
} }
ty_tup(ts) { for tt in ts { walk_ty(cx, walker, tt); } } ty_tup(ts) { for tt in ts { walk_ty(cx, tt, walker); } }
ty_fn(f) { ty_fn(f) {
for a: arg in f.inputs { walk_ty(cx, walker, a.ty); } for a: arg in f.inputs { walk_ty(cx, a.ty, walker); }
walk_ty(cx, walker, f.output); walk_ty(cx, f.output, walker);
} }
ty_native_fn(args, ret_ty) { ty_native_fn(args, ret_ty) {
for a: arg in args { walk_ty(cx, walker, a.ty); } for a: arg in args { walk_ty(cx, a.ty, walker); }
walk_ty(cx, walker, ret_ty); walk_ty(cx, ret_ty, walker);
} }
ty_res(_, sub, tps) { ty_res(_, sub, tps) {
walk_ty(cx, walker, sub); walk_ty(cx, sub, walker);
for tp: t in tps { walk_ty(cx, walker, tp); } for tp: t in tps { walk_ty(cx, tp, walker); }
} }
ty_constr(sub, _) { walk_ty(cx, walker, sub); } ty_constr(sub, _) { walk_ty(cx, sub, walker); }
ty_var(_) {/* no-op */ } ty_var(_) {/* no-op */ }
ty_param(_, _) {/* no-op */ } ty_param(_, _) {/* no-op */ }
ty_uniq(tm) { walk_ty(cx, walker, tm.ty); } ty_uniq(tm) { walk_ty(cx, tm.ty, walker); }
} }
walker(ty); walker(ty);
} }
@ -1273,7 +1269,7 @@ fn vars_in_type(cx: ctxt, ty: t) -> [int] {
alt struct(cx, ty) { ty_var(v) { *vars += [v]; } _ { } } alt struct(cx, ty) { ty_var(v) { *vars += [v]; } _ { } }
} }
let rslt: @mutable [int] = @mutable []; let rslt: @mutable [int] = @mutable [];
walk_ty(cx, bind collect_var(cx, rslt, _), ty); walk_ty(cx, ty) {|t| collect_var(cx, rslt, t)}
// Works because of a "convenient" bug that lets us // Works because of a "convenient" bug that lets us
// return a mutable vec as if it's immutable // return a mutable vec as if it's immutable
ret *rslt; ret *rslt;
@ -1529,7 +1525,7 @@ fn count_ty_params(cx: ctxt, ty: t) -> uint {
} }
let param_indices: @mutable [uint] = @mutable []; let param_indices: @mutable [uint] = @mutable [];
let f = bind counter(cx, param_indices, _); let f = bind counter(cx, param_indices, _);
walk_ty(cx, f, ty); walk_ty(cx, ty, f);
ret vec::len::<uint>(*param_indices); ret vec::len::<uint>(*param_indices);
} }
@ -2695,7 +2691,6 @@ fn lookup_item_type(cx: ctxt, did: ast::def_id) -> ty_param_bounds_and_ty {
if did.crate == ast::local_crate { if did.crate == ast::local_crate {
// The item is in this crate. The caller should have added it to the // The item is in this crate. The caller should have added it to the
// type cache already; we simply return it. // type cache already; we simply return it.
ret cx.tcache.get(did); ret cx.tcache.get(did);
} }
alt cx.tcache.find(did) { alt cx.tcache.find(did) {

View file

@ -415,16 +415,15 @@ fn ty_of_item(tcx: ty::ctxt, mode: mode, it: @ast::item)
ret tpt; ret tpt;
} }
ast::item_iface(tps, ms) { ast::item_iface(tps, ms) {
let {bounds, params} = mk_ty_params(tcx, tps); let s_tp = vec::len(tps) - 1u;
let t = ty::mk_named(tcx, ty::mk_iface(tcx, local_def(it.id), tcx.ty_param_bounds.insert(tps[s_tp].id, @[]);
params), let {bounds, params} = mk_ty_params(tcx, vec::slice(tps, 0u, s_tp));
let t = ty::mk_named(tcx, ty::mk_iface(tcx, local_def(it.id), params),
@it.ident); @it.ident);
let tpt = {bounds: bounds, ty: t}; let tpt = {bounds: bounds, ty: t};
tcx.tcache.insert(local_def(it.id), tpt); tcx.tcache.insert(local_def(it.id), tpt);
ret tpt; ret tpt;
} }
ast::item_impl(_, _, _, _) | ast::item_mod(_) |
ast::item_native_mod(_) { fail; }
} }
} }
fn ty_of_native_item(tcx: ty::ctxt, mode: mode, it: @ast::native_item) fn ty_of_native_item(tcx: ty::ctxt, mode: mode, it: @ast::native_item)
@ -604,12 +603,20 @@ fn mk_ty_params(tcx: ty::ctxt, atps: [ast::ty_param])
} }
fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method, fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method,
impl_tps: uint, if_m: ty::method, substs: [ty::t]) { impl_tps: uint, if_m: ty::method, substs: [ty::t])
-> ty::t {
if impl_m.tps != if_m.tps { if impl_m.tps != if_m.tps {
tcx.sess.span_err(sp, "method `" + if_m.ident + tcx.sess.span_err(sp, "method `" + if_m.ident +
"` has an incompatible set of type parameters"); "` has an incompatible set of type parameters");
ty::mk_fn(tcx, impl_m.fty)
} else { } else {
let impl_fty = ty::mk_fn(tcx, impl_m.fty); let auto_modes = vec::map2(impl_m.fty.inputs, if_m.fty.inputs, {|i, f|
alt ty::struct(tcx, f.ty) {
ty::ty_param(0u, _) { {mode: ast::by_ref with i} }
_ { i }
}
});
let impl_fty = ty::mk_fn(tcx, {inputs: auto_modes with impl_m.fty});
// Add dummy substs for the parameters of the impl method // Add dummy substs for the parameters of the impl method
let substs = substs + vec::init_fn(vec::len(*if_m.tps), {|i| let substs = substs + vec::init_fn(vec::len(*if_m.tps), {|i|
ty::mk_param(tcx, i + impl_tps, {crate: 0, node: 0}) ty::mk_param(tcx, i + impl_tps, {crate: 0, node: 0})
@ -621,8 +628,9 @@ fn compare_impl_method(tcx: ty::ctxt, sp: span, impl_m: ty::method,
tcx.sess.span_err(sp, "method `" + if_m.ident + tcx.sess.span_err(sp, "method `" + if_m.ident +
"` has an incompatible type: " + "` has an incompatible type: " +
ty::type_err_to_str(err)); ty::type_err_to_str(err));
impl_fty
} }
_ {} ty::unify::ures_ok(tp) { tp }
} }
} }
} }
@ -690,15 +698,15 @@ mod collect {
for m in ms { for m in ms {
let bounds = ty_param_bounds(cx.tcx, m_collect, m.tps); let bounds = ty_param_bounds(cx.tcx, m_collect, m.tps);
let mty = ty_of_method(cx.tcx, m_collect, m); let mty = ty_of_method(cx.tcx, m_collect, m);
my_methods += [mty]; my_methods += [{mty: mty, id: m.id}];
let fty = ty::mk_fn(cx.tcx, mty.fty); let fty = ty::mk_fn(cx.tcx, mty.fty);
cx.tcx.tcache.insert(local_def(m.id), cx.tcx.tcache.insert(local_def(m.id),
{bounds: @(*i_bounds + *bounds), {bounds: @(*i_bounds + *bounds),
ty: fty}); ty: fty});
write::ty_only(cx.tcx, m.id, fty); write::ty_only(cx.tcx, m.id, fty);
} }
write::ty_only(cx.tcx, it.id, ast_ty_to_ty(cx.tcx, m_collect, let selfty = ast_ty_to_ty(cx.tcx, m_collect, selfty);
selfty)); write::ty_only(cx.tcx, it.id, selfty);
alt ifce { alt ifce {
some(t) { some(t) {
let iface_ty = ast_ty_to_ty(cx.tcx, m_collect, t); let iface_ty = ast_ty_to_ty(cx.tcx, m_collect, t);
@ -708,10 +716,18 @@ mod collect {
ty::ty_iface(did, tys) { ty::ty_iface(did, tys) {
for if_m in *ty::iface_methods(cx.tcx, did) { for if_m in *ty::iface_methods(cx.tcx, did) {
alt vec::find(my_methods, alt vec::find(my_methods,
{|m| if_m.ident == m.ident}) { {|m| if_m.ident == m.mty.ident}) {
some(m) { some({mty: m, id}) {
compare_impl_method(cx.tcx, t.span, m, let mt = compare_impl_method(
vec::len(tps), if_m, tys); cx.tcx, t.span, m, vec::len(tps), if_m,
tys + [selfty]);
let old = cx.tcx.tcache.get(local_def(id));
if old.ty != mt {
cx.tcx.tcache.insert(local_def(id),
{bounds: old.bounds,
ty: mt});
write::ty_only(cx.tcx, id, mt);
}
} }
none { none {
cx.tcx.sess.span_err(t.span, "missing method `" + cx.tcx.sess.span_err(t.span, "missing method `" +
@ -1501,7 +1517,7 @@ fn lookup_method(fcx: @fn_ctxt, isc: resolve::iscopes,
let m = ifce_methods[pos]; let m = ifce_methods[pos];
ret some({method_ty: ty::mk_fn(tcx, m.fty), ret some({method_ty: ty::mk_fn(tcx, m.fty),
n_tps: vec::len(*m.tps), n_tps: vec::len(*m.tps),
substs: tps, substs: tps + [ty],
origin: method_param(iid, pos, n, bound_n)}); origin: method_param(iid, pos, n, bound_n)});
} }
_ {} _ {}
@ -1519,7 +1535,7 @@ fn lookup_method(fcx: @fn_ctxt, isc: resolve::iscopes,
if m.ident == name { if m.ident == name {
ret some({method_ty: ty::mk_fn(tcx, m.fty), ret some({method_ty: ty::mk_fn(tcx, m.fty),
n_tps: vec::len(*m.tps), n_tps: vec::len(*m.tps),
substs: tps, substs: tps + [ty::mk_int(tcx)],
origin: method_iface(i)}); origin: method_iface(i)});
} }
i += 1u; i += 1u;
@ -1528,17 +1544,6 @@ fn lookup_method(fcx: @fn_ctxt, isc: resolve::iscopes,
_ {} _ {}
} }
fn ty_from_did(tcx: ty::ctxt, did: ast::def_id) -> ty::t {
if did.crate == ast::local_crate {
alt tcx.items.get(did.node) {
ast_map::node_method(m) {
let mt = ty_of_method(tcx, m_check, m);
ty::mk_fn(tcx, mt.fty)
}
}
} else { csearch::get_type(tcx, did).ty }
}
let result = none; let result = none;
std::list::iter(isc) {|impls| std::list::iter(isc) {|impls|
if option::is_some(result) { ret; } if option::is_some(result) { ret; }
@ -1557,7 +1562,7 @@ fn lookup_method(fcx: @fn_ctxt, isc: resolve::iscopes,
sp, "multiple applicable methods in scope"); sp, "multiple applicable methods in scope");
} else { } else {
result = some({ result = some({
method_ty: ty_from_did(tcx, m.did), method_ty: ty::lookup_item_type(tcx, m.did).ty,
n_tps: m.n_tps, n_tps: m.n_tps,
substs: vars, substs: vars,
origin: method_static(m.did) origin: method_static(m.did)
@ -1812,16 +1817,11 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
check_binop_type_compat(fcx, expr.span, lhs_t, binop); check_binop_type_compat(fcx, expr.span, lhs_t, binop);
let t = let t = alt binop {
alt binop { ast::eq | ast::lt | ast::le | ast::ne | ast::ge |
ast::eq { ty::mk_bool(tcx) } ast::gt { ty::mk_bool(tcx) }
ast::lt { ty::mk_bool(tcx) } _ { lhs_t }
ast::le { ty::mk_bool(tcx) } };
ast::ne { ty::mk_bool(tcx) }
ast::ge { ty::mk_bool(tcx) }
ast::gt { ty::mk_bool(tcx) }
_ { lhs_t }
};
write::ty_only_fixup(fcx, id, t); write::ty_only_fixup(fcx, id, t);
} }
ast::expr_unary(unop, oper) { ast::expr_unary(unop, oper) {
@ -2915,9 +2915,38 @@ mod dict {
} }
} }
ast::expr_cast(src, _) { ast::expr_cast(src, _) {
// Ifaces that refer to a self type can not be cast to -- callers
// wouldn't know what self refers to.
fn type_refers_to_self(tcx: ty::ctxt, t: ty::t, s_param: uint)
-> bool {
let found = false;
if ty::type_contains_params(tcx, t) {
ty::walk_ty(tcx, t) {|t|
alt ty::struct(tcx, t) {
ty::ty_param(n, _) if n == s_param { found = true; }
_ {}
}
}
}
found
}
fn method_refers_to_self(tcx: ty::ctxt, m: ty::method,
s_param: uint) -> bool {
vec::any(m.fty.inputs, {|in|
type_refers_to_self(tcx, in.ty, s_param)
}) || type_refers_to_self(tcx, m.fty.output, s_param)
}
let target_ty = expr_ty(cx.tcx, ex); let target_ty = expr_ty(cx.tcx, ex);
alt ty::struct(cx.tcx, target_ty) { alt ty::struct(cx.tcx, target_ty) {
ty::ty_iface(_, _) { ty::ty_iface(id, tps) {
for m in *ty::iface_methods(cx.tcx, id) {
if method_refers_to_self(cx.tcx, m, vec::len(tps)) {
cx.tcx.sess.span_err(
ex.span, "can not cast to an iface type that \
refers to `self` " + m.ident);
break;
}
}
let impls = cx.impl_map.get(ex.id); let impls = cx.impl_map.get(ex.id);
let dict = lookup_dict(fcx, impls, ex.span, let dict = lookup_dict(fcx, impls, ex.span,
expr_ty(cx.tcx, src), target_ty); expr_ty(cx.tcx, src), target_ty);

View file

@ -1831,9 +1831,10 @@ fn parse_method(p: parser) -> @ast::method {
fn parse_item_iface(p: parser, attrs: [ast::attribute]) -> @ast::item { fn parse_item_iface(p: parser, attrs: [ast::attribute]) -> @ast::item {
let lo = p.last_span.lo, ident = parse_ident(p), let lo = p.last_span.lo, ident = parse_ident(p),
tps = parse_ty_params(p), meths = parse_ty_methods(p); tps = parse_ty_params(p), meths = parse_ty_methods(p),
self_tp = {ident: "self", id: p.get_id(), bounds: @[]};
ret mk_item(p, lo, p.last_span.hi, ident, ret mk_item(p, lo, p.last_span.hi, ident,
ast::item_iface(tps, meths), attrs); ast::item_iface(tps + [self_tp], meths), attrs);
} }
// Parses three variants (with the initial params always optional): // Parses three variants (with the initial params always optional):

View file

@ -488,7 +488,8 @@ fn print_item(s: ps, &&item: @ast::item) {
ast::item_iface(tps, methods) { ast::item_iface(tps, methods) {
head(s, "iface"); head(s, "iface");
word(s.s, item.ident); word(s.s, item.ident);
print_type_params(s, tps); print_type_params(s, vec::slice(tps, 0u, vec::len(tps) - 1u));
nbsp(s);
bopen(s); bopen(s);
for meth in methods { print_ty_method(s, meth); } for meth in methods { print_ty_method(s, meth); }
bclose(s, item.span); bclose(s, item.span);

View file

@ -12,9 +12,11 @@ impl of to_str for () {
} }
iface map<T> { iface map<T> {
fn iter(fn(T));
fn map<U>(f: fn(T) -> U) -> [U]; fn map<U>(f: fn(T) -> U) -> [U];
} }
impl <T> of map<T> for [T] { impl <T> of map<T> for [T] {
fn iter(_f: fn(T)) {}
fn map<U>(f: fn(T) -> U) -> [U] { fn map<U>(f: fn(T) -> U) -> [U] {
let r = []; let r = [];
for x in self { r += [f(x)]; } for x in self { r += [f(x)]; }