support autoderef on method calls
This commit is contained in:
parent
514e8ded2f
commit
773a640303
5 changed files with 239 additions and 168 deletions
|
|
@ -68,7 +68,6 @@ type parameter).
|
|||
|
||||
import astconv::{ast_conv, ast_ty_to_ty};
|
||||
import collect::{methods}; // ccx.to_ty()
|
||||
import method::{methods}; // methods for method::lookup
|
||||
import middle::ty::{tv_vid, vid};
|
||||
import regionmanip::{replace_bound_regions_in_fn_ty, region_of};
|
||||
import rscope::{anon_rscope, binding_rscope, empty_rscope, in_anon_rscope};
|
||||
|
|
@ -905,15 +904,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
opname: str, args: [option<@ast::expr>])
|
||||
-> option<(ty::t, bool)> {
|
||||
let callee_id = ast_util::op_expr_callee_id(op_ex);
|
||||
let lkup = method::lookup({fcx: fcx,
|
||||
expr: op_ex,
|
||||
self_expr: self_ex,
|
||||
borrow_scope: op_ex.id,
|
||||
node_id: callee_id,
|
||||
m_name: @opname,
|
||||
self_ty: self_t,
|
||||
supplied_tps: [],
|
||||
include_private: false});
|
||||
let lkup = method::lookup(fcx, op_ex, self_ex, op_ex.id,
|
||||
callee_id, @opname, self_t, [], false);
|
||||
alt lkup.method() {
|
||||
some(origin) {
|
||||
let {fty: method_ty, bot: bot} = {
|
||||
|
|
@ -1629,15 +1621,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
// encloses the method call
|
||||
let borrow_scope = fcx.tcx().region_map.get(expr.id);
|
||||
|
||||
let lkup = method::lookup({fcx: fcx,
|
||||
expr: expr,
|
||||
self_expr: base,
|
||||
borrow_scope: borrow_scope,
|
||||
node_id: expr.id,
|
||||
m_name: field,
|
||||
self_ty: expr_t,
|
||||
supplied_tps: tps,
|
||||
include_private: is_self_ref});
|
||||
let lkup = method::lookup(fcx, expr, base, borrow_scope,
|
||||
expr.id, field, expr_t, tps,
|
||||
is_self_ref);
|
||||
alt lkup.method() {
|
||||
some(entry) {
|
||||
fcx.ccx.method_map.insert(id, entry);
|
||||
|
|
@ -1686,15 +1672,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
|||
|
||||
let p_ty = fcx.expr_ty(p);
|
||||
|
||||
let lkup = method::lookup({fcx: fcx,
|
||||
expr: p,
|
||||
self_expr: p,
|
||||
borrow_scope: expr.id,
|
||||
node_id: alloc_id,
|
||||
m_name: @"alloc",
|
||||
self_ty: p_ty,
|
||||
supplied_tps: [],
|
||||
include_private: false});
|
||||
let lkup = method::lookup(fcx, p, p, expr.id, alloc_id,
|
||||
@"alloc", p_ty, [], false);
|
||||
alt lkup.method() {
|
||||
some(entry) {
|
||||
fcx.ccx.method_map.insert(alloc_id, entry);
|
||||
|
|
|
|||
|
|
@ -2,56 +2,162 @@
|
|||
|
||||
import syntax::ast_map;
|
||||
import middle::typeck::infer::methods; // next_ty_vars
|
||||
import dvec::{dvec, extensions};
|
||||
|
||||
enum lookup = {
|
||||
fcx: @fn_ctxt,
|
||||
expr: @ast::expr, // expr for a.b in a.b()
|
||||
self_expr: @ast::expr, // a in a.b(...)
|
||||
borrow_scope: ast::node_id, // if we have to borrow the expr, what scope?
|
||||
node_id: ast::node_id, // node id of call (not always expr.id)
|
||||
m_name: ast::ident, // b in a.b(...)
|
||||
self_ty: ty::t, // type of a in a.b(...)
|
||||
supplied_tps: [ty::t], // Xs in a.b::<Xs>(...)
|
||||
include_private: bool
|
||||
type candidate = {
|
||||
self_ty: ty::t, // type of a in a.b()
|
||||
self_substs: ty::substs, // values for any tvars def'd on the class
|
||||
rcvr_ty: ty::t, // type of receiver in the method def
|
||||
n_tps_m: uint, // number of tvars defined on the method
|
||||
fty: ty::t, // type of the method
|
||||
entry: method_map_entry
|
||||
};
|
||||
|
||||
impl methods for lookup {
|
||||
class lookup {
|
||||
let fcx: @fn_ctxt;
|
||||
let expr: @ast::expr;
|
||||
let self_expr: @ast::expr;
|
||||
let borrow_scope: ast::node_id;
|
||||
let node_id: ast::node_id;
|
||||
let m_name: ast::ident;
|
||||
let mut self_ty: ty::t;
|
||||
let mut derefs: uint;
|
||||
let candidates: dvec<candidate>;
|
||||
let supplied_tps: [ty::t];
|
||||
let include_private: bool;
|
||||
|
||||
new(fcx: @fn_ctxt,
|
||||
expr: @ast::expr, //expr for a.b in a.b()
|
||||
self_expr: @ast::expr, //a in a.b(...)
|
||||
borrow_scope: ast::node_id, //scope to borrow the expr for
|
||||
node_id: ast::node_id, //node id where to store type of fn
|
||||
m_name: ast::ident, //b in a.b(...)
|
||||
self_ty: ty::t, //type of a in a.b(...)
|
||||
supplied_tps: [ty::t], //Xs in a.b::<Xs>(...)
|
||||
include_private: bool) {
|
||||
|
||||
self.fcx = fcx;
|
||||
self.expr = expr;
|
||||
self.self_expr = self_expr;
|
||||
self.borrow_scope = borrow_scope;
|
||||
self.node_id = node_id;
|
||||
self.m_name = m_name;
|
||||
self.self_ty = self_ty;
|
||||
self.derefs = 0u;
|
||||
self.candidates = dvec();
|
||||
self.supplied_tps = supplied_tps;
|
||||
self.include_private = include_private;
|
||||
}
|
||||
|
||||
// Entrypoint:
|
||||
fn method() -> option<method_map_entry> {
|
||||
#debug["method lookup(m_name=%s, self_ty=%s)",
|
||||
*self.m_name, self.fcx.infcx.ty_to_str(self.self_ty)];
|
||||
|
||||
// First, see whether this is an interface-bounded parameter
|
||||
let pass1 = alt ty::get(self.self_ty).struct {
|
||||
ty::ty_param(n, did) {
|
||||
self.method_from_param(n, did)
|
||||
}
|
||||
ty::ty_iface(did, substs) {
|
||||
self.method_from_iface(did, substs)
|
||||
}
|
||||
ty::ty_class(did, substs) {
|
||||
self.method_from_class(did, substs)
|
||||
}
|
||||
_ {
|
||||
none
|
||||
}
|
||||
};
|
||||
loop {
|
||||
// First, see whether this is an interface-bounded parameter
|
||||
alt ty::get(self.self_ty).struct {
|
||||
ty::ty_param(n, did) {
|
||||
self.add_candidates_from_param(n, did);
|
||||
}
|
||||
ty::ty_iface(did, substs) {
|
||||
self.add_candidates_from_iface(did, substs);
|
||||
}
|
||||
ty::ty_class(did, substs) {
|
||||
self.add_candidates_from_class(did, substs);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
|
||||
alt pass1 {
|
||||
some(r) { some(r) }
|
||||
none { self.method_from_scope() }
|
||||
// if we found anything, stop now. otherwise continue to
|
||||
// loop for impls in scope. Note: I don't love these
|
||||
// semantics, but that's what we had so I am preserving
|
||||
// it.
|
||||
if self.candidates.len() > 0u {
|
||||
break;
|
||||
}
|
||||
|
||||
self.add_candidates_from_scope();
|
||||
|
||||
// if we found anything, stop before attempting auto-deref.
|
||||
if self.candidates.len() > 0u {
|
||||
break;
|
||||
}
|
||||
|
||||
// check whether we can autoderef and if so loop around again.
|
||||
alt ty::deref(self.tcx(), self.self_ty, false) {
|
||||
none { break; }
|
||||
some(mt) {
|
||||
self.self_ty = mt.ty;
|
||||
self.derefs += 1u;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.candidates.len() == 0u { ret none; }
|
||||
|
||||
if self.candidates.len() > 1u {
|
||||
self.tcx().sess.span_err(
|
||||
self.expr.span,
|
||||
"multiple applicable methods in scope");
|
||||
|
||||
for self.candidates.eachi { |i, candidate|
|
||||
alt candidate.entry.origin {
|
||||
method_static(did) {
|
||||
self.report_static_candidate(i, did);
|
||||
}
|
||||
method_param(p) {
|
||||
self.report_param_candidate(i, p.iface_id);
|
||||
}
|
||||
method_iface(did, _) {
|
||||
self.report_iface_candidate(i, did);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
some(self.write_mty_from_candidate(self.candidates[0u]))
|
||||
}
|
||||
|
||||
fn tcx() -> ty::ctxt { self.fcx.ccx.tcx }
|
||||
|
||||
fn method_from_param(n: uint, did: ast::def_id)
|
||||
-> option<method_map_entry> {
|
||||
fn report_static_candidate(idx: uint, did: ast::def_id) {
|
||||
let span = if did.crate == ast::local_crate {
|
||||
alt check self.tcx().items.get(did.node) {
|
||||
ast_map::node_method(m, _, _) { m.span }
|
||||
}
|
||||
} else {
|
||||
self.expr.span
|
||||
};
|
||||
self.tcx().sess.span_note(
|
||||
span,
|
||||
#fmt["candidate #%u is `%s`",
|
||||
(idx+1u),
|
||||
ty::item_path_str(self.tcx(), did)]);
|
||||
}
|
||||
|
||||
fn report_param_candidate(idx: uint, did: ast::def_id) {
|
||||
self.tcx().sess.span_note(
|
||||
self.expr.span,
|
||||
#fmt["candidate #%u derives from the bound `%s`",
|
||||
(idx+1u),
|
||||
ty::item_path_str(self.tcx(), did)]);
|
||||
}
|
||||
|
||||
fn report_iface_candidate(idx: uint, did: ast::def_id) {
|
||||
self.tcx().sess.span_note(
|
||||
self.expr.span,
|
||||
#fmt["candidate #%u derives from the type of the receiver, \
|
||||
which is the iface `%s`",
|
||||
(idx+1u),
|
||||
ty::item_path_str(self.tcx(), did)]);
|
||||
}
|
||||
|
||||
fn add_candidates_from_param(n: uint, did: ast::def_id) {
|
||||
|
||||
let tcx = self.tcx();
|
||||
let mut iface_bnd_idx = 0u; // count only iface bounds
|
||||
let bounds = tcx.ty_param_bounds.get(did.node);
|
||||
let mut candidates = [];
|
||||
for vec::each(*bounds) {|bound|
|
||||
let (iid, bound_substs) = alt bound {
|
||||
ty::bound_copy | ty::bound_send | ty::bound_const {
|
||||
|
|
@ -81,41 +187,20 @@ impl methods for lookup {
|
|||
// permitted).
|
||||
let substs = {self_ty: some(self.self_ty)
|
||||
with bound_substs};
|
||||
candidates += [(substs, ifce_methods[pos],
|
||||
iid, pos, n, iface_bnd_idx)];
|
||||
|
||||
self.add_candidates_from_m(
|
||||
substs, ifce_methods[pos],
|
||||
method_param({iface_id:iid,
|
||||
method_num:pos,
|
||||
param_num:n,
|
||||
bound_num:iface_bnd_idx}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if candidates.len() == 0u {
|
||||
ret none;
|
||||
}
|
||||
|
||||
if candidates.len() > 1u {
|
||||
self.tcx().sess.span_err(
|
||||
self.expr.span,
|
||||
"multiple applicable methods in scope");
|
||||
|
||||
for candidates.eachi { |i, candidate|
|
||||
let (_, _, iid, _, _, _) = candidate;
|
||||
self.tcx().sess.span_note(
|
||||
self.expr.span,
|
||||
#fmt["candidate #%u derives from the bound `%s`",
|
||||
(i+1u), ty::item_path_str(self.tcx(), iid)]);
|
||||
}
|
||||
}
|
||||
|
||||
let (substs, mty, iid, pos, n, iface_bnd_idx) = candidates[0u];
|
||||
ret some(self.write_mty_from_m(
|
||||
substs, mty, method_param({iface_id:iid,
|
||||
method_num:pos,
|
||||
param_num:n,
|
||||
bound_num:iface_bnd_idx})));
|
||||
}
|
||||
|
||||
fn method_from_iface(
|
||||
did: ast::def_id, iface_substs: ty::substs)
|
||||
-> option<method_map_entry> {
|
||||
fn add_candidates_from_iface(did: ast::def_id, iface_substs: ty::substs) {
|
||||
|
||||
let ms = *ty::iface_methods(self.tcx(), did);
|
||||
for ms.eachi {|i, m|
|
||||
|
|
@ -143,15 +228,12 @@ impl methods for lookup {
|
|||
let substs = {self_ty: some(self.self_ty)
|
||||
with iface_substs};
|
||||
|
||||
ret some(self.write_mty_from_m(
|
||||
substs, m, method_iface(did, i)));
|
||||
self.add_candidates_from_m(
|
||||
substs, m, method_iface(did, i));
|
||||
}
|
||||
|
||||
ret none;
|
||||
}
|
||||
|
||||
fn method_from_class(did: ast::def_id, class_substs: ty::substs)
|
||||
-> option<method_map_entry> {
|
||||
fn add_candidates_from_class(did: ast::def_id, class_substs: ty::substs) {
|
||||
|
||||
let ms = *ty::iface_methods(self.tcx(), did);
|
||||
|
||||
|
|
@ -169,12 +251,9 @@ impl methods for lookup {
|
|||
let m_declared = ty::lookup_class_method_by_name(
|
||||
self.tcx(), did, self.m_name, self.expr.span);
|
||||
|
||||
ret some(self.write_mty_from_m(
|
||||
class_substs, m,
|
||||
method_static(m_declared)));
|
||||
self.add_candidates_from_m(
|
||||
class_substs, m, method_static(m_declared));
|
||||
}
|
||||
|
||||
ret none;
|
||||
}
|
||||
|
||||
fn ty_from_did(did: ast::def_id) -> ty::t {
|
||||
|
|
@ -202,11 +281,11 @@ impl methods for lookup {
|
|||
*/
|
||||
}
|
||||
|
||||
fn method_from_scope() -> option<method_map_entry> {
|
||||
fn add_candidates_from_scope() {
|
||||
let impls_vecs = self.fcx.ccx.impl_map.get(self.expr.id);
|
||||
let mut added_any = false;
|
||||
|
||||
for list::each(impls_vecs) {|impls|
|
||||
let mut results = [];
|
||||
for vec::each(*impls) {|im|
|
||||
// Check whether this impl has a method with the right name.
|
||||
for im.methods.find({|m| m.ident == self.m_name}).each {|m|
|
||||
|
|
@ -223,89 +302,72 @@ impl methods for lookup {
|
|||
self.self_ty, impl_ty) {
|
||||
result::err(_) { /* keep looking */ }
|
||||
result::ok(_) {
|
||||
results += [(impl_ty, impl_substs, m.n_tps, m.did)];
|
||||
let fty = self.ty_from_did(m.did);
|
||||
self.candidates.push(
|
||||
{self_ty: self.self_ty,
|
||||
self_substs: impl_substs,
|
||||
rcvr_ty: impl_ty,
|
||||
n_tps_m: m.n_tps,
|
||||
fty: fty,
|
||||
entry: {derefs: self.derefs,
|
||||
origin: method_static(m.did)}});
|
||||
added_any = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if results.len() >= 1u {
|
||||
if results.len() > 1u {
|
||||
self.tcx().sess.span_err(
|
||||
self.expr.span,
|
||||
"multiple applicable methods in scope");
|
||||
|
||||
// I would like to print out how each impl was imported,
|
||||
// but I cannot for the life of me figure out how to
|
||||
// annotate resolve to preserve this information.
|
||||
for results.eachi { |i, result|
|
||||
let (_, _, _, did) = result;
|
||||
let span = if did.crate == ast::local_crate {
|
||||
alt check self.tcx().items.get(did.node) {
|
||||
ast_map::node_method(m, _, _) { m.span }
|
||||
}
|
||||
} else {
|
||||
self.expr.span
|
||||
};
|
||||
self.tcx().sess.span_note(
|
||||
span,
|
||||
#fmt["candidate #%u is `%s`",
|
||||
(i+1u),
|
||||
ty::item_path_str(self.tcx(), did)]);
|
||||
}
|
||||
}
|
||||
|
||||
let (impl_ty, impl_substs, n_tps, did) = results[0];
|
||||
alt self.fcx.mk_assignty(self.self_expr, self.borrow_scope,
|
||||
self.self_ty, impl_ty) {
|
||||
result::ok(_) {}
|
||||
result::err(_) {
|
||||
self.tcx().sess.span_bug(
|
||||
self.expr.span,
|
||||
#fmt["%s was assignable to %s but now is not?",
|
||||
self.fcx.infcx.ty_to_str(self.self_ty),
|
||||
self.fcx.infcx.ty_to_str(impl_ty)]);
|
||||
}
|
||||
}
|
||||
let fty = self.ty_from_did(did);
|
||||
ret some(self.write_mty_from_fty(
|
||||
impl_substs, n_tps, fty,
|
||||
method_static(did)));
|
||||
}
|
||||
// we want to find the innermost scope that has any
|
||||
// matches and then ignore outer scopes
|
||||
if added_any {ret;}
|
||||
}
|
||||
|
||||
ret none;
|
||||
}
|
||||
|
||||
fn write_mty_from_m(self_substs: ty::substs,
|
||||
m: ty::method,
|
||||
origin: method_origin) -> method_map_entry {
|
||||
fn add_candidates_from_m(self_substs: ty::substs,
|
||||
m: ty::method,
|
||||
origin: method_origin) {
|
||||
let tcx = self.fcx.ccx.tcx;
|
||||
|
||||
// a bit hokey, but the method unbound has a bare protocol, whereas
|
||||
// a.b has a protocol like fn@() (perhaps eventually fn&()):
|
||||
let fty = ty::mk_fn(tcx, {proto: ast::proto_box with m.fty});
|
||||
|
||||
ret self.write_mty_from_fty(self_substs, (*m.tps).len(),
|
||||
fty, origin);
|
||||
self.candidates.push(
|
||||
{self_ty: self.self_ty,
|
||||
self_substs: self_substs,
|
||||
rcvr_ty: self.self_ty,
|
||||
n_tps_m: (*m.tps).len(),
|
||||
fty: fty,
|
||||
entry: {derefs: self.derefs, origin: origin}});
|
||||
}
|
||||
|
||||
fn write_mty_from_fty(self_substs: ty::substs,
|
||||
n_tps_m: uint,
|
||||
fty: ty::t,
|
||||
origin: method_origin) -> method_map_entry {
|
||||
|
||||
fn write_mty_from_candidate(cand: candidate) -> method_map_entry {
|
||||
let tcx = self.fcx.ccx.tcx;
|
||||
|
||||
#debug["write_mty_from_fty(n_tps_m=%u, fty=%s, origin=%?)",
|
||||
n_tps_m,
|
||||
self.fcx.infcx.ty_to_str(fty),
|
||||
origin];
|
||||
#debug["write_mty_from_candidate(n_tps_m=%u, fty=%s, entry=%?)",
|
||||
cand.n_tps_m,
|
||||
self.fcx.infcx.ty_to_str(cand.fty),
|
||||
cand.entry];
|
||||
|
||||
// Here I will use the "c_" prefix to refer to the method's
|
||||
// owner. You can read it as class, but it may also be an iface.
|
||||
// Make the actual receiver type (cand.self_ty) assignable to the
|
||||
// required receiver type (cand.rcvr_ty). If this method is not
|
||||
// from an impl, this'll basically be a no-nop.
|
||||
alt self.fcx.mk_assignty(self.self_expr, self.borrow_scope,
|
||||
cand.self_ty, cand.rcvr_ty) {
|
||||
result::ok(_) {}
|
||||
result::err(_) {
|
||||
self.tcx().sess.span_bug(
|
||||
self.expr.span,
|
||||
#fmt["%s was assignable to %s but now is not?",
|
||||
self.fcx.infcx.ty_to_str(cand.self_ty),
|
||||
self.fcx.infcx.ty_to_str(cand.rcvr_ty)]);
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the full set of type parameters for the method,
|
||||
// which is equal to the class tps + the method tps.
|
||||
let n_tps_supplied = self.supplied_tps.len();
|
||||
let n_tps_m = cand.n_tps_m;
|
||||
let m_substs = {
|
||||
if n_tps_supplied == 0u {
|
||||
self.fcx.infcx.next_ty_vars(n_tps_m)
|
||||
|
|
@ -325,12 +387,12 @@ impl methods for lookup {
|
|||
}
|
||||
};
|
||||
|
||||
let all_substs = {tps: self_substs.tps + m_substs
|
||||
with self_substs};
|
||||
let all_substs = {tps: cand.self_substs.tps + m_substs
|
||||
with cand.self_substs};
|
||||
|
||||
self.fcx.write_ty_substs(self.node_id, fty, all_substs);
|
||||
self.fcx.write_ty_substs(self.node_id, cand.fty, all_substs);
|
||||
|
||||
ret {derefs:0u, origin:origin};
|
||||
ret cand.entry;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
10
src/test/run-pass/autoderef-method-newtype.rs
Normal file
10
src/test/run-pass/autoderef-method-newtype.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
impl methods for uint {
|
||||
fn double() -> uint { self * 2u }
|
||||
}
|
||||
|
||||
enum foo = uint;
|
||||
|
||||
fn main() {
|
||||
let x = foo(3u);
|
||||
assert x.double() == 6u;
|
||||
}
|
||||
12
src/test/run-pass/autoderef-method-priority.rs
Normal file
12
src/test/run-pass/autoderef-method-priority.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
impl methods for uint {
|
||||
fn double() -> uint { self }
|
||||
}
|
||||
|
||||
impl methods for @uint {
|
||||
fn double() -> uint { *self * 2u }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = @3u;
|
||||
assert x.double() == 6u;
|
||||
}
|
||||
8
src/test/run-pass/autoderef-method.rs
Normal file
8
src/test/run-pass/autoderef-method.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
impl methods for uint {
|
||||
fn double() -> uint { self * 2u }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = @3u;
|
||||
assert x.double() == 6u;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue