From 3ac5b4a86fa37d2b2c17ef5ffdb6e521630ea4ac Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 18 Jul 2012 09:31:53 -0700 Subject: [PATCH] syntax: Parse multiple trait refs in a single implementation --- src/libsyntax/ast.rs | 6 +++-- src/libsyntax/ext/pipes/pipec.rs | 1 + src/libsyntax/parse/parser.rs | 29 +++++++++++++------- src/libsyntax/print/pprust.rs | 10 ++++--- src/libsyntax/visit.rs | 6 +++-- src/rustc/metadata/encoder.rs | 20 ++++++-------- src/rustc/middle/resolve3.rs | 20 ++++++++------ src/rustc/middle/ty.rs | 9 ++++--- src/rustc/middle/typeck/check/method.rs | 14 +++++++++- src/rustc/middle/typeck/coherence.rs | 36 ++++++++++++++++++++++--- src/rustdoc/doc.rs | 2 +- src/rustdoc/extract.rs | 2 +- src/rustdoc/markdown_pass.rs | 16 ++++++----- src/rustdoc/tystr_pass.rs | 18 ++++++------- 14 files changed, 126 insertions(+), 63 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 65ff9245fd1..79523acb99b 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -635,8 +635,10 @@ enum item_ { option ), item_trait(~[ty_param], ~[trait_method]), - item_impl(~[ty_param], option<@trait_ref> /* trait */, - @ty /* self */, ~[@method]), + item_impl(~[ty_param], + ~[@trait_ref], /* traits this impl implements */ + @ty, /* self */ + ~[@method]), item_mac(mac), } diff --git a/src/libsyntax/ext/pipes/pipec.rs b/src/libsyntax/ext/pipes/pipec.rs index c6b1a2d6932..df5df748a74 100644 --- a/src/libsyntax/ext/pipes/pipec.rs +++ b/src/libsyntax/ext/pipes/pipec.rs @@ -21,6 +21,7 @@ import ast_builder::append_types; import ast_builder::ast_builder; import ast_builder::methods; import ast_builder::path; +import ast_builder::path_concat; trait gen_send { fn gen_send(cx: ext_ctxt) -> @ast::item; diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 8336d6b39ab..6bcf22c8220 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2085,13 +2085,22 @@ class parser { (some(id), self.parse_ty_params()) } }; - let ifce = if self.eat_keyword(~"of") { - let path = self.parse_path_with_tps(false); - if option::is_none(ident) { - ident = some(vec::last(path.idents)); + let traits; + if self.eat_keyword(~"of") { + let for_atom = interner::intern(*self.reader.interner(), @~"for"); + 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()}) - } else { none }; + if traits.len() == 0 { + self.fatal(~"BUG: 'of' but no trait"); + } + if traits.len() > 1 { + self.fatal(~"BUG: multiple traits"); + } + } else { + traits = ~[]; + }; let ident = alt ident { some(name) { name } none { self.expect_keyword(~"of"); fail; } @@ -2103,7 +2112,7 @@ class parser { while !self.eat(token::RBRACE) { 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 with references to as arguments. @@ -2127,9 +2136,9 @@ class parser { 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( - token::LBRACE, seq_sep_trailing_disallowed(token::COMMA), + ket, seq_sep_trailing_disallowed(token::COMMA), |p| p.parse_trait_ref()) } @@ -2139,7 +2148,7 @@ class parser { let ty_params = self.parse_ty_params(); let class_path = self.ident_to_path_tys(class_name, ty_params); let traits : ~[@trait_ref] = if self.eat(token::COLON) - { self.parse_trait_ref_list() } + { self.parse_trait_ref_list(token::LBRACE) } else { ~[] }; self.expect(token::LBRACE); let mut ms: ~[@class_member] = ~[]; diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 2d087d8d9df..b1730bfc587 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -558,16 +558,18 @@ fn print_item(s: ps, &&item: @ast::item) { } bclose(s, item.span); } - ast::item_impl(tps, ifce, ty, methods) { + ast::item_impl(tps, traits, ty, methods) { head(s, ~"impl"); word(s.s, *item.ident); print_type_params(s, tps); space(s.s); - option::iter(ifce, |p| { + if vec::len(traits) != 0u { 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); - }); + } word_nbsp(s, ~"for"); print_type(s, ty); space(s.s); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index ead8e981d7a..14fe18edd09 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -135,9 +135,11 @@ fn visit_item(i: @item, e: E, v: vt) { 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); - 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); for methods.each |m| { visit_method_helper(m, e, v) diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index 4df1e0aa513..e11008f4d6f 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -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 */ 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! // ...or an iface ref 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); ebml_w.end_tag(); } - item_impl(tps, ifce, _, methods) { + item_impl(tps, traits, _, methods) { add_to_index(); ebml_w.start_tag(tag_items_data_item); 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.end_tag(); } - do option::iter(ifce) |t| { - encode_trait_ref(ebml_w, ecx, t) - }; + if traits.len() > 1 { + 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)); ebml_w.end_tag(); diff --git a/src/rustc/middle/resolve3.rs b/src/rustc/middle/resolve3.rs index 04a7448f798..55bc99fa688 100644 --- a/src/rustc/middle/resolve3.rs +++ b/src/rustc/middle/resolve3.rs @@ -2966,16 +2966,20 @@ class Resolver { } } - item_impl(type_parameters, interface_reference, self_type, + item_impl(type_parameters, implemented_traits, self_type, methods) { - self.resolve_implementation(item.id, - item.span, - type_parameters, - interface_reference, - self_type, - methods, - visitor); + // XXX: Should take an array of traits. + let trait_reference; + if implemented_traits.len() == 0 { + trait_reference = none; + } else { + trait_reference = some(implemented_traits[0]); + } + + self.resolve_implementation(item.id, item.span, + type_parameters, trait_reference, + self_type, methods, visitor); } item_trait(type_parameters, methods) { diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs index 08ef9fb6bff..b750870ff86 100644 --- a/src/rustc/middle/ty.rs +++ b/src/rustc/middle/ty.rs @@ -2474,13 +2474,16 @@ fn trait_methods(cx: ctxt, id: ast::def_id) -> @~[method] { result } +// XXX: Needs to return an array of traits. fn impl_trait(cx: ctxt, id: ast::def_id) -> option { if id.crate == ast::local_crate { #debug("(impl_trait) searching for trait impl %?", id); alt cx.items.find(id.node) { - some(ast_map::node_item(@{node: ast::item_impl( - _, some(@{ref_id: id, _}), _, _), _}, _)) { - some(node_id_to_type(cx, id)) + some(ast_map::node_item(@{ + node: ast::item_impl(_, traits, _, _), + _}, + _)) if traits.len() >= 1 { + some(node_id_to_type(cx, traits[0].ref_id)) } some(ast_map::node_item(@{node: ast::item_class(*), _},_)) { diff --git a/src/rustc/middle/typeck/check/method.rs b/src/rustc/middle/typeck/check/method.rs index c2135249c9c..7f269944059 100644 --- a/src/rustc/middle/typeck/check/method.rs +++ b/src/rustc/middle/typeck/check/method.rs @@ -454,7 +454,7 @@ class lookup { for (*trait_ids).each |trait_id| { #debug("(adding inherent and extension candidates) \ 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; alt coherence_info.extension_methods.find(trait_id) { @@ -463,6 +463,10 @@ class lookup { } some(extension_methods) { 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 (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 { let tcx = self.fcx.ccx.tcx; diff --git a/src/rustc/middle/typeck/coherence.rs b/src/rustc/middle/typeck/coherence.rs index 19b4450bb62..f0c70cbc3a9 100644 --- a/src/rustc/middle/typeck/coherence.rs +++ b/src/rustc/middle/typeck/coherence.rs @@ -154,9 +154,21 @@ class CoherenceChecker { visit_crate(*crate, (), mk_simple_visitor(@{ visit_item: |item| { + #debug("(checking coherence) item '%s'", *item.ident); + alt item.node { - item_impl(_, associated_trait, self_type, _) { - self.check_implementation(item, associated_trait); + item_impl(_, associated_traits, self_type, _) { + // 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. @@ -189,6 +201,10 @@ class CoherenceChecker { let self_type = self.crate_context.tcx.tcache.get(local_def(item.id)); alt optional_associated_trait { none { + #debug("(checking implementation) no associated trait for \ + item '%s'", + *item.ident); + alt get_base_type_def_id(self.inference_context, item.span, self_type.ty) { @@ -207,6 +223,12 @@ class CoherenceChecker { some(associated_trait) { let def = self.crate_context.tcx.def_map.get (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); self.add_trait_method(def_id_of_def(def), implementation); } @@ -362,7 +384,15 @@ class CoherenceChecker { 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)) { none { // Nothing to do. diff --git a/src/rustdoc/doc.rs b/src/rustdoc/doc.rs index efbd9c7cd60..e6c780e6e08 100644 --- a/src/rustdoc/doc.rs +++ b/src/rustdoc/doc.rs @@ -110,7 +110,7 @@ type methoddoc = { type impldoc = { item: itemdoc, - trait_ty: option<~str>, + trait_types: ~[~str], self_ty: option<~str>, methods: ~[methoddoc] }; diff --git a/src/rustdoc/extract.rs b/src/rustdoc/extract.rs index 343c018a312..bdcfb8cc590 100644 --- a/src/rustdoc/extract.rs +++ b/src/rustdoc/extract.rs @@ -235,7 +235,7 @@ fn impldoc_from_impl( ) -> doc::impldoc { { item: itemdoc, - trait_ty: none, + trait_types: ~[], self_ty: none, methods: do vec::map(methods) |method| { { diff --git a/src/rustdoc/markdown_pass.rs b/src/rustdoc/markdown_pass.rs index f3962f2a30e..b04210f4d31 100644 --- a/src/rustdoc/markdown_pass.rs +++ b/src/rustdoc/markdown_pass.rs @@ -224,14 +224,16 @@ fn header_name(doc: doc::itemtag) -> ~str { doc::impltag(doc) { assert option::is_some(doc.self_ty); let self_ty = option::get(doc.self_ty); - alt doc.trait_ty { - some(trait_ty) { - #fmt("%s of %s for %s", doc.name(), trait_ty, self_ty) - } - none { - #fmt("%s for %s", doc.name(), self_ty) - } + let mut trait_part = ~""; + for doc.trait_types.eachi |i, trait_type| { + if i == 0 { + trait_part += ~" of "; + } else { + trait_part += ", "; + } + trait_part += trait_type; } + #fmt("%s%s for %s", doc.name(), trait_part, self_ty) } _ { doc.name() diff --git a/src/rustdoc/tystr_pass.rs b/src/rustdoc/tystr_pass.rs index b22c7e121c7..774106a6fb4 100644 --- a/src/rustdoc/tystr_pass.rs +++ b/src/rustdoc/tystr_pass.rs @@ -230,22 +230,22 @@ fn fold_impl( 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()) { 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) }); - (trait_ty, some(pprust::ty_to_str(self_ty))) + (trait_types, some(pprust::ty_to_str(self_ty))) } _ { fail ~"expected impl" } } }; { - trait_ty: trait_ty, + trait_types: trait_types, self_ty: self_ty, methods: merge_methods(fold.ctxt, doc.id(), doc.methods) with doc @@ -253,15 +253,15 @@ fn fold_impl( } #[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() { } }"); - assert doc.cratemod().impls()[0].trait_ty == some(~"j"); + assert doc.cratemod().impls()[0].trait_types[0] == ~"j"; } #[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() { } }"); - assert doc.cratemod().impls()[0].trait_ty == none; + assert vec::len(doc.cratemod().impls()[0].trait_types) == 0; } #[test]