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
This commit is contained in:
Tim Chevalier 2012-04-26 12:15:46 -07:00
parent 8d9f67003a
commit 3d4ef74c9b
8 changed files with 187 additions and 42 deletions

View File

@ -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

View File

@ -156,8 +156,7 @@ fn item_impl_iface(item: ebml::doc, tcx: ty::ctxt, cdata: cmd)
-> option<ty::t> {
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);

View File

@ -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<str>] {
let mut index: [entry<str>] = [];
@ -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));

View File

@ -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};
}

View File

@ -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]>;

View File

@ -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; }

View File

@ -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 }
}
}

View File

@ -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<T: to_str>(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");
}