From 3d4ef74c9bdd2179199c02d7cbe58266c849b057 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Thu, 26 Apr 2012 12:15:46 -0700 Subject: [PATCH] Encode the ifaces a class implements in metadata This lets you use class A as if it had type B if A implements B, and A and B are in different crates from your own. Closes #2285 --- src/rustc/metadata/common.rs | 13 ++- src/rustc/metadata/decoder.rs | 43 ++++++--- src/rustc/metadata/encoder.rs | 91 +++++++++++++++---- src/rustc/metadata/tydecode.rs | 10 +- src/rustc/middle/resolve.rs | 10 +- src/rustc/middle/typeck.rs | 4 +- src/test/auxiliary/cci_class_cast.rs | 40 ++++++++ .../class-cast-to-iface-cross-crate-2.rs | 18 ++++ 8 files changed, 187 insertions(+), 42 deletions(-) create mode 100644 src/test/auxiliary/cci_class_cast.rs create mode 100644 src/test/run-pass/class-cast-to-iface-cross-crate-2.rs diff --git a/src/rustc/metadata/common.rs b/src/rustc/metadata/common.rs index 4262266a0d9..54a05cc206d 100644 --- a/src/rustc/metadata/common.rs +++ b/src/rustc/metadata/common.rs @@ -70,7 +70,7 @@ const tag_crate_dep_vers: uint = 0x2cu; const tag_mod_impl: uint = 0x30u; -const tag_item_method: uint = 0x31u; +const tag_item_iface_method: uint = 0x31u; const tag_impl_iface: uint = 0x32u; // discriminator value for variants @@ -85,6 +85,17 @@ const tag_item_field: uint = 0x44u; const tag_class_mut: uint = 0x45u; const tag_region_param: uint = 0x46u; +const tag_mod_impl_use: uint = 0x47u; +const tag_mod_impl_iface: uint = 0x48u; +/* + iface items contain tag_item_iface_method elements, + impl items contain tag_item_impl_method elements, and classes + have both. That's because some code treats classes like ifaces, + and other code treats them like impls. Because classes can contain + both, tag_item_iface_method and tag_item_impl_method have to be two + different tags. + */ +const tag_item_impl_method: uint = 0x49u; // used to encode crate_ctxt side tables enum astencode_tag { // Reserves 0x50 -- 0x6f diff --git a/src/rustc/metadata/decoder.rs b/src/rustc/metadata/decoder.rs index 50a1093153b..ef0bd869482 100644 --- a/src/rustc/metadata/decoder.rs +++ b/src/rustc/metadata/decoder.rs @@ -156,8 +156,7 @@ fn item_impl_iface(item: ebml::doc, tcx: ty::ctxt, cdata: cmd) -> option { let mut result = none; ebml::tagged_docs(item, tag_impl_iface) {|ity| - result = some(parse_ty_data(ity.data, cdata.cnum, ity.start, tcx, - {|did| translate_def_id(cdata, did)})); + result = some(doc_type(ity, tcx, cdata)); }; result } @@ -304,7 +303,7 @@ fn get_impl_iface(cdata: cmd, id: ast::node_id, tcx: ty::ctxt) fn get_impl_method(cdata: cmd, id: ast::node_id, name: str) -> ast::def_id { let items = ebml::get_doc(ebml::doc(cdata.data), tag_items); let mut found = none; - ebml::tagged_docs(find_item(id, items), tag_item_method) {|mid| + ebml::tagged_docs(find_item(id, items), tag_item_impl_method) {|mid| let m_did = parse_def_id(ebml::doc_data(mid)); if item_name(find_item(m_did.node, items)) == name { found = some(translate_def_id(cdata, m_did)); @@ -320,7 +319,7 @@ fn get_class_method(cdata: cmd, id: ast::node_id, name: str) -> ast::def_id { some(it) { it } none { fail (#fmt("get_class_method: class id not found \ when looking up method %s", name)) }}; - ebml::tagged_docs(cls_items, tag_item_method) {|mid| + ebml::tagged_docs(cls_items, tag_item_iface_method) {|mid| let m_did = class_member_id(mid, cdata); if item_name(mid) == name { found = some(m_did); @@ -398,10 +397,12 @@ fn get_enum_variants(cdata: cmd, id: ast::node_id, tcx: ty::ctxt) fn item_impl_methods(cdata: cmd, item: ebml::doc, base_tps: uint) -> [@middle::resolve::method_info] { let mut rslt = []; - ebml::tagged_docs(item, tag_item_method) {|doc| + ebml::tagged_docs(item, tag_item_impl_method) {|doc| let m_did = parse_def_id(ebml::doc_data(doc)); let mth_item = lookup_item(m_did.node, cdata.data); rslt += [@{did: translate_def_id(cdata, m_did), + /* FIXME tjc: take a look at this, it may relate + to #2323 */ n_tps: item_ty_param_count(mth_item) - base_tps, ident: item_name(mth_item)}]; } @@ -416,21 +417,39 @@ fn get_impls_for_mod(cdata: cmd, m_id: ast::node_id, let mod_item = lookup_item(m_id, data); let mut result = []; ebml::tagged_docs(mod_item, tag_mod_impl) {|doc| - let did = parse_def_id(ebml::doc_data(doc)); + /* + Pair of an item did and an iface did. + The second one is unneeded if the first id names + an impl; disambiguates if it's a class + */ + let did = parse_def_id(ebml::doc_data(ebml::get_doc(doc, + tag_mod_impl_use))); let local_did = translate_def_id(cdata, did); - // The impl may be defined in a different crate. Ask the caller - // to give us the metadata + /* + // iface is optional + let iface_did = option::map(ebml::maybe_get_doc(doc, + tag_mod_impl_iface)) {|d| + parse_def_id(ebml::doc_data(d))}; + option::iter(iface_did) {|x| + let _local_iface_did = translate_def_id(cdata, x); + }; + */ + // CONFUSED -- previous code is pointless + // The impl may be defined in a different crate. Ask the caller + // to give us the metadata let impl_cdata = get_cdata(local_did.crate); let impl_data = impl_cdata.data; let item = lookup_item(local_did.node, impl_data); let nm = item_name(item); if alt name { some(n) { n == nm } none { true } } { - let base_tps = item_ty_param_count(item); - result += [@{ + let base_tps = item_ty_param_count(item); + result += [@{ + // here, we need to... reconstruct the iface_ref? + // probz broken did: local_did, ident: nm, methods: item_impl_methods(impl_cdata, item, base_tps) }]; - } + }; } @result } @@ -441,7 +460,7 @@ fn get_iface_methods(cdata: cmd, id: ast::node_id, tcx: ty::ctxt) let data = cdata.data; let item = lookup_item(id, data); let mut result = []; - ebml::tagged_docs(item, tag_item_method) {|mth| + ebml::tagged_docs(item, tag_item_iface_method) {|mth| let bounds = item_ty_param_bounds(mth, tcx, cdata); let name = item_name(mth); let ty = doc_type(mth, tcx, cdata); diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index a1c996dccf1..9fd63ecf2fd 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -1,5 +1,7 @@ // Metadata encoding +import util::ppaux::ty_to_str; + import std::{ebml, map, list}; import std::map::hashmap; import io::writer_util; @@ -197,6 +199,12 @@ fn encode_module_item_paths(ebml_w: ebml::writer, ecx: @encode_ctxt, } } +fn encode_iface_ref(ebml_w: ebml::writer, ecx: @encode_ctxt, t: @iface_ref) { + ebml_w.start_tag(tag_impl_iface); + encode_type(ecx, ebml_w, node_id_to_type(ecx.ccx.tcx, t.id)); + ebml_w.end_tag(); +} + fn encode_item_paths(ebml_w: ebml::writer, ecx: @encode_ctxt, crate: @crate) -> [entry] { let mut index: [entry] = []; @@ -361,12 +369,52 @@ fn encode_info_for_mod(ecx: @encode_ctxt, ebml_w: ebml::writer, md: _mod, list::cons(impls, @list::nil) { for vec::each(*impls) {|i| if ast_util::is_exported(i.ident, md) { - ebml_w.wr_tagged_str(tag_mod_impl, def_to_str(i.did)); - } - } + ebml_w.start_tag(tag_mod_impl); + /* If did stands for an iface + ref, we need to map it to its parent class */ + ebml_w.start_tag(tag_mod_impl_use); + let iface_ty = alt ecx.ccx.tcx.items.get(i.did.node) { + ast_map::node_item(it@@{node: cl@item_class(*),_},_) { + ebml_w.wr_str(def_to_str(local_def(it.id))); + some(ty::lookup_item_type(ecx.ccx.tcx, i.did).ty) + } + ast_map::node_item(@{node: item_impl(_,_, + some(ifce),_,_),_},_) { + ebml_w.wr_str(def_to_str(i.did)); + some(ty::node_id_to_type(ecx.ccx.tcx, ifce.id)) + } + _ { + ebml_w.wr_str(def_to_str(i.did)); none + } + }; + ebml_w.end_tag(); + + /* + /* Write the iface did if it exists */ + option::iter(iface_ty) {|i| + alt ty::get(i).struct { + ty::ty_iface(did, tys) { + // FIXME: tys? + ebml_w.start_tag(tag_mod_impl_iface); + ebml_w.wr_str(def_to_str(did)); + ebml_w.end_tag(); + + } + t { + ecx.ccx.tcx.sess.bug(#fmt("Expected item to implement \ + an iface, but found %s", + util::ppaux::ty_to_str(ecx.ccx.tcx, i))); + } + }} + */ + ebml_w.end_tag(); + } // if + } // for + } // list::cons alt + _ { + ecx.ccx.tcx.sess.bug(#fmt("encode_info_for_mod: empty impl_map \ + entry for %?", path)); } - _ { ecx.ccx.tcx.sess.bug(#fmt("encode_info_for_mod: empty impl_map \ - entry for %?", path)); } } encode_path(ebml_w, path, ast_map::path_mod(name)); ebml_w.end_tag(); @@ -571,7 +619,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, encode_enum_variant_info(ecx, ebml_w, item.id, variants, path, index, tps); } - item_class(tps, _ifaces, items, ctor, rp) { + item_class(tps, ifaces, items, ctor, rp) { /* First, encode the fields and methods These come first because we need to write them to make the index, and the index needs to be in the item for the @@ -589,7 +637,9 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, encode_name(ebml_w, item.ident); encode_path(ebml_w, path, ast_map::path_name(item.ident)); encode_region_param(ebml_w, rp); - /* FIXME: encode ifaces */ + for ifaces.each {|t| + encode_iface_ref(ebml_w, ecx, t); + } /* Encode def_ids for each field and method for methods, write all the stuff get_iface_method needs to know*/ @@ -605,14 +655,21 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, alt m.privacy { priv { /* do nothing */ } pub { - ebml_w.start_tag(tag_item_method); - #debug("Writing %s %d", m.ident, m.id); + /* Write the info that's needed when viewing this class + as an iface */ + 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(ecx, ebml_w, node_id_to_type(tcx, m.id)); encode_def_id(ebml_w, local_def(m.id)); ebml_w.end_tag(); + /* Write the info that's needed when viewing this class + as an impl (just the method def_id) */ + ebml_w.start_tag(tag_item_impl_method); + ebml_w.writer.write(str::bytes(def_to_str(local_def(m.id)))); + ebml_w.end_tag(); + } } } @@ -659,19 +716,13 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, encode_type(ecx, ebml_w, node_id_to_type(tcx, item.id)); encode_name(ebml_w, item.ident); for methods.each {|m| - ebml_w.start_tag(tag_item_method); + ebml_w.start_tag(tag_item_impl_method); ebml_w.writer.write(str::bytes(def_to_str(local_def(m.id)))); ebml_w.end_tag(); } - alt ifce { - some(t) { - let i_ty = ty::node_id_to_type(tcx, t.id); - ebml_w.start_tag(tag_impl_iface); - write_type(ecx, ebml_w, i_ty); - ebml_w.end_tag(); - } - _ {} - } + option::iter(ifce) {|t| + encode_iface_ref(ebml_w, ecx, t) + }; encode_path(ebml_w, path, ast_map::path_name(item.ident)); ebml_w.end_tag(); @@ -693,7 +744,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item, encode_name(ebml_w, item.ident); let mut i = 0u; for vec::each(*ty::iface_methods(tcx, local_def(item.id))) {|mty| - ebml_w.start_tag(tag_item_method); + ebml_w.start_tag(tag_item_iface_method); encode_name(ebml_w, mty.ident); encode_type_param_bounds(ebml_w, ecx, ms[i].tps); encode_type(ecx, ebml_w, ty::mk_fn(tcx, mty.fty)); diff --git a/src/rustc/metadata/tydecode.rs b/src/rustc/metadata/tydecode.rs index 46c39256938..7f627e39039 100644 --- a/src/rustc/metadata/tydecode.rs +++ b/src/rustc/metadata/tydecode.rs @@ -481,15 +481,13 @@ fn parse_def_id(buf: [u8]) -> ast::def_id { let crate_num = alt uint::parse_buf(crate_part, 10u) { some(cn) { cn as int } - none { fail (#fmt("internal error: parse_def_id: error parsing %? \ - as crate", - crate_part)); } + none { fail (#fmt("internal error: parse_def_id: crate number \ + expected, but found %?", crate_part)); } }; let def_num = alt uint::parse_buf(def_part, 10u) { some(dn) { dn as int } - none { fail (#fmt("internal error: parse_def_id: error parsing %? \ - as id", - def_part)); } + none { fail (#fmt("internal error: parse_def_id: id expected, but \ + found %?", def_part)); } }; ret {crate: crate_num, node: def_num}; } diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs index 3e380816dfd..cd0696c4104 100644 --- a/src/rustc/middle/resolve.rs +++ b/src/rustc/middle/resolve.rs @@ -2115,8 +2115,14 @@ fn check_exports(e: @env) { // Impl resolution type method_info = {did: def_id, n_tps: uint, ident: ast::ident}; -/* what are the did and ident here? */ -/* ident = the name of the impl */ +/* An _impl represents an implementation that's currently in scope. + Its fields: + * did: the def id of the class or impl item + * ident: the name of the impl, unless it has no name (as in + "impl of X") in which case the ident + is the ident of the iface that's being implemented + * methods: the item's methods +*/ type _impl = {did: def_id, ident: ast::ident, methods: [@method_info]}; type iscopes = list<@[@_impl]>; diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index 931d3d9e790..944d04ee402 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -4686,8 +4686,10 @@ mod vtable { let mut found = []; for list::each(isc) {|impls| + /* For each impl in scope... */ for vec::each(*impls) {|im| - // find the iface that the impl is an impl of (if any) + // im = one specific impl + // find the iface that im implements (if any) let of_ty = alt ty::impl_iface(tcx, im.did) { some(of_ty) { of_ty } _ { cont; } diff --git a/src/test/auxiliary/cci_class_cast.rs b/src/test/auxiliary/cci_class_cast.rs new file mode 100644 index 00000000000..997cab28155 --- /dev/null +++ b/src/test/auxiliary/cci_class_cast.rs @@ -0,0 +1,40 @@ +import to_str::*; +import to_str::to_str; + +mod kitty { + +class cat implements to_str { + priv { + let mut meows : uint; + fn meow() { + #error("Meow"); + self.meows += 1u; + if self.meows % 5u == 0u { + self.how_hungry += 1; + } + } + } + + let mut how_hungry : int; + let name : str; + + new(in_x : uint, in_y : int, in_name: str) + { self.meows = in_x; self.how_hungry = in_y; self.name = in_name; } + + fn speak() { self.meow(); } + + fn eat() -> bool { + if self.how_hungry > 0 { + #error("OM NOM NOM"); + self.how_hungry -= 2; + ret true; + } + else { + #error("Not hungry!"); + ret false; + } + } + + fn to_str() -> str { self.name } +} +} diff --git a/src/test/run-pass/class-cast-to-iface-cross-crate-2.rs b/src/test/run-pass/class-cast-to-iface-cross-crate-2.rs new file mode 100644 index 00000000000..733af04f175 --- /dev/null +++ b/src/test/run-pass/class-cast-to-iface-cross-crate-2.rs @@ -0,0 +1,18 @@ +// xfail-fast +// aux-build:cci_class_cast.rs +use cci_class_cast; +import cci_class_cast::kitty::*; +import to_str::*; +import to_str::to_str; + +fn print_out(thing: T, expected: str) { + let actual = thing.to_str(); + #debug("%s", actual); + assert(actual == expected); +} + +fn main() { + let nyan : to_str = cat(0u, 2, "nyan") as to_str; + print_out(nyan, "nyan"); +} +