about summary refs log tree commit diff
path: root/src/comp
diff options
context:
space:
mode:
authorLindsey Kuper <lkuper@mozilla.com>2011-06-09 18:15:55 -0700
committerLindsey Kuper <lkuper@mozilla.com>2011-06-09 18:16:28 -0700
commitc51c6ba35463d4d48eae0ea805e03ba5ab1ed84e (patch)
tree0f31fe7403abd9cd87dc3795c53dd307868ed045 /src/comp
parent40e3a9fcbcd6d9ebb98bc29729b1ef1b4dbec95c (diff)
downloadrust-c51c6ba35463d4d48eae0ea805e03ba5ab1ed84e.tar.gz
rust-c51c6ba35463d4d48eae0ea805e03ba5ab1ed84e.zip
Sketching trans_anon_obj.
Diffstat (limited to 'src/comp')
-rw-r--r--src/comp/middle/trans.rs226
1 files changed, 219 insertions, 7 deletions
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 0846f597103..2ce915d0fc6 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);
+
+    // 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);
+
+    //  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();
 
-    // TODO: everything else.
+    // Insert the mandatory first few basic blocks before lltop.
+    finish_fn(fcx, lltop);
 
-    cx.fcx.lcx.ccx.sess.unimpl("support for anonymous objects");
+    // Return the object we built.
+    ret res(bcx, pair);
 }
 
 fn init_local(&@block_ctxt cx, &@ast::local local) -> result {