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:
parent
8d9f67003a
commit
3d4ef74c9b
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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};
|
||||
}
|
||||
|
@ -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]>;
|
||||
|
||||
|
@ -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; }
|
||||
|
40
src/test/auxiliary/cci_class_cast.rs
Normal file
40
src/test/auxiliary/cci_class_cast.rs
Normal 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 }
|
||||
}
|
||||
}
|
18
src/test/run-pass/class-cast-to-iface-cross-crate-2.rs
Normal file
18
src/test/run-pass/class-cast-to-iface-cross-crate-2.rs
Normal 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");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user