about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLindsey Kuper <lkuper@mozilla.com>2011-08-09 16:42:55 -0700
committerLindsey Kuper <lkuper@mozilla.com>2011-08-09 16:42:55 -0700
commit5d9680fc7e97b57527c4a94d2bba32aeff4a195e (patch)
tree82982affd0f3ef19e8d81d7f2f89cf6d55e62fbd
parent61425c8d1356451845171230def89b6b83bef9a9 (diff)
downloadrust-5d9680fc7e97b57527c4a94d2bba32aeff4a195e.tar.gz
rust-5d9680fc7e97b57527c4a94d2bba32aeff4a195e.zip
Move object-system-related trans stuff to its own file.
-rw-r--r--src/comp/middle/trans.rs1011
-rw-r--r--src/comp/middle/trans_objects.rs1001
-rw-r--r--src/comp/rustc.rc1
3 files changed, 1005 insertions, 1008 deletions
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index b23388f290d..01a1101b369 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 00000000000..454a4f3b39a
--- /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 3181d348071..8e174f3f736 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;