e1dc40b271
Reached a point where simple uses of interfaces without bounds work. Issue #1227
950 lines
37 KiB
Rust
950 lines
37 KiB
Rust
// Translation of object-related things to LLVM IR.
|
|
|
|
import core::{str, option, vec};
|
|
import option::{none, some};
|
|
|
|
import lib::llvm::{llvm, True};
|
|
import lib::llvm::llvm::{TypeRef, ValueRef};
|
|
|
|
import back::abi;
|
|
import back::link::{mangle_internal_name_by_path,
|
|
mangle_internal_name_by_path_and_seq};
|
|
import syntax::{ast, ast_util};
|
|
import syntax::codemap::span;
|
|
|
|
import trans_common::*;
|
|
import trans::*;
|
|
import trans_build::*;
|
|
|
|
export trans_anon_obj;
|
|
export trans_obj;
|
|
|
|
// trans_obj: create an LLVM function that is the object constructor for the
|
|
// object being translated.
|
|
fn trans_obj(cx: @local_ctxt, sp: span, ob: ast::_obj, ctor_id: ast::node_id,
|
|
ty_params: [ast::ty_param]) {
|
|
|
|
// To make a function, we have to create a function context and, inside
|
|
// that, a number of block contexts for which code is generated.
|
|
let ccx = cx.ccx;
|
|
let llctor_decl;
|
|
alt ccx.item_ids.find(ctor_id) {
|
|
some(x) { llctor_decl = x; }
|
|
_ { cx.ccx.sess.span_fatal(sp, "unbound llctor_decl in trans_obj"); }
|
|
}
|
|
|
|
// Much like trans_fn, we must create an LLVM function, but since we're
|
|
// starting with an ast::_obj rather than an ast::_fn, we have some setup
|
|
// work to do.
|
|
|
|
// The fields of our object will become the arguments to the function
|
|
// we're creating.
|
|
let fn_args: [ast::arg] = [];
|
|
for f: ast::obj_field in ob.fields {
|
|
fn_args += [{mode: ast::by_copy, ty: f.ty, ident: f.ident,
|
|
id: f.id}];
|
|
}
|
|
let fcx = new_fn_ctxt(cx, sp, llctor_decl);
|
|
|
|
// Create the first block context in the function and keep a handle on it
|
|
// to pass to finish_fn later.
|
|
let bcx = new_top_block_ctxt(fcx);
|
|
let lltop = bcx.llbb;
|
|
|
|
// Both regular arguments and type parameters are handled here.
|
|
create_llargs_for_fn_args(fcx, no_self, fn_args, ty_params);
|
|
let arg_tys: [ty::arg] = arg_tys_of_fn(ccx, ctor_id);
|
|
bcx = copy_args_to_allocas(fcx, bcx, fn_args, arg_tys);
|
|
|
|
// Pick up the type of this object by looking at our own output type, that
|
|
// is, the output type of the object constructor we're building.
|
|
let self_ty = ty::ret_ty_of_fn(ccx.tcx, ctor_id);
|
|
|
|
// Set up the two-word pair that we're going to return from the object
|
|
// constructor we're building. The two elements of this pair will be a
|
|
// vtable pointer and a body pointer. (llretptr already points to the
|
|
// place where this two-word pair should go; it was pre-allocated by the
|
|
// caller of the function.)
|
|
let pair = bcx.fcx.llretptr;
|
|
|
|
// Grab onto the first and second elements of the pair.
|
|
// abi::obj_field_vtbl and abi::obj_field_box simply specify words 0 and 1
|
|
// of 'pair'.
|
|
let pair_vtbl = GEPi(bcx, pair, [0, abi::obj_field_vtbl]);
|
|
let pair_box = GEPi(bcx, pair, [0, abi::obj_field_box]);
|
|
|
|
// Make a vtable for this object: a static array of pointers to functions.
|
|
// It will be located in the read-only memory of the executable we're
|
|
// creating and will contain ValueRefs for all of this object's methods.
|
|
// create_vtbl returns a pointer to the vtable, which we store.
|
|
let vtbl = create_vtbl(cx, sp, self_ty, ob, ty_params, none, []);
|
|
vtbl = PointerCast(bcx, vtbl, T_ptr(T_empty_struct()));
|
|
|
|
Store(bcx, vtbl, pair_vtbl);
|
|
|
|
// Next we have to take care of the other half of the pair we're
|
|
// returning: a boxed (reference-counted) tuple containing a tydesc,
|
|
// typarams, and fields.
|
|
let llbox_ty: TypeRef = T_ptr(T_empty_struct());
|
|
|
|
if vec::len(ty_params) == 0u &&
|
|
vec::len(arg_tys) == 0u {
|
|
// If the object we're translating has no fields or type parameters,
|
|
// there's not much to do.
|
|
|
|
// Store null into pair, if no args or typarams.
|
|
Store(bcx, C_null(llbox_ty), pair_box);
|
|
} else {
|
|
let obj_fields: [ty::t] = [];
|
|
for a: ty::arg in arg_tys { obj_fields += [a.ty]; }
|
|
|
|
let tps: [ty::t] = [];
|
|
let tydesc_ty = ty::mk_type(ccx.tcx);
|
|
for tp: ast::ty_param in ty_params { tps += [tydesc_ty]; }
|
|
|
|
// Synthesize an object body type and hand it off to
|
|
// trans_malloc_boxed, which allocates a box, including space for a
|
|
// refcount.
|
|
let body_ty: ty::t =
|
|
create_object_body_type(ccx.tcx, obj_fields, tps, none);
|
|
|
|
// We have to get this type descriptor now so that
|
|
// trans_malloc_boxed() doesn't generate a type descriptor with the
|
|
// wrong storage type and override the type descriptor we're about to
|
|
// generate.
|
|
let ti = none;
|
|
let storage = tps_obj(vec::len(ty_params));
|
|
let body_td = get_tydesc(bcx, body_ty, true, storage, ti).result;
|
|
bcx = body_td.bcx;
|
|
lazily_emit_tydesc_glue(bcx, abi::tydesc_field_drop_glue, ti);
|
|
lazily_emit_tydesc_glue(bcx, abi::tydesc_field_free_glue, ti);
|
|
|
|
let box = trans_malloc_boxed(bcx, body_ty);
|
|
bcx = box.bcx;
|
|
let body = box.body;
|
|
|
|
// Put together a tydesc for the body, so that the object can later be
|
|
// freed by calling through its tydesc.
|
|
|
|
// Every object (not just those with type parameters) needs to have a
|
|
// tydesc to describe its body, since all objects have unknown type to
|
|
// the user of the object. So the tydesc is needed to keep track of
|
|
// the types of the object's fields, so that the fields can be freed
|
|
// later.
|
|
|
|
// postcondition on create_object_body_type?
|
|
check type_is_tup_like(bcx, body_ty);
|
|
let body_tydesc =
|
|
GEP_tup_like(bcx, body_ty, body, [0, abi::obj_body_elt_tydesc]);
|
|
bcx = body_tydesc.bcx;
|
|
|
|
check type_is_tup_like(bcx, body_ty);
|
|
let r =
|
|
GEP_tup_like(bcx, body_ty, body, [0, abi::obj_body_elt_typarams]);
|
|
bcx = r.bcx;
|
|
let body_typarams = r.val;
|
|
|
|
Store(bcx, body_td.val, body_tydesc.val);
|
|
|
|
// Copy the object's type parameters and fields into the space we
|
|
// allocated for the object body. (This is something like saving the
|
|
// lexical environment of a function in its closure: the "captured
|
|
// typarams" are any type parameters that are passed to the object
|
|
// constructor and are then available to the object's methods.
|
|
// Likewise for the object's fields.)
|
|
|
|
// Copy typarams into captured typarams.
|
|
// TODO: can we just get typarams_ty out of body_ty instead?
|
|
let typarams_ty: ty::t = ty::mk_tup(ccx.tcx, tps);
|
|
let i: int = 0;
|
|
for tp: ast::ty_param in ty_params {
|
|
let typaram = bcx.fcx.lltyparams[i].desc;
|
|
// Silly check
|
|
check type_is_tup_like(bcx, typarams_ty);
|
|
let capture =
|
|
GEP_tup_like(bcx, typarams_ty, body_typarams, [0, i]);
|
|
bcx = capture.bcx;
|
|
bcx = copy_val(bcx, INIT, capture.val, typaram, tydesc_ty);
|
|
i += 1;
|
|
}
|
|
|
|
// Copy args into body fields.
|
|
// how to get rid of this check?
|
|
check type_is_tup_like(bcx, body_ty);
|
|
let body_fields =
|
|
GEP_tup_like(bcx, body_ty, body, [0, abi::obj_body_elt_fields]);
|
|
bcx = body_fields.bcx;
|
|
// TODO: can we just get fields_ty out of body_ty instead?
|
|
let fields_ty = ty::mk_tup(ccx.tcx, obj_fields);
|
|
i = 0;
|
|
for f: ast::obj_field in ob.fields {
|
|
alt bcx.fcx.llargs.find(f.id) {
|
|
some(local_mem(arg)) {
|
|
// Silly check
|
|
check type_is_tup_like(bcx, fields_ty);
|
|
let field =
|
|
GEP_tup_like(bcx, fields_ty, body_fields.val, [0, i]);
|
|
bcx = memmove_ty(field.bcx, field.val, arg, arg_tys[i].ty);
|
|
i += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Store box ptr in outer pair.
|
|
let p = PointerCast(bcx, box.box, llbox_ty);
|
|
Store(bcx, p, pair_box);
|
|
}
|
|
build_return(bcx);
|
|
|
|
// Insert the mandatory first few basic blocks before lltop.
|
|
finish_fn(fcx, lltop);
|
|
}
|
|
|
|
// trans_anon_obj: create and return a pointer to an object. This code
|
|
// differs from trans_obj in that, rather than creating an object constructor
|
|
// function and putting it in the generated code as an object item, we are
|
|
// instead "inlining" the construction of the object and returning the object
|
|
// itself.
|
|
fn trans_anon_obj(bcx: @block_ctxt, sp: span, anon_obj: ast::anon_obj,
|
|
id: ast::node_id, dest: trans::dest) -> @block_ctxt {
|
|
let bcx = bcx;
|
|
if dest == trans::ignore {
|
|
alt anon_obj.inner_obj {
|
|
some(e) { ret trans::trans_expr(bcx, e, trans::ignore); }
|
|
none. { ret bcx; }
|
|
}
|
|
}
|
|
|
|
let ccx = bcx_ccx(bcx);
|
|
|
|
// Fields. FIXME (part of issue #538): Where do we fill in the field
|
|
// *values* from the outer object?
|
|
let additional_fields: [ast::anon_obj_field] = [];
|
|
let additional_field_vals: [result] = [];
|
|
let additional_field_tys: [ty::t] = [];
|
|
alt anon_obj.fields {
|
|
none. { }
|
|
some(fields) {
|
|
additional_fields = fields;
|
|
for f: ast::anon_obj_field in fields {
|
|
additional_field_tys += [node_id_type(ccx, f.id)];
|
|
additional_field_vals += [trans_temp_expr(bcx, f.expr)];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the type of the eventual entire anonymous object, possibly with
|
|
// extensions. NB: This type includes both inner and outer methods.
|
|
let outer_obj_ty = ty::node_id_to_type(ccx.tcx, id);
|
|
|
|
// Create a vtable for the anonymous object.
|
|
|
|
// create_vtbl() wants an ast::_obj and all we have is an ast::anon_obj,
|
|
// so we need to roll our own. NB: wrapper_obj includes only outer
|
|
// methods, not inner ones.
|
|
let wrapper_obj: ast::_obj =
|
|
{fields:
|
|
vec::map(additional_fields,
|
|
ast_util::obj_field_from_anon_obj_field),
|
|
methods: anon_obj.methods};
|
|
|
|
let inner_obj_ty: ty::t;
|
|
let vtbl;
|
|
alt anon_obj.inner_obj {
|
|
none. {
|
|
// We need a dummy inner_obj_ty for setting up the object body later.
|
|
inner_obj_ty = ty::mk_type(ccx.tcx);
|
|
|
|
// If there's no inner_obj -- that is, if we're creating a new object
|
|
// from nothing rather than extending an existing object -- then we
|
|
// just pass the outer object to create_vtbl(). Our vtable won't need
|
|
// to have any forwarding slots.
|
|
vtbl =
|
|
create_vtbl(bcx.fcx.lcx, sp, outer_obj_ty, wrapper_obj, [], none,
|
|
additional_field_tys);
|
|
}
|
|
some(e) {
|
|
// TODO: What makes more sense to get the type of an expr -- calling
|
|
// ty::expr_ty(ccx.tcx, e) on it or calling
|
|
// ty::node_id_to_type(ccx.tcx, id) on its id?
|
|
inner_obj_ty = ty::expr_ty(ccx.tcx, e);
|
|
//inner_obj_ty = ty::node_id_to_type(ccx.tcx, e.id);
|
|
|
|
// If there's a inner_obj, we pass its type along to create_vtbl().
|
|
// Part of what create_vtbl() will do is take the set difference of
|
|
// methods defined on the original and methods being added. For every
|
|
// method defined on the original that does *not* have one with a
|
|
// matching name and type being added, we'll need to create a
|
|
// forwarding slot. And, of course, we need to create a normal vtable
|
|
// entry for every method being added.
|
|
vtbl =
|
|
create_vtbl(bcx.fcx.lcx, sp, outer_obj_ty, wrapper_obj, [],
|
|
some(inner_obj_ty), additional_field_tys);
|
|
}
|
|
}
|
|
|
|
vtbl = PointerCast(bcx, vtbl, T_ptr(T_empty_struct()));
|
|
|
|
// Next we have to take care of the other half of the pair we're
|
|
// returning: a boxed (reference-counted) tuple containing a tydesc,
|
|
// typarams, fields, and a pointer to our inner_obj.
|
|
let llbox_ty: TypeRef = T_ptr(T_empty_struct());
|
|
|
|
let box = C_null(llbox_ty);
|
|
if vec::len(additional_fields) > 0u || anon_obj.inner_obj != none {
|
|
// Synthesize a type for the object body and hand it off to
|
|
// trans_malloc_boxed, which allocates a box, including space for a
|
|
// refcount.
|
|
let body_ty: ty::t =
|
|
create_object_body_type(ccx.tcx, additional_field_tys, [],
|
|
some(inner_obj_ty));
|
|
let box_r = trans_malloc_boxed(bcx, body_ty);
|
|
box = box_r.box;
|
|
bcx = box_r.bcx;
|
|
add_clean_free(bcx, box, false);
|
|
let body = box_r.body;
|
|
|
|
// Put together a tydesc for the body, so that the object can later be
|
|
// freed by calling through its tydesc.
|
|
|
|
// Every object (not just those with type parameters) needs to have a
|
|
// tydesc to describe its body, since all objects have unknown type to
|
|
// the user of the object. So the tydesc is needed to keep track of
|
|
// the types of the object's fields, so that the fields can be freed
|
|
// later.
|
|
// postcondition on create_object_body_type?
|
|
check type_is_tup_like(bcx, body_ty);
|
|
let body_tydesc =
|
|
GEP_tup_like(bcx, body_ty, body, [0, abi::obj_body_elt_tydesc]);
|
|
bcx = body_tydesc.bcx;
|
|
let ti = none;
|
|
let body_td = get_tydesc(bcx, body_ty, true, tps_normal, ti).result;
|
|
lazily_emit_tydesc_glue(bcx, abi::tydesc_field_drop_glue, ti);
|
|
lazily_emit_tydesc_glue(bcx, abi::tydesc_field_free_glue, ti);
|
|
bcx = body_td.bcx;
|
|
Store(bcx, body_td.val, body_tydesc.val);
|
|
|
|
// Copy the object's fields into the space we allocated for the object
|
|
// body. (This is something like saving the lexical environment of a
|
|
// function in its closure: the fields were passed to the object
|
|
// constructor and are now available to the object's methods.
|
|
check type_is_tup_like(bcx, body_ty);
|
|
let body_fields =
|
|
GEP_tup_like(bcx, body_ty, body, [0, abi::obj_body_elt_fields]);
|
|
bcx = body_fields.bcx;
|
|
let i: int = 0;
|
|
for f: ast::anon_obj_field in additional_fields {
|
|
// FIXME (part of issue #538): make this work eventually, when we
|
|
// have additional field exprs in the AST.
|
|
load_if_immediate(bcx, additional_field_vals[i].val,
|
|
additional_field_tys[i]);
|
|
let fields_ty: ty::t = ty::mk_tup(ccx.tcx, additional_field_tys);
|
|
// Silly check
|
|
check type_is_tup_like(bcx, fields_ty);
|
|
let field = GEP_tup_like(bcx, fields_ty, body_fields.val, [0, i]);
|
|
bcx = field.bcx;
|
|
bcx =
|
|
copy_val(bcx, INIT, field.val, additional_field_vals[i].val,
|
|
additional_field_tys[i]);
|
|
i += 1;
|
|
}
|
|
|
|
// If there's a inner_obj, copy a pointer to it into the object's
|
|
// body.
|
|
alt anon_obj.inner_obj {
|
|
none. { }
|
|
some(e) {
|
|
// If inner_obj (the object being extended) exists, translate it.
|
|
// Translating inner_obj returns a ValueRef (pointer to a 2-word
|
|
// value) wrapped in a result.
|
|
check type_is_tup_like(bcx, body_ty);
|
|
let {bcx: cx, val: body_inner_obj} = GEP_tup_like
|
|
(bcx, body_ty, body, [0, abi::obj_body_elt_inner_obj]);
|
|
bcx = trans_expr_save_in(cx, e, body_inner_obj);
|
|
}
|
|
}
|
|
revoke_clean(bcx, box);
|
|
box = PointerCast(bcx, box, llbox_ty);
|
|
}
|
|
let pair = trans::get_dest_addr(dest);
|
|
let pair_vtbl = GEPi(bcx, pair, [0, abi::obj_field_vtbl]);
|
|
Store(bcx, vtbl, pair_vtbl);
|
|
let pair_box = GEPi(bcx, pair, [0, abi::obj_field_box]);
|
|
Store(bcx, box, pair_box);
|
|
ret bcx;
|
|
}
|
|
|
|
// Used only inside create_vtbl and create_backwarding_vtbl to distinguish
|
|
// different kinds of slots we'll have to create.
|
|
tag vtbl_mthd {
|
|
|
|
// Normal methods are complete AST nodes, but for forwarding methods, the
|
|
// only information we'll have about them is their type.
|
|
normal_mthd(@ast::method);
|
|
fwding_mthd(@ty::method);
|
|
}
|
|
|
|
// Alphabetize ast::methods by ident. A helper for create_vtbl.
|
|
fn ast_mthd_lteq(&&a: @ast::method, &&b: @ast::method) -> bool {
|
|
ret str::lteq(a.ident, b.ident);
|
|
}
|
|
|
|
// Alphabetize vtbl_mthds by ident. A helper for create_vtbl.
|
|
fn vtbl_mthd_lteq(a: vtbl_mthd, b: vtbl_mthd) -> bool {
|
|
alt a {
|
|
normal_mthd(ma) {
|
|
alt b {
|
|
normal_mthd(mb) { ret str::lteq(ma.ident, mb.ident); }
|
|
fwding_mthd(mb) { ret str::lteq(ma.ident, mb.ident); }
|
|
}
|
|
}
|
|
fwding_mthd(ma) {
|
|
alt b {
|
|
normal_mthd(mb) { ret str::lteq(ma.ident, mb.ident); }
|
|
fwding_mthd(mb) { ret str::lteq(ma.ident, mb.ident); }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// filtering_fn: Used by create_vtbl to filter a list of methods to remove the
|
|
// ones that we don't need forwarding slots for.
|
|
fn filtering_fn(cx: @local_ctxt, m: vtbl_mthd, addtl_meths: [@ast::method]) ->
|
|
option::t<vtbl_mthd> {
|
|
|
|
// Since m is a fwding_mthd, and we're checking to see if it's in
|
|
// addtl_meths (which only contains normal_mthds), we can't just check if
|
|
// it's a member of addtl_meths. Instead, we have to go through
|
|
// addtl_meths and see if there's some method in it that has the same name
|
|
// as m.
|
|
alt m {
|
|
fwding_mthd(fm) {
|
|
for am: @ast::method in addtl_meths {
|
|
if str::eq(am.ident, fm.ident) { ret none; }
|
|
}
|
|
ret some(fwding_mthd(fm));
|
|
}
|
|
normal_mthd(_) {
|
|
cx.ccx.sess.bug("create_vtbl(): shouldn't be any \
|
|
normal_mthds in meths here");
|
|
}
|
|
}
|
|
}
|
|
|
|
// create_vtbl: Create a vtable for a regular object or for an outer anonymous
|
|
// object, and return a pointer to it.
|
|
fn create_vtbl(cx: @local_ctxt, sp: span, outer_obj_ty: ty::t, ob: ast::_obj,
|
|
ty_params: [ast::ty_param], inner_obj_ty: option::t<ty::t>,
|
|
additional_field_tys: [ty::t]) -> ValueRef {
|
|
|
|
let llmethods: [ValueRef] = [];
|
|
|
|
alt inner_obj_ty {
|
|
none. {
|
|
// We're creating a vtable for a regular object, or for an anonymous
|
|
// object that doesn't extend an existing one.
|
|
|
|
// Sort and process all the methods.
|
|
let meths =
|
|
std::sort::merge_sort(bind ast_mthd_lteq(_, _), ob.methods);
|
|
|
|
for m: @ast::method in meths {
|
|
llmethods +=
|
|
[process_normal_mthd(cx, m, outer_obj_ty, ty_params)];
|
|
}
|
|
}
|
|
some(inner_obj_ty) {
|
|
// We're creating a vtable for an anonymous object that extends an
|
|
// existing one.
|
|
|
|
// The vtable needs to contain 'forwarding slots' for any methods that
|
|
// were on the inner object and are not being overridden by the outer
|
|
// one. To find the set of methods that we need forwarding slots for,
|
|
// we take the set difference of { methods on the original object }
|
|
// and { methods being added, whether entirely new or overriding }.
|
|
|
|
let meths: [vtbl_mthd] = [];
|
|
|
|
// Gather up methods on the inner object.
|
|
alt ty::struct(cx.ccx.tcx, inner_obj_ty) {
|
|
ty::ty_obj(inner_obj_methods) {
|
|
for m: ty::method in inner_obj_methods {
|
|
meths += [fwding_mthd(@m)];
|
|
}
|
|
}
|
|
_ {
|
|
cx.ccx.sess.bug("create_vtbl(): trying to extend a \
|
|
non-object");
|
|
}
|
|
}
|
|
|
|
// Filter out any methods that we don't need forwarding slots for
|
|
// because they're being overridden.
|
|
let f = bind filtering_fn(cx, _, ob.methods);
|
|
meths = vec::filter_map(meths, f);
|
|
|
|
// And now add the additional ones, both overriding ones and entirely
|
|
// new ones. These will just be normal methods.
|
|
for m: @ast::method in ob.methods { meths += [normal_mthd(m)]; }
|
|
|
|
// Sort all the methods and process them.
|
|
meths =
|
|
std::sort::merge_sort(bind vtbl_mthd_lteq(_, _), meths);
|
|
|
|
// To create forwarding methods, we'll need a "backwarding" vtbl. See
|
|
// create_backwarding_vtbl and process_bkwding_method for details.
|
|
let backwarding_vtbl: ValueRef =
|
|
create_backwarding_vtbl(cx, sp, inner_obj_ty, outer_obj_ty);
|
|
|
|
for m: vtbl_mthd in meths {
|
|
alt m {
|
|
normal_mthd(nm) {
|
|
llmethods +=
|
|
[process_normal_mthd(cx, nm, outer_obj_ty, ty_params)];
|
|
}
|
|
fwding_mthd(fm) {
|
|
llmethods +=
|
|
[process_fwding_mthd(cx, sp, fm, ty_params, inner_obj_ty,
|
|
backwarding_vtbl,
|
|
additional_field_tys)];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ret finish_vtbl(cx, llmethods, "vtbl");
|
|
}
|
|
|
|
// create_backwarding_vtbl: Create a vtable for the inner object of an
|
|
// anonymous object, so that any self-calls made from the inner object's
|
|
// methods get redirected appropriately.
|
|
fn create_backwarding_vtbl(cx: @local_ctxt, sp: span, inner_obj_ty: ty::t,
|
|
outer_obj_ty: ty::t) -> ValueRef {
|
|
|
|
// This vtbl needs to have slots for all of the methods on an inner
|
|
// object, and it needs to forward them to the corresponding slots on the
|
|
// outer object. All we know about either one are their types.
|
|
|
|
let llmethods: [ValueRef] = [];
|
|
let meths: [ty::method] = [];
|
|
|
|
// Gather up methods on the inner object.
|
|
alt ty::struct(cx.ccx.tcx, inner_obj_ty) {
|
|
ty::ty_obj(inner_obj_methods) {
|
|
for m: ty::method in inner_obj_methods { meths += [m]; }
|
|
}
|
|
_ {
|
|
// Shouldn't happen.
|
|
cx.ccx.sess.bug("create_backwarding_vtbl(): trying to extend a \
|
|
non-object");
|
|
}
|
|
}
|
|
|
|
// Methods should have already been sorted, so no need to do so again.
|
|
for m: ty::method in meths {
|
|
// We pass outer_obj_ty to process_fwding_mthd() because it's the one
|
|
// being forwarded to.
|
|
llmethods += [process_bkwding_mthd(cx, sp, @m, [], outer_obj_ty, [])];
|
|
}
|
|
ret finish_vtbl(cx, llmethods, "backwarding_vtbl");
|
|
}
|
|
|
|
// finish_vtbl: Given a vector of vtable entries, create the table in
|
|
// read-only memory and return a pointer to it.
|
|
fn finish_vtbl(cx: @local_ctxt, llmethods: [ValueRef], name: str) ->
|
|
ValueRef {
|
|
let vtbl = C_struct(llmethods);
|
|
let vtbl_name = mangle_internal_name_by_path(cx.ccx, cx.path + [name]);
|
|
let gvar =
|
|
str::as_buf(vtbl_name,
|
|
{|buf|
|
|
llvm::LLVMAddGlobal(cx.ccx.llmod, val_ty(vtbl), buf)
|
|
});
|
|
llvm::LLVMSetInitializer(gvar, vtbl);
|
|
llvm::LLVMSetGlobalConstant(gvar, True);
|
|
llvm::LLVMSetLinkage(gvar,
|
|
lib::llvm::LLVMInternalLinkage as llvm::Linkage);
|
|
ret gvar;
|
|
}
|
|
|
|
// begin_fn: Set up an LLVM function for backwarding and forwarding functions.
|
|
fn begin_fn(cx: @local_ctxt, sp: span, m: @ty::method,
|
|
ty_params: [ast::ty_param], fn_name: str) -> ValueRef {
|
|
|
|
// Create a local context that's aware of the name of the method we're
|
|
// creating.
|
|
let mcx: @local_ctxt = @{path: cx.path + ["method", m.ident] with *cx};
|
|
|
|
// Make up a name for the function.
|
|
let s: str =
|
|
mangle_internal_name_by_path_and_seq(mcx.ccx, mcx.path, fn_name);
|
|
|
|
// Get the function's type and declare it.
|
|
let llfn_ty: TypeRef = type_of_meth(cx.ccx, sp, m, ty_params);
|
|
let llfn: ValueRef = decl_internal_cdecl_fn(cx.ccx.llmod, s, llfn_ty);
|
|
|
|
ret llfn;
|
|
}
|
|
|
|
// process_bkwding_mthd: Create the backwarding function that appears in a
|
|
// backwarding vtable slot.
|
|
//
|
|
// Backwarding functions are used in situations where method calls dispatch
|
|
// back through an outer object. For example, suppose an inner object has
|
|
// methods foo and bar, and bar contains the call self.foo(). We extend that
|
|
// object with a foo method that overrides the inner foo. Now, a call to
|
|
// outer.bar() should send us to to inner.bar() via a normal forwarding
|
|
// function, and then to self.foo(). But inner.bar() was already compiled
|
|
// under the assumption that self.foo() is inner.foo(), when we really want to
|
|
// reach outer.foo(). So, we give 'self' a vtable of backwarding functions,
|
|
// one for each method on inner, each of which takes all the same arguments as
|
|
// the corresponding method on inner does, calls that method on outer, and
|
|
// returns the value returned from that call.
|
|
fn process_bkwding_mthd(cx: @local_ctxt, sp: span, m: @ty::method,
|
|
ty_params: [ast::ty_param], outer_obj_ty: ty::t,
|
|
_additional_field_tys: [ty::t]) -> ValueRef {
|
|
|
|
let llbackwarding_fn = begin_fn(cx, sp, m, ty_params, "backwarding_fn");
|
|
let fcx = new_fn_ctxt(cx, sp, llbackwarding_fn);
|
|
let bcx = new_top_block_ctxt(fcx);
|
|
let lltop = bcx.llbb;
|
|
|
|
// The self-object will arrive in the backwarding function via the
|
|
// llenv argument, but we need to jump past the first item in the
|
|
// self-stack to get to the one we really want.
|
|
|
|
// Cast to self-stack's type.
|
|
let llenv =
|
|
PointerCast(bcx, fcx.llenv,
|
|
T_ptr(T_struct([cx.ccx.rust_object_type,
|
|
T_ptr(cx.ccx.rust_object_type)])));
|
|
let llself_obj_ptr = GEPi(bcx, llenv, [0, 1]);
|
|
llself_obj_ptr = Load(bcx, llself_obj_ptr);
|
|
|
|
// Cast it back to pointer-to-object-type, so LLVM won't complain.
|
|
llself_obj_ptr =
|
|
PointerCast(bcx, llself_obj_ptr, T_ptr(cx.ccx.rust_object_type));
|
|
|
|
// The 'llretptr' that will arrive in the backwarding function we're
|
|
// creating also needs to be the correct type. Cast it to the method's
|
|
// return type, if necessary.
|
|
let llretptr = fcx.llretptr;
|
|
let ccx = cx.ccx;
|
|
if ty::type_contains_params(ccx.tcx, m.fty.output) {
|
|
let m_output = m.fty.output;
|
|
check non_ty_var(ccx, m_output);
|
|
let llretty = type_of_inner(ccx, sp, m_output);
|
|
llretptr = PointerCast(bcx, llretptr, T_ptr(llretty));
|
|
}
|
|
|
|
// Get the index of the method we want.
|
|
let ix: uint = 0u;
|
|
alt ty::struct(bcx_tcx(bcx), outer_obj_ty) {
|
|
ty::ty_obj(methods) {
|
|
ix = option::get(ty::method_idx(m.ident, methods));
|
|
}
|
|
_ {
|
|
// Shouldn't happen.
|
|
cx.ccx.sess.bug("process_bkwding_mthd(): non-object type passed \
|
|
as outer_obj_ty");
|
|
}
|
|
}
|
|
|
|
// Pick out the method being backwarded to from the outer object's vtable.
|
|
let vtbl_type = T_ptr(T_array(T_ptr(T_nil()), ix + 1u));
|
|
|
|
let llouter_obj_vtbl =
|
|
GEPi(bcx, llself_obj_ptr, [0, abi::obj_field_vtbl]);
|
|
llouter_obj_vtbl = Load(bcx, llouter_obj_vtbl);
|
|
llouter_obj_vtbl = PointerCast(bcx, llouter_obj_vtbl, vtbl_type);
|
|
|
|
let llouter_mthd =
|
|
GEPi(bcx, llouter_obj_vtbl, [0, ix as int]);
|
|
|
|
// Set up the outer method to be called.
|
|
let llouter_mthd_ty = type_of_meth(bcx_ccx(bcx), sp, m, ty_params);
|
|
llouter_mthd =
|
|
PointerCast(bcx, llouter_mthd, T_ptr(T_ptr(llouter_mthd_ty)));
|
|
llouter_mthd = Load(bcx, llouter_mthd);
|
|
|
|
// Set up the three implicit arguments to the outer method we'll need to
|
|
// call.
|
|
let llouter_mthd_args: [ValueRef] = [llretptr, llself_obj_ptr];
|
|
|
|
// Copy the explicit arguments that are being passed into the forwarding
|
|
// function (they're in fcx.llargs) to llouter_mthd_args.
|
|
|
|
let a: uint = 2u; // retptr, env come first
|
|
for arg: ty::arg in m.fty.inputs {
|
|
llouter_mthd_args += [llvm::LLVMGetParam(llbackwarding_fn, a)];
|
|
a += 1u;
|
|
}
|
|
|
|
// And, finally, call the outer method.
|
|
Call(bcx, llouter_mthd, llouter_mthd_args);
|
|
|
|
build_return(bcx);
|
|
finish_fn(fcx, lltop);
|
|
|
|
ret llbackwarding_fn;
|
|
|
|
}
|
|
|
|
// process_fwding_mthd: Create the forwarding function that appears in a
|
|
// vtable slot for method calls that need to forward to another object. A
|
|
// helper function for create_vtbl.
|
|
//
|
|
// Forwarding functions are used for method calls that fall through to an
|
|
// inner object. For example, suppose an inner object has method foo and we
|
|
// extend it with a method bar. The only version of 'foo' we have is on the
|
|
// inner object, but we would like to be able to call outer.foo(). So we use
|
|
// a forwarding function to make the foo method available on the outer object.
|
|
// It takes all the same arguments as the foo method on the inner object does,
|
|
// calls inner.foo() with those arguments, and then returns the value returned
|
|
// from that call. (The inner object won't exist until run-time, but we know
|
|
// its type statically.)
|
|
fn process_fwding_mthd(cx: @local_ctxt, sp: span, m: @ty::method,
|
|
ty_params: [ast::ty_param], inner_obj_ty: ty::t,
|
|
backwarding_vtbl: ValueRef,
|
|
additional_field_tys: [ty::t]) -> ValueRef unsafe {
|
|
|
|
// Create a new function context and block context for the function,
|
|
// holding onto a pointer to the first block.
|
|
let llforwarding_fn = begin_fn(cx, sp, m, ty_params, "forwarding_fn");
|
|
let fcx = new_fn_ctxt(cx, sp, llforwarding_fn);
|
|
let bcx = new_top_block_ctxt(fcx);
|
|
let lltop = bcx.llbb;
|
|
|
|
// The outer object will arrive in the forwarding function via the llenv
|
|
// argument.
|
|
let llself_obj_ptr = fcx.llenv;
|
|
|
|
// The 'llretptr' that will arrive in the forwarding function we're
|
|
// creating also needs to be the correct type. Cast it to the method's
|
|
// return type, if necessary.
|
|
let llretptr = fcx.llretptr;
|
|
let ccx = cx.ccx;
|
|
if ty::type_contains_params(ccx.tcx, m.fty.output) {
|
|
let m_output = m.fty.output;
|
|
check non_ty_var(ccx, m_output);
|
|
let llretty = type_of_inner(ccx, sp, m_output);
|
|
llretptr = PointerCast(bcx, llretptr, T_ptr(llretty));
|
|
}
|
|
|
|
// Now, we have to get the the inner_obj's vtbl out of the self_obj. This
|
|
// is a multi-step process:
|
|
|
|
// First, grab the box out of the self_obj. It contains a refcount and a
|
|
// body.
|
|
let llself_obj_box =
|
|
GEPi(bcx, llself_obj_ptr, [0, abi::obj_field_box]);
|
|
llself_obj_box = Load(bcx, llself_obj_box);
|
|
|
|
let ccx = bcx_ccx(bcx);
|
|
let llbox_ty = T_opaque_obj_ptr(ccx);
|
|
llself_obj_box = PointerCast(bcx, llself_obj_box, llbox_ty);
|
|
|
|
// Now, reach into the box and grab the body.
|
|
let llself_obj_body =
|
|
GEPi(bcx, llself_obj_box, [0, abi::box_rc_field_body]);
|
|
|
|
// Now, we need to figure out exactly what type the body is supposed to be
|
|
// cast to.
|
|
let body_ty: ty::t =
|
|
create_object_body_type(cx.ccx.tcx, additional_field_tys, [],
|
|
some(inner_obj_ty));
|
|
// And cast to that type.
|
|
// create_object_body_type maybe should have a postcondition...
|
|
|
|
let cx_ccx = cx.ccx;
|
|
check (type_has_static_size(cx_ccx, body_ty));
|
|
|
|
llself_obj_body =
|
|
PointerCast(bcx, llself_obj_body,
|
|
T_ptr(type_of(cx_ccx, sp, body_ty)));
|
|
|
|
// Now, reach into the body and grab the inner_obj.
|
|
check type_is_tup_like(bcx, body_ty);
|
|
let llinner_obj =
|
|
GEP_tup_like(bcx, body_ty, llself_obj_body,
|
|
[0, abi::obj_body_elt_inner_obj]);
|
|
bcx = llinner_obj.bcx;
|
|
|
|
// And, now, somewhere in inner_obj is a vtable with an entry for the
|
|
// method we want. First, pick out the vtable, and then pluck that
|
|
// method's entry out of the vtable so that the forwarding function can
|
|
// call it.
|
|
let llinner_obj_vtbl =
|
|
GEPi(bcx, llinner_obj.val, [0, abi::obj_field_vtbl]);
|
|
llinner_obj_vtbl = Load(bcx, llinner_obj_vtbl);
|
|
|
|
let llinner_obj_body =
|
|
GEPi(bcx, llinner_obj.val, [0, abi::obj_field_box]);
|
|
llinner_obj_body = Load(bcx, llinner_obj_body);
|
|
|
|
// Get the index of the method we want.
|
|
let ix: uint = 0u;
|
|
alt ty::struct(bcx_tcx(bcx), inner_obj_ty) {
|
|
ty::ty_obj(methods) {
|
|
ix = option::get(ty::method_idx(m.ident, methods));
|
|
}
|
|
_ {
|
|
// Shouldn't happen.
|
|
cx.ccx.sess.bug("process_fwding_mthd(): non-object type passed \
|
|
as target_obj_ty");
|
|
}
|
|
}
|
|
|
|
// Pick out the original method from the vtable.
|
|
let vtbl_type = T_ptr(T_array(T_ptr(T_nil()), ix + 1u));
|
|
llinner_obj_vtbl = PointerCast(bcx, llinner_obj_vtbl, vtbl_type);
|
|
|
|
let llorig_mthd =
|
|
GEPi(bcx, llinner_obj_vtbl, [0, ix as int]);
|
|
|
|
// Set up the original method to be called.
|
|
let llorig_mthd_ty = type_of_meth(bcx_ccx(bcx), sp, m, ty_params);
|
|
llorig_mthd = PointerCast(bcx, llorig_mthd, T_ptr(T_ptr(llorig_mthd_ty)));
|
|
llorig_mthd = Load(bcx, llorig_mthd);
|
|
|
|
// Set up the self-stack.
|
|
let self_stack =
|
|
alloca(bcx,
|
|
T_struct([cx.ccx.rust_object_type,
|
|
T_ptr(cx.ccx.rust_object_type)]));
|
|
self_stack =
|
|
populate_self_stack(bcx, self_stack, llself_obj_ptr, backwarding_vtbl,
|
|
llinner_obj_body);
|
|
|
|
// Cast self_stack back to pointer-to-object-type to make LLVM happy.
|
|
self_stack = PointerCast(bcx, self_stack, T_ptr(cx.ccx.rust_object_type));
|
|
|
|
// Set up the three implicit arguments to the original method we'll need
|
|
// to call.
|
|
let llorig_mthd_args: [ValueRef] = [llretptr, self_stack];
|
|
|
|
// Copy the explicit arguments that are being passed into the forwarding
|
|
// function (they're in fcx.llargs) to llorig_mthd_args.
|
|
|
|
let a: uint = 2u; // retptr, env come first
|
|
for arg: ty::arg in m.fty.inputs {
|
|
llorig_mthd_args += [llvm::LLVMGetParam(llforwarding_fn, a)];
|
|
a += 1u;
|
|
}
|
|
|
|
// And, finally, call the original (inner) method.
|
|
Call(bcx, llorig_mthd, llorig_mthd_args);
|
|
|
|
build_return(bcx);
|
|
finish_fn(fcx, lltop);
|
|
|
|
ret llforwarding_fn;
|
|
}
|
|
|
|
// create_object_body_type: Synthesize a big structural tuple type for an
|
|
// object body: [tydesc, [typaram, ...], [field, ...], inner_obj].
|
|
fn create_object_body_type(tcx: ty::ctxt, fields_ty: [ty::t],
|
|
typarams_ty: [ty::t],
|
|
maybe_inner_obj_ty: option::t<ty::t>) -> ty::t {
|
|
|
|
let tydesc_ty: ty::t = ty::mk_type(tcx);
|
|
let typarams_ty_tup: ty::t = ty::mk_tup(tcx, typarams_ty);
|
|
let fields_ty_tup: ty::t = ty::mk_tup(tcx, fields_ty);
|
|
|
|
let body_ty: ty::t;
|
|
alt maybe_inner_obj_ty {
|
|
some(inner_obj_ty) {
|
|
body_ty =
|
|
ty::mk_tup(tcx,
|
|
[tydesc_ty, typarams_ty_tup, fields_ty_tup,
|
|
inner_obj_ty]);
|
|
}
|
|
none {
|
|
body_ty =
|
|
ty::mk_tup(tcx, [tydesc_ty, typarams_ty_tup, fields_ty_tup]);
|
|
}
|
|
}
|
|
|
|
ret body_ty;
|
|
}
|
|
|
|
// process_normal_mthd: Create the contents of a normal vtable slot. A helper
|
|
// function for create_vtbl.
|
|
fn process_normal_mthd(cx: @local_ctxt, m: @ast::method, self_ty: ty::t,
|
|
ty_params: [ast::ty_param]) -> ValueRef {
|
|
|
|
let llfnty = T_nil();
|
|
let ccx = cx.ccx;
|
|
alt ty::struct(cx.ccx.tcx, node_id_type(cx.ccx, m.id)) {
|
|
ty::ty_fn(f) {
|
|
let out = f.output;
|
|
check non_ty_var(ccx, out);
|
|
llfnty = type_of_fn(
|
|
ccx, m.span, true, f.inputs, out,
|
|
vec::map(ty_params, {|p| param_bounds(ccx, p)}));
|
|
}
|
|
}
|
|
let mcx: @local_ctxt =
|
|
@{path: cx.path + ["method", m.ident] with *cx};
|
|
let s: str = mangle_internal_name_by_path(mcx.ccx, mcx.path);
|
|
let llfn: ValueRef = decl_internal_cdecl_fn(ccx.llmod, s, llfnty);
|
|
|
|
// Every method on an object gets its node_id inserted into the crate-wide
|
|
// item_ids map, together with the ValueRef that points to where that
|
|
// method's definition will be in the executable.
|
|
ccx.item_ids.insert(m.id, llfn);
|
|
ccx.item_symbols.insert(m.id, s);
|
|
trans_fn(mcx, m.span, m.decl, m.body, llfn, obj_self(self_ty), ty_params,
|
|
m.id);
|
|
|
|
ret llfn;
|
|
}
|
|
|
|
// Update a self-stack structure ([[wrapper_self_pair], self_pair*]) to
|
|
// [[backwarding_vtbl*, inner_obj_body*], outer_obj*].
|
|
//
|
|
// We do this when we're receiving the outer object in a forwarding function
|
|
// via the llenv argument, and we want the forwarding function to call a
|
|
// method on a "self" that's inner-obj-shaped, but we also want to hold onto
|
|
// the outer obj for potential use later by backwarding functions.
|
|
fn populate_self_stack(bcx: @block_ctxt, self_stack: ValueRef,
|
|
outer_obj: ValueRef, backwarding_vtbl: ValueRef,
|
|
inner_obj_body: ValueRef) -> ValueRef {
|
|
|
|
// Drop the outer obj into the second slot.
|
|
let self_pair_ptr = GEPi(bcx, self_stack, [0, 1]);
|
|
Store(bcx, outer_obj, self_pair_ptr);
|
|
|
|
// Drop in the backwarding vtbl.
|
|
let wrapper_pair = GEPi(bcx, self_stack, [0, 0]);
|
|
let wrapper_vtbl_ptr = GEPi(bcx, wrapper_pair, [0, 0]);
|
|
let backwarding_vtbl_cast =
|
|
PointerCast(bcx, backwarding_vtbl, T_ptr(T_empty_struct()));
|
|
Store(bcx, backwarding_vtbl_cast, wrapper_vtbl_ptr);
|
|
|
|
// Drop in the inner obj body.
|
|
let wrapper_body_ptr = GEPi(bcx, wrapper_pair, [0, 1]);
|
|
Store(bcx, inner_obj_body, wrapper_body_ptr);
|
|
|
|
ret self_stack;
|
|
}
|
|
|
|
fn type_of_meth(ccx: @crate_ctxt, sp: span, m: @ty::method,
|
|
tps: [ast::ty_param]) -> TypeRef {
|
|
let out_ty = m.fty.output;
|
|
check non_ty_var(ccx, out_ty);
|
|
type_of_fn(ccx, sp, true, m.fty.inputs, out_ty,
|
|
vec::map(tps, {|p| param_bounds(ccx, p)}))
|
|
}
|
|
|
|
//
|
|
// Local Variables:
|
|
// mode: rust
|
|
// fill-column: 78;
|
|
// indent-tabs-mode: nil
|
|
// c-basic-offset: 4
|
|
// buffer-file-coding-system: utf-8-unix
|
|
// End:
|
|
//
|