Skip to content

Commit 3d4ef74

Browse files
committed
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
1 parent 8d9f670 commit 3d4ef74

File tree

8 files changed

+187
-42
lines changed

8 files changed

+187
-42
lines changed

src/rustc/metadata/common.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const tag_crate_dep_vers: uint = 0x2cu;
7070

7171
const tag_mod_impl: uint = 0x30u;
7272

73-
const tag_item_method: uint = 0x31u;
73+
const tag_item_iface_method: uint = 0x31u;
7474
const tag_impl_iface: uint = 0x32u;
7575

7676
// discriminator value for variants
@@ -85,6 +85,17 @@ const tag_item_field: uint = 0x44u;
8585
const tag_class_mut: uint = 0x45u;
8686

8787
const tag_region_param: uint = 0x46u;
88+
const tag_mod_impl_use: uint = 0x47u;
89+
const tag_mod_impl_iface: uint = 0x48u;
90+
/*
91+
iface items contain tag_item_iface_method elements,
92+
impl items contain tag_item_impl_method elements, and classes
93+
have both. That's because some code treats classes like ifaces,
94+
and other code treats them like impls. Because classes can contain
95+
both, tag_item_iface_method and tag_item_impl_method have to be two
96+
different tags.
97+
*/
98+
const tag_item_impl_method: uint = 0x49u;
8899

