From 5d9680fc7e97b57527c4a94d2bba32aeff4a195e Mon Sep 17 00:00:00 2001 From: Lindsey Kuper Date: Tue, 9 Aug 2011 16:42:55 -0700 Subject: [PATCH] Move object-system-related trans stuff to its own file. --- src/comp/middle/trans.rs | 1011 +----------------------------- src/comp/middle/trans_objects.rs | 1001 +++++++++++++++++++++++++++++ src/comp/rustc.rc | 1 + 3 files changed, 1005 insertions(+), 1008 deletions(-) create mode 100644 src/comp/middle/trans_objects.rs diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index b23388f290d9..01a1101b3691 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -75,6 +75,9 @@ import trans_comm::trans_spawn; import trans_comm::trans_send; import trans_comm::trans_recv; +import trans_objects::trans_anon_obj; +import trans_objects::trans_obj; + // This function now fails if called on a type with dynamic size (as its // return value was always meaningless in that case anyhow). Beware! // @@ -5591,242 +5594,6 @@ fn trans_be(cx: &@block_ctxt, e: &@ast::expr) -> result { ret trans_ret(cx, some(e)); } -/* - - Suppose we create an anonymous object my_b from a regular object a: - - obj a() { - fn foo() -> int { - ret 2; - } - fn bar() -> int { - ret self.foo(); - } - } - - auto my_a = a(); - auto my_b = obj { fn baz() -> int { ret self.foo() } with my_a }; - - Here we're extending the my_a object with an additional method baz, creating - an object my_b. Since it's an object, my_b is a pair of a vtable pointer and - a body pointer: - - my_b: [vtbl* | body*] - - my_b's vtable has entries for foo, bar, and baz, whereas my_a's vtable has - only foo and bar. my_b's 3-entry vtable consists of two forwarding functions - and one real method. - - my_b's body just contains the pair a: [ a_vtable | a_body ], wrapped up with - any additional fields that my_b added. None were added, so my_b is just the - wrapped inner object. - -*/ - -// 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) -> result { - - - 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_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: - std::ivec::map(ast::obj_field_from_anon_obj_field, - additional_fields), - 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 just adding new - // fields 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); - } - } - - // Allocate the object that we're going to return. - let pair = alloca(bcx, ccx.rust_object_type); - - // Take care of cleanups. - let t = node_id_type(ccx, id); - add_clean_temp(bcx, pair, t); - - // 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 = - bcx.build.GEP(pair, ~[C_int(0), C_int(abi::obj_field_vtbl)]); - let pair_box = - bcx.build.GEP(pair, ~[C_int(0), C_int(abi::obj_field_box)]); - - vtbl = bcx.build.PointerCast(vtbl, T_ptr(T_empty_struct())); - bcx.build.Store(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, fields, and a pointer to our inner_obj. - let llbox_ty: TypeRef = T_ptr(T_empty_struct()); - - if std::ivec::len[ast::anon_obj_field](additional_fields) == 0u && - anon_obj.inner_obj == none { - // If the object we're translating has no fields and no inner_obj, - // there's not much to do. - bcx.build.Store(C_null(llbox_ty), pair_box); - } else { - - // Synthesize a tuple type for fields: [field, ...] - let fields_ty: ty::t = ty::mk_imm_tup(ccx.tcx, additional_field_tys); - - // Type for tydescs. - let tydesc_ty: ty::t = ty::mk_type(ccx.tcx); - - // Placeholder for non-existent typarams, since anon objs don't have - // them. - let typarams_ty: ty::t = ty::mk_imm_tup(ccx.tcx, ~[]); - - // Tuple type for body: - // [tydesc, [typaram, ...], [field, ...], inner_obj] - let body_ty: ty::t = - ty::mk_imm_tup(ccx.tcx, - ~[tydesc_ty, typarams_ty, fields_ty, - inner_obj_ty]); - - // Hand this type we've synthesized off to trans_malloc_boxed, which - // allocates a box, including space for a refcount. - 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. - let body_tydesc = - GEP_tup_like(bcx, body_ty, body, - ~[0, abi::obj_body_elt_tydesc]); - bcx = body_tydesc.bcx; - let ti = none[@tydesc_info]; - let body_td = get_tydesc(bcx, body_ty, true, ti); - 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; - bcx.build.Store(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. - 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 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)).bcx; - 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. - let inner_obj_val: result = trans_expr(bcx, e); - - let body_inner_obj = - GEP_tup_like(bcx, body_ty, body, - ~[0, abi::obj_body_elt_inner_obj]); - bcx = body_inner_obj.bcx; - bcx = - copy_val(bcx, INIT, body_inner_obj.val, inner_obj_val.val, - inner_obj_ty).bcx; - } - } - - // Store box ptr in outer pair. - let p = bcx.build.PointerCast(box.box, llbox_ty); - bcx.build.Store(p, pair_box); - } - - // return the object we built. - ret rslt(bcx, pair); -} - fn init_local(bcx: @block_ctxt, local: &@ast::local) -> result { let ty = node_id_type(bcx_ccx(bcx), local.node.id); let llptr = bcx.fcx.lllocals.get(local.node.id); @@ -6464,778 +6231,6 @@ fn trans_fn(cx: @local_ctxt, sp: &span, f: &ast::_fn, llfndecl: ValueRef, log_fn_time(cx.ccx, str::connect_ivec(cx.path, "::"), start, end); } -// 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 = bcx.build.GEP(self_stack, - ~[C_int(0), - C_int(1)]); - bcx.build.Store(outer_obj, self_pair_ptr); - - // Drop in the backwarding vtbl. - let wrapper_pair = bcx.build.GEP(self_stack, - ~[C_int(0), - C_int(0)]); - let wrapper_vtbl_ptr = bcx.build.GEP(wrapper_pair, - ~[C_int(0), - C_int(0)]); - let backwarding_vtbl_cast = - bcx.build.PointerCast(backwarding_vtbl, T_ptr(T_empty_struct())); - bcx.build.Store(backwarding_vtbl_cast, wrapper_vtbl_ptr); - - // Drop in the inner obj body. - let wrapper_body_ptr = bcx.build.GEP(wrapper_pair, - ~[C_int(0), - C_int(1)]); - bcx.build.Store(inner_obj_body, wrapper_body_ptr); - - ret self_stack; -} - -// 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 { - - // 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 backwarding function. - let fn_name: str = "backwarding_fn"; - let s: str = mangle_internal_name_by_path_and_seq(mcx.ccx, mcx.path, - fn_name); - - // Get the backwarding function's type and declare it. - let llbackwarding_fn_ty: TypeRef = - type_of_fn_full(cx.ccx, sp, m.proto, true, m.inputs, m.output, - std::ivec::len[ast::ty_param](ty_params)); - let llbackwarding_fn: ValueRef = - decl_internal_fastcall_fn(cx.ccx.llmod, s, llbackwarding_fn_ty); - - // Create a new function context and block context for the backwarding - // function, holding onto a pointer to the first block. - 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 = bcx.build.PointerCast( - fcx.llenv, - T_ptr(T_struct(~[cx.ccx.rust_object_type, - T_ptr(cx.ccx.rust_object_type)]))); - - let llself_obj_ptr = bcx.build.GEP(llenv, - ~[C_int(0), - C_int(1)]); - llself_obj_ptr = bcx.build.Load(llself_obj_ptr); - - // Cast it back to pointer-to-object-type, so LLVM won't complain. - llself_obj_ptr = bcx.build.PointerCast(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; - if ty::type_contains_params(cx.ccx.tcx, m.output) { - let llretty = type_of_inner(cx.ccx, sp, m.output); - llretptr = bcx.build.PointerCast(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 = ty::method_idx(cx.ccx.sess, sp, 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 = - bcx.build.GEP(llself_obj_ptr, - ~[C_int(0), C_int(abi::obj_field_vtbl)]); - llouter_obj_vtbl = bcx.build.Load(llouter_obj_vtbl); - llouter_obj_vtbl = bcx.build.PointerCast(llouter_obj_vtbl, vtbl_type); - - let llouter_mthd = - bcx.build.GEP(llouter_obj_vtbl, ~[C_int(0), C_int(ix as int)]); - - // Set up the outer method to be called. - let outer_mthd_ty = ty::method_ty_to_fn_ty(cx.ccx.tcx, *m); - let llouter_mthd_ty = - type_of_fn_full(bcx_ccx(bcx), sp, - ty::ty_fn_proto(bcx_tcx(bcx), outer_mthd_ty), true, - m.inputs, m.output, - std::ivec::len[ast::ty_param](ty_params)); - llouter_mthd = - bcx.build.PointerCast(llouter_mthd, T_ptr(T_ptr(llouter_mthd_ty))); - llouter_mthd = bcx.build.Load(llouter_mthd); - - // Set up the three implicit arguments to the outer method we'll need - // to call. - let self_arg = llself_obj_ptr; - let llouter_mthd_args: [ValueRef] = ~[llretptr, fcx.lltaskptr, self_arg]; - - // Copy the explicit arguments that are being passed into the forwarding - // function (they're in fcx.llargs) to llouter_mthd_args. - - let a: uint = 3u; // retptr, task ptr, env come first - let passed_arg: ValueRef = llvm::LLVMGetParam(llbackwarding_fn, a); - for arg: ty::arg in m.inputs { - if arg.mode == ty::mo_val { - passed_arg = load_if_immediate(bcx, passed_arg, arg.ty); - } - llouter_mthd_args += ~[passed_arg]; - a += 1u; - } - - // And, finally, call the outer method. - bcx.build.FastCall(llouter_mthd, llouter_mthd_args); - - bcx.build.RetVoid(); - 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 { - - // 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 forwarding function. - let fn_name: str = "forwarding_fn"; - let s: str = mangle_internal_name_by_path_and_seq(mcx.ccx, mcx.path, - fn_name); - - // Get the forwarding function's type and declare it. - let llforwarding_fn_ty: TypeRef = - type_of_fn_full(cx.ccx, sp, m.proto, true, m.inputs, m.output, - std::ivec::len[ast::ty_param](ty_params)); - let llforwarding_fn: ValueRef = - decl_internal_fastcall_fn(cx.ccx.llmod, s, llforwarding_fn_ty); - - // Create a new function context and block context for the forwarding - // function, holding onto a pointer to the first block. - 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; - if ty::type_contains_params(cx.ccx.tcx, m.output) { - let llretty = type_of_inner(cx.ccx, sp, m.output); - llretptr = bcx.build.PointerCast(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 = - bcx.build.GEP(llself_obj_ptr, ~[C_int(0), C_int(abi::obj_field_box)]); - llself_obj_box = bcx.build.Load(llself_obj_box); - - let ccx = bcx_ccx(bcx); - let llbox_ty = T_opaque_obj_ptr(*ccx); - llself_obj_box = bcx.build.PointerCast(llself_obj_box, llbox_ty); - - // Now, reach into the box and grab the body. - let llself_obj_body = - bcx.build.GEP(llself_obj_box, - ~[C_int(0), C_int(abi::box_rc_field_body)]); - - // Now, we need to figure out exactly what type the body is supposed to be - // cast to. - - // NB: This next part is almost flat-out copypasta from trans_anon_obj. - // It would be great to factor this out. - - // Synthesize a tuple type for fields: [field, ...] - let fields_ty: ty::t = ty::mk_imm_tup(cx.ccx.tcx, additional_field_tys); - - // Type for tydescs. - let tydesc_ty: ty::t = ty::mk_type(cx.ccx.tcx); - - // Placeholder for non-existent typarams, since anon objs don't have them. - let typarams_ty: ty::t = ty::mk_imm_tup(cx.ccx.tcx, ~[]); - - // Tuple type for body: - // [tydesc, [typaram, ...], [field, ...], inner_obj] - - let body_ty: ty::t = - ty::mk_imm_tup(cx.ccx.tcx, - ~[tydesc_ty, typarams_ty, fields_ty, inner_obj_ty]); - - // And cast to that type. - llself_obj_body = - bcx.build.PointerCast(llself_obj_body, - T_ptr(type_of(cx.ccx, sp, body_ty))); - - // Now, reach into the body and grab the inner_obj. - 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 = - bcx.build.GEP(llinner_obj.val, - ~[C_int(0), C_int(abi::obj_field_vtbl)]); - llinner_obj_vtbl = bcx.build.Load(llinner_obj_vtbl); - - let llinner_obj_body = - bcx.build.GEP(llinner_obj.val, - ~[C_int(0), C_int(abi::obj_field_box)]); - llinner_obj_body = bcx.build.Load(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 = ty::method_idx(cx.ccx.sess, sp, 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 = bcx.build.PointerCast(llinner_obj_vtbl, vtbl_type); - - let llorig_mthd = - bcx.build.GEP(llinner_obj_vtbl, ~[C_int(0), C_int(ix as int)]); - - // Set up the original method to be called. - let orig_mthd_ty = ty::method_ty_to_fn_ty(cx.ccx.tcx, *m); - let llorig_mthd_ty = - type_of_fn_full(bcx_ccx(bcx), sp, - ty::ty_fn_proto(bcx_tcx(bcx), orig_mthd_ty), true, - m.inputs, m.output, - std::ivec::len[ast::ty_param](ty_params)); - llorig_mthd = - bcx.build.PointerCast(llorig_mthd, T_ptr(T_ptr(llorig_mthd_ty))); - llorig_mthd = bcx.build.Load(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 = bcx.build.PointerCast(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, fcx.lltaskptr, 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 = 3u; // retptr, task ptr, env come first - let passed_arg: ValueRef = llvm::LLVMGetParam(llforwarding_fn, a); - for arg: ty::arg in m.inputs { - if arg.mode == ty::mo_val { - passed_arg = load_if_immediate(bcx, passed_arg, arg.ty); - } - llorig_mthd_args += ~[passed_arg]; - a += 1u; - } - - // And, finally, call the original (inner) method. - bcx.build.FastCall(llorig_mthd, llorig_mthd_args); - - bcx.build.RetVoid(); - finish_fn(fcx, lltop); - - ret llforwarding_fn; -} - -// 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(); - alt ty::struct(cx.ccx.tcx, node_id_type(cx.ccx, m.node.id)) { - ty::ty_fn(proto, inputs, output, _, _) { - llfnty = - type_of_fn_full(cx.ccx, m.span, proto, true, inputs, output, - std::ivec::len[ast::ty_param](ty_params)); - } - } - let mcx: @local_ctxt = - @{path: cx.path + ~["method", m.node.ident] with *cx}; - let s: str = mangle_internal_name_by_path(mcx.ccx, mcx.path); - let llfn: ValueRef = decl_internal_fastcall_fn(cx.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. - cx.ccx.item_ids.insert(m.node.id, llfn); - cx.ccx.item_symbols.insert(m.node.id, s); - trans_fn(mcx, m.span, m.node.meth, llfn, some(self_ty), ty_params, - m.node.id); - - ret llfn; -} - -// 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.node.ident, b.node.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.node.ident, mb.node.ident); } - fwding_mthd(mb) { ret str::lteq(ma.node.ident, mb.ident); } - } - } - fwding_mthd(ma) { - alt b { - normal_mthd(mb) { ret str::lteq(ma.ident, mb.node.ident); } - fwding_mthd(mb) { ret str::lteq(ma.ident, mb.ident); } - } - } - } -} - -// 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.node.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 a vtable for an object being translated. Returns a pointer into -// read-only memory. -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. { - // Sort and process all the methods. - let meths = - std::sort::ivector::merge_sort[@ast::method] - (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) { - // If this vtable is being created for an extended object, then the - // vtable needs to contain 'forwarding slots' for methods that were on - // the original object and are not being overridden by the extended - // one. So, to find the set of methods that we need forwarding slots - // for, we need to take the set difference of inner_obj_methods - // (methods on the original object) and ob.methods (methods being - // added, whether entirely new or overriding). - - // inner_obj_ty is the type of the inner object being forwarded to, - // and "ob" is the wrapper object. We need to take apart - // inner_obj_ty, which is the type of the object being forwarded to - // (it had better have an object type with methods!) and put those - // original methods onto the list of methods we need forwarding - // methods for. - - let meths: [vtbl_mthd] = ~[]; - - // Gather up methods on the original object in 'meths'. - 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"); - } - } - - // Now, 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 = std::ivec::filter_map[vtbl_mthd, vtbl_mthd](f, meths); - - // 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::ivector::merge_sort[vtbl_mthd] - (bind vtbl_mthd_lteq(_, _), meths); - - 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)]; - } - } - } - } - } - - let vtbl = C_struct(llmethods); - let vtbl_name = mangle_internal_name_by_path(cx.ccx, cx.path + ~["vtbl"]); - let gvar = - llvm::LLVMAddGlobal(cx.ccx.llmod, val_ty(vtbl), str::buf(vtbl_name)); - llvm::LLVMSetInitializer(gvar, vtbl); - llvm::LLVMSetGlobalConstant(gvar, True); - llvm::LLVMSetLinkage(gvar, - lib::llvm::LLVMInternalLinkage as llvm::Linkage); - ret gvar; -} - -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, ~[])]; - } - - let vtbl = C_struct(llmethods); - let vtbl_name = - mangle_internal_name_by_path(cx.ccx, - cx.path + ~["backwarding_vtbl"]); - let gvar = - llvm::LLVMAddGlobal(cx.ccx.llmod, val_ty(vtbl), str::buf(vtbl_name)); - llvm::LLVMSetInitializer(gvar, vtbl); - llvm::LLVMSetGlobalConstant(gvar, True); - llvm::LLVMSetLinkage(gvar, - lib::llvm::LLVMInternalLinkage as llvm::Linkage); - - ret gvar; - -} - -// trans_obj: creates 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::alias(false), ty: f.ty, ident: f.ident, id: f.id}]; - } - let fcx = new_fn_ctxt(cx, sp, llctor_decl); - - // Both regular arguments and type parameters are handled here. - create_llargs_for_fn_args(fcx, ast::proto_fn, none[ty::t], - ty::ret_ty_of_fn(ccx.tcx, ctor_id), fn_args, - ty_params); - let arg_tys: [ty::arg] = arg_tys_of_fn(ccx, ctor_id); - copy_args_to_allocas(fcx, fn_args, arg_tys); - - // 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; - - // 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 = - bcx.build.GEP(pair, ~[C_int(0), C_int(abi::obj_field_vtbl)]); - let pair_box = - bcx.build.GEP(pair, ~[C_int(0), C_int(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 = bcx.build.PointerCast(vtbl, T_ptr(T_empty_struct())); - - bcx.build.Store(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. - - // FIXME: What about inner_obj? Do we have to think about it here? - // (Pertains to issues #538/#539/#540/#543.) - - let llbox_ty: TypeRef = T_ptr(T_empty_struct()); - - if std::ivec::len[ast::ty_param](ty_params) == 0u && - std::ivec::len[ty::arg](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. - bcx.build.Store(C_null(llbox_ty), pair_box); - } else { - // Otherwise, we have to synthesize a big structural type for the - // object body. - let obj_fields: [ty::t] = ~[]; - for a: ty::arg in arg_tys { obj_fields += ~[a.ty]; } - - // Tuple type for fields: [field, ...] - let fields_ty: ty::t = ty::mk_imm_tup(ccx.tcx, obj_fields); - - let tydesc_ty = ty::mk_type(ccx.tcx); - let tps: [ty::t] = ~[]; - for tp: ast::ty_param in ty_params { tps += ~[tydesc_ty]; } - - // Tuple type for typarams: [typaram, ...] - let typarams_ty: ty::t = ty::mk_imm_tup(ccx.tcx, tps); - - // Tuple type for body: [tydesc_ty, [typaram, ...], [field, ...]] - let body_ty: ty::t = - ty::mk_imm_tup(ccx.tcx, ~[tydesc_ty, typarams_ty, fields_ty]); - - // Hand this type we've synthesized off to trans_malloc_boxed, which - // allocates a box, including space for a refcount. - 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. - - let body_tydesc = - GEP_tup_like(bcx, body_ty, body, - ~[0, abi::obj_body_elt_tydesc]); - bcx = body_tydesc.bcx; - let ti = none[@tydesc_info]; - let body_td = get_tydesc(bcx, body_ty, true, ti); - 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; - bcx.build.Store(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. - let body_typarams = - GEP_tup_like(bcx, body_ty, body, - ~[0, abi::obj_body_elt_typarams]); - bcx = body_typarams.bcx; - let i: int = 0; - for tp: ast::ty_param in ty_params { - let typaram = bcx.fcx.lltydescs.(i); - let capture = - GEP_tup_like(bcx, typarams_ty, body_typarams.val, ~[0, i]); - bcx = capture.bcx; - bcx = copy_val(bcx, INIT, capture.val, typaram, tydesc_ty).bcx; - i += 1; - } - - // Copy args into body fields. - let body_fields = - GEP_tup_like(bcx, body_ty, body, - ~[0, abi::obj_body_elt_fields]); - bcx = body_fields.bcx; - i = 0; - for f: ast::obj_field in ob.fields { - alt bcx.fcx.llargs.find(f.id) { - some(arg1) { - let arg = load_if_immediate(bcx, arg1, arg_tys.(i).ty); - let field = - GEP_tup_like(bcx, fields_ty, body_fields.val, ~[0, i]); - bcx = field.bcx; - bcx = copy_val(bcx, INIT, field.val, arg, arg_tys.(i).ty).bcx; - i += 1; - } - none. { - bcx_ccx(bcx).sess.span_fatal(f.ty.span, - "internal error in trans_obj"); - } - } - } - - // Store box ptr in outer pair. - let p = bcx.build.PointerCast(box.box, llbox_ty); - bcx.build.Store(p, pair_box); - } - bcx.build.RetVoid(); - - // Insert the mandatory first few basic blocks before lltop. - finish_fn(fcx, lltop); -} - fn trans_res_ctor(cx: @local_ctxt, sp: &span, dtor: &ast::_fn, ctor_id: ast::node_id, ty_params: &[ast::ty_param]) { // Create a function for the constructor diff --git a/src/comp/middle/trans_objects.rs b/src/comp/middle/trans_objects.rs new file mode 100644 index 000000000000..454a4f3b39a4 --- /dev/null +++ b/src/comp/middle/trans_objects.rs @@ -0,0 +1,1001 @@ +// Translation of object-related things to LLVM IR. + +import std::str; +import std::option; +import option::none; +import option::some; + +import lib::llvm::llvm; +import lib::llvm::Bool; +import lib::llvm::True; +import lib::llvm::llvm::TypeRef; +import lib::llvm::llvm::ValueRef; + +import back::abi; +import back::link::mangle_internal_name_by_path; +import back::link::mangle_internal_name_by_path_and_seq; +import syntax::ast; +import syntax::codemap::span; + +import trans_common::*; +import trans::*; + +export trans_anon_obj; +export trans_obj; + +// trans_obj: creates 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::alias(false), ty: f.ty, ident: f.ident, id: f.id}]; + } + let fcx = new_fn_ctxt(cx, sp, llctor_decl); + + // Both regular arguments and type parameters are handled here. + create_llargs_for_fn_args(fcx, ast::proto_fn, none[ty::t], + ty::ret_ty_of_fn(ccx.tcx, ctor_id), fn_args, + ty_params); + let arg_tys: [ty::arg] = arg_tys_of_fn(ccx, ctor_id); + copy_args_to_allocas(fcx, fn_args, arg_tys); + + // 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; + + // 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 = + bcx.build.GEP(pair, ~[C_int(0), C_int(abi::obj_field_vtbl)]); + let pair_box = + bcx.build.GEP(pair, ~[C_int(0), C_int(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 = bcx.build.PointerCast(vtbl, T_ptr(T_empty_struct())); + + bcx.build.Store(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. + + // FIXME: What about inner_obj? Do we have to think about it here? + // (Pertains to issues #538/#539/#540/#543.) + + let llbox_ty: TypeRef = T_ptr(T_empty_struct()); + + if std::ivec::len[ast::ty_param](ty_params) == 0u && + std::ivec::len[ty::arg](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. + bcx.build.Store(C_null(llbox_ty), pair_box); + } else { + // Otherwise, we have to synthesize a big structural type for the + // object body. + let obj_fields: [ty::t] = ~[]; + for a: ty::arg in arg_tys { obj_fields += ~[a.ty]; } + + // Tuple type for fields: [field, ...] + let fields_ty: ty::t = ty::mk_imm_tup(ccx.tcx, obj_fields); + + let tydesc_ty = ty::mk_type(ccx.tcx); + let tps: [ty::t] = ~[]; + for tp: ast::ty_param in ty_params { tps += ~[tydesc_ty]; } + + // Tuple type for typarams: [typaram, ...] + let typarams_ty: ty::t = ty::mk_imm_tup(ccx.tcx, tps); + + // Tuple type for body: [tydesc_ty, [typaram, ...], [field, ...]] + let body_ty: ty::t = + ty::mk_imm_tup(ccx.tcx, ~[tydesc_ty, typarams_ty, fields_ty]); + + // Hand this type we've synthesized off to trans_malloc_boxed, which + // allocates a box, including space for a refcount. + 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. + + let body_tydesc = + GEP_tup_like(bcx, body_ty, body, + ~[0, abi::obj_body_elt_tydesc]); + bcx = body_tydesc.bcx; + let ti = none[@tydesc_info]; + let body_td = get_tydesc(bcx, body_ty, true, ti); + 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; + bcx.build.Store(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. + let body_typarams = + GEP_tup_like(bcx, body_ty, body, + ~[0, abi::obj_body_elt_typarams]); + bcx = body_typarams.bcx; + let i: int = 0; + for tp: ast::ty_param in ty_params { + let typaram = bcx.fcx.lltydescs.(i); + let capture = + GEP_tup_like(bcx, typarams_ty, body_typarams.val, ~[0, i]); + bcx = capture.bcx; + bcx = copy_val(bcx, INIT, capture.val, typaram, tydesc_ty).bcx; + i += 1; + } + + // Copy args into body fields. + let body_fields = + GEP_tup_like(bcx, body_ty, body, + ~[0, abi::obj_body_elt_fields]); + bcx = body_fields.bcx; + i = 0; + for f: ast::obj_field in ob.fields { + alt bcx.fcx.llargs.find(f.id) { + some(arg1) { + let arg = load_if_immediate(bcx, arg1, arg_tys.(i).ty); + let field = + GEP_tup_like(bcx, fields_ty, body_fields.val, ~[0, i]); + bcx = field.bcx; + bcx = copy_val(bcx, INIT, field.val, arg, arg_tys.(i).ty).bcx; + i += 1; + } + none. { + bcx_ccx(bcx).sess.span_fatal(f.ty.span, + "internal error in trans_obj"); + } + } + } + + // Store box ptr in outer pair. + let p = bcx.build.PointerCast(box.box, llbox_ty); + bcx.build.Store(p, pair_box); + } + bcx.build.RetVoid(); + + // 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) -> result { + + + 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_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: + std::ivec::map(ast::obj_field_from_anon_obj_field, + additional_fields), + 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 just adding new + // fields 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); + } + } + + // Allocate the object that we're going to return. + let pair = alloca(bcx, ccx.rust_object_type); + + // Take care of cleanups. + let t = node_id_type(ccx, id); + add_clean_temp(bcx, pair, t); + + // 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 = + bcx.build.GEP(pair, ~[C_int(0), C_int(abi::obj_field_vtbl)]); + let pair_box = + bcx.build.GEP(pair, ~[C_int(0), C_int(abi::obj_field_box)]); + + vtbl = bcx.build.PointerCast(vtbl, T_ptr(T_empty_struct())); + bcx.build.Store(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, fields, and a pointer to our inner_obj. + let llbox_ty: TypeRef = T_ptr(T_empty_struct()); + + if std::ivec::len[ast::anon_obj_field](additional_fields) == 0u && + anon_obj.inner_obj == none { + // If the object we're translating has no fields and no inner_obj, + // there's not much to do. + bcx.build.Store(C_null(llbox_ty), pair_box); + } else { + + // Synthesize a tuple type for fields: [field, ...] + let fields_ty: ty::t = ty::mk_imm_tup(ccx.tcx, additional_field_tys); + + // Type for tydescs. + let tydesc_ty: ty::t = ty::mk_type(ccx.tcx); + + // Placeholder for non-existent typarams, since anon objs don't have + // them. + let typarams_ty: ty::t = ty::mk_imm_tup(ccx.tcx, ~[]); + + // Tuple type for body: + // [tydesc, [typaram, ...], [field, ...], inner_obj] + let body_ty: ty::t = + ty::mk_imm_tup(ccx.tcx, + ~[tydesc_ty, typarams_ty, fields_ty, + inner_obj_ty]); + + // Hand this type we've synthesized off to trans_malloc_boxed, which + // allocates a box, including space for a refcount. + 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. + let body_tydesc = + GEP_tup_like(bcx, body_ty, body, + ~[0, abi::obj_body_elt_tydesc]); + bcx = body_tydesc.bcx; + let ti = none[@tydesc_info]; + let body_td = get_tydesc(bcx, body_ty, true, ti); + 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; + bcx.build.Store(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. + 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 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)).bcx; + 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. + let inner_obj_val: result = trans_expr(bcx, e); + + let body_inner_obj = + GEP_tup_like(bcx, body_ty, body, + ~[0, abi::obj_body_elt_inner_obj]); + bcx = body_inner_obj.bcx; + bcx = + copy_val(bcx, INIT, body_inner_obj.val, inner_obj_val.val, + inner_obj_ty).bcx; + } + } + + // Store box ptr in outer pair. + let p = bcx.build.PointerCast(box.box, llbox_ty); + bcx.build.Store(p, pair_box); + } + + // return the object we built. + ret rslt(bcx, pair); +} + +// 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.node.ident, b.node.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.node.ident, mb.node.ident); } + fwding_mthd(mb) { ret str::lteq(ma.node.ident, mb.ident); } + } + } + fwding_mthd(ma) { + alt b { + normal_mthd(mb) { ret str::lteq(ma.ident, mb.node.ident); } + fwding_mthd(mb) { ret str::lteq(ma.ident, mb.ident); } + } + } + } +} + +// 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.node.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 a vtable for an object being translated. Returns a pointer into +// read-only memory. +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. { + // Sort and process all the methods. + let meths = + std::sort::ivector::merge_sort[@ast::method] + (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) { + // If this vtable is being created for an extended object, then the + // vtable needs to contain 'forwarding slots' for methods that were on + // the original object and are not being overridden by the extended + // one. So, to find the set of methods that we need forwarding slots + // for, we need to take the set difference of inner_obj_methods + // (methods on the original object) and ob.methods (methods being + // added, whether entirely new or overriding). + + // inner_obj_ty is the type of the inner object being forwarded to, + // and "ob" is the wrapper object. We need to take apart + // inner_obj_ty, which is the type of the object being forwarded to + // (it had better have an object type with methods!) and put those + // original methods onto the list of methods we need forwarding + // methods for. + + let meths: [vtbl_mthd] = ~[]; + + // Gather up methods on the original object in 'meths'. + 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"); + } + } + + // Now, 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 = std::ivec::filter_map[vtbl_mthd, vtbl_mthd](f, meths); + + // 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::ivector::merge_sort[vtbl_mthd] + (bind vtbl_mthd_lteq(_, _), meths); + + 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)]; + } + } + } + } + } + + let vtbl = C_struct(llmethods); + let vtbl_name = mangle_internal_name_by_path(cx.ccx, cx.path + ~["vtbl"]); + let gvar = + llvm::LLVMAddGlobal(cx.ccx.llmod, val_ty(vtbl), str::buf(vtbl_name)); + llvm::LLVMSetInitializer(gvar, vtbl); + llvm::LLVMSetGlobalConstant(gvar, True); + llvm::LLVMSetLinkage(gvar, + lib::llvm::LLVMInternalLinkage as llvm::Linkage); + ret gvar; +} + +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, ~[])]; + } + + let vtbl = C_struct(llmethods); + let vtbl_name = + mangle_internal_name_by_path(cx.ccx, + cx.path + ~["backwarding_vtbl"]); + let gvar = + llvm::LLVMAddGlobal(cx.ccx.llmod, val_ty(vtbl), str::buf(vtbl_name)); + llvm::LLVMSetInitializer(gvar, vtbl); + llvm::LLVMSetGlobalConstant(gvar, True); + llvm::LLVMSetLinkage(gvar, + lib::llvm::LLVMInternalLinkage as llvm::Linkage); + + ret gvar; + +} + + +// 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 { + + // 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 backwarding function. + let fn_name: str = "backwarding_fn"; + let s: str = mangle_internal_name_by_path_and_seq(mcx.ccx, mcx.path, + fn_name); + + // Get the backwarding function's type and declare it. + let llbackwarding_fn_ty: TypeRef = + type_of_fn_full(cx.ccx, sp, m.proto, true, m.inputs, m.output, + std::ivec::len[ast::ty_param](ty_params)); + let llbackwarding_fn: ValueRef = + decl_internal_fastcall_fn(cx.ccx.llmod, s, llbackwarding_fn_ty); + + // Create a new function context and block context for the backwarding + // function, holding onto a pointer to the first block. + 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 = bcx.build.PointerCast( + fcx.llenv, + T_ptr(T_struct(~[cx.ccx.rust_object_type, + T_ptr(cx.ccx.rust_object_type)]))); + + let llself_obj_ptr = bcx.build.GEP(llenv, + ~[C_int(0), + C_int(1)]); + llself_obj_ptr = bcx.build.Load(llself_obj_ptr); + + // Cast it back to pointer-to-object-type, so LLVM won't complain. + llself_obj_ptr = bcx.build.PointerCast(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; + if ty::type_contains_params(cx.ccx.tcx, m.output) { + let llretty = type_of_inner(cx.ccx, sp, m.output); + llretptr = bcx.build.PointerCast(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 = ty::method_idx(cx.ccx.sess, sp, 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 = + bcx.build.GEP(llself_obj_ptr, + ~[C_int(0), C_int(abi::obj_field_vtbl)]); + llouter_obj_vtbl = bcx.build.Load(llouter_obj_vtbl); + llouter_obj_vtbl = bcx.build.PointerCast(llouter_obj_vtbl, vtbl_type); + + let llouter_mthd = + bcx.build.GEP(llouter_obj_vtbl, ~[C_int(0), C_int(ix as int)]); + + // Set up the outer method to be called. + let outer_mthd_ty = ty::method_ty_to_fn_ty(cx.ccx.tcx, *m); + let llouter_mthd_ty = + type_of_fn_full(bcx_ccx(bcx), sp, + ty::ty_fn_proto(bcx_tcx(bcx), outer_mthd_ty), true, + m.inputs, m.output, + std::ivec::len[ast::ty_param](ty_params)); + llouter_mthd = + bcx.build.PointerCast(llouter_mthd, T_ptr(T_ptr(llouter_mthd_ty))); + llouter_mthd = bcx.build.Load(llouter_mthd); + + // Set up the three implicit arguments to the outer method we'll need + // to call. + let self_arg = llself_obj_ptr; + let llouter_mthd_args: [ValueRef] = ~[llretptr, fcx.lltaskptr, self_arg]; + + // Copy the explicit arguments that are being passed into the forwarding + // function (they're in fcx.llargs) to llouter_mthd_args. + + let a: uint = 3u; // retptr, task ptr, env come first + let passed_arg: ValueRef = llvm::LLVMGetParam(llbackwarding_fn, a); + for arg: ty::arg in m.inputs { + if arg.mode == ty::mo_val { + passed_arg = load_if_immediate(bcx, passed_arg, arg.ty); + } + llouter_mthd_args += ~[passed_arg]; + a += 1u; + } + + // And, finally, call the outer method. + bcx.build.FastCall(llouter_mthd, llouter_mthd_args); + + bcx.build.RetVoid(); + 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 { + + // 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 forwarding function. + let fn_name: str = "forwarding_fn"; + let s: str = mangle_internal_name_by_path_and_seq(mcx.ccx, mcx.path, + fn_name); + + // Get the forwarding function's type and declare it. + let llforwarding_fn_ty: TypeRef = + type_of_fn_full(cx.ccx, sp, m.proto, true, m.inputs, m.output, + std::ivec::len[ast::ty_param](ty_params)); + let llforwarding_fn: ValueRef = + decl_internal_fastcall_fn(cx.ccx.llmod, s, llforwarding_fn_ty); + + // Create a new function context and block context for the forwarding + // function, holding onto a pointer to the first block. + 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; + if ty::type_contains_params(cx.ccx.tcx, m.output) { + let llretty = type_of_inner(cx.ccx, sp, m.output); + llretptr = bcx.build.PointerCast(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 = + bcx.build.GEP(llself_obj_ptr, ~[C_int(0), C_int(abi::obj_field_box)]); + llself_obj_box = bcx.build.Load(llself_obj_box); + + let ccx = bcx_ccx(bcx); + let llbox_ty = T_opaque_obj_ptr(*ccx); + llself_obj_box = bcx.build.PointerCast(llself_obj_box, llbox_ty); + + // Now, reach into the box and grab the body. + let llself_obj_body = + bcx.build.GEP(llself_obj_box, + ~[C_int(0), C_int(abi::box_rc_field_body)]); + + // Now, we need to figure out exactly what type the body is supposed to be + // cast to. + + // NB: This next part is almost flat-out copypasta from trans_anon_obj. + // It would be great to factor this out. + + // Synthesize a tuple type for fields: [field, ...] + let fields_ty: ty::t = ty::mk_imm_tup(cx.ccx.tcx, additional_field_tys); + + // Type for tydescs. + let tydesc_ty: ty::t = ty::mk_type(cx.ccx.tcx); + + // Placeholder for non-existent typarams, since anon objs don't have them. + let typarams_ty: ty::t = ty::mk_imm_tup(cx.ccx.tcx, ~[]); + + // Tuple type for body: + // [tydesc, [typaram, ...], [field, ...], inner_obj] + + let body_ty: ty::t = + ty::mk_imm_tup(cx.ccx.tcx, + ~[tydesc_ty, typarams_ty, fields_ty, inner_obj_ty]); + + // And cast to that type. + llself_obj_body = + bcx.build.PointerCast(llself_obj_body, + T_ptr(type_of(cx.ccx, sp, body_ty))); + + // Now, reach into the body and grab the inner_obj. + 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 = + bcx.build.GEP(llinner_obj.val, + ~[C_int(0), C_int(abi::obj_field_vtbl)]); + llinner_obj_vtbl = bcx.build.Load(llinner_obj_vtbl); + + let llinner_obj_body = + bcx.build.GEP(llinner_obj.val, + ~[C_int(0), C_int(abi::obj_field_box)]); + llinner_obj_body = bcx.build.Load(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 = ty::method_idx(cx.ccx.sess, sp, 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 = bcx.build.PointerCast(llinner_obj_vtbl, vtbl_type); + + let llorig_mthd = + bcx.build.GEP(llinner_obj_vtbl, ~[C_int(0), C_int(ix as int)]); + + // Set up the original method to be called. + let orig_mthd_ty = ty::method_ty_to_fn_ty(cx.ccx.tcx, *m); + let llorig_mthd_ty = + type_of_fn_full(bcx_ccx(bcx), sp, + ty::ty_fn_proto(bcx_tcx(bcx), orig_mthd_ty), true, + m.inputs, m.output, + std::ivec::len[ast::ty_param](ty_params)); + llorig_mthd = + bcx.build.PointerCast(llorig_mthd, T_ptr(T_ptr(llorig_mthd_ty))); + llorig_mthd = bcx.build.Load(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 = bcx.build.PointerCast(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, fcx.lltaskptr, 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 = 3u; // retptr, task ptr, env come first + let passed_arg: ValueRef = llvm::LLVMGetParam(llforwarding_fn, a); + for arg: ty::arg in m.inputs { + if arg.mode == ty::mo_val { + passed_arg = load_if_immediate(bcx, passed_arg, arg.ty); + } + llorig_mthd_args += ~[passed_arg]; + a += 1u; + } + + // And, finally, call the original (inner) method. + bcx.build.FastCall(llorig_mthd, llorig_mthd_args); + + bcx.build.RetVoid(); + finish_fn(fcx, lltop); + + ret llforwarding_fn; +} + +// 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(); + alt ty::struct(cx.ccx.tcx, node_id_type(cx.ccx, m.node.id)) { + ty::ty_fn(proto, inputs, output, _, _) { + llfnty = + type_of_fn_full(cx.ccx, m.span, proto, true, inputs, output, + std::ivec::len[ast::ty_param](ty_params)); + } + } + let mcx: @local_ctxt = + @{path: cx.path + ~["method", m.node.ident] with *cx}; + let s: str = mangle_internal_name_by_path(mcx.ccx, mcx.path); + let llfn: ValueRef = decl_internal_fastcall_fn(cx.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. + cx.ccx.item_ids.insert(m.node.id, llfn); + cx.ccx.item_symbols.insert(m.node.id, s); + trans_fn(mcx, m.span, m.node.meth, llfn, some(self_ty), ty_params, + m.node.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 = bcx.build.GEP(self_stack, + ~[C_int(0), + C_int(1)]); + bcx.build.Store(outer_obj, self_pair_ptr); + + // Drop in the backwarding vtbl. + let wrapper_pair = bcx.build.GEP(self_stack, + ~[C_int(0), + C_int(0)]); + let wrapper_vtbl_ptr = bcx.build.GEP(wrapper_pair, + ~[C_int(0), + C_int(0)]); + let backwarding_vtbl_cast = + bcx.build.PointerCast(backwarding_vtbl, T_ptr(T_empty_struct())); + bcx.build.Store(backwarding_vtbl_cast, wrapper_vtbl_ptr); + + // Drop in the inner obj body. + let wrapper_body_ptr = bcx.build.GEP(wrapper_pair, + ~[C_int(0), + C_int(1)]); + bcx.build.Store(inner_obj_body, wrapper_body_ptr); + + ret self_stack; +} diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc index 3181d3480718..8e174f3f736a 100644 --- a/src/comp/rustc.rc +++ b/src/comp/rustc.rc @@ -20,6 +20,7 @@ mod middle { mod trans_alt; mod trans_comm; mod trans_dps; + mod trans_objects; mod trans_vec; mod ty; mod ast_map;