From c51c6ba35463d4d48eae0ea805e03ba5ab1ed84e Mon Sep 17 00:00:00 2001 From: Lindsey Kuper Date: Thu, 9 Jun 2011 18:15:55 -0700 Subject: [PATCH] Sketching trans_anon_obj. --- src/comp/middle/trans.rs | 226 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 219 insertions(+), 7 deletions(-) diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 0846f5971031..2ce915d0fc63 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -5701,7 +5701,7 @@ fn trans_expr_out(&@block_ctxt cx, &@ast::expr e, out_method output) } case (ast::expr_anon_obj(?anon_obj, ?tps, ?odid, ?ann)) { - ret trans_anon_obj(cx, e.span, anon_obj, tps, odid, ann); + ret trans_anon_obj(cx, e.span, anon_obj, tps, odid.ctor, ann); } case (_) { @@ -6352,12 +6352,27 @@ fn recv_val(&@block_ctxt cx, ValueRef lhs, &@ast::expr rhs, wrapped inner object. */ -fn trans_anon_obj(&@block_ctxt cx, &span sp, + +// trans_anon_obj: create (and return!) an LLVM function that is the object +// constructor for the anonymous object being translated. +// +// 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. +fn trans_anon_obj(@block_ctxt cx, &span sp, &ast::anon_obj anon_obj, &vec[ast::ty_param] ty_params, - &ast::obj_def_ids oid, + ast::def_id oid, &ast::ann ann) -> result { + auto ccx = cx.fcx.lcx.ccx; + // A crate_ctxt has an item_ids hashmap, which has all of the def_ids of + // everything in the crate. By looking up a def_id, you can get the + // ValueRef of that item. + auto llctor_decl = ccx.item_ids.get(oid); + + // If with_obj (the object being extended) exists, translate it, producing + // a result. let option::t[result] with_obj_val = none[result]; alt (anon_obj.with_obj) { case (none) { } @@ -6369,25 +6384,222 @@ fn trans_anon_obj(&@block_ctxt cx, &span sp, } } + // If the anonymous object we're translating adds any additional fields, + // they'll become the arguments to the function we're creating. + + // FIXME (part of issue #417): all of the following code is copypasta from + // trans_obj for translating the anonymous wrapper object. Eventually we + // should abstract this code out of trans_anon_obj and trans_obj. + // For the anon obj's additional fields, if any exist, translate object // constructor arguments to function arguments. - let option::t[vec[ast::obj_field]] addtl_fields - = none[vec[ast::obj_field]]; + let vec[ast::obj_field] addtl_fields = []; let vec[ast::arg] addtl_fn_args = []; alt (anon_obj.fields) { case (none) { } case (some(?fields)) { + addtl_fields = fields; for (ast::obj_field f in fields) { addtl_fn_args += [rec(mode=ast::alias, ty=f.ty, ident=f.ident, id=f.id)]; } } } + auto fcx = new_fn_ctxt(cx.fcx.lcx, sp, llctor_decl); - // TODO: everything else. + // Both regular arguments and type parameters are handled here. + create_llargs_for_fn_args(fcx, ast::proto_fn, + none[ty_self_pair], + ret_ty_of_fn(ccx, ann), + addtl_fn_args, ty_params); + + let vec[ty::arg] arg_tys = arg_tys_of_fn(ccx, ann); + copy_args_to_allocas(fcx, addtl_fn_args, arg_tys); - cx.fcx.lcx.ccx.sess.unimpl("support for anonymous objects"); + // Create the first block context in the function and keep a handle on it + // to pass to finish_fn later. + auto bcx = new_top_block_ctxt(fcx); + auto 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. + auto self_ty = ret_ty_of_fn(ccx, ann); + auto llself_ty = type_of(ccx, sp, self_ty); + + // 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.) + auto 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'. + auto pair_vtbl = bcx.build.GEP(pair, + [C_int(0), + C_int(abi::obj_field_vtbl)]); + auto 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. + + // create_vtbl() wants an ast::_obj and all we have is an + // ast::anon_obj, so we need to roll our own. + let ast::_obj wrapper_obj = rec( + fields = addtl_fields, + methods = anon_obj.methods, + dtor = none[@ast::method]); + + auto vtbl = create_vtbl(cx.fcx.lcx, llself_ty, self_ty, wrapper_obj, + ty_params); + bcx.build.Store(vtbl, pair_vtbl); + + // FIXME (part of issue #417): This vtable needs to contain "forwarding + // slots" for the methods that exist in the with_obj, as well. How do we + // do that? + + // 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 (part of issue #417): Because this is an anonymous object, we + // also have to fill in the with_obj field of this tuple. + + let TypeRef llbox_ty = T_opaque_obj_ptr(ccx.tn); + + // FIXME: we should probably also allocate a box for empty objs that have + // a dtor, since otherwise they are never dropped, and the dtor never + // runs. + if (vec::len[ast::ty_param](ty_params) == 0u && + vec::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 vec[ty::t] obj_fields = []; + for (ty::arg a in arg_tys) { + vec::push[ty::t](obj_fields, a.ty); + } + + // Tuple type for fields: [field, ...] + let ty::t fields_ty = ty::mk_imm_tup(ccx.tcx, obj_fields); + + // Tuple type for typarams: [typaram, ...] + auto tydesc_ty = ty::mk_type(ccx.tcx); + let vec[ty::t] tps = []; + for (ast::ty_param tp in ty_params) { + vec::push[ty::t](tps, tydesc_ty); + } + let ty::t typarams_ty = ty::mk_imm_tup(ccx.tcx, tps); + + // Tuple type for body: [tydesc_ty, [typaram, ...], [field, ...]] + let ty::t body_ty = 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. + auto box = trans_malloc_boxed(bcx, body_ty); + bcx = box.bcx; + + // mk_imm_box throws a refcount into the type we're synthesizing, so + // that it looks like: [rc, [tydesc_ty, [typaram, ...], [field, ...]]] + let ty::t boxed_body_ty = ty::mk_imm_box(ccx.tcx, body_ty); + + // Grab onto the refcount and body parts of the box we allocated. + auto rc = GEP_tup_like(bcx, boxed_body_ty, box.val, + [0, + abi::box_rc_field_refcnt]); + bcx = rc.bcx; + + auto body = GEP_tup_like(bcx, boxed_body_ty, box.val, + [0, abi::box_rc_field_body]); + bcx = body.bcx; + + bcx.build.Store(C_int(1), rc.val); + + // 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. + auto body_tydesc = + GEP_tup_like(bcx, body_ty, body.val, + [0, abi::obj_body_elt_tydesc]); + bcx = body_tydesc.bcx; + + auto ti = none[@tydesc_info]; + auto 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. + auto body_typarams = + GEP_tup_like(bcx, body_ty, body.val, + [0, abi::obj_body_elt_typarams]); + bcx = body_typarams.bcx; + let int i = 0; + for (ast::ty_param tp in ty_params) { + auto typaram = bcx.fcx.lltydescs.(i); + auto 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. + auto body_fields = + GEP_tup_like(bcx, body_ty, body.val, + [0, abi::obj_body_elt_fields]); + bcx = body_fields.bcx; + + i = 0; + for (ast::obj_field f in wrapper_obj.fields) { + auto arg = bcx.fcx.llargs.get(f.id); + arg = load_if_immediate(bcx, arg, arg_tys.(i).ty); + auto 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; + } + // Store box ptr in outer pair. + auto p = bcx.build.PointerCast(box.val, llbox_ty); + bcx.build.Store(p, pair_box); + } + bcx.build.RetVoid(); + + // Insert the mandatory first few basic blocks before lltop. + finish_fn(fcx, lltop); + + // Return the object we built. + ret res(bcx, pair); } fn init_local(&@block_ctxt cx, &@ast::local local) -> result {