syntax: Parse multiple trait refs in a single implementation

This commit is contained in:
Patrick Walton 2012-07-18 09:31:53 -07:00
parent 1528256fdc
commit 3ac5b4a86f
14 changed files with 126 additions and 63 deletions

View File

@ -635,8 +635,10 @@ enum item_ {
option<class_dtor> option<class_dtor>
), ),
item_trait(~[ty_param], ~[trait_method]), item_trait(~[ty_param], ~[trait_method]),
item_impl(~[ty_param], option<@trait_ref> /* trait */, item_impl(~[ty_param],
@ty /* self */, ~[@method]), ~[@trait_ref], /* traits this impl implements */
@ty, /* self */
~[@method]),
item_mac(mac), item_mac(mac),
} }

View File

@ -21,6 +21,7 @@ import ast_builder::append_types;
import ast_builder::ast_builder; import ast_builder::ast_builder;
import ast_builder::methods; import ast_builder::methods;
import ast_builder::path; import ast_builder::path;
import ast_builder::path_concat;
trait gen_send { trait gen_send {
fn gen_send(cx: ext_ctxt) -> @ast::item; fn gen_send(cx: ext_ctxt) -> @ast::item;

View File

@ -2085,13 +2085,22 @@ class parser {
(some(id), self.parse_ty_params()) (some(id), self.parse_ty_params())
} }
}; };
let ifce = if self.eat_keyword(~"of") { let traits;
let path = self.parse_path_with_tps(false); if self.eat_keyword(~"of") {
if option::is_none(ident) { let for_atom = interner::intern(*self.reader.interner(), @~"for");
ident = some(vec::last(path.idents)); traits = self.parse_trait_ref_list(token::IDENT(for_atom, false));
if traits.len() >= 1 && option::is_none(ident) {
ident = some(vec::last(traits[0].path.idents));
} }
some(@{path: path, ref_id: self.get_id(), impl_id: self.get_id()}) if traits.len() == 0 {
} else { none }; self.fatal(~"BUG: 'of' but no trait");
}
if traits.len() > 1 {
self.fatal(~"BUG: multiple traits");
}
} else {
traits = ~[];
};
let ident = alt ident { let ident = alt ident {
some(name) { name } some(name) { name }
none { self.expect_keyword(~"of"); fail; } none { self.expect_keyword(~"of"); fail; }
@ -2103,7 +2112,7 @@ class parser {
while !self.eat(token::RBRACE) { while !self.eat(token::RBRACE) {
vec::push(meths, self.parse_method(public)); vec::push(meths, self.parse_method(public));
} }
(ident, item_impl(tps, ifce, ty, meths), none) (ident, item_impl(tps, traits, ty, meths), none)
} }
// Instantiates ident <i> with references to <typarams> as arguments. // Instantiates ident <i> with references to <typarams> as arguments.
@ -2127,9 +2136,9 @@ class parser {
ref_id: self.get_id(), impl_id: self.get_id()} ref_id: self.get_id(), impl_id: self.get_id()}
} }
fn parse_trait_ref_list() -> ~[@trait_ref] { fn parse_trait_ref_list(ket: token::token) -> ~[@trait_ref] {
self.parse_seq_to_before_end( self.parse_seq_to_before_end(
token::LBRACE, seq_sep_trailing_disallowed(token::COMMA), ket, seq_sep_trailing_disallowed(token::COMMA),
|p| p.parse_trait_ref()) |p| p.parse_trait_ref())
} }
@ -2139,7 +2148,7 @@ class parser {
let ty_params = self.parse_ty_params(); let ty_params = self.parse_ty_params();
let class_path = self.ident_to_path_tys(class_name, ty_params); let class_path = self.ident_to_path_tys(class_name, ty_params);
let traits : ~[@trait_ref] = if self.eat(token::COLON) let traits : ~[@trait_ref] = if self.eat(token::COLON)
{ self.parse_trait_ref_list() } { self.parse_trait_ref_list(token::LBRACE) }
else { ~[] }; else { ~[] };
self.expect(token::LBRACE); self.expect(token::LBRACE);
let mut ms: ~[@class_member] = ~[]; let mut ms: ~[@class_member] = ~[];

View File

@ -558,16 +558,18 @@ fn print_item(s: ps, &&item: @ast::item) {
} }
bclose(s, item.span); bclose(s, item.span);
} }
ast::item_impl(tps, ifce, ty, methods) { ast::item_impl(tps, traits, ty, methods) {
head(s, ~"impl"); head(s, ~"impl");
word(s.s, *item.ident); word(s.s, *item.ident);
print_type_params(s, tps); print_type_params(s, tps);
space(s.s); space(s.s);
option::iter(ifce, |p| { if vec::len(traits) != 0u {
word_nbsp(s, ~"of"); word_nbsp(s, ~"of");
print_path(s, p.path, false); do commasep(s, inconsistent, traits) |s, p| {
print_path(s, p.path, false);
}
space(s.s); space(s.s);
}); }
word_nbsp(s, ~"for"); word_nbsp(s, ~"for");
print_type(s, ty); print_type(s, ty);
space(s.s); space(s.s);

View File

@ -135,9 +135,11 @@ fn visit_item<E>(i: @item, e: E, v: vt<E>) {
for vr.node.args.each |va| { v.visit_ty(va.ty, e, v); } for vr.node.args.each |va| { v.visit_ty(va.ty, e, v); }
} }
} }
item_impl(tps, ifce, ty, methods) { item_impl(tps, traits, ty, methods) {
v.visit_ty_params(tps, e, v); v.visit_ty_params(tps, e, v);
option::iter(ifce, |p| visit_path(p.path, e, v)); for traits.each |p| {
visit_path(p.path, e, v);
}
v.visit_ty(ty, e, v); v.visit_ty(ty, e, v);
for methods.each |m| { for methods.each |m| {
visit_method_helper(m, e, v) visit_method_helper(m, e, v)

View File

@ -410,14 +410,7 @@ fn encode_info_for_mod(ecx: @encode_ctxt, ebml_w: ebml::writer, md: _mod,
ref, we need to map it to its parent class */ ref, we need to map it to its parent class */
ebml_w.wr_str(def_to_str(local_def(it.id))); ebml_w.wr_str(def_to_str(local_def(it.id)));
} }
some(ast_map::node_item(@{node: item_impl(_, _ {
some(ifce),_,_),_},_)) {
ebml_w.wr_str(def_to_str(did));
}
some(_) {
ebml_w.wr_str(def_to_str(did));
}
none {
// Must be a re-export, then! // Must be a re-export, then!
// ...or an iface ref // ...or an iface ref
ebml_w.wr_str(def_to_str(did)); ebml_w.wr_str(def_to_str(did));
@ -715,7 +708,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
encode_index(ebml_w, bkts, write_int); encode_index(ebml_w, bkts, write_int);
ebml_w.end_tag(); ebml_w.end_tag();
} }
item_impl(tps, ifce, _, methods) { item_impl(tps, traits, _, methods) {
add_to_index(); add_to_index();
ebml_w.start_tag(tag_items_data_item); ebml_w.start_tag(tag_items_data_item);
encode_def_id(ebml_w, local_def(item.id)); encode_def_id(ebml_w, local_def(item.id));
@ -729,9 +722,12 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
ebml_w.writer.write(str::bytes(def_to_str(local_def(m.id)))); ebml_w.writer.write(str::bytes(def_to_str(local_def(m.id))));
ebml_w.end_tag(); ebml_w.end_tag();
} }
do option::iter(ifce) |t| { if traits.len() > 1 {
encode_trait_ref(ebml_w, ecx, t) fail ~"multiple traits!!";
}; }
for traits.each |associated_trait| {
encode_trait_ref(ebml_w, ecx, associated_trait)
}
encode_path(ebml_w, path, ast_map::path_name(item.ident)); encode_path(ebml_w, path, ast_map::path_name(item.ident));
ebml_w.end_tag(); ebml_w.end_tag();

View File

@ -2966,16 +2966,20 @@ class Resolver {
} }
} }
item_impl(type_parameters, interface_reference, self_type, item_impl(type_parameters, implemented_traits, self_type,
methods) { methods) {
self.resolve_implementation(item.id, // XXX: Should take an array of traits.
item.span, let trait_reference;
type_parameters, if implemented_traits.len() == 0 {
interface_reference, trait_reference = none;
self_type, } else {
methods, trait_reference = some(implemented_traits[0]);
visitor); }
self.resolve_implementation(item.id, item.span,
type_parameters, trait_reference,
self_type, methods, visitor);
} }
item_trait(type_parameters, methods) { item_trait(type_parameters, methods) {

View File

@ -2474,13 +2474,16 @@ fn trait_methods(cx: ctxt, id: ast::def_id) -> @~[method] {
result result
} }
// XXX: Needs to return an array of traits.
fn impl_trait(cx: ctxt, id: ast::def_id) -> option<t> { fn impl_trait(cx: ctxt, id: ast::def_id) -> option<t> {
if id.crate == ast::local_crate { if id.crate == ast::local_crate {
#debug("(impl_trait) searching for trait impl %?", id); #debug("(impl_trait) searching for trait impl %?", id);
alt cx.items.find(id.node) { alt cx.items.find(id.node) {
some(ast_map::node_item(@{node: ast::item_impl( some(ast_map::node_item(@{
_, some(@{ref_id: id, _}), _, _), _}, _)) { node: ast::item_impl(_, traits, _, _),
some(node_id_to_type(cx, id)) _},
_)) if traits.len() >= 1 {
some(node_id_to_type(cx, traits[0].ref_id))
} }
some(ast_map::node_item(@{node: ast::item_class(*), some(ast_map::node_item(@{node: ast::item_class(*),
_},_)) { _},_)) {

View File

@ -454,7 +454,7 @@ class lookup {
for (*trait_ids).each |trait_id| { for (*trait_ids).each |trait_id| {
#debug("(adding inherent and extension candidates) \ #debug("(adding inherent and extension candidates) \
trying trait: %s", trying trait: %s",
node_id_to_str(self.tcx().items, trait_id.node)); self.def_id_to_str(trait_id));
let coherence_info = self.fcx.ccx.coherence_info; let coherence_info = self.fcx.ccx.coherence_info;
alt coherence_info.extension_methods.find(trait_id) { alt coherence_info.extension_methods.find(trait_id) {
@ -463,6 +463,10 @@ class lookup {
} }
some(extension_methods) { some(extension_methods) {
for extension_methods.each |implementation| { for extension_methods.each |implementation| {
#debug("(adding inherent and extension \
candidates) adding impl %s",
self.def_id_to_str
(implementation.did));
self.add_candidates_from_impl self.add_candidates_from_impl
(implementation, use_assignability); (implementation, use_assignability);
} }
@ -473,6 +477,14 @@ class lookup {
} }
} }
fn def_id_to_str(def_id: ast::def_id) -> ~str {
if def_id.crate == ast::local_crate {
node_id_to_str(self.tcx().items, def_id.node)
} else {
ast_map::path_to_str(csearch::get_item_path(self.tcx(), def_id))
}
}
fn write_mty_from_candidate(cand: candidate) -> method_map_entry { fn write_mty_from_candidate(cand: candidate) -> method_map_entry {
let tcx = self.fcx.ccx.tcx; let tcx = self.fcx.ccx.tcx;

View File

@ -154,9 +154,21 @@ class CoherenceChecker {
visit_crate(*crate, (), mk_simple_visitor(@{ visit_crate(*crate, (), mk_simple_visitor(@{
visit_item: |item| { visit_item: |item| {
#debug("(checking coherence) item '%s'", *item.ident);
alt item.node { alt item.node {
item_impl(_, associated_trait, self_type, _) { item_impl(_, associated_traits, self_type, _) {
self.check_implementation(item, associated_trait); // XXX: Accept an array of traits.
let optional_associated_trait;
if associated_traits.len() == 0 {
optional_associated_trait = none;
} else {
optional_associated_trait =
some(associated_traits[0]);
}
self.check_implementation(item,
optional_associated_trait);
} }
_ { _ {
// Nothing to do. // Nothing to do.
@ -189,6 +201,10 @@ class CoherenceChecker {
let self_type = self.crate_context.tcx.tcache.get(local_def(item.id)); let self_type = self.crate_context.tcx.tcache.get(local_def(item.id));
alt optional_associated_trait { alt optional_associated_trait {
none { none {
#debug("(checking implementation) no associated trait for \
item '%s'",
*item.ident);
alt get_base_type_def_id(self.inference_context, alt get_base_type_def_id(self.inference_context,
item.span, item.span,
self_type.ty) { self_type.ty) {
@ -207,6 +223,12 @@ class CoherenceChecker {
some(associated_trait) { some(associated_trait) {
let def = self.crate_context.tcx.def_map.get let def = self.crate_context.tcx.def_map.get
(associated_trait.ref_id); (associated_trait.ref_id);
#debug("(checking implementation) adding impl for trait \
'%s', item '%s'",
ast_map::node_id_to_str(self.crate_context.tcx.items,
associated_trait.ref_id),
*item.ident);
let implementation = self.create_impl_from_item(item); let implementation = self.create_impl_from_item(item);
self.add_trait_method(def_id_of_def(def), implementation); self.add_trait_method(def_id_of_def(def), implementation);
} }
@ -362,7 +384,15 @@ class CoherenceChecker {
self.privileged_types.remove(privileged_type); self.privileged_types.remove(privileged_type);
} }
} }
item_impl(_, optional_trait_ref, _, _) { item_impl(_, associated_traits, _, _) {
// XXX: Accept an array of traits.
let optional_trait_ref;
if associated_traits.len() == 0 {
optional_trait_ref = none;
} else {
optional_trait_ref = some(associated_traits[0]);
}
alt self.base_type_def_ids.find(local_def(item.id)) { alt self.base_type_def_ids.find(local_def(item.id)) {
none { none {
// Nothing to do. // Nothing to do.

View File

@ -110,7 +110,7 @@ type methoddoc = {
type impldoc = { type impldoc = {
item: itemdoc, item: itemdoc,
trait_ty: option<~str>, trait_types: ~[~str],
self_ty: option<~str>, self_ty: option<~str>,
methods: ~[methoddoc] methods: ~[methoddoc]
}; };

View File

@ -235,7 +235,7 @@ fn impldoc_from_impl(
) -> doc::impldoc { ) -> doc::impldoc {
{ {
item: itemdoc, item: itemdoc,
trait_ty: none, trait_types: ~[],
self_ty: none, self_ty: none,
methods: do vec::map(methods) |method| { methods: do vec::map(methods) |method| {
{ {

View File

@ -224,14 +224,16 @@ fn header_name(doc: doc::itemtag) -> ~str {
doc::impltag(doc) { doc::impltag(doc) {
assert option::is_some(doc.self_ty); assert option::is_some(doc.self_ty);
let self_ty = option::get(doc.self_ty); let self_ty = option::get(doc.self_ty);
alt doc.trait_ty { let mut trait_part = ~"";
some(trait_ty) { for doc.trait_types.eachi |i, trait_type| {
#fmt("%s of %s for %s", doc.name(), trait_ty, self_ty) if i == 0 {
} trait_part += ~" of ";
none { } else {
#fmt("%s for %s", doc.name(), self_ty) trait_part += ", ";
} }
trait_part += trait_type;
} }
#fmt("%s%s for %s", doc.name(), trait_part, self_ty)
} }
_ { _ {
doc.name() doc.name()

View File

@ -230,22 +230,22 @@ fn fold_impl(
let srv = fold.ctxt; let srv = fold.ctxt;
let (trait_ty, self_ty) = do astsrv::exec(srv) |ctxt| { let (trait_types, self_ty) = do astsrv::exec(srv) |ctxt| {
alt ctxt.ast_map.get(doc.id()) { alt ctxt.ast_map.get(doc.id()) {
ast_map::node_item(@{ ast_map::node_item(@{
node: ast::item_impl(_, trait_ty, self_ty, _), _ node: ast::item_impl(_, trait_types, self_ty, _), _
}, _) { }, _) {
let trait_ty = option::map(trait_ty, |p| { let trait_types = vec::map(trait_types, |p| {
pprust::path_to_str(p.path) pprust::path_to_str(p.path)
}); });
(trait_ty, some(pprust::ty_to_str(self_ty))) (trait_types, some(pprust::ty_to_str(self_ty)))
} }
_ { fail ~"expected impl" } _ { fail ~"expected impl" }
} }
}; };
{ {
trait_ty: trait_ty, trait_types: trait_types,
self_ty: self_ty, self_ty: self_ty,
methods: merge_methods(fold.ctxt, doc.id(), doc.methods) methods: merge_methods(fold.ctxt, doc.id(), doc.methods)
with doc with doc
@ -253,15 +253,15 @@ fn fold_impl(
} }
#[test] #[test]
fn should_add_impl_trait_ty() { fn should_add_impl_trait_types() {
let doc = test::mk_doc(~"impl i of j for int { fn a<T>() { } }"); let doc = test::mk_doc(~"impl i of j for int { fn a<T>() { } }");
assert doc.cratemod().impls()[0].trait_ty == some(~"j"); assert doc.cratemod().impls()[0].trait_types[0] == ~"j";
} }
#[test] #[test]
fn should_not_add_impl_trait_ty_if_none() { fn should_not_add_impl_trait_types_if_none() {
let doc = test::mk_doc(~"impl i for int { fn a() { } }"); let doc = test::mk_doc(~"impl i for int { fn a() { } }");
assert doc.cratemod().impls()[0].trait_ty == none; assert vec::len(doc.cratemod().impls()[0].trait_types) == 0;
} }
#[test] #[test]