2012-01-02 05:21:44 -06:00
|
|
|
import trans::*;
|
|
|
|
import trans_common::*;
|
2012-01-02 05:58:31 -06:00
|
|
|
import trans_build::*;
|
2012-01-02 05:21:44 -06:00
|
|
|
import option::{some, none};
|
2012-01-02 06:26:51 -06:00
|
|
|
import syntax::{ast, ast_util};
|
2012-01-05 06:57:27 -06:00
|
|
|
import metadata::csearch;
|
2012-01-02 06:26:51 -06:00
|
|
|
import back::link;
|
2012-01-06 07:22:31 -06:00
|
|
|
import lib::llvm::llvm;
|
|
|
|
import llvm::{ValueRef, TypeRef, LLVMGetParam};
|
2012-01-02 05:21:44 -06:00
|
|
|
|
2012-01-06 10:50:55 -06:00
|
|
|
// Translation functionality related to impls and ifaces
|
|
|
|
//
|
|
|
|
// Terminology:
|
|
|
|
// vtable: a table of function pointers pointing to method wrappers
|
|
|
|
// of an impl that implements an iface
|
|
|
|
// dict: a record containing a vtable pointer along with pointers to
|
|
|
|
// all tydescs and other dicts needed to run methods in this vtable
|
|
|
|
// (i.e. corresponding to the type parameters of the impl)
|
|
|
|
// wrapper: a function that takes a dict as first argument, along
|
|
|
|
// with the method-specific tydescs for a method (and all
|
|
|
|
// other args the method expects), which fetches the extra
|
|
|
|
// tydescs and dicts from the dict, splices them into the
|
|
|
|
// arglist, and calls through to the actual method
|
|
|
|
//
|
|
|
|
// Generic functions take, along with their normal arguments, a number
|
|
|
|
// of extra tydesc and dict arguments -- one tydesc for each type
|
|
|
|
// parameter, one dict (following the tydesc in the arg order) for
|
|
|
|
// each interface bound on a type parameter.
|
|
|
|
//
|
|
|
|
// Most dicts are completely static, and are allocated and filled at
|
|
|
|
// compile time. Dicts that depend on run-time values (tydescs or
|
|
|
|
// dicts for type parameter types) are built at run-time, and interned
|
|
|
|
// through upcall_intern_dict in the runtime. This means that dict
|
|
|
|
// pointers are self-contained things that do not need to be cleaned
|
|
|
|
// up.
|
|
|
|
//
|
|
|
|
// The trans_constants pass in trans.rs outputs the vtables. Typeck
|
|
|
|
// annotates notes with information about the methods and dicts that
|
|
|
|
// are referenced (ccx.method_map and ccx.dict_map).
|
|
|
|
|
2012-01-02 05:21:44 -06:00
|
|
|
fn trans_impl(cx: @local_ctxt, name: ast::ident, methods: [@ast::method],
|
2012-01-05 06:57:27 -06:00
|
|
|
id: ast::node_id, tps: [ast::ty_param]) {
|
2012-01-02 05:21:44 -06:00
|
|
|
let sub_cx = extend_path(cx, name);
|
|
|
|
for m in methods {
|
|
|
|
alt cx.ccx.item_ids.find(m.id) {
|
|
|
|
some(llfn) {
|
|
|
|
trans_fn(extend_path(sub_cx, m.ident), m.span, m.decl, m.body,
|
|
|
|
llfn, impl_self(ty::node_id_to_monotype(cx.ccx.tcx, id)),
|
|
|
|
tps + m.tps, m.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-01-02 06:26:51 -06:00
|
|
|
|
2012-01-02 09:50:51 -06:00
|
|
|
fn trans_self_arg(bcx: @block_ctxt, base: @ast::expr) -> result {
|
|
|
|
let tz = [], tr = [];
|
|
|
|
let basety = ty::expr_ty(bcx_tcx(bcx), base);
|
|
|
|
let {bcx, val} = trans_arg_expr(bcx, {mode: ast::by_ref, ty: basety},
|
|
|
|
T_ptr(type_of_or_i8(bcx, basety)), tz,
|
|
|
|
tr, base);
|
2012-01-05 18:19:12 -06:00
|
|
|
rslt(bcx, PointerCast(bcx, val, T_opaque_cbox_ptr(bcx_ccx(bcx))))
|
2012-01-02 09:50:51 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
fn trans_static_callee(bcx: @block_ctxt, e: @ast::expr, base: @ast::expr,
|
|
|
|
did: ast::def_id) -> lval_maybe_callee {
|
|
|
|
let {bcx, val} = trans_self_arg(bcx, base);
|
|
|
|
{env: obj_env(val) with lval_static_fn(bcx, did, e.id)}
|
|
|
|
}
|
|
|
|
|
2012-01-04 04:32:26 -06:00
|
|
|
fn trans_dict_callee(bcx: @block_ctxt, e: @ast::expr, base: @ast::expr,
|
2012-01-02 09:50:51 -06:00
|
|
|
iface_id: ast::def_id, n_method: uint,
|
|
|
|
n_param: uint, n_bound: uint) -> lval_maybe_callee {
|
2012-01-04 04:32:26 -06:00
|
|
|
let tcx = bcx_tcx(bcx);
|
2012-01-02 09:50:51 -06:00
|
|
|
let {bcx, val} = trans_self_arg(bcx, base);
|
|
|
|
let dict = option::get(bcx.fcx.lltyparams[n_param].dicts)[n_bound];
|
2012-01-04 04:32:26 -06:00
|
|
|
let method = ty::iface_methods(tcx, iface_id)[n_method];
|
2012-01-04 15:12:43 -06:00
|
|
|
let fty = ty::expr_ty(tcx, e);
|
|
|
|
let bare_fn_ty = type_of_fn_from_ty(bcx_ccx(bcx), ast_util::dummy_sp(),
|
|
|
|
fty, *method.tps);
|
2012-01-02 09:50:51 -06:00
|
|
|
let {inputs: bare_inputs, output} = llfn_arg_tys(bare_fn_ty);
|
|
|
|
let fn_ty = T_fn([val_ty(dict)] + bare_inputs, output);
|
|
|
|
let vtable = PointerCast(bcx, Load(bcx, GEPi(bcx, dict, [0, 0])),
|
|
|
|
T_ptr(T_array(T_ptr(fn_ty), n_method + 1u)));
|
|
|
|
let mptr = Load(bcx, GEPi(bcx, vtable, [0, n_method as int]));
|
2012-01-04 04:32:26 -06:00
|
|
|
let generic = none;
|
|
|
|
if vec::len(*method.tps) > 0u {
|
|
|
|
let tydescs = [], tis = [];
|
2012-01-06 03:23:55 -06:00
|
|
|
let tptys = ty::node_id_to_type_params(tcx, e.id);
|
|
|
|
for t in vec::tail_n(tptys, vec::len(tptys) - vec::len(*method.tps)) {
|
2012-01-04 04:32:26 -06:00
|
|
|
// TODO: Doesn't always escape.
|
|
|
|
let ti = none;
|
|
|
|
let td = get_tydesc(bcx, t, true, tps_normal, ti).result;
|
|
|
|
tis += [ti];
|
|
|
|
tydescs += [td.val];
|
|
|
|
bcx = td.bcx;
|
|
|
|
}
|
2012-01-04 15:12:43 -06:00
|
|
|
generic = some({item_type: fty,
|
2012-01-04 04:32:26 -06:00
|
|
|
static_tis: tis,
|
|
|
|
tydescs: tydescs,
|
|
|
|
param_bounds: method.tps,
|
|
|
|
origins: bcx_ccx(bcx).dict_map.find(e.id)});
|
|
|
|
}
|
2012-01-02 09:50:51 -06:00
|
|
|
{bcx: bcx, val: mptr, kind: owned,
|
|
|
|
env: dict_env(dict, val),
|
2012-01-04 04:32:26 -06:00
|
|
|
generic: generic}
|
2012-01-02 09:50:51 -06:00
|
|
|
}
|
|
|
|
|
2012-01-02 06:26:51 -06:00
|
|
|
fn llfn_arg_tys(ft: TypeRef) -> {inputs: [TypeRef], output: TypeRef} {
|
2012-01-06 07:22:31 -06:00
|
|
|
let out_ty = llvm::LLVMGetReturnType(ft);
|
|
|
|
let n_args = llvm::LLVMCountParamTypes(ft);
|
2012-01-02 06:26:51 -06:00
|
|
|
let args = vec::init_elt(0 as TypeRef, n_args);
|
2012-01-06 07:22:31 -06:00
|
|
|
unsafe { llvm::LLVMGetParamTypes(ft, vec::to_ptr(args)); }
|
2012-01-02 06:26:51 -06:00
|
|
|
{inputs: args, output: out_ty}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn trans_wrapper(ccx: @crate_ctxt, pt: [ast::ident],
|
|
|
|
extra_tps: [ty::param_bounds], m: @ast::method) -> ValueRef {
|
|
|
|
let real_fn = ccx.item_ids.get(m.id);
|
|
|
|
let {inputs: real_args, output: real_ret} =
|
2012-01-06 07:22:31 -06:00
|
|
|
llfn_arg_tys(llvm::LLVMGetElementType(val_ty(real_fn)));
|
2012-01-02 09:50:51 -06:00
|
|
|
let extra_ptrs = [];
|
|
|
|
for tp in extra_tps {
|
|
|
|
extra_ptrs += [T_ptr(ccx.tydesc_type)];
|
|
|
|
for bound in *tp {
|
|
|
|
alt bound {
|
|
|
|
ty::bound_iface(_) { extra_ptrs += [T_ptr(T_dict())]; }
|
|
|
|
_ {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let env_ty = T_ptr(T_struct([T_ptr(T_i8())] + extra_ptrs));
|
|
|
|
let n_extra_ptrs = vec::len(extra_ptrs);
|
|
|
|
|
|
|
|
let wrap_args = [T_ptr(T_dict())] + vec::slice(real_args, 0u, 2u) +
|
|
|
|
vec::slice(real_args, 2u + vec::len(extra_ptrs), vec::len(real_args));
|
2012-01-02 06:26:51 -06:00
|
|
|
let llfn_ty = T_fn(wrap_args, real_ret);
|
|
|
|
|
|
|
|
let lcx = @{path: pt + ["wrapper", m.ident], module_path: [],
|
|
|
|
obj_typarams: [], obj_fields: [], ccx: ccx};
|
|
|
|
let name = link::mangle_internal_name_by_path_and_seq(ccx, pt, m.ident);
|
|
|
|
let llfn = decl_internal_cdecl_fn(ccx.llmod, name, llfn_ty);
|
|
|
|
let fcx = new_fn_ctxt(lcx, ast_util::dummy_sp(), llfn);
|
|
|
|
let bcx = new_top_block_ctxt(fcx), lltop = bcx.llbb;
|
|
|
|
|
2012-01-02 09:50:51 -06:00
|
|
|
let dict = PointerCast(bcx, LLVMGetParam(llfn, 0u), env_ty);
|
2012-01-02 06:26:51 -06:00
|
|
|
// retptr, self
|
2012-01-02 09:50:51 -06:00
|
|
|
let args = [LLVMGetParam(llfn, 1u), LLVMGetParam(llfn, 2u)], i = 0u;
|
2012-01-02 06:26:51 -06:00
|
|
|
// saved tydescs/dicts
|
2012-01-02 09:50:51 -06:00
|
|
|
while i < n_extra_ptrs {
|
|
|
|
i += 1u;
|
|
|
|
args += [load_inbounds(bcx, dict, [0, i as int])];
|
2012-01-02 06:26:51 -06:00
|
|
|
}
|
|
|
|
// the rest of the parameters
|
2012-01-06 07:22:31 -06:00
|
|
|
let i = 3u, params_total = llvm::LLVMCountParamTypes(llfn_ty);
|
2012-01-02 06:26:51 -06:00
|
|
|
while i < params_total {
|
|
|
|
args += [LLVMGetParam(llfn, i)];
|
|
|
|
i += 1u;
|
|
|
|
}
|
|
|
|
Call(bcx, ccx.item_ids.get(m.id), args);
|
2012-01-02 09:50:51 -06:00
|
|
|
build_return(bcx);
|
2012-01-02 06:26:51 -06:00
|
|
|
finish_fn(fcx, lltop);
|
|
|
|
ret llfn;
|
|
|
|
}
|
|
|
|
|
2012-01-06 07:22:31 -06:00
|
|
|
fn dict_is_static(tcx: ty::ctxt, origin: typeck::dict_origin) -> bool {
|
|
|
|
alt origin {
|
|
|
|
typeck::dict_static(_, ts, origs) {
|
|
|
|
vec::all(ts, {|t| !ty::type_contains_params(tcx, t)}) &&
|
|
|
|
vec::all(*origs, {|o| dict_is_static(tcx, o)})
|
|
|
|
}
|
|
|
|
typeck::dict_param(_, _) { false }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-02 09:50:51 -06:00
|
|
|
fn get_dict(bcx: @block_ctxt, origin: typeck::dict_origin) -> result {
|
2012-01-06 10:29:06 -06:00
|
|
|
let ccx = bcx_ccx(bcx);
|
2012-01-06 07:22:31 -06:00
|
|
|
alt origin {
|
|
|
|
typeck::dict_static(impl_did, tys, sub_origins) {
|
2012-01-06 10:29:06 -06:00
|
|
|
if dict_is_static(ccx.tcx, origin) {
|
2012-01-06 07:22:31 -06:00
|
|
|
ret rslt(bcx, get_static_dict(bcx, origin));
|
|
|
|
}
|
|
|
|
let {bcx, ptrs} = get_dict_ptrs(bcx, origin);
|
|
|
|
let pty = T_ptr(T_i8()), dict_ty = T_array(pty, vec::len(ptrs));
|
|
|
|
let dict = alloca(bcx, dict_ty), i = 0;
|
|
|
|
for ptr in ptrs {
|
|
|
|
Store(bcx, PointerCast(bcx, ptr, pty), GEPi(bcx, dict, [0, i]));
|
|
|
|
i += 1;
|
|
|
|
}
|
2012-01-06 10:29:06 -06:00
|
|
|
dict = Call(bcx, ccx.upcalls.intern_dict,
|
|
|
|
[C_uint(ccx, vec::len(ptrs)),
|
|
|
|
PointerCast(bcx, dict, T_ptr(T_dict()))]);
|
|
|
|
rslt(bcx, dict)
|
2012-01-06 07:22:31 -06:00
|
|
|
}
|
|
|
|
typeck::dict_param(n_param, n_bound) {
|
|
|
|
rslt(bcx, option::get(bcx.fcx.lltyparams[n_param].dicts)[n_bound])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dict_id(tcx: ty::ctxt, origin: typeck::dict_origin) -> dict_id {
|
|
|
|
alt origin {
|
|
|
|
typeck::dict_static(did, ts, origs) {
|
|
|
|
let d_params = [], orig = 0u;
|
|
|
|
if vec::len(ts) == 0u { ret @{impl_def: did, params: d_params}; }
|
|
|
|
let impl_params = ty::lookup_item_type(tcx, did).bounds;
|
|
|
|
vec::iter2(ts, *impl_params) {|t, bounds|
|
|
|
|
d_params += [dict_param_ty(t)];
|
|
|
|
for bound in *bounds {
|
|
|
|
alt bound {
|
|
|
|
ty::bound_iface(_) {
|
|
|
|
d_params += [dict_param_dict(dict_id(tcx, origs[orig]))];
|
|
|
|
orig += 1u;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@{impl_def: did, params: d_params}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_static_dict(bcx: @block_ctxt, origin: typeck::dict_origin)
|
|
|
|
-> ValueRef {
|
|
|
|
let ccx = bcx_ccx(bcx);
|
|
|
|
let id = dict_id(ccx.tcx, origin);
|
|
|
|
alt ccx.dicts.find(id) {
|
|
|
|
some(d) { ret d; }
|
|
|
|
none. {}
|
|
|
|
}
|
|
|
|
let ptrs = C_struct(get_dict_ptrs(bcx, origin).ptrs);
|
|
|
|
let name = ccx.names.next("dict");
|
|
|
|
let gvar = str::as_buf(name, {|buf|
|
|
|
|
llvm::LLVMAddGlobal(ccx.llmod, val_ty(ptrs), buf)
|
|
|
|
});
|
|
|
|
llvm::LLVMSetGlobalConstant(gvar, lib::llvm::True);
|
|
|
|
llvm::LLVMSetInitializer(gvar, ptrs);
|
|
|
|
llvm::LLVMSetLinkage(gvar,
|
|
|
|
lib::llvm::LLVMInternalLinkage as llvm::Linkage);
|
|
|
|
let cast = llvm::LLVMConstPointerCast(gvar, T_ptr(T_dict()));
|
|
|
|
ccx.dicts.insert(id, cast);
|
|
|
|
cast
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_dict_ptrs(bcx: @block_ctxt, origin: typeck::dict_origin)
|
|
|
|
-> {bcx: @block_ctxt, ptrs: [ValueRef]} {
|
|
|
|
let ccx = bcx_ccx(bcx);
|
2012-01-02 09:50:51 -06:00
|
|
|
alt origin {
|
|
|
|
typeck::dict_static(impl_did, tys, sub_origins) {
|
2012-01-05 06:57:27 -06:00
|
|
|
let vtable = if impl_did.crate == ast::local_crate {
|
|
|
|
ccx.item_ids.get(impl_did.node)
|
|
|
|
} else {
|
|
|
|
let name = csearch::get_symbol(ccx.sess.get_cstore(), impl_did);
|
|
|
|
get_extern_const(ccx.externs, ccx.llmod, name, T_ptr(T_i8()))
|
|
|
|
};
|
2012-01-02 09:50:51 -06:00
|
|
|
let impl_params = ty::lookup_item_type(ccx.tcx, impl_did).bounds;
|
2012-01-06 07:22:31 -06:00
|
|
|
let ptrs = [vtable], origin = 0u, ti = none, bcx = bcx;
|
|
|
|
vec::iter2(*impl_params, tys) {|param, ty|
|
|
|
|
let rslt = get_tydesc(bcx, ty, true, tps_normal, ti).result;
|
2012-01-02 09:50:51 -06:00
|
|
|
ptrs += [rslt.val];
|
|
|
|
bcx = rslt.bcx;
|
|
|
|
for bound in *param {
|
|
|
|
alt bound {
|
|
|
|
ty::bound_iface(_) {
|
|
|
|
let res = get_dict(bcx, sub_origins[origin]);
|
|
|
|
ptrs += [res.val];
|
|
|
|
bcx = res.bcx;
|
|
|
|
origin += 1u;
|
|
|
|
}
|
|
|
|
_ {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-01-06 07:22:31 -06:00
|
|
|
{bcx: bcx, ptrs: ptrs}
|
2012-01-03 09:37:41 -06:00
|
|
|
}
|
2012-01-02 09:50:51 -06:00
|
|
|
}
|
2012-01-06 07:22:31 -06:00
|
|
|
}
|