89100
// used to encode crate_ctxt side tables
90101
enum astencode_tag { // Reserves 0x50 -- 0x6f

src/rustc/metadata/decoder.rs

+31-12
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,7 @@ fn item_impl_iface(item: ebml::doc, tcx: ty::ctxt, cdata: cmd)
156156
-> option<ty::t> {
157157
let mut result = none;
158158
ebml::tagged_docs(item, tag_impl_iface) {|ity|
159-
result = some(parse_ty_data(ity.data, cdata.cnum, ity.start, tcx,
160-
{|did| translate_def_id(cdata, did)}));
159+
result = some(doc_type(ity, tcx, cdata));
161160
};
162161
result
163162
}
@@ -304,7 +303,7 @@ fn get_impl_iface(cdata: cmd, id: ast::node_id, tcx: ty::ctxt)
304303
fn get_impl_method(cdata: cmd, id: ast::node_id, name: str) -> ast::def_id {
305304
let items = ebml::get_doc(ebml::doc(cdata.data), tag_items);
306305
let mut found = none;
307-
ebml::tagged_docs(find_item(id, items), tag_item_method) {|mid|
306+
ebml::tagged_docs(find_item(id, items), tag_item_impl_method) {|mid|
308307
let m_did = parse_def_id(ebml::doc_data(mid));
309308
if item_name(find_item(m_did.node, items)) == name {
310309
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 {
320319
some(it) { it }
321320
none { fail (#fmt("get_class_method: class id not found \
322321
when looking up method %s", name)) }};
323-
ebml::tagged_docs(cls_items, tag_item_method) {|mid|
322+
ebml::tagged_docs(cls_items, tag_item_iface_method) {|mid|
324323
let m_did = class_member_id(mid, cdata);
325324
if item_name(mid) == name {
326325
found = some(m_did);
@@ -398,10 +397,12 @@ fn get_enum_variants(cdata: cmd, id: ast::node_id, tcx: ty::ctxt)
398397
fn item_impl_methods(cdata: cmd, item: ebml::doc, base_tps: uint)
399398
-> [@middle::resolve::method_info] {
400399
let mut rslt = [];
401-
ebml::tagged_docs(item, tag_item_method) {|doc|
400+
ebml::tagged_docs(item, tag_item_impl_method) {|doc|
402401
let m_did = parse_def_id(ebml::doc_data(doc));
403402
let mth_item = lookup_item(m_did.node, cdata.data);
404403
rslt += [@{did: translate_def_id(cdata, m_did),
404+
/* FIXME tjc: take a look at this, it may relate
405+
to #2323 */
405406
n_tps: item_ty_param_count(mth_item) - base_tps,
406407
ident: item_name(mth_item)}];
407408
}
@@ -416,21 +417,39 @@ fn get_impls_for_mod(cdata: cmd, m_id: ast::node_id,
416417
let mod_item = lookup_item(m_id, data);
417418
let mut result = [];
418419
ebml::tagged_docs(mod_item, tag_mod_impl) {|doc|
419-
let did = parse_def_id(ebml::doc_data(doc));
420+
/*
421+
Pair of an item did and an iface did.
422+
The second one is unneeded if the first id names
423+
an impl; disambiguates if it's a class
424+
*/
425+
let did = parse_def_id(ebml::doc_data(ebml::get_doc(doc,
426+
tag_mod_impl_use)));
420427
let local_did = translate_def_id(cdata, did);
421-
// The impl may be defined in a different crate. Ask the caller
422-
// to give us the metadata
428+
/*
429+
// iface is optional
430+
let iface_did = option::map(ebml::maybe_get_doc(doc,
431+
tag_mod_impl_iface)) {|d|
432+
parse_def_id(ebml::doc_data(d))};
433+
option::iter(iface_did) {|x|
434+
let _local_iface_did = translate_def_id(cdata, x);
435+
};
436+
*/
437+
// CONFUSED -- previous code is pointless
438+
// The impl may be defined in a different crate. Ask the caller
439+
// to give us the metadata
423440
let impl_cdata = get_cdata(local_did.crate);
424441
let impl_data = impl_cdata.data;
425442
let item = lookup_item(local_did.node, impl_data);
426443
let nm = item_name(item);
427444
if alt name { some(n) { n == nm } none { true } } {
428-
let base_tps = item_ty_param_count(item);
429-
result += [@{
445+
let base_tps = item_ty_param_count(item);
446+
result += [@{
447+
// here, we need to... reconstruct the iface_ref?
448+
// probz broken
430449
did: local_did, ident: nm,
431450
methods: item_impl_methods(impl_cdata, item, base_tps)
432451
}];
433-
}
452+
};
434453
}
435454
@result
436455
}
@@ -441,7 +460,7 @@ fn get_iface_methods(cdata: cmd, id: ast::node_id, tcx: ty::ctxt)
441460
let data = cdata.data;
442461
let item = lookup_item(id, data);
443462
let mut result = [];
444-
ebml::tagged_docs(item, tag_item_method) {|mth|
463+
ebml::tagged_docs(item, tag_item_iface_method) {|mth|
445464
let bounds = item_ty_param_bounds(mth, tcx, cdata);
446465
let name = item_name(mth);
447466
let ty = doc_type(mth, tcx, cdata);

src/rustc/metadata/encoder.rs

+71-20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Metadata encoding
22

3+
import util::ppaux::ty_to_str;
4+
35
import std::{ebml, map, list};
46
import std::map::hashmap;
57
import io::writer_util;
@@ -197,6 +199,12 @@ fn encode_module_item_paths(ebml_w: ebml::writer, ecx: @encode_ctxt,
197199
}
198200
}
199201

202+
fn encode_iface_ref(ebml_w: ebml::writer, ecx: @encode_ctxt, t: @iface_ref) {
203+
ebml_w.start_tag(tag_impl_iface);
204+
encode_type(ecx, ebml_w, node_id_to_type(ecx.ccx.tcx, t.id));
205+
ebml_w.end_tag();
206+
}
207+
200208
fn encode_item_paths(ebml_w: ebml::writer, ecx: @encode_ctxt, crate: @crate)
201209
-> [entry<str>] {
202210
let mut index: [entry<str>] = [];
@@ -361,12 +369,52 @@ fn encode_info_for_mod(ecx: @encode_ctxt, ebml_w: ebml::writer, md: _mod,
361369
list::cons(impls, @list::nil) {
362370
for vec::each(*impls) {|i|
363371
if ast_util::is_exported(i.ident, md) {
364-
ebml_w.wr_tagged_str(tag_mod_impl, def_to_str(i.did));
365-
}
366-
}
372+
ebml_w.start_tag(tag_mod_impl);
373+
/* If did stands for an iface
374+
ref, we need to map it to its parent class */
375+
ebml_w.start_tag(tag_mod_impl_use);
376+
let iface_ty = alt ecx.ccx.tcx.items.get(i.did.node) {
377+
ast_map::node_item(it@@{node: cl@item_class(*),_},_) {
378+
ebml_w.wr_str(def_to_str(local_def(it.id)));
379+
some(ty::lookup_item_type(ecx.ccx.tcx, i.did).ty)
380+
}
381+
ast_map::node_item(@{node: item_impl(_,_,
382+
some(ifce),_,_),_},_) {
383+
ebml_w.wr_str(def_to_str(i.did));
384+
some(ty::node_id_to_type(ecx.ccx.tcx, ifce.id))
385+
}
386+
_ {
387+
ebml_w.wr_str(def_to_str(i.did)); none
388+
}
389+
};
390+
ebml_w.end_tag();
391+
392+
/*
393+
/* Write the iface did if it exists */
394+
option::iter(iface_ty) {|i|
395+
alt ty::get(i).struct {
396+
ty::ty_iface(did, tys) {
397+
// FIXME: tys?
398+
ebml_w.start_tag(tag_mod_impl_iface);
399+
ebml_w.wr_str(def_to_str(did));
400+
ebml_w.end_tag();
401+
402+
}
403+
t {
404+
ecx.ccx.tcx.sess.bug(#fmt("Expected item to implement \
405+
an iface, but found %s",
406+
util::ppaux::ty_to_str(ecx.ccx.tcx, i)));
407+
}
408+
}}
409+
*/
410+
ebml_w.end_tag();
411+
} // if
412+
} // for
413+
} // list::cons alt
414+
_ {
415+
ecx.ccx.tcx.sess.bug(#fmt("encode_info_for_mod: empty impl_map \
416+
entry for %?", path));
367417
}
368-
_ { ecx.ccx.tcx.sess.bug(#fmt("encode_info_for_mod: empty impl_map \
369-
entry for %?", path)); }
370418
}
371419
encode_path(ebml_w, path, ast_map::path_mod(name));
372420
ebml_w.end_tag();
@@ -571,7 +619,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
571619
encode_enum_variant_info(ecx, ebml_w, item.id, variants,
572620
path, index, tps);
573621
}
574-
item_class(tps, _ifaces, items, ctor, rp) {
622+
item_class(tps, ifaces, items, ctor, rp) {
575623
/* First, encode the fields and methods
576624
These come first because we need to write them to make
577625
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,
589637
encode_name(ebml_w, item.ident);
590638
encode_path(ebml_w, path, ast_map::path_name(item.ident));
591639
encode_region_param(ebml_w, rp);
592-
/* FIXME: encode ifaces */
640+
for ifaces.each {|t|
641+
encode_iface_ref(ebml_w, ecx, t);
642+
}
593643
/* Encode def_ids for each field and method
594644
for methods, write all the stuff get_iface_method
595645
needs to know*/
@@ -605,14 +655,21 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
605655
alt m.privacy {
606656
priv { /* do nothing */ }
607657
pub {
608-
ebml_w.start_tag(tag_item_method);
609-
#debug("Writing %s %d", m.ident, m.id);
658+
/* Write the info that's needed when viewing this class
659+
as an iface */
660+
ebml_w.start_tag(tag_item_iface_method);
610661
encode_family(ebml_w, purity_fn_family(m.decl.purity));
611662
encode_name(ebml_w, m.ident);
612663
encode_type_param_bounds(ebml_w, ecx, tps + m.tps);
613664
encode_type(ecx, ebml_w, node_id_to_type(tcx, m.id));
614665
encode_def_id(ebml_w, local_def(m.id));
615666
ebml_w.end_tag();
667+
/* Write the info that's needed when viewing this class
668+
as an impl (just the method def_id) */
669+
ebml_w.start_tag(tag_item_impl_method);
670+
ebml_w.writer.write(str::bytes(def_to_str(local_def(m.id))));
671+
ebml_w.end_tag();
672+
616673
}
617674
}
618675
}
@@ -659,19 +716,13 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
659716
encode_type(ecx, ebml_w, node_id_to_type(tcx, item.id));
660717
encode_name(ebml_w, item.ident);
661718
for methods.each {|m|
662-
ebml_w.start_tag(tag_item_method);
719+
ebml_w.start_tag(tag_item_impl_method);
663720
ebml_w.writer.write(str::bytes(def_to_str(local_def(m.id))));
664721
ebml_w.end_tag();
665722
}
666-
alt ifce {
667-
some(t) {
668-
let i_ty = ty::node_id_to_type(tcx, t.id);
669-
ebml_w.start_tag(tag_impl_iface);
670-
write_type(ecx, ebml_w, i_ty);
671-
ebml_w.end_tag();
672-
}
673-
_ {}
674-
}
723+
option::iter(ifce) {|t|
724+
encode_iface_ref(ebml_w, ecx, t)
725+
};
675726
encode_path(ebml_w, path, ast_map::path_name(item.ident));
676727
ebml_w.end_tag();
677728

@@ -693,7 +744,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
693744
encode_name(ebml_w, item.ident);
694745
let mut i = 0u;
695746
for vec::each(*ty::iface_methods(tcx, local_def(item.id))) {|mty|
696-
ebml_w.start_tag(tag_item_method);
747+
ebml_w.start_tag(tag_item_iface_method);
697748
encode_name(ebml_w, mty.ident);
698749
encode_type_param_bounds(ebml_w, ecx, ms[i].tps);
699750
encode_type(ecx, ebml_w, ty::mk_fn(tcx, mty.fty));

src/rustc/metadata/tydecode.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -481,15 +481,13 @@ fn parse_def_id(buf: [u8]) -> ast::def_id {
481481

482482
let crate_num = alt uint::parse_buf(crate_part, 10u) {
483483
some(cn) { cn as int }
484-
none { fail (#fmt("internal error: parse_def_id: error parsing %? \
485-
as crate",
486-
crate_part)); }
484+
none { fail (#fmt("internal error: parse_def_id: crate number \
485+
expected, but found %?", crate_part)); }
487486
};
488487
let def_num = alt uint::parse_buf(def_part, 10u) {
489488
some(dn) { dn as int }
490-
none { fail (#fmt("internal error: parse_def_id: error parsing %? \
491-
as id",
492-
def_part)); }
489+
none { fail (#fmt("internal error: parse_def_id: id expected, but \
490+
found %?", def_part)); }
493491
};
494492
ret {crate: crate_num, node: def_num};
495493
}

src/rustc/middle/resolve.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -2115,8 +2115,14 @@ fn check_exports(e: @env) {
21152115
// Impl resolution
21162116

21172117
type method_info = {did: def_id, n_tps: uint, ident: ast::ident};
2118-
/* what are the did and ident here? */
2119-
/* ident = the name of the impl */
2118+
/* An _impl represents an implementation that's currently in scope.
2119+
Its fields:
2120+
* did: the def id of the class or impl item
2121+
* ident: the name of the impl, unless it has no name (as in
2122+
"impl of X") in which case the ident
2123+
is the ident of the iface that's being implemented
2124+
* methods: the item's methods
2125+
*/
21202126
type _impl = {did: def_id, ident: ast::ident, methods: [@method_info]};
21212127
type iscopes = list<@[@_impl]>;
21222128

src/rustc/middle/typeck.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -4686,8 +4686,10 @@ mod vtable {
46864686
let mut found = [];
46874687

46884688
for list::each(isc) {|impls|
4689+
/* For each impl in scope... */
46894690
for vec::each(*impls) {|im|
4690-
// find the iface that the impl is an impl of (if any)
4691+
// im = one specific impl
4692+
// find the iface that im implements (if any)
46914693
let of_ty = alt ty::impl_iface(tcx, im.did) {
46924694
some(of_ty) { of_ty }
46934695
_ { cont; }

src/test/auxiliary/cci_class_cast.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import to_str::*;
2+
import to_str::to_str;
3+
4+
mod kitty {
5+
6+
class cat implements to_str {
7+
priv {
8+
let mut meows : uint;
9+
fn meow() {
10+
#error("Meow");
11+
self.meows += 1u;
12+
if self.meows % 5u == 0u {
13+
self.how_hungry += 1;
14+
}
15+
}
16+
}
17+
18+
let mut how_hungry : int;
19+
let name : str;
20+
21+
new(in_x : uint, in_y : int, in_name: str)
22+
{ self.meows = in_x; self.how_hungry = in_y; self.name = in_name; }
23+
24+
fn speak() { self.meow(); }
25+
26+
fn eat() -> bool {
27+
if self.how_hungry > 0 {
28+
#error("OM NOM NOM");
29+
self.how_hungry -= 2;
30+
ret true;
31+
}
32+
else {
33+
#error("Not hungry!");
34+
ret false;
35+
}
36+
}
37+
38+
fn to_str() -> str { self.name }
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// xfail-fast
2+
// aux-build:cci_class_cast.rs
3+
use cci_class_cast;
4+
import cci_class_cast::kitty::*;
5+
import to_str::*;
6+
import to_str::to_str;
7+
8+
fn print_out<T: to_str>(thing: T, expected: str) {
9+
let actual = thing.to_str();
10+
#debug("%s", actual);
11+
assert(actual == expected);
12+
}
13+
14+
fn main() {
15+
let nyan : to_str = cat(0u, 2, "nyan") as to_str;
16+
print_out(nyan, "nyan");
17+
}
18+

0 commit comments

Comments
 (0)