Skip to content

Commit fa5a7e7

Browse files
committed
Simple anonymous objects get through translation.
1 parent 59a254a commit fa5a7e7

File tree

3 files changed

+105
-195
lines changed

3 files changed

+105
-195
lines changed

src/comp/middle/trans.rs

Lines changed: 67 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,18 @@ type stats =
113113
mutable uint n_null_glues,
114114
mutable uint n_real_glues);
115115

116+
// Crate context. Every crate we compile has one of these.
116117
type crate_ctxt =
117118
rec(session::session sess,
118119
ModuleRef llmod,
119120
target_data td,
120121
type_names tn,
121122
hashmap[str, ValueRef] externs,
122123
hashmap[str, ValueRef] intrinsics,
124+
125+
// A mapping from the def_id of each item in this crate to the address
126+
// of the first instruction of the item's definition in the executable
127+
// we're generating.
123128
hashmap[ast::def_id, ValueRef] item_ids,
124129
hashmap[ast::def_id, @ast::item] items,
125130
hashmap[ast::def_id, @ast::native_item] native_items,
@@ -5966,24 +5971,26 @@ fn recv_val(&@block_ctxt cx, ValueRef lhs, &@ast::expr rhs, &ty::t unit_ty,
59665971
59675972
*/
59685973

5969-
// trans_anon_obj: create (and return!) an LLVM function that is the object
5970-
// constructor for the anonymous object being translated.
5971-
//
5972-
// This code differs from trans_obj in that, rather than creating an object
5973-
// constructor function and putting it in the generated code as an object
5974-
// item, we are instead "inlining" the construction of the object.
5975-
fn trans_anon_obj(@block_ctxt cx, &span sp, &ast::anon_obj anon_obj,
5974+
// trans_anon_obj: create and return a pointer to an object. This code
5975+
// differs from trans_obj in that, rather than creating an object constructor
5976+
// function and putting it in the generated code as an object item, we are
5977+
// instead "inlining" the construction of the object and returning the object
5978+
// itself.
5979+
fn trans_anon_obj(@block_ctxt bcx, &span sp, &ast::anon_obj anon_obj,
59765980
&vec[ast::ty_param] ty_params, ast::def_id oid,
59775981
&ast::ann ann) -> result {
5978-
auto ccx = cx.fcx.lcx.ccx;
5979-
// A crate_ctxt has an item_ids hashmap, which has all of the def_ids of
5980-
// everything in the crate. By looking up a def_id, you can get the
5981-
// ValueRef of that item.
59825982

5983-
auto llctor_decl = ccx.item_ids.get(oid);
5983+
// Right now, we're assuming that anon objs don't take ty params, even
5984+
// though the AST supports it. It's nonsensical to write an expression
5985+
// like "obj[T](){ ... with ... }", since T is never instantiated;
5986+
// nevertheless, such an expression will parse. FIXME for the future:
5987+
// support typarams (issue #n).
5988+
assert vec::len(ty_params) == 0u;
5989+
5990+
auto ccx = bcx.fcx.lcx.ccx;
5991+
59845992
// If with_obj (the object being extended) exists, translate it, producing
59855993
// a result.
5986-
59875994
let option::t[result] with_obj_val = none[result];
59885995
alt (anon_obj.with_obj) {
59895996
case (none) { }
@@ -5992,81 +5999,44 @@ fn trans_anon_obj(@block_ctxt cx, &span sp, &ast::anon_obj anon_obj,
59925999
// value) wrapped in a result. We want to allocate space for this
59936000
// value in our outer object, then copy it into the outer object.
59946001

5995-
with_obj_val = some[result](trans_expr(cx, e));
6002+
with_obj_val = some[result](trans_expr(bcx, e));
59966003
}
59976004
}
5998-
// If the anonymous object we're translating adds any additional fields,
5999-
// they'll become the arguments to the function we're creating.
60006005

60016006
// FIXME (part of issue #417): all of the following code is copypasta from
60026007
// trans_obj for translating the anonymous wrapper object. Eventually we
60036008
// should abstract this code out of trans_anon_obj and trans_obj.
60046009

6005-
// For the anon obj's additional fields, if any exist, translate object
6006-
// constructor arguments to function arguments.
6007-
6008-
let vec[ast::obj_field] addtl_fields = [];
6009-
let vec[ast::arg] addtl_fn_args = [];
6010-
alt (anon_obj.fields) {
6011-
case (none) { }
6012-
case (some(?fields)) {
6013-
addtl_fields = fields;
6014-
for (ast::obj_field f in fields) {
6015-
addtl_fn_args +=
6016-
[rec(mode=ast::alias(false),
6017-
ty=f.ty,
6018-
ident=f.ident,
6019-
id=f.id)];
6020-
}
6021-
}
6022-
}
6023-
auto fcx = new_fn_ctxt(cx.fcx.lcx, sp, llctor_decl);
6024-
// Both regular arguments and type parameters are handled here.
6025-
6026-
create_llargs_for_fn_args(fcx, ast::proto_fn, none[ty_self_pair],
6027-
ret_ty_of_fn(ccx, ann), addtl_fn_args,
6028-
ty_params);
6029-
let vec[ty::arg] arg_tys = arg_tys_of_fn(ccx, ann);
6030-
copy_args_to_allocas(fcx, addtl_fn_args, arg_tys);
6031-
// Create the first block context in the function and keep a handle on it
6032-
// to pass to finish_fn later.
6033-
6034-
auto bcx = new_top_block_ctxt(fcx);
6035-
auto lltop = bcx.llbb;
6036-
// Pick up the type of this object by looking at our own output type, that
6037-
// is, the output type of the object constructor we're building.
6038-
6039-
auto self_ty = ret_ty_of_fn(ccx, ann);
6010+
auto self_ty = ty::ann_to_type(ccx.tcx, ann);
60406011
auto llself_ty = type_of(ccx, sp, self_ty);
6041-
// Set up the two-word pair that we're going to return from the object
6042-
// constructor we're building. The two elements of this pair will be a
6043-
// vtable pointer and a body pointer. (llretptr already points to the
6044-
// place where this two-word pair should go; it was pre-allocated by the
6045-
// caller of the function.)
60466012

6047-
auto pair = bcx.fcx.llretptr;
6013+
// Allocate the object that we're going to return. It's a two-word pair
6014+
// containing a vtable pointer and a body pointer.
6015+
auto pair = alloca(bcx, llself_ty);
6016+
60486017
// Grab onto the first and second elements of the pair.
60496018
// abi::obj_field_vtbl and abi::obj_field_box simply specify words 0 and 1
60506019
// of 'pair'.
6051-
60526020
auto pair_vtbl =
60536021
bcx.build.GEP(pair, [C_int(0), C_int(abi::obj_field_vtbl)]);
60546022
auto pair_box =
60556023
bcx.build.GEP(pair, [C_int(0), C_int(abi::obj_field_box)]);
6056-
// Make a vtable for this object: a static array of pointers to functions.
6057-
// It will be located in the read-only memory of the executable we're
6058-
// creating and will contain ValueRefs for all of this object's methods.
6059-
// create_vtbl returns a pointer to the vtable, which we store.
60606024

6061-
// create_vtbl() wants an ast::_obj and all we have is an
6062-
// ast::anon_obj, so we need to roll our own.
6025+
// Make a vtable for the outer object. create_vtbl() wants an ast::_obj
6026+
// and all we have is an ast::anon_obj, so we need to roll our own.
6027+
let vec[ast::obj_field] addtl_fields = [];
6028+
alt (anon_obj.fields) {
6029+
case (none) { }
6030+
case (some(?fields)) { addtl_fields = fields; }
6031+
}
6032+
let ast::_obj wrapper_obj = rec(
6033+
fields = addtl_fields,
6034+
methods = anon_obj.methods,
6035+
dtor = none[@ast::method]);
6036+
6037+
auto vtbl = create_vtbl(bcx.fcx.lcx, llself_ty, self_ty, wrapper_obj,
6038+
ty_params);
60636039

6064-
let ast::_obj wrapper_obj =
6065-
rec(fields=addtl_fields,
6066-
methods=anon_obj.methods,
6067-
dtor=none[@ast::method]);
6068-
auto vtbl =
6069-
create_vtbl(cx.fcx.lcx, llself_ty, self_ty, wrapper_obj, ty_params);
60706040
bcx.build.Store(vtbl, pair_vtbl);
60716041
// FIXME (part of issue #417): This vtable needs to contain "forwarding
60726042
// slots" for the methods that exist in the with_obj, as well. How do we
@@ -6080,127 +6050,29 @@ fn trans_anon_obj(@block_ctxt cx, &span sp, &ast::anon_obj anon_obj,
60806050
// also have to fill in the with_obj field of this tuple.
60816051

60826052
let TypeRef llbox_ty = T_opaque_obj_ptr(ccx.tn);
6083-
// FIXME: we should probably also allocate a box for empty objs that have
6084-
// a dtor, since otherwise they are never dropped, and the dtor never
6085-
// runs.
6086-
6087-
if (vec::len[ast::ty_param](ty_params) == 0u &&
6088-
vec::len[ty::arg](arg_tys) == 0u) {
6089-
// If the object we're translating has no fields or type parameters,
6090-
// there's not much to do.
6091-
6092-
// Store null into pair, if no args or typarams.
6093-
6094-
bcx.build.Store(C_null(llbox_ty), pair_box);
6095-
} else {
6096-
// Otherwise, we have to synthesize a big structural type for the
6097-
// object body.
6098-
6099-
let vec[ty::t] obj_fields = [];
6100-
for (ty::arg a in arg_tys) { vec::push[ty::t](obj_fields, a.ty); }
6101-
// Tuple type for fields: [field, ...]
6053+
6054+
alt (anon_obj.fields) {
6055+
case (none) {
6056+
// If the object we're translating has no fields or type
6057+
// parameters, there's not much to do.
61026058

6103-
let ty::t fields_ty = ty::mk_imm_tup(ccx.tcx, obj_fields);
6104-
// Tuple type for typarams: [typaram, ...]
6059+
// Store null into pair, if no args or typarams.
6060+
bcx.build.Store(C_null(llbox_ty), pair_box);
61056061

6106-
auto tydesc_ty = ty::mk_type(ccx.tcx);
6107-
let vec[ty::t] tps = [];
6108-
for (ast::ty_param tp in ty_params) {
6109-
vec::push[ty::t](tps, tydesc_ty);
61106062
}
6111-
let ty::t typarams_ty = ty::mk_imm_tup(ccx.tcx, tps);
6112-
// Tuple type for body: [tydesc_ty, [typaram, ...], [field, ...]]
6113-
6114-
let ty::t body_ty =
6115-
ty::mk_imm_tup(ccx.tcx, [tydesc_ty, typarams_ty, fields_ty]);
6116-
// Hand this type we've synthesized off to trans_malloc_boxed, which
6117-
// allocates a box, including space for a refcount.
61186063

6119-
auto box = trans_malloc_boxed(bcx, body_ty);
6120-
bcx = box.bcx;
6121-
// mk_imm_box throws a refcount into the type we're synthesizing, so
6122-
// that it looks like: [rc, [tydesc_ty, [typaram, ...], [field, ...]]]
6123-
6124-
let ty::t boxed_body_ty = ty::mk_imm_box(ccx.tcx, body_ty);
6125-
// Grab onto the refcount and body parts of the box we allocated.
6126-
6127-
auto rc =
6128-
GEP_tup_like(bcx, boxed_body_ty, box.val,
6129-
[0, abi::box_rc_field_refcnt]);
6130-
bcx = rc.bcx;
6131-
auto body =
6132-
GEP_tup_like(bcx, boxed_body_ty, box.val,
6133-
[0, abi::box_rc_field_body]);
6134-
bcx = body.bcx;
6135-
bcx.build.Store(C_int(1), rc.val);
6136-
// Put together a tydesc for the body, so that the object can later be
6137-
// freed by calling through its tydesc.
6138-
6139-
// Every object (not just those with type parameters) needs to have a
6140-
// tydesc to describe its body, since all objects have unknown type to
6141-
// the user of the object. So the tydesc is needed to keep track of
6142-
// the types of the object's fields, so that the fields can be freed
6143-
// later.
6144-
6145-
auto body_tydesc =
6146-
GEP_tup_like(bcx, body_ty, body.val,
6147-
[0, abi::obj_body_elt_tydesc]);
6148-
bcx = body_tydesc.bcx;
6149-
auto ti = none[@tydesc_info];
6150-
auto body_td = get_tydesc(bcx, body_ty, true, ti);
6151-
lazily_emit_tydesc_glue(bcx, abi::tydesc_field_drop_glue, ti);
6152-
lazily_emit_tydesc_glue(bcx, abi::tydesc_field_free_glue, ti);
6153-
bcx = body_td.bcx;
6154-
bcx.build.Store(body_td.val, body_tydesc.val);
6155-
// Copy the object's type parameters and fields into the space we
6156-
// allocated for the object body. (This is something like saving the
6157-
// lexical environment of a function in its closure: the "captured
6158-
// typarams" are any type parameters that are passed to the object
6159-
// constructor and are then available to the object's methods.
6160-
// Likewise for the object's fields.)
6161-
6162-
// Copy typarams into captured typarams.
6163-
6164-
auto body_typarams =
6165-
GEP_tup_like(bcx, body_ty, body.val,
6166-
[0, abi::obj_body_elt_typarams]);
6167-
bcx = body_typarams.bcx;
6168-
let int i = 0;
6169-
for (ast::ty_param tp in ty_params) {
6170-
auto typaram = bcx.fcx.lltydescs.(i);
6171-
auto capture =
6172-
GEP_tup_like(bcx, typarams_ty, body_typarams.val, [0, i]);
6173-
bcx = capture.bcx;
6174-
bcx = copy_val(bcx, INIT, capture.val, typaram, tydesc_ty).bcx;
6175-
i += 1;
6176-
}
6177-
// Copy args into body fields.
6064+
case (some(?fields)) {
6065+
// For the moment let's pretend that there are no additional
6066+
// fields.
6067+
bcx.fcx.lcx.ccx.sess.unimpl("anon objs don't support "
6068+
+ "adding fields yet");
61786069

6179-
auto body_fields =
6180-
GEP_tup_like(bcx, body_ty, body.val,
6181-
[0, abi::obj_body_elt_fields]);
6182-
bcx = body_fields.bcx;
6183-
i = 0;
6184-
for (ast::obj_field f in wrapper_obj.fields) {
6185-
auto arg = bcx.fcx.llargs.get(f.id);
6186-
arg = load_if_immediate(bcx, arg, arg_tys.(i).ty);
6187-
auto field =
6188-
GEP_tup_like(bcx, fields_ty, body_fields.val, [0, i]);
6189-
bcx = field.bcx;
6190-
bcx = copy_val(bcx, INIT, field.val, arg, arg_tys.(i).ty).bcx;
6191-
i += 1;
6070+
// FIXME (issue #417): drop these fields into the newly created
6071+
// object.
61926072
}
6193-
// Store box ptr in outer pair.
6194-
6195-
auto p = bcx.build.PointerCast(box.val, llbox_ty);
6196-
bcx.build.Store(p, pair_box);
61976073
}
6198-
bcx.build.RetVoid();
6199-
// Insert the mandatory first few basic blocks before lltop.
62006074

6201-
finish_fn(fcx, lltop);
62026075
// Return the object we built.
6203-
62046076
ret res(bcx, pair);
62056077
}
62066078

@@ -6657,6 +6529,10 @@ fn arg_tys_of_fn(&@crate_ctxt ccx, ast::ann ann) -> vec[ty::arg] {
66576529
fn ret_ty_of_fn_ty(&@crate_ctxt ccx, ty::t t) -> ty::t {
66586530
alt (ty::struct(ccx.tcx, t)) {
66596531
case (ty::ty_fn(_, _, ?ret_ty, _, _)) { ret ret_ty; }
6532+
case (_) {
6533+
ccx.sess.bug("ret_ty_of_fn_ty() called on non-function type: "
6534+
+ ty_to_str(ccx.tcx, t));
6535+
}
66606536
}
66616537
}
66626538

@@ -6806,6 +6682,10 @@ fn create_vtbl(@local_ctxt cx, TypeRef llself_ty, ty::t self_ty,
68066682
let str s = mangle_internal_name_by_path(mcx.ccx, mcx.path);
68076683
let ValueRef llfn =
68086684
decl_internal_fastcall_fn(cx.ccx.llmod, s, llfnty);
6685+
6686+
// Every method on an object gets its def_id inserted into the
6687+
// crate-wide item_ids map, together with the ValueRef that points to
6688+
// where that method's definition will be in the executable.
68096689
cx.ccx.item_ids.insert(m.node.id, llfn);
68106690
cx.ccx.item_symbols.insert(m.node.id, s);
68116691
trans_fn(mcx, m.span, m.node.meth, llfn,
@@ -7156,6 +7036,11 @@ fn trans_item(@local_ctxt cx, &ast::item item) {
71567036
}
71577037
}
71587038

7039+
// Translate a module. Doing this amounts to translating the items in the
7040+
// module; there ends up being no artifact (aside from linkage names) of
7041+
// separate modules in the compiled program. That's because modules exist
7042+
// only as a convenience for humans working with the code, to organize names
7043+
// and control visibility.
71597044
fn trans_mod(@local_ctxt cx, &ast::_mod m) {
71607045
for (@ast::item item in m.items) { trans_item(cx, *item); }
71617046
}

src/test/run-pass/anon-objs.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,30 @@
55
use std;
66

77
fn main() {
8+
89
obj a() {
9-
fn foo() -> int { ret 2; }
10-
fn bar() -> int { ret self.foo(); }
10+
fn foo() -> int {
11+
ret 2;
12+
}
13+
fn bar() -> int {
14+
ret self.foo();
15+
}
1116
}
17+
1218
auto my_a = a();
19+
1320
// Extending an object with a new method
21+
auto my_b = obj {
22+
fn baz() -> int {
23+
ret self.foo();
24+
}
25+
with my_a
26+
};
1427

15-
auto my_b = anon obj;
1628
// Extending an object with a new field
29+
auto my_c = obj(int quux) { with my_a } ;
1730

18-
auto my_c = anon obj;
1931
// Should this be legal?
20-
21-
auto my_d = anon obj;
22-
}
32+
auto my_d = obj() { with my_a } ;
33+
34+
}

0 commit comments

Comments
 (0)