import std::prettyprint::serializer;
import std::smallintmap::map;
import middle::{ty, typeck};
-import middle::typeck::{method_origin,
- serialize_method_origin,
- deserialize_method_origin,
+import middle::typeck::{method_origin, method_map_entry,
+ serialize_method_map_entry,
+ deserialize_method_map_entry,
vtable_res,
vtable_origin};
import driver::session::session;
}
// ______________________________________________________________________
-// Encoding and decoding of method_origin
-
-fn encode_method_origin(ebml_w: ebml::writer, mo: method_origin) {
- serialize_method_origin(ebml_w, mo)
-}
+// Encoding and decoding of method_map_entry
impl helper for ebml::ebml_deserializer {
- fn read_method_origin(xcx: extended_decode_ctxt) -> method_origin {
- let fv = deserialize_method_origin(self);
- fv.tr(xcx)
+ fn read_method_map_entry(xcx: extended_decode_ctxt) -> method_map_entry {
+ let mme = deserialize_method_map_entry(self);
+ {derefs: mme.derefs, origin: mme.origin.tr(xcx)}
}
}
typeck::method_static(did) {
typeck::method_static(did.tr(xcx))
}
- typeck::method_param(did, m, p, b) {
- typeck::method_param(did.tr(xcx), m, p, b)
+ typeck::method_param(mp) {
+ typeck::method_param({iface_id:mp.iface_id.tr(xcx) with mp})
}
typeck::method_iface(did, m) {
typeck::method_iface(did.tr(xcx), m)
// impl_map is not used except when emitting metadata,
// don't need to keep it.
- option::iter(maps.method_map.find(id)) {|mo|
+ option::iter(maps.method_map.find(id)) {|mme|
ebml_w.tag(c::tag_table_method_map) {||
ebml_w.id(id);
ebml_w.tag(c::tag_table_val) {||
- serialize_method_origin(ebml_w, mo)
+ serialize_method_map_entry(ebml_w, mme)
}
}
}
let dvec = @dvec::from_vec(vec::to_mut(ids));
dcx.maps.last_use_map.insert(id, dvec);
} else if tag == (c::tag_table_method_map as uint) {
- dcx.maps.method_map.insert(id,
- val_dsr.read_method_origin(xcx));
+ dcx.maps.method_map.insert(
+ id,
+ val_dsr.read_method_map_entry(xcx));
} else if tag == (c::tag_table_vtable_map as uint) {
dcx.maps.vtable_map.insert(id,
val_dsr.read_vtable_res(xcx));
ty::lookup_item_type(cx.tcx, did).bounds
}
expr_field(base, _, _) {
- alt cx.method_map.get(e.id) {
+ alt cx.method_map.get(e.id).origin {
typeck::method_static(did) {
// n.b.: When we encode class/impl methods, the bounds
// that we encode include both the class/impl bounds
// and then the method bounds themselves...
ty::lookup_item_type(cx.tcx, did).bounds
}
- typeck::method_param(ifce_id, n_mth, _, _) |
+ typeck::method_param({iface_id:ifce_id,
+ method_num:n_mth, _}) |
typeck::method_iface(ifce_id, n_mth) {
// ...iface methods bounds, in contrast, include only the
// method bounds, so we must preprend the tps from the
let _icx = bcx.insn_ctxt("trans_unary");
// Check for user-defined method call
alt bcx.ccx().maps.method_map.find(un_expr.id) {
- some(origin) {
+ some(mentry) {
let callee_id = ast_util::op_expr_callee_id(un_expr);
let fty = node_id_type(bcx, callee_id);
ret trans_call_inner(
bcx, un_expr.info(), fty,
expr_ty(bcx, un_expr),
- {|bcx| impl::trans_method_callee(bcx, callee_id, e, origin) },
+ {|bcx| impl::trans_method_callee(bcx, callee_id, e, mentry) },
arg_exprs([]), dest);
}
_ {}
add_root_cleanup(bcx, scope_id, root_loc, ty);
}
+// autoderefs the value `v`, either as many times as we can (if `max ==
+// uint::max_value`) or `max` times.
fn autoderef(cx: block, e_id: ast::node_id,
- v: ValueRef, t: ty::t) -> result_t {
+ v: ValueRef, t: ty::t,
+ max: uint) -> result_t {
let _icx = cx.insn_ctxt("autoderef");
let mut v1: ValueRef = v;
let mut t1: ty::t = t;
let ccx = cx.ccx();
let mut derefs = 0u;
- loop {
+ while derefs < max {
#debug["autoderef(e_id=%d, v1=%s, t1=%s, derefs=%u)",
e_id, val_str(ccx.tn, v1), ty_to_str(ccx.tcx, t1),
derefs];
}
v1 = load_if_immediate(cx, v1, t1);
}
+
+ // either we were asked to deref a specific number of times, in which case
+ // we should have, or we asked to deref as many times as we can
+ assert derefs == max || max == uint::max_value;
+
ret {bcx: cx, val: v1, ty: t1};
}
field: ast::ident) -> lval_result {
let _icx = bcx.insn_ctxt("trans_rec_field");
let {bcx, val} = trans_temp_expr(bcx, base);
- let {bcx, val, ty} = autoderef(bcx, base.id, val, expr_ty(bcx, base));
+ let {bcx, val, ty} =
+ autoderef(bcx, base.id, val, expr_ty(bcx, base),
+ uint::max_value);
trans_rec_field_inner(bcx, val, ty, field, base.span)
}
let _icx = cx.insn_ctxt("trans_index");
let base_ty = expr_ty(cx, base);
let exp = trans_temp_expr(cx, base);
- let lv = autoderef(exp.bcx, base.id, exp.val, base_ty);
+ let lv = autoderef(exp.bcx, base.id, exp.val, base_ty, uint::max_value);
let ix = trans_temp_expr(lv.bcx, idx);
let v = lv.val;
let bcx = ix.bcx;
// temp_cleanups: cleanups that should run only if failure occurs before the
// call takes place:
fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
- &temp_cleanups: [ValueRef], ret_flag: option<ValueRef>)
+ &temp_cleanups: [ValueRef], ret_flag: option<ValueRef>,
+ derefs: uint)
-> result {
#debug("+++ trans_arg_expr on %s", expr_to_str(e));
let _icx = cx.insn_ctxt("trans_arg_expr");
let ccx = cx.ccx();
let e_ty = expr_ty(cx, e);
let is_bot = ty::type_is_bot(e_ty);
+
+ // translate the arg expr as an lvalue
let lv = alt ret_flag {
// If there is a ret_flag, this *must* be a loop body
some(ptr) {
}
}
}
- none { trans_temp_lval(cx, e) }
+ none {
+ trans_temp_lval(cx, e)
+ }
};
+
+ // auto-deref value as required (this only applies to method
+ // call receivers) of method
+ #debug(" pre-deref value: %s", val_str(lv.bcx.ccx().tn, lv.val));
+ let {lv, e_ty} = if derefs == 0u {
+ {lv: lv, e_ty: e_ty}
+ } else {
+ let {bcx, val} = lval_result_to_result(lv, e_ty);
+ let {bcx, val, ty: e_ty} =
+ autoderef(bcx, e.id, val, e_ty, derefs);
+ {lv: {bcx: bcx, val: val, kind: temporary},
+ e_ty: e_ty}
+ };
+
+ // borrow value (convert from @T to &T and so forth)
#debug(" pre-adaptation value: %s", val_str(lv.bcx.ccx().tn, lv.val));
- let {lv, arg} = adapt_borrowed_value(lv, arg, e);
+ let {lv, ty: e_ty} = adapt_borrowed_value(lv, e, e_ty);
let mut bcx = lv.bcx;
let mut val = lv.val;
#debug(" adapted value: %s", val_str(bcx.ccx().tn, val));
+
+ // finally, deal with the various modes
let arg_mode = ty::resolved_mode(ccx.tcx, arg.mode);
if is_bot {
// For values of type _|_, we generate an
// be inspected. It's important for the value
// to have type lldestty (the callee's expected type).
val = llvm::LLVMGetUndef(lldestty);
- } else if arg_mode == ast::by_ref || arg_mode == ast::by_val {
- let imm = ty::type_is_immediate(arg.ty);
- #debug[" arg.ty=%s, imm=%b, arg_mode=%?, lv.kind=%?",
- ty_to_str(bcx.tcx(), arg.ty), imm, arg_mode, lv.kind];
- if arg_mode == ast::by_ref && lv.kind != owned && imm {
- val = do_spill_noroot(bcx, val);
- }
- if arg_mode == ast::by_val && (lv.kind == owned || !imm) {
- val = Load(bcx, val);
- }
- } else if arg_mode == ast::by_copy || arg_mode == ast::by_move {
- let alloc = alloc_ty(bcx, arg.ty);
- let move_out = arg_mode == ast::by_move ||
- ccx.maps.last_use_map.contains_key(e.id);
- if lv.kind == temporary { revoke_clean(bcx, val); }
- if lv.kind == owned || !ty::type_is_immediate(arg.ty) {
- memmove_ty(bcx, alloc, val, arg.ty);
- if move_out && ty::type_needs_drop(ccx.tcx, arg.ty) {
- bcx = zero_mem(bcx, val, arg.ty);
+ } else {
+ alt arg_mode {
+ ast::by_ref | ast::by_mutbl_ref {
+ // Ensure that the value is spilled into memory:
+ if lv.kind != owned && ty::type_is_immediate(e_ty) {
+ val = do_spill_noroot(bcx, val);
}
- } else { Store(bcx, val, alloc); }
- val = alloc;
- if lv.kind != temporary && !move_out {
- bcx = take_ty(bcx, val, arg.ty);
- }
+ }
+
+ ast::by_val {
+ // Ensure that the value is not spilled into memory:
+ if lv.kind == owned || !ty::type_is_immediate(e_ty) {
+ val = Load(bcx, val);
+ }
+ }
- // In the event that failure occurs before the call actually
- // happens, have to cleanup this copy:
- add_clean_temp_mem(bcx, val, arg.ty);
- temp_cleanups += [val];
- } else if ty::type_is_immediate(arg.ty) && lv.kind != owned {
- val = do_spill(bcx, val, arg.ty);
+ ast::by_copy | ast::by_move {
+ // Ensure that an owned copy of the value is in memory:
+ let alloc = alloc_ty(bcx, arg.ty);
+ let move_out = arg_mode == ast::by_move ||
+ ccx.maps.last_use_map.contains_key(e.id);
+ if lv.kind == temporary { revoke_clean(bcx, val); }
+ if lv.kind == owned || !ty::type_is_immediate(arg.ty) {
+ memmove_ty(bcx, alloc, val, arg.ty);
+ if move_out && ty::type_needs_drop(ccx.tcx, arg.ty) {
+ bcx = zero_mem(bcx, val, arg.ty);
+ }
+ } else { Store(bcx, val, alloc); }
+ val = alloc;
+ if lv.kind != temporary && !move_out {
+ bcx = take_ty(bcx, val, arg.ty);
+ }
+
+ // In the event that failure occurs before the call actually
+ // happens, have to cleanup this copy:
+ add_clean_temp_mem(bcx, val, arg.ty);
+ temp_cleanups += [val];
+ }
+ }
}
if !is_bot && arg.ty != e_ty || ty::type_has_params(arg.ty) {
#debug(" casting from %s", val_str(bcx.ccx().tn, val));
val = PointerCast(bcx, val, lldestty);
}
+
#debug("--- trans_arg_expr passing %s", val_str(bcx.ccx().tn, val));
ret rslt(bcx, val);
}
-fn load_value_from_lval_result(lv: lval_result) -> ValueRef {
- alt lv.kind {
- temporary { lv.val }
- owned { Load(lv.bcx, lv.val) }
- owned_imm { lv.val }
- }
-}
-
// when invoking a method, an argument of type @T or ~T can be implicltly
// converted to an argument of type &T. Similarly, [T] can be converted to
// [T]/& and so on. If such a conversion (called borrowing) is necessary,
// routine consults this table and performs these adaptations. It returns a
// new location for the borrowed result as well as a new type for the argument
// that reflects the borrowed value and not the original.
-fn adapt_borrowed_value(lv: lval_result, arg: ty::arg,
- e: @ast::expr) -> {lv: lval_result,
- arg: ty::arg} {
+fn adapt_borrowed_value(lv: lval_result,
+ e: @ast::expr,
+ e_ty: ty::t) -> {lv: lval_result,
+ ty: ty::t} {
let bcx = lv.bcx;
if !expr_is_borrowed(bcx, e) {
- ret {lv:lv, arg:arg};
+ ret {lv:lv, ty:e_ty};
}
- let e_ty = expr_ty(bcx, e);
alt ty::get(e_ty).struct {
ty::ty_uniq(mt) | ty::ty_box(mt) {
- let box_ptr = load_value_from_lval_result(lv);
+ let box_ptr = load_value_from_lval_result(lv, e_ty);
let body_ptr = GEPi(bcx, box_ptr, [0u, abi::box_field_body]);
let rptr_ty = ty::mk_rptr(bcx.tcx(), ty::re_static, mt);
- ret {lv: lval_temp(bcx, body_ptr),
- arg: {ty: rptr_ty with arg}};
+ ret {lv: lval_temp(bcx, body_ptr), ty: rptr_ty};
}
ty::ty_str | ty::ty_vec(_) |
{ty: unit_ty, mutbl: ast::m_imm},
ty::vstore_slice(ty::re_static));
- ret {lv: lval_temp(bcx, p),
- arg: {ty: slice_ty with arg}};
+ ret {lv: lval_temp(bcx, p), ty: slice_ty};
}
_ {
vec::iteri(es) {|i, e|
let r = trans_arg_expr(bcx, arg_tys[i], llarg_tys[i],
e, temp_cleanups, if i == last { ret_flag }
- else { none });
+ else { none }, 0u);
bcx = r.bcx;
llargs += [r.val];
}
// expressions that must 'end up somewhere' (or get ignored).
fn trans_temp_expr(bcx: block, e: @ast::expr) -> result {
let _icx = bcx.insn_ctxt("trans_temp_expr");
- let mut {bcx, val, kind} = trans_temp_lval(bcx, e);
- if kind == owned {
- val = load_if_immediate(bcx, val, expr_ty(bcx, e));
+ lval_result_to_result(trans_temp_lval(bcx, e), expr_ty(bcx, e))
+}
+
+fn load_value_from_lval_result(lv: lval_result, ty: ty::t) -> ValueRef {
+ alt lv.kind {
+ temporary { lv.val }
+ owned { load_if_immediate(lv.bcx, lv.val, ty) }
+ owned_imm { lv.val }
}
- ret {bcx: bcx, val: val};
+}
+
+fn lval_result_to_result(lv: lval_result, ty: ty::t) -> result {
+ let val = load_value_from_lval_result(lv, ty);
+ {bcx: lv.bcx, val: val}
}
// Arranges for the value found in `*root_loc` to be dropped once the scope
}
}
-fn trans_self_arg(bcx: block, base: @ast::expr) -> result {
+fn trans_self_arg(bcx: block, base: @ast::expr, derefs: uint) -> result {
let _icx = bcx.insn_ctxt("impl::trans_self_arg");
let basety = expr_ty(bcx, base);
let m_by_ref = ast::expl(ast::by_ref);
let mut temp_cleanups = [];
let result = trans_arg_expr(bcx, {mode: m_by_ref, ty: basety},
T_ptr(type_of::type_of(bcx.ccx(), basety)),
- base, temp_cleanups, none);
+ base, temp_cleanups, none, derefs);
// by-ref self argument should not require cleanup in the case of
// other arguments failing:
}
fn trans_method_callee(bcx: block, callee_id: ast::node_id,
- self: @ast::expr, origin: typeck::method_origin)
+ self: @ast::expr, mentry: typeck::method_map_entry)
-> lval_maybe_callee {
let _icx = bcx.insn_ctxt("impl::trans_method_callee");
- alt origin {
+ alt mentry.origin {
typeck::method_static(did) {
- let {bcx, val} = trans_self_arg(bcx, self);
+ let {bcx, val} = trans_self_arg(bcx, self, mentry.derefs);
{env: self_env(val, node_id_type(bcx, self.id), none)
with lval_static_fn(bcx, did, callee_id)}
}
- typeck::method_param(iid, off, p, b) {
+ typeck::method_param({iface_id:iid, method_num:off,
+ param_num:p, bound_num:b}) {
alt check bcx.fcx.param_substs {
some(substs) {
- trans_monomorphized_callee(bcx, callee_id, self,
+ trans_monomorphized_callee(bcx, callee_id, self, mentry.derefs,
iid, off, p, b, substs)
}
}
}
fn trans_monomorphized_callee(bcx: block, callee_id: ast::node_id,
- base: @ast::expr, iface_id: ast::def_id,
- n_method: uint, n_param: uint, n_bound: uint,
+ base: @ast::expr, derefs: uint,
+ iface_id: ast::def_id, n_method: uint,
+ n_param: uint, n_bound: uint,
substs: param_substs) -> lval_maybe_callee {
let _icx = bcx.insn_ctxt("impl::trans_monomorphized_callee");
alt find_vtable_in_fn_ctxt(substs, n_param, n_bound) {
let node_substs = node_id_type_params(bcx, callee_id);
let ty_substs = impl_substs +
vec::tailn(node_substs, node_substs.len() - n_m_tps);
- let {bcx, val} = trans_self_arg(bcx, base);
+ let {bcx, val} = trans_self_arg(bcx, base, derefs);
let lval = lval_static_fn_inner(bcx, mth_id, callee_id, ty_substs,
some(sub_origins));
{env: self_env(val, node_id_type(bcx, base.id), none),
}
expr_field(_, _, _) {
alt cx.method_map.find(e.id) {
- some(typeck::method_static(did)) { traverse_def_id(cx, did); }
+ some({origin: typeck::method_static(did), _}) {
+ traverse_def_id(cx, did);
+ }
_ {}
}
}
import lib::llvm::{ValueRef, TypeRef};
import back::abi;
import base::{call_memmove,
- INIT, copy_val, load_if_immediate, get_tydesc,
- sub_block, do_spill_noroot,
- dest, bcx_icx, non_gc_box_cast,
- heap, heap_exchange, heap_shared};
+ INIT, copy_val, load_if_immediate, get_tydesc,
+ sub_block, do_spill_noroot,
+ dest, bcx_icx, non_gc_box_cast,
+ heap, heap_exchange, heap_shared};
import syntax::codemap::span;
import shape::llsize_of;
import build::*;
type_needs(cx, use_repr, ty::type_autoderef(cx.ccx.tcx, base_ty));
option::iter(cx.ccx.maps.method_map.find(e.id)) {|mth|
- alt mth {
+ alt mth.origin {
typeck::method_static(did) {
option::iter(cx.ccx.tcx.node_type_substs.find(e.id)) {|ts|
vec::iter2(type_uses_for(cx.ccx, did, ts.len()), ts)
{|uses, subst| type_needs(cx, uses, subst)}
}
}
- typeck::method_param(_, _, param, _) {
+ typeck::method_param({param_num: param, _}) {
cx.uses[param] |= use_tydesc;
}
typeck::method_iface(_, _) {}
export infer;
export method_map;
export method_origin, serialize_method_origin, deserialize_method_origin;
+export method_map_entry, serialize_method_map_entry;
+export deserialize_method_map_entry;
export vtable_map;
export vtable_res;
export vtable_origin;
#[auto_serialize]
enum method_origin {
+ // fully statically resolved method
method_static(ast::def_id),
- // iface id, method num, param num, bound num
- method_param(ast::def_id, uint, uint, uint),
+
+ // method invoked on a type parameter with a bounded iface
+ method_param(method_param),
+
+ // method invoked on a boxed iface
method_iface(ast::def_id, uint),
}
-type method_map = hashmap<ast::node_id, method_origin>;
+
+// details for a method invoked with a receiver whose type is a type parameter
+// with a bounded iface.
+#[auto_serialize]
+type method_param = {
+ // the iface containing the method to be invoked
+ iface_id: ast::def_id,
+
+ // index of the method to be invoked amongst the iface's methods
+ method_num: uint,
+
+ // index of the type parameter (from those that are in scope) that is
+ // the type of the receiver
+ param_num: uint,
+
+ // index of the bound for this type parameter which specifies the iface
+ bound_num: uint
+};
+
+#[auto_serialize]
+type method_map_entry = {
+ // number of derefs that are required on the receiver
+ derefs: uint,
+
+ // method details being invoked
+ origin: method_origin
+};
+
+// maps from an expression id that corresponds to a method call to the details
+// of the method to be invoked
+type method_map = hashmap<ast::node_id, method_map_entry>;
// Resolutions for bounds of all parameters, left to right, for a given path.
type vtable_res = @[vtable_origin];
supplied_tps: tps,
include_private: is_self_ref});
alt lkup.method() {
- some(origin) {
- fcx.ccx.method_map.insert(id, origin);
+ some(entry) {
+ fcx.ccx.method_map.insert(id, entry);
}
none {
let t_err = fcx.infcx.resolve_type_vars_if_possible(expr_t);
supplied_tps: [],
include_private: false});
alt lkup.method() {
- some(origin) {
- fcx.ccx.method_map.insert(alloc_id, origin);
+ some(entry) {
+ fcx.ccx.method_map.insert(alloc_id, entry);
// Check that the alloc() method has the expected type, which
// should be fn(sz: uint, align: uint) -> *().
impl methods for lookup {
// Entrypoint:
- fn method() -> option<method_origin> {
+ 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)];
fn tcx() -> ty::ctxt { self.fcx.ccx.tcx }
- fn method_from_param(n: uint, did: ast::def_id) -> option<method_origin> {
+ fn method_from_param(n: uint, did: ast::def_id)
+ -> option<method_map_entry> {
+
let tcx = self.tcx();
let mut iface_bnd_idx = 0u; // count only iface bounds
let bounds = tcx.ty_param_bounds.get(did.node);
let (substs, mty, iid, pos, n, iface_bnd_idx) = candidates[0u];
ret some(self.write_mty_from_m(
- substs, mty, method_param(iid, pos, n, iface_bnd_idx)));
+ 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_origin> {
+ did: ast::def_id, iface_substs: ty::substs)
+ -> option<method_map_entry> {
let ms = *ty::iface_methods(self.tcx(), did);
for ms.eachi {|i, m|
}
fn method_from_class(did: ast::def_id, class_substs: ty::substs)
- -> option<method_origin> {
+ -> option<method_map_entry> {
let ms = *ty::iface_methods(self.tcx(), did);
*/
}
- fn method_from_scope() -> option<method_origin> {
+ fn method_from_scope() -> option<method_map_entry> {
let impls_vecs = self.fcx.ccx.impl_map.get(self.expr.id);
for list::each(impls_vecs) {|impls|
fn write_mty_from_m(self_substs: ty::substs,
m: ty::method,
- origin: method_origin) -> method_origin {
+ origin: method_origin) -> method_map_entry {
let tcx = self.fcx.ccx.tcx;
// a bit hokey, but the method unbound has a bare protocol, whereas
fn write_mty_from_fty(self_substs: ty::substs,
n_tps_m: uint,
fty: ty::t,
- origin: method_origin) -> method_origin {
+ origin: method_origin) -> method_map_entry {
let tcx = self.fcx.ccx.tcx;
self.fcx.write_ty_substs(self.node_id, fty, all_substs);
- ret origin;
+ ret {derefs:0u, origin:origin};
}
}
ast::expr_unary(*) | ast::expr_assign_op(*) |
ast::expr_index(*) {
alt cx.method_map.find(ex.id) {
- some(method_static(did)) {
+ some({origin: method_static(did), _}) {
let bounds = ty::lookup_item_type(cx.tcx, did).bounds;
if has_iface_bounds(*bounds) {
let callee_id = alt ex.node {
-// xfail-fast (compile-flags unsupported on windows)
-// compile-flags:--borrowck=err
-
fn main() {
let x = [22]/1;
let y = &x[0];