From d529757515671d5826d8d2203ce0a4f82a3bdee5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 16 Dec 2011 11:37:38 +0100 Subject: [PATCH] Make polymorphic impl methods work Something will still have to be done to the AST to make it possible to say `x.foo::()`, since currently field access never allows type parameters. Issue #1227 --- src/comp/middle/resolve.rs | 3 +- src/comp/middle/trans.rs | 26 +++++++---- src/comp/middle/typeck.rs | 81 ++++++++++++++++++++------------- src/comp/syntax/ast.rs | 2 +- src/comp/syntax/fold.rs | 3 +- src/comp/syntax/parse/parser.rs | 11 +++-- src/comp/syntax/visit.rs | 12 ++--- 7 files changed, 82 insertions(+), 56 deletions(-) diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index bc095e992f0..9fdba389c24 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -751,10 +751,11 @@ fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace) ast::item_obj(ob, ty_params, _) { ret lookup_in_obj(name, ob, ty_params, ns, it.id); } - ast::item_impl(_, _, _) { + ast::item_impl(ty_params, _, _) { if (name == "self" && ns == ns_value) { ret some(ast::def_self(local_def(it.id))); } + if ns == ns_type { ret lookup_in_ty_params(name, ty_params); } } ast::item_tag(_, ty_params) { if ns == ns_type { ret lookup_in_ty_params(name, ty_params); } diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 2c724ba8fca..37e35ee3efa 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -4353,15 +4353,19 @@ fn create_llargs_for_fn_args(cx: @fn_ctxt, ty_self: self_arg, // way. let arg_n = 2u; alt ty_self { - obj_self(tt) | impl_self(tt) { cx.llself = some({v: cx.llenv, t: tt}); } - no_self. { - let i = 0u; - for tp: ast::ty_param in ty_params { + obj_self(tt) | impl_self(tt) { + cx.llself = some({v: cx.llenv, t: tt}); + } + no_self. {} + } + alt ty_self { + obj_self(_) {} + _ { + for tp in ty_params { let llarg = llvm::LLVMGetParam(cx.llfn, arg_n); assert (llarg as int != 0); cx.lltydescs += [llarg]; arg_n += 1u; - i += 1u; } } } @@ -4659,14 +4663,14 @@ fn trans_tag_variant(cx: @local_ctxt, tag_id: ast::node_id, } fn trans_impl(cx: @local_ctxt, name: ast::ident, methods: [@ast::method], - id: ast::node_id) { + id: ast::node_id, tps: [ast::ty_param]) { let sub_cx = extend_path(cx, name); for m in methods { alt cx.ccx.item_ids.find(m.node.id) { some(llfn) { trans_fn(extend_path(sub_cx, m.node.ident), m.span, m.node.meth, llfn, impl_self(ty::node_id_to_monotype(cx.ccx.tcx, id)), - [], m.node.id); + tps + m.node.tps, m.node.id); } } } @@ -4980,7 +4984,9 @@ fn trans_item(cx: @local_ctxt, item: ast::item) { with *extend_path(cx, item.ident)}; trans_obj(sub_cx, item.span, ob, ctor_id, tps); } - ast::item_impl(_, _, ms) { trans_impl(cx, item.ident, ms, item.id); } + ast::item_impl(tps, _, ms) { + trans_impl(cx, item.ident, ms, item.id, tps); + } ast::item_res(dtor, dtor_id, tps, ctor_id) { trans_res_ctor(cx, item.span, dtor, ctor_id, tps); @@ -5304,11 +5310,11 @@ fn collect_item_2(ccx: @crate_ctxt, i: @ast::item, &&pt: [str], ccx.obj_methods.insert(m.node.id, ()); } } - ast::item_impl(_, _, methods) { + ast::item_impl(tps, _, methods) { let name = ccx.names.next(i.ident); for m in methods { register_fn(ccx, i.span, pt + [name, m.node.ident], - "impl_method", [], m.node.id); + "impl_method", tps + m.node.tps, m.node.id); } } ast::item_res(_, dtor_id, tps, ctor_id) { diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index d697421bde4..d86215a14c0 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -962,8 +962,7 @@ mod writeback { typ) { fix_ok(new_type) { ret some(new_type); } fix_err(vid) { - fcx.ccx.tcx.sess.span_err(sp, - "cannot determine a type \ + fcx.ccx.tcx.sess.span_err(sp, "cannot determine a type \ for this expression"); ret none; } @@ -1461,6 +1460,42 @@ fn check_expr_with(fcx: @fn_ctxt, expr: @ast::expr, expected: ty::t) -> bool { ret check_expr_with_unifier(fcx, expr, demand::simple, expected); } +// FIXME[impl] notice/resolve conflicts +fn lookup_method(fcx: @fn_ctxt, isc: resolve::iscopes, + name: ast::ident, ty: ty::t) + -> option::t<{method: @ast::method, ids: [int]}> { + let result = none; + std::list::iter(isc) {|impls| + for im in *impls { + alt im.node { + ast::item_impl(tps, slf, mthds) { + let self_ty = ast_ty_to_ty_crate(fcx.ccx, slf); + let tp_count = vec::len(tps); + let {ids, ty: self_ty} = if tp_count > 0u { + bind_params_in_type(ast_util::dummy_sp(), fcx.ccx.tcx, + bind next_ty_var_id(fcx), self_ty, + tp_count) + } else { {ids: [], ty: self_ty} }; + // FIXME[impl] Don't unify in the current fcx, use + // scratch context + alt unify::unify(fcx, ty, self_ty) { + ures_ok(_) { + for m in mthds { + if m.node.ident == name { + result = some({method: m, ids: ids}); + ret; + } + } + } + _ {} + } + } + } + } + } + result +} + fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, expected: ty::t) -> bool { //log_err "typechecking expr " + syntax::print::pprust::expr_to_str(expr); @@ -2089,42 +2124,24 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, } } ast::expr_field(base, field) { - // FIXME proper type compare, notice conflicts - fn lookup_method(fcx: @fn_ctxt, isc: resolve::iscopes, - name: ast::ident, ty: ty::t) - -> option::t<@ast::method> { - let result = none; - std::list::iter(isc) {|impls| - for im in *impls { - alt im.node { - ast::item_impl(_, slf, mthds) { - let self_ty = ast_ty_to_ty_crate(fcx.ccx, slf); - alt unify::unify(fcx, ty, self_ty) { - ures_ok(_) {} - _ { cont; } - } - for m in mthds { - if m.node.ident == name { - result = some(m); - ret; - } - } - } - } - } - } - result - } - bot |= check_expr(fcx, base); let base_t = expr_ty(tcx, base); let iscope = fcx.ccx.impl_map.get(expr.id); alt lookup_method(fcx, iscope, field, base_t) { - some(method) { - let mt = ty_of_method(fcx.ccx.tcx, m_check, method); + some({method, ids}) { + let mt = ty_of_method(fcx.ccx.tcx, m_check, method), ids = ids; let fty = ty::mk_fn(fcx.ccx.tcx, mt.proto, mt.inputs, mt.output, mt.cf, mt.constrs); - write::ty_only_fixup(fcx, id, fty); + let tp_count = vec::len(method.node.tps); + if tp_count > 0u { + let b = bind_params_in_type(expr.span, tcx, + bind next_ty_var_id(fcx), + fty, tp_count); + ids += b.ids; + fty = b.ty; + } + let substs = vec::map({|id| ty::mk_var(tcx, id)}, ids); + write::ty_fixup(fcx, id, {substs: some(substs), ty: fty}); fcx.ccx.method_map.insert(id, local_def(method.node.id)); } _ { diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs index fd79869c27b..d85849b2571 100644 --- a/src/comp/syntax/ast.rs +++ b/src/comp/syntax/ast.rs @@ -423,7 +423,7 @@ tag ret_style { type _fn = {decl: fn_decl, proto: proto, body: blk}; -type method_ = {ident: ident, meth: _fn, id: node_id}; +type method_ = {ident: ident, meth: _fn, id: node_id, tps: [ty_param]}; type method = spanned; diff --git a/src/comp/syntax/fold.rs b/src/comp/syntax/fold.rs index aa949b11f34..b3a48cc418c 100644 --- a/src/comp/syntax/fold.rs +++ b/src/comp/syntax/fold.rs @@ -246,7 +246,8 @@ fn noop_fold_item_underscore(i: item_, fld: ast_fold) -> item_ { } fn noop_fold_method(m: method_, fld: ast_fold) -> method_ { - ret {ident: fld.fold_ident(m.ident), meth: fld.fold_fn(m.meth), id: m.id}; + ret {ident: fld.fold_ident(m.ident), meth: fld.fold_fn(m.meth) + with m}; } diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index 3d00c333ad4..12df9ecd1ad 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -854,7 +854,7 @@ fn parse_bottom_expr(p: parser) -> @ast::expr { while p.peek() != token::RBRACE { if eat_word(p, "with") { inner_obj = some(parse_expr(p)); - } else { meths += [parse_method(p)]; } + } else { meths += [parse_method(p, false)]; } } hi = p.get_hi_pos(); expect(p, token::RBRACE); @@ -1832,12 +1832,13 @@ fn parse_anon_obj_field(p: parser) -> ast::anon_obj_field { ret {mut: mut, ty: ty, expr: expr, ident: ident, id: p.get_id()}; } -fn parse_method(p: parser) -> @ast::method { +fn parse_method(p: parser, allow_tps: bool) -> @ast::method { let lo = p.get_lo_pos(); let proto = parse_method_proto(p); let ident = parse_value_ident(p); + let tps = allow_tps ? parse_ty_params(p) : []; let f = parse_fn(p, proto, ast::impure_fn, ast::il_normal); - let meth = {ident: ident, meth: f, id: p.get_id()}; + let meth = {ident: ident, meth: f, id: p.get_id(), tps: tps}; ret @spanned(lo, f.body.span.hi, meth); } @@ -1850,7 +1851,7 @@ fn parse_item_obj(p: parser, attrs: [ast::attribute]) -> @ast::item { parse_obj_field, p); let meths: [@ast::method] = []; expect(p, token::LBRACE); - while p.peek() != token::RBRACE { meths += [parse_method(p)]; } + while p.peek() != token::RBRACE { meths += [parse_method(p, false)]; } let hi = p.get_hi_pos(); expect(p, token::RBRACE); let ob: ast::_obj = {fields: fields.node, methods: meths}; @@ -1864,7 +1865,7 @@ fn parse_item_impl(p: parser, attrs: [ast::attribute]) -> @ast::item { expect_word(p, "for"); let ty = parse_ty(p, false), meths = []; expect(p, token::LBRACE); - while !eat(p, token::RBRACE) { meths += [parse_method(p)]; } + while !eat(p, token::RBRACE) { meths += [parse_method(p, true)]; } ret mk_item(p, lo, p.get_last_hi_pos(), ident, ast::item_impl(tps, ty, meths), attrs); } diff --git a/src/comp/syntax/visit.rs b/src/comp/syntax/visit.rs index 5091c2d9ea0..a1feec5bec9 100644 --- a/src/comp/syntax/visit.rs +++ b/src/comp/syntax/visit.rs @@ -101,15 +101,15 @@ fn visit_item(i: @item, e: E, v: vt) { item_obj(ob, _, _) { for f: obj_field in ob.fields { v.visit_ty(f.ty, e, v); } for m: @method in ob.methods { - v.visit_fn(m.node.meth, [], m.span, some(m.node.ident), m.node.id, - e, v); + v.visit_fn(m.node.meth, m.node.tps, m.span, some(m.node.ident), + m.node.id, e, v); } } item_impl(_, ty, methods) { visit_ty(ty, e, v); for m in methods { - v.visit_fn(m.node.meth, [], m.span, some(m.node.ident), m.node.id, - e, v); + v.visit_fn(m.node.meth, m.node.tps, m.span, some(m.node.ident), + m.node.id, e, v); } } } @@ -321,8 +321,8 @@ fn visit_expr(ex: @expr, e: E, v: vt) { some(ex) { v.visit_expr(ex, e, v); } } for m: @method in anon_obj.methods { - v.visit_fn(m.node.meth, [], m.span, some(m.node.ident), m.node.id, - e, v); + v.visit_fn(m.node.meth, m.node.tps, m.span, some(m.node.ident), + m.node.id, e, v); } } expr_mac(mac) { visit_mac(mac, e, v); }