From fc9c4c32451dadab4f8a81897e8fc83ae6473d95 Mon Sep 17 00:00:00 2001 From: Lindsey Kuper Date: Tue, 10 Jul 2012 13:44:20 -0700 Subject: [PATCH] Front-end support for default impls in traits. --- src/libsyntax/ast.rs | 11 ++++- src/libsyntax/ast_util.rs | 3 ++ src/libsyntax/parse/parser.rs | 56 ++++++++++++++++++------ src/libsyntax/print/pprust.rs | 10 ++++- src/libsyntax/visit.rs | 24 ++++++++++- src/rustc/metadata/encoder.rs | 21 ++++++--- src/rustc/middle/resolve.rs | 39 ++++++++++++++--- src/rustc/middle/resolve3.rs | 69 ++++++++++++++++++++---------- src/rustc/middle/typeck.rs | 1 + src/rustc/middle/typeck/collect.rs | 11 ++++- src/rustdoc/attr_pass.rs | 9 +++- src/rustdoc/doc.rs | 8 +++- src/rustdoc/extract.rs | 32 ++++++++++---- src/rustdoc/tystr_pass.rs | 26 ++++++++--- 14 files changed, 252 insertions(+), 68 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index e79e5e2ab77e..f79f6cbcae2a 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -452,6 +452,15 @@ type ty_field = spanned; type ty_method = {ident: ident, attrs: ~[attribute], decl: fn_decl, tps: ~[ty_param], span: span}; +#[auto_serialize] +// A trait method is either required (meaning it doesn't have an +// implementation, just a signature) or provided (meaning it has a default +// implementation). +enum trait_method { + required(ty_method), + provided(@method), +} + #[auto_serialize] enum int_ty { ty_i, ty_char, ty_i8, ty_i16, ty_i32, ty_i64, } @@ -695,7 +704,7 @@ enum item_ { /* dtor is optional */ option ), - item_trait(~[ty_param], ~[ty_method]), + item_trait(~[ty_param], ~[trait_method]), item_impl(~[ty_param], option<@trait_ref> /* trait */, @ty /* self */, ~[@method]), item_mac(mac), diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 1e2de4f5bfa4..5675ecadf628 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -511,6 +511,9 @@ fn id_visitor(vfn: fn@(node_id)) -> visit::vt<()> { visit_ty_method: fn@(_ty_m: ty_method) { }, + visit_trait_method: fn@(_ty_m: trait_method) { + }, + visit_class_item: fn@(c: @class_member) { alt c.node { instance_var(_, _, _, id,_) { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index e1f3eb3217c9..5a1eb7636ba3 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -42,14 +42,14 @@ import ast::{_mod, add, alt_check, alt_exhaustive, arg, arm, attribute, mtc_rep, mtc_tok, mul, mutability, neg, noreturn, not, pat, pat_box, pat_enum, pat_ident, pat_lit, pat_range, pat_rec, pat_tup, pat_uniq, pat_wild, path, private, proto, proto_any, - proto_bare, proto_block, proto_box, proto_uniq, public, pure_fn, - purity, re_anon, re_named, region, rem, ret_style, - return_val, shl, shr, stmt, stmt_decl, - stmt_expr, stmt_semi, subtract, token_tree, trait_ref, tt_delim, - tt_dotdotdot, tt_flat, tt_interpolate, ty, ty_, ty_bot, ty_box, - ty_constr, ty_constr_, ty_constr_arg, ty_field, ty_fn, ty_infer, - ty_mac, ty_method, ty_nil, ty_param, ty_path, ty_ptr, ty_rec, - ty_rptr, ty_tup, ty_u32, ty_uniq, ty_vec, ty_vstore, + proto_bare, proto_block, proto_box, proto_uniq, provided, public, + pure_fn, purity, re_anon, re_named, region, rem, required, + ret_style, return_val, shl, shr, stmt, stmt_decl, stmt_expr, + stmt_semi, subtract, token_tree, trait_method, trait_ref, + tt_delim, tt_dotdotdot, tt_flat, tt_interpolate, ty, ty_, ty_bot, + ty_box, ty_constr, ty_constr_, ty_constr_arg, ty_field, ty_fn, + ty_infer, ty_mac, ty_method, ty_nil, ty_param, ty_path, ty_ptr, + ty_rec, ty_rptr, ty_tup, ty_u32, ty_uniq, ty_vec, ty_vstore, unchecked_blk, uniq, unsafe_blk, unsafe_fn, variant, view_item, view_item_, view_item_export, view_item_import, view_item_use, view_path, view_path_glob, view_path_list, view_path_simple, @@ -275,7 +275,7 @@ class parser { constraints: constrs}; } - fn parse_ty_methods() -> ~[ty_method] { + fn parse_trait_methods() -> ~[trait_method] { do self.parse_unspanned_seq(token::LBRACE, token::RBRACE, seq_sep_none()) |p| { let attrs = p.parse_outer_attributes(); @@ -284,12 +284,42 @@ class parser { let ident = p.parse_method_name(); let tps = p.parse_ty_params(); let d = p.parse_ty_fn_decl(pur), fhi = p.last_span.hi; - self.expect(token::SEMI); - {ident: ident, attrs: attrs, decl: {purity: pur with d}, tps: tps, - span: mk_sp(flo, fhi)} + #debug["parse_trait_methods(): trait method ends in %s", + token_to_str(self.reader, self.token)]; + alt self.token { + token::SEMI { + self.bump(); + required({ident: ident, attrs: attrs, + decl: {purity: pur with d}, tps: tps, + span: mk_sp(flo, fhi)}) + } + token::LBRACE { + self.bump(); + let (inner_attrs, body) = + self.parse_inner_attrs_and_block(true); + let attrs = vec::append(attrs, inner_attrs); + self.eat(token::RBRACE); + provided(@{ident: ident, + attrs: attrs, + tps: tps, + decl: d, + body: body, + id: self.get_id(), + span: mk_sp(flo, fhi), + self_id: self.get_id(), + // Provided traits methods always public for now + vis: public}) + } + + _ { self.fatal("expected ';' or '}` \ + but found `" + + token_to_str(self.reader, self.token) + "`"); + } + } } } + fn parse_mt() -> mt { let mutbl = self.parse_mutability(); let t = self.parse_ty(false); @@ -2127,7 +2157,7 @@ class parser { let ident = self.parse_ident(); self.parse_region_param(); let tps = self.parse_ty_params(); - let meths = self.parse_ty_methods(); + let meths = self.parse_trait_methods(); (ident, item_trait(tps, meths), none) } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index d43ed8043ee1..c802e40dd053 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -5,6 +5,7 @@ import pp::{break_offset, word, printer, space, zerobreak, hardbreak, breaks, consistent, inconsistent, eof}; import diagnostic; +import ast::{required, provided}; import ast_util::operator_prec; import dvec::{dvec, extensions}; import parse::classify::*; @@ -593,7 +594,7 @@ fn print_item(s: ps, &&item: @ast::item) { print_type_params(s, tps); word(s.s, " "); bopen(s); - for methods.each |meth| { print_ty_method(s, meth); } + for methods.each |meth| { print_trait_method(s, meth); } bclose(s, item.span); } ast::item_mac({node: ast::mac_invoc_tt(pth, tts), _}) { @@ -647,6 +648,13 @@ fn print_ty_method(s: ps, m: ast::ty_method) { word(s.s, ";"); } +fn print_trait_method(s: ps, m: ast::trait_method) { + alt m { + required(ty_m) { print_ty_method(s, ty_m) } + provided(m) { print_method(s, m) } + } +} + fn print_method(s: ps, meth: @ast::method) { hardbreak_if_not_bol(s); maybe_print_comment(s, meth.span.lo); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 5dc42fd4823e..be9f76650daa 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -62,6 +62,7 @@ type visitor = visit_constr: fn@(@path, span, node_id, E, vt), visit_fn: fn@(fn_kind, fn_decl, blk, span, node_id, E, vt), visit_ty_method: fn@(ty_method, E, vt), + visit_trait_method: fn@(trait_method, E, vt), visit_class_item: fn@(@class_member, E, vt)}; fn default_visitor() -> visitor { @@ -81,6 +82,7 @@ fn default_visitor() -> visitor { visit_constr: |a,b,c,d,e|visit_constr::(a, b, c, d, e), visit_fn: |a,b,c,d,e,f,g|visit_fn::(a, b, c, d, e, f, g), visit_ty_method: |a,b,c|visit_ty_method::(a, b, c), + visit_trait_method: |a,b,c|visit_trait_method::(a, b, c), visit_class_item: |a,b,c|visit_class_item::(a, b, c)}; } @@ -160,7 +162,7 @@ fn visit_item(i: @item, e: E, v: vt) { item_trait(tps, methods) { v.visit_ty_params(tps, e, v); for methods.each |m| { - v.visit_ty_method(m, e, v); + v.visit_trait_method(m, e, v); } } item_mac(m) { visit_mac(m, e, v) } @@ -317,6 +319,17 @@ fn visit_ty_method(m: ty_method, e: E, v: vt) { v.visit_ty(m.decl.output, e, v); } +fn visit_trait_method(m: trait_method, e: E, v: vt) { + alt m { + required(ty_m) { + v.visit_ty_method(ty_m, e, v) + } + provided(m) { + visit_method_helper(m, e, v) + } + } +} + fn visit_block(b: ast::blk, e: E, v: vt) { for b.node.view_items.each |vi| { v.visit_view_item(vi, e, v); } for b.node.stmts.each |s| { v.visit_stmt(s, e, v); } @@ -465,6 +478,7 @@ type simple_visitor = visit_constr: fn@(@path, span, node_id), visit_fn: fn@(fn_kind, fn_decl, blk, span, node_id), visit_ty_method: fn@(ty_method), + visit_trait_method: fn@(trait_method), visit_class_item: fn@(@class_member)}; fn simple_ignore_ty(_t: @ty) {} @@ -487,6 +501,7 @@ fn default_simple_visitor() -> simple_visitor { visit_fn: fn@(_fk: fn_kind, _d: fn_decl, _b: blk, _sp: span, _id: node_id) { }, visit_ty_method: fn@(_m: ty_method) { }, + visit_trait_method: fn@(_m: trait_method) { }, visit_class_item: fn@(_c: @class_member) {} }; } @@ -546,6 +561,11 @@ fn mk_simple_visitor(v: simple_visitor) -> vt<()> { f(ty); visit_ty_method(ty, e, v); } + fn v_trait_method(f: fn@(trait_method), m: trait_method, &&e: (), + v: vt<()>) { + f(m); + visit_trait_method(m, e, v); + } fn v_ty_params(f: fn@(~[ty_param]), ps: ~[ty_param], &&e: (), v: vt<()>) { @@ -596,6 +616,8 @@ fn mk_simple_visitor(v: simple_visitor) -> vt<()> { v_fn(v.visit_fn, a, b, c, d, e, f, g), visit_ty_method: |a,b,c| v_ty_method(v.visit_ty_method, a, b, c), + visit_trait_method: |a,b,c| + v_trait_method(v.visit_trait_method, a, b, c), visit_class_item: |a,b,c| v_class_item(v.visit_class_item, a, b, c) }); diff --git a/src/rustc/metadata/encoder.rs b/src/rustc/metadata/encoder.rs index 7539d2be00bf..74fd9e1c17ea 100644 --- a/src/rustc/metadata/encoder.rs +++ b/src/rustc/metadata/encoder.rs @@ -744,12 +744,21 @@ 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::trait_methods(tcx, local_def(item.id))) |mty| { - ebml_w.start_tag(tag_item_trait_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)); - encode_family(ebml_w, purity_fn_family(mty.purity)); - ebml_w.end_tag(); + alt ms[i] { + required(ty_m) { + ebml_w.start_tag(tag_item_trait_method); + encode_name(ebml_w, mty.ident); + encode_type_param_bounds(ebml_w, ecx, ty_m.tps); + encode_type(ecx, ebml_w, ty::mk_fn(tcx, mty.fty)); + encode_family(ebml_w, purity_fn_family(mty.purity)); + ebml_w.end_tag(); + } + provided(m) { + encode_info_for_method(ecx, ebml_w, path, + should_inline(m.attrs), item.id, + m, m.tps); + } + } i += 1u; } encode_path(ebml_w, path, ast_map::path_name(item.ident)); diff --git a/src/rustc/middle/resolve.rs b/src/rustc/middle/resolve.rs index f2eea665522c..8efdfac95ea3 100644 --- a/src/rustc/middle/resolve.rs +++ b/src/rustc/middle/resolve.rs @@ -1,6 +1,7 @@ import syntax::{ast, ast_util, codemap, ast_map}; import syntax::ast::*; import ast::{ident, fn_ident, def, def_id, node_id}; +import ast::{required, provided}; import syntax::ast_util::{local_def, def_id_of_def, new_def_hash, class_item_ident, path_to_ident}; import pat_util::*; @@ -566,12 +567,8 @@ fn visit_item_with_scope(e: @env, i: @ast::item, } ast::item_trait(tps, methods) { v.visit_ty_params(tps, sc, v); - let isc = @cons(scope_method(i.id, tps), sc); for methods.each |m| { - v.visit_ty_params(m.tps, isc, v); - let msc = @cons(scope_method(i.id, vec::append(tps, m.tps)), sc); - for m.decl.inputs.each |a| { v.visit_ty(a.ty, msc, v); } - v.visit_ty(m.decl.output, msc, v); + visit_trait_method(m, i, tps, sc, v); } } ast::item_class(tps, traits, members, ctor, m_dtor) { @@ -618,6 +615,27 @@ fn visit_item_with_scope(e: @env, i: @ast::item, e.resolve_unexported = old_resolve_unexported; } +fn visit_trait_method(m: trait_method, i: @ast::item, + tps: ~[ast::ty_param], sc: scopes, + v: vt) { + alt m { + required(ty_m) { + let isc = @cons(scope_method(i.id, tps), sc); + v.visit_ty_params(ty_m.tps, isc, v); + let msc = @cons(scope_method(i.id, vec::append(tps, ty_m.tps)), sc); + for ty_m.decl.inputs.each |a| { v.visit_ty(a.ty, msc, v); } + v.visit_ty(ty_m.decl.output, msc, v); + } + provided(m) { + v.visit_ty_params(m.tps, sc, v); + let msc = @cons(scope_method(m.self_id, vec::append(tps, m.tps)), + sc); + v.visit_fn(visit::fk_method(m.ident, ~[], m), + m.decl, m.body, m.span, m.id, msc, v); + } + } +} + fn visit_foreign_item_with_scope(ni: @ast::foreign_item, &&sc: scopes, v: vt) { visit::visit_foreign_item(ni, @cons(scope_foreign_item(ni), sc), v); @@ -1785,7 +1803,16 @@ fn check_item(e: @env, i: @ast::item, &&x: (), v: vt<()>) { "type parameter"); } ast::item_trait(_, methods) { - ensure_unique(*e, i.span, methods, |m| m.ident, + ensure_unique(*e, i.span, methods, |m| { + alt m { + required(ty_m) { + ty_m.ident + } + provided(m) { + m.ident + } + } + }, "method"); } ast::item_impl(_, _, _, methods) { diff --git a/src/rustc/middle/resolve3.rs b/src/rustc/middle/resolve3.rs index ae1b4eebef40..138f021982a7 100644 --- a/src/rustc/middle/resolve3.rs +++ b/src/rustc/middle/resolve3.rs @@ -26,6 +26,7 @@ import syntax::ast::{ty_param, ty_path, ty_str, ty_u, ty_u16, ty_u32, ty_u64}; import syntax::ast::{ty_u8, ty_uint, variant, view_item, view_item_export}; import syntax::ast::{view_item_import, view_item_use, view_path_glob}; import syntax::ast::{view_path_list, view_path_simple}; +import syntax::ast::{required, provided}; import syntax::ast_util::{def_id_of_def, dummy_sp, local_def, new_def_hash}; import syntax::ast_util::{walk_pat}; import syntax::attr::{attr_metas, contains_name}; @@ -2928,21 +2929,33 @@ class Resolver { // // XXX: Do we need a node ID here? - do self.with_type_parameter_rib - (HasTypeParameters(&method.tps, + alt method { + required(ty_m) { + do self.with_type_parameter_rib + (HasTypeParameters(&ty_m.tps, item.id, type_parameters.len(), NormalRibKind)) - || { + || { - // Resolve the method-specific type parameters. - self.resolve_type_parameters(method.tps, visitor); + // Resolve the method-specific type + // parameters. + self.resolve_type_parameters(ty_m.tps, + visitor); - for method.decl.inputs.each |argument| { - self.resolve_type(argument.ty, visitor); + for ty_m.decl.inputs.each |argument| { + self.resolve_type(argument.ty, visitor); + } + + self.resolve_type(ty_m.decl.output, visitor); } - - self.resolve_type(method.decl.output, visitor); + } + provided(m) { + self.resolve_method(NormalRibKind, + m, + type_parameters.len(), + visitor) + } } } } @@ -3242,19 +3255,10 @@ class Resolver { for class_members.each |class_member| { alt class_member.node { class_method(method) { - let borrowed_method_type_parameters = &method.tps; - let type_parameters = - HasTypeParameters(borrowed_method_type_parameters, - method.id, - outer_type_parameter_count, - NormalRibKind); - self.resolve_function(NormalRibKind, - some(@method.decl), - type_parameters, - method.body, - HasSelfBinding(method.self_id), - NoCaptureClause, - visitor); + self.resolve_method(NormalRibKind, + method, + outer_type_parameter_count, + visitor); } instance_var(_, field_type, _, _, _) { self.resolve_type(field_type, visitor); @@ -3291,6 +3295,27 @@ class Resolver { } } + // Does this really need to take a RibKind or is it always going + // to be NormalRibKind? + fn resolve_method(rib_kind: RibKind, + method: @method, + outer_type_parameter_count: uint, + visitor: ResolveVisitor) { + let borrowed_method_type_parameters = &method.tps; + let type_parameters = + HasTypeParameters(borrowed_method_type_parameters, + method.id, + outer_type_parameter_count, + rib_kind); + self.resolve_function(rib_kind, + some(@method.decl), + type_parameters, + method.body, + HasSelfBinding(method.self_id), + NoCaptureClause, + visitor); + } + fn resolve_implementation(id: node_id, span: span, type_parameters: ~[ty_param], diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index 3b10bb3bae37..d8db0533156e 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -41,6 +41,7 @@ independently: import result::{result, extensions}; import syntax::{ast, ast_util, ast_map}; import ast::spanned; +import ast::{required, provided}; import syntax::ast_map::node_id_to_str; import syntax::ast_util::{local_def, respan, split_class_items}; import syntax::visit; diff --git a/src/rustc/middle/typeck/collect.rs b/src/rustc/middle/typeck/collect.rs index fa5ffc61baed..30c5c04dc045 100644 --- a/src/rustc/middle/typeck/collect.rs +++ b/src/rustc/middle/typeck/collect.rs @@ -147,8 +147,15 @@ fn ensure_trait_methods(ccx: @crate_ctxt, id: ast::node_id) { let rp = tcx.region_paramd_items.contains_key(id); alt check tcx.items.get(id) { ast_map::node_item(@{node: ast::item_trait(_, ms), _}, _) { - store_methods::(ccx, id, ms, |m| { - ty_of_ty_method(ccx, m, rp) + store_methods::(ccx, id, ms, |m| { + alt m { + required(ty_m) { + ty_of_ty_method(ccx, ty_m, rp) + } + provided(m) { + ty_of_method(ccx, m, rp) + } + } }); } ast_map::node_item(@{node: ast::item_class(_,_,its,_,_), _}, _) { diff --git a/src/rustdoc/attr_pass.rs b/src/rustdoc/attr_pass.rs index cf2077bfb5ee..0d950b33fe7f 100644 --- a/src/rustdoc/attr_pass.rs +++ b/src/rustdoc/attr_pass.rs @@ -207,7 +207,14 @@ fn merge_method_attrs( node: ast::item_trait(_, methods), _ }, _) { par::seqmap(methods, |method| { - (*method.ident, attr_parser::parse_desc(method.attrs)) + alt method { + ast::required(ty_m) { + (*ty_m.ident, attr_parser::parse_desc(ty_m.attrs)) + } + ast::provided(m) { + (*m.ident, attr_parser::parse_desc(m.attrs)) + } + } }) } ast_map::node_item(@{ diff --git a/src/rustdoc/doc.rs b/src/rustdoc/doc.rs index facd84c7a310..9db3f9a607e5 100644 --- a/src/rustdoc/doc.rs +++ b/src/rustdoc/doc.rs @@ -11,6 +11,11 @@ enum page { itempage(itemtag) } +enum implementation { + required, + provided, +} + /** * Most rustdocs can be parsed into 'sections' according to their markdown * headers @@ -91,7 +96,8 @@ type methoddoc = { brief: option, desc: option, sections: ~[section], - sig: option + sig: option, + implementation: implementation, }; type impldoc = { diff --git a/src/rustdoc/extract.rs b/src/rustdoc/extract.rs index 955c0976c23f..72b7150a72ef 100644 --- a/src/rustdoc/extract.rs +++ b/src/rustdoc/extract.rs @@ -185,17 +185,32 @@ fn should_extract_enum_variants() { fn traitdoc_from_trait( itemdoc: doc::itemdoc, - methods: ~[ast::ty_method] + methods: ~[ast::trait_method] ) -> doc::traitdoc { { item: itemdoc, methods: do par::seqmap(methods) |method| { - { - name: *method.ident, - brief: none, - desc: none, - sections: ~[], - sig: none + alt method { + ast::required(ty_m) { + { + name: *ty_m.ident, + brief: none, + desc: none, + sections: ~[], + sig: none, + implementation: doc::required, + } + } + ast::provided(m) { + { + name: *m.ident, + brief: none, + desc: none, + sections: ~[], + sig: none, + implementation: doc::provided, + } + } } } } @@ -227,7 +242,8 @@ fn impldoc_from_impl( brief: none, desc: none, sections: ~[], - sig: none + sig: none, + implementation: doc::provided, } } } diff --git a/src/rustdoc/tystr_pass.rs b/src/rustdoc/tystr_pass.rs index ba5abf9f66db..011420bdcf4b 100644 --- a/src/rustdoc/tystr_pass.rs +++ b/src/rustdoc/tystr_pass.rs @@ -171,14 +171,28 @@ fn get_method_sig( node: ast::item_trait(_, methods), _ }, _) { alt check vec::find(methods, |method| { - *method.ident == method_name + alt method { + ast::required(ty_m) { *ty_m.ident == method_name } + ast::provided(m) { *m.ident == method_name } + } }) { some(method) { - some(pprust::fun_to_str( - method.decl, - method.ident, - method.tps - )) + alt method { + ast::required(ty_m) { + some(pprust::fun_to_str( + ty_m.decl, + ty_m.ident, + ty_m.tps + )) + } + ast::provided(m) { + some(pprust::fun_to_str( + m.decl, + m.ident, + m.tps + )) + } + } } } }