better support for classes with polymorphic methods

This commit is contained in:
Niko Matsakis 2012-06-04 11:21:11 -07:00
parent 7213274e57
commit 3c4baf694e
8 changed files with 37 additions and 70 deletions

View File

@ -664,7 +664,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
ebml_w.start_tag(tag_item_iface_method); ebml_w.start_tag(tag_item_iface_method);
encode_family(ebml_w, purity_fn_family(m.decl.purity)); encode_family(ebml_w, purity_fn_family(m.decl.purity));
encode_name(ebml_w, m.ident); encode_name(ebml_w, m.ident);
encode_type_param_bounds(ebml_w, ecx, tps + m.tps); encode_type_param_bounds(ebml_w, ecx, m.tps);
encode_type(ecx, ebml_w, node_id_to_type(tcx, m.id)); encode_type(ecx, ebml_w, node_id_to_type(tcx, m.id));
encode_def_id(ebml_w, local_def(m.id)); encode_def_id(ebml_w, local_def(m.id));
ebml_w.end_tag(); ebml_w.end_tag();

View File

@ -254,29 +254,16 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
expr_field(base, _, _) { expr_field(base, _, _) {
alt cx.method_map.get(e.id) { alt cx.method_map.get(e.id) {
typeck::method_static(did) { typeck::method_static(did) {
/* // n.b.: When we encode class/impl methods, the bounds
If this is a class method, we want to use the // that we encode include both the class/impl bounds
class bounds plus the method bounds -- otherwise the // and then the method bounds themselves...
indices come out wrong. So we check base's type... ty::lookup_item_type(cx.tcx, did).bounds
*/
let mut bounds = ty::lookup_item_type(cx.tcx, did).bounds;
alt ty::get(ty::node_id_to_type(cx.tcx, base.id)).struct {
ty::ty_class(parent_id, ts) {
/* ...and if it has a class type, prepend the
class bounds onto the method bounds */
/* n.b. this code is very likely sketchy --
currently, class-impl-very-parameterized-iface
fails here and is thus xfailed */
bounds =
@(*ty::lookup_item_type(cx.tcx, parent_id).bounds
+ *bounds);
}
_ { }
}
bounds
} }
typeck::method_param(ifce_id, n_mth, _, _) | typeck::method_param(ifce_id, n_mth, _, _) |
typeck::method_iface(ifce_id, n_mth) { typeck::method_iface(ifce_id, n_mth) {
// ...iface methods bounds, in contrast, include only the
// method bounds, so we must preprend the tps from the
// iface itself. This ought to be harmonized.
let ifce_bounds = let ifce_bounds =
ty::lookup_item_type(cx.tcx, ifce_id).bounds; ty::lookup_item_type(cx.tcx, ifce_id).bounds;
let mth = ty::iface_methods(cx.tcx, ifce_id)[n_mth]; let mth = ty::iface_methods(cx.tcx, ifce_id)[n_mth];

View File

@ -18,6 +18,9 @@ enum lookup = {
impl methods for lookup { impl methods for lookup {
// Entrypoint: // Entrypoint:
fn method() -> option<method_origin> { fn method() -> option<method_origin> {
#debug["method lookup(m_name=%s, self_ty=%s)",
self.m_name, self.fcx.infcx.ty_to_str(self.self_ty)];
// First, see whether this is an interface-bounded parameter // First, see whether this is an interface-bounded parameter
let pass1 = alt ty::get(self.self_ty).struct { let pass1 = alt ty::get(self.self_ty).struct {
ty::ty_param(n, did) { ty::ty_param(n, did) {
@ -288,6 +291,11 @@ impl methods for lookup {
let tcx = self.fcx.ccx.tcx; let tcx = self.fcx.ccx.tcx;
#debug["write_mty_from_fty(n_tps_m=%u, fty=%s, origin=%?)",
n_tps_m,
self.fcx.infcx.ty_to_str(fty),
origin];
// Here I will use the "c_" prefix to refer to the method's // Here I will use the "c_" prefix to refer to the method's
// owner. You can read it as class, but it may also be an iface. // owner. You can read it as class, but it may also be an iface.

View File

@ -228,17 +228,15 @@ fn check_methods_against_iface(ccx: @crate_ctxt,
rp: ast::region_param, rp: ast::region_param,
selfty: ty::t, selfty: ty::t,
a_ifacety: @ast::iface_ref, a_ifacety: @ast::iface_ref,
ms: [@ast::method]) { ms: [converted_method]) {
let tcx = ccx.tcx; let tcx = ccx.tcx;
let i_bounds = ty_param_bounds(ccx, tps);
let my_methods = convert_methods(ccx, ms, rp, i_bounds, selfty);
let (did, tpt) = instantiate_iface_ref(ccx, a_ifacety, rp); let (did, tpt) = instantiate_iface_ref(ccx, a_ifacety, rp);
if did.crate == ast::local_crate { if did.crate == ast::local_crate {
ensure_iface_methods(ccx, did.node); ensure_iface_methods(ccx, did.node);
} }
for vec::each(*ty::iface_methods(tcx, did)) {|if_m| for vec::each(*ty::iface_methods(tcx, did)) {|if_m|
alt vec::find(my_methods, {|m| if_m.ident == m.mty.ident}) { alt vec::find(ms, {|m| if_m.ident == m.mty.ident}) {
some({mty: m, id, span}) { some({mty: m, id, span}) {
if m.purity != if_m.purity { if m.purity != if_m.purity {
ccx.tcx.sess.span_err( ccx.tcx.sess.span_err(
@ -274,12 +272,13 @@ fn convert_class_item(ccx: @crate_ctxt,
ccx.tcx.tcache.insert(local_def(v.id), {bounds: bounds, rp: rp, ty: tt}); ccx.tcx.tcache.insert(local_def(v.id), {bounds: bounds, rp: rp, ty: tt});
} }
type converted_method = {mty: ty::method, id: ast::node_id, span: span};
fn convert_methods(ccx: @crate_ctxt, fn convert_methods(ccx: @crate_ctxt,
ms: [@ast::method], ms: [@ast::method],
rp: ast::region_param, rp: ast::region_param,
i_bounds: @[ty::param_bounds], rcvr_bounds: @[ty::param_bounds],
self_ty: ty::t) self_ty: ty::t) -> [converted_method] {
-> [{mty: ty::method, id: ast::node_id, span: span}] {
let tcx = ccx.tcx; let tcx = ccx.tcx;
vec::map(ms) { |m| vec::map(ms) { |m|
@ -289,9 +288,10 @@ fn convert_methods(ccx: @crate_ctxt,
let fty = ty::mk_fn(tcx, mty.fty); let fty = ty::mk_fn(tcx, mty.fty);
tcx.tcache.insert( tcx.tcache.insert(
local_def(m.id), local_def(m.id),
// n.b. This code is kind of sketchy (concat'ing i_bounds
// with bounds), but removing *i_bounds breaks other stuff // n.b.: the type of a method is parameterized by both
{bounds: @(*i_bounds + *bounds), rp: rp, ty: fty}); // the tps on the receiver and those on the method itself
{bounds: @(*rcvr_bounds + *bounds), rp: rp, ty: fty});
write_ty_to_tcx(tcx, m.id, fty); write_ty_to_tcx(tcx, m.id, fty);
{mty: mty, id: m.id, span: m.span} {mty: mty, id: m.id, span: m.span}
} }
@ -316,19 +316,10 @@ fn convert(ccx: @crate_ctxt, it: @ast::item) {
{bounds: i_bounds, {bounds: i_bounds,
rp: rp, rp: rp,
ty: selfty}); ty: selfty});
alt ifce {
some(t) { let cms = convert_methods(ccx, ms, rp, i_bounds, selfty);
check_methods_against_iface( for ifce.each { |t|
ccx, tps, rp, check_methods_against_iface(ccx, tps, rp, selfty, t, cms);
selfty, t, ms);
}
_ {
// Still have to do this to write method types
// into the table
convert_methods(
ccx, ms, rp,
i_bounds, selfty);
}
} }
} }
ast::item_res(decl, tps, _, dtor_id, ctor_id, rp) { ast::item_res(decl, tps, _, dtor_id, ctor_id, rp) {
@ -412,23 +403,11 @@ fn convert(ccx: @crate_ctxt, it: @ast::item) {
for fields.each {|f| for fields.each {|f|
convert_class_item(ccx, rp, tpt.bounds, f); convert_class_item(ccx, rp, tpt.bounds, f);
} }
// The selfty is just the class type let {bounds, substs} = mk_substs(ccx, tps, rp);
let {bounds:_, substs} = mk_substs(ccx, tps, rp);
let selfty = ty::mk_class(tcx, local_def(it.id), substs); let selfty = ty::mk_class(tcx, local_def(it.id), substs);
// Need to convert all methods so we can check internal let cms = convert_methods(ccx, methods, rp, bounds, selfty);
// references to private methods
// NDM to TJC---I think we ought to be using bounds here, not @[].
// But doing so causes errors later on.
convert_methods(ccx, methods, rp, @[], selfty);
/*
Finally, check that the class really implements the ifaces
that it claims to implement.
*/
for ifaces.each { |ifce| for ifaces.each { |ifce|
check_methods_against_iface(ccx, tps, rp, selfty, check_methods_against_iface(ccx, tps, rp, selfty, ifce, cms);
ifce, methods);
// FIXME #2434---this is somewhat bogus, but it seems that // FIXME #2434---this is somewhat bogus, but it seems that
// the id of iface_ref is also the id of the impl, and so // the id of iface_ref is also the id of the impl, and so

View File

@ -1,4 +1,3 @@
// xfail-test (still buggy)
// xfail-fast (compile-flags unsupported on windows) // xfail-fast (compile-flags unsupported on windows)
// compile-flags:--borrowck=err // compile-flags:--borrowck=err
// exec-env:RUST_POISON_ON_FREE=1 // exec-env:RUST_POISON_ON_FREE=1

View File

@ -1,5 +1,3 @@
// xfail-test
// xfail-fast
use std; use std;
import std::map::*; import std::map::*;
@ -59,7 +57,7 @@ class cat<T: copy> implements map<int, T> {
} }
else { none } else { none }
} }
fn remove(&&k:int) -> option<T> { fn remove(&&k:int) -> option<T> {
alt self.find(k) { alt self.find(k) {
some(x) { some(x) {
@ -76,7 +74,7 @@ class cat<T: copy> implements map<int, T> {
n -= 1; n -= 1;
} }
} }
fn each_key(&&f: fn(&&int) -> bool) { fn each_key(&&f: fn(&&int) -> bool) {
for self.each {|k, _v| if !f(k) { break; } cont;}; for self.each {|k, _v| if !f(k) { break; } cont;};
} }
@ -88,11 +86,11 @@ class cat<T: copy> implements map<int, T> {
fn main() { fn main() {
let nyan : cat<str> = cat(0, 2, "nyan"); let nyan : cat<str> = cat(0, 2, "nyan");
uint::range(1u, 5u) {|_i| nyan.speak(); } for uint::range(1u, 5u) {|_i| nyan.speak(); }
assert(nyan.find(1) == some("nyan")); assert(nyan.find(1) == some("nyan"));
assert(nyan.find(10) == none); assert(nyan.find(10) == none);
let spotty : cat<cat_type> = cat(2, 57, tuxedo); let spotty : cat<cat_type> = cat(2, 57, tuxedo);
uint::range(0u, 6u) {|_i| spotty.speak(); } for uint::range(0u, 6u) {|_i| spotty.speak(); }
assert(spotty.size() == 8u); assert(spotty.size() == 8u);
assert(spotty.contains_key(2)); assert(spotty.contains_key(2));
assert(spotty.get(3) == tuxedo); assert(spotty.get(3) == tuxedo);

View File

@ -1,5 +1,3 @@
// xfail-test
// xfail-fast // xfail-fast
// aux-build:cci_class_6.rs // aux-build:cci_class_6.rs
use cci_class_6; use cci_class_6;

View File

@ -1,5 +1,3 @@
// xfail-test
// needs metadata encoding on Windows
class cat<U> { class cat<U> {
priv { priv {
let mut meows : uint; let mut meows : uint;