From 3c4baf694e663bf3891caf550883fce66160b6b4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 4 Jun 2012 11:21:11 -0700 Subject: [PATCH] better support for classes with polymorphic methods --- src/rustc/metadata/encoder.rs | 2 +- src/rustc/middle/kind.rs | 27 +++------ src/rustc/middle/typeck/check/method.rs | 8 +++ src/rustc/middle/typeck/collect.rs | 55 ++++++------------- .../run-pass/borrowck-preserve-box-in-pat.rs | 1 - .../class-impl-very-parameterized-iface.rs | 10 ++-- .../class-poly-methods-cross-crate.rs | 2 - src/test/run-pass/class-typarams.rs | 2 - 8 files changed, 37 insertions(+), 70 deletions(-) diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index 5a429f6f7d2..113fafb73b3 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -664,7 +664,7 @@ fn add_to_index_(item: @item, ebml_w: ebml::writer, ebml_w.start_tag(tag_item_iface_method); encode_family(ebml_w, purity_fn_family(m.decl.purity)); 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_def_id(ebml_w, local_def(m.id)); ebml_w.end_tag(); diff --git a/src/rustc/middle/kind.rs b/src/rustc/middle/kind.rs index 1a62586ad05..fef2a0ad28e 100644 --- a/src/rustc/middle/kind.rs +++ b/src/rustc/middle/kind.rs @@ -254,29 +254,16 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt) { expr_field(base, _, _) { alt cx.method_map.get(e.id) { typeck::method_static(did) { - /* - If this is a class method, we want to use the - class bounds plus the method bounds -- otherwise the - indices come out wrong. So we check base's type... - */ - 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 + // n.b.: When we encode class/impl methods, the bounds + // that we encode include both the class/impl bounds + // and then the method bounds themselves... + ty::lookup_item_type(cx.tcx, did).bounds } typeck::method_param(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 = ty::lookup_item_type(cx.tcx, ifce_id).bounds; let mth = ty::iface_methods(cx.tcx, ifce_id)[n_mth]; diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs index d2fb60d51f2..d6d43b0d3b2 100644 --- a/src/rustc/middle/typeck/check/method.rs +++ b/src/rustc/middle/typeck/check/method.rs @@ -18,6 +18,9 @@ enum lookup = { impl methods for lookup { // Entrypoint: fn method() -> option { + #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 let pass1 = alt ty::get(self.self_ty).struct { ty::ty_param(n, did) { @@ -288,6 +291,11 @@ fn write_mty_from_fty(self_substs: ty::substs, 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 // owner. You can read it as class, but it may also be an iface. diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs index 7087abfcd9e..e97629a823b 100644 --- a/src/rustc/middle/typeck/collect.rs +++ b/src/rustc/middle/typeck/collect.rs @@ -228,17 +228,15 @@ fn check_methods_against_iface(ccx: @crate_ctxt, rp: ast::region_param, selfty: ty::t, a_ifacety: @ast::iface_ref, - ms: [@ast::method]) { + ms: [converted_method]) { 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); if did.crate == ast::local_crate { ensure_iface_methods(ccx, did.node); } 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}) { if m.purity != if_m.purity { 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}); } +type converted_method = {mty: ty::method, id: ast::node_id, span: span}; + fn convert_methods(ccx: @crate_ctxt, ms: [@ast::method], rp: ast::region_param, - i_bounds: @[ty::param_bounds], - self_ty: ty::t) - -> [{mty: ty::method, id: ast::node_id, span: span}] { + rcvr_bounds: @[ty::param_bounds], + self_ty: ty::t) -> [converted_method] { let tcx = ccx.tcx; vec::map(ms) { |m| @@ -289,9 +288,10 @@ fn convert_methods(ccx: @crate_ctxt, let fty = ty::mk_fn(tcx, mty.fty); tcx.tcache.insert( 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 - {bounds: @(*i_bounds + *bounds), rp: rp, ty: fty}); + + // n.b.: the type of a method is parameterized by both + // 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); {mty: mty, id: m.id, span: m.span} } @@ -316,19 +316,10 @@ fn convert(ccx: @crate_ctxt, it: @ast::item) { {bounds: i_bounds, rp: rp, ty: selfty}); - alt ifce { - some(t) { - check_methods_against_iface( - ccx, tps, rp, - selfty, t, ms); - } - _ { - // Still have to do this to write method types - // into the table - convert_methods( - ccx, ms, rp, - i_bounds, selfty); - } + + let cms = convert_methods(ccx, ms, rp, i_bounds, selfty); + for ifce.each { |t| + check_methods_against_iface(ccx, tps, rp, selfty, t, cms); } } 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| 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); - // Need to convert all methods so we can check internal - // 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. - */ + let cms = convert_methods(ccx, methods, rp, bounds, selfty); for ifaces.each { |ifce| - check_methods_against_iface(ccx, tps, rp, selfty, - ifce, methods); + check_methods_against_iface(ccx, tps, rp, selfty, ifce, cms); // FIXME #2434---this is somewhat bogus, but it seems that // the id of iface_ref is also the id of the impl, and so diff --git a/src/test/run-pass/borrowck-preserve-box-in-pat.rs b/src/test/run-pass/borrowck-preserve-box-in-pat.rs index 3a644900193..718507d1efc 100644 --- a/src/test/run-pass/borrowck-preserve-box-in-pat.rs +++ b/src/test/run-pass/borrowck-preserve-box-in-pat.rs @@ -1,4 +1,3 @@ -// xfail-test (still buggy) // xfail-fast (compile-flags unsupported on windows) // compile-flags:--borrowck=err // exec-env:RUST_POISON_ON_FREE=1 diff --git a/src/test/run-pass/class-impl-very-parameterized-iface.rs b/src/test/run-pass/class-impl-very-parameterized-iface.rs index b66268acc05..74d8f921186 100644 --- a/src/test/run-pass/class-impl-very-parameterized-iface.rs +++ b/src/test/run-pass/class-impl-very-parameterized-iface.rs @@ -1,5 +1,3 @@ -// xfail-test -// xfail-fast use std; import std::map::*; @@ -59,7 +57,7 @@ fn find(&&k:int) -> option { if k <= self.meows { } else { none } } - + fn remove(&&k:int) -> option { alt self.find(k) { some(x) { @@ -76,7 +74,7 @@ fn each(f: fn(&&int, &&T) -> bool) { n -= 1; } } - + fn each_key(&&f: fn(&&int) -> bool) { for self.each {|k, _v| if !f(k) { break; } cont;}; } @@ -88,11 +86,11 @@ fn each_value(&&f: fn(&&T) -> bool) { fn main() { let nyan : cat = 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(10) == none); let spotty : cat = 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.contains_key(2)); assert(spotty.get(3) == tuxedo); diff --git a/src/test/run-pass/class-poly-methods-cross-crate.rs b/src/test/run-pass/class-poly-methods-cross-crate.rs index d46f501f0fa..28ddbfdd1e2 100644 --- a/src/test/run-pass/class-poly-methods-cross-crate.rs +++ b/src/test/run-pass/class-poly-methods-cross-crate.rs @@ -1,5 +1,3 @@ -// xfail-test - // xfail-fast // aux-build:cci_class_6.rs use cci_class_6; diff --git a/src/test/run-pass/class-typarams.rs b/src/test/run-pass/class-typarams.rs index ace8650b3b9..caefb72a6a4 100644 --- a/src/test/run-pass/class-typarams.rs +++ b/src/test/run-pass/class-typarams.rs @@ -1,5 +1,3 @@ -// xfail-test -// needs metadata encoding on Windows class cat { priv { let mut meows : uint; -- GitLab