Monomorphize class constructors, support generic classes and class methods

Allow class methods to have type parameters (this is a change from the
original classes proposal).

Add test cases for classes with type parameters, and classes with methods
that have their own type parameters.
This commit is contained in:
Tim Chevalier 2012-04-02 10:48:32 -07:00
parent 4f4b7b10bb
commit 1f892dcb01
11 changed files with 217 additions and 118 deletions

View File

@ -2062,11 +2062,24 @@ fn parse_item_res(p: parser, attrs: [ast::attribute]) -> @ast::item {
attrs);
}
// Instantiates ident <i> with references to <typarams> as arguments
fn ident_to_path_tys(p: parser, i: ast::ident,
typarams: [ast::ty_param]) -> @ast::path {
let s = p.last_span;
let p_: ast::path_ = {global: false, idents: [i],
types: vec::map(typarams,
{|tp| @{id: p.get_id(),
node: ast::ty_path(ident_to_path(s, tp.ident),
p.get_id()),
span: s}})};
@spanned(s.lo, s.hi, p_)
}
fn parse_item_class(p: parser, attrs: [ast::attribute]) -> @ast::item {
let lo = p.last_span.lo;
let class_name = parse_value_ident(p);
let class_path = ident_to_path(p.last_span, class_name);
let ty_params = parse_ty_params(p);
let class_path = ident_to_path_tys(p, class_name, ty_params);
expect(p, token::LBRACE);
let mut ms: [@ast::class_member] = [];
let ctor_id = p.get_id();
@ -2104,15 +2117,16 @@ fn parse_item_class(p: parser, attrs: [ast::attribute]) -> @ast::item {
enum class_contents { ctor_decl(ast::fn_decl, ast::blk, codemap::span),
members([@ast::class_member]) }
fn parse_class_item(p:parser, class_name:@ast::path) -> class_contents {
fn parse_class_item(p:parser, class_name_with_tps:@ast::path)
-> class_contents {
if eat_word(p, "new") {
let lo = p.last_span.lo;
// Can ctors have attrs?
// result type is always the type of the class
let decl_ = parse_fn_decl(p, ast::impure_fn);
let decl = {output: @{id: p.get_id(),
node: ast::ty_path(class_name, p.get_id()),
span: decl_.output.span}
node: ast::ty_path(class_name_with_tps, p.get_id()),
span: decl_.output.span}
with decl_};
let body = parse_block(p);
ret ctor_decl(decl, body, ast_util::mk_sp(lo, p.last_span.hi));

View File

@ -189,10 +189,26 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
let did = ast_util::def_id_of_def(cx.tcx.def_map.get(e.id));
ty::lookup_item_type(cx.tcx, did).bounds
}
expr_field(_, _, _) {
expr_field(base, _, _) {
alt cx.method_map.get(e.id) {
typeck::method_static(did) {
ty::lookup_item_type(cx.tcx, did).bounds
/*
If this is a class method, we want to use the
class bounds plus the method bounds -- otherwise the
indices come out wrong. So we check base's type...
*/
let mut bounds = ty::lookup_item_type(cx.tcx, did).bounds;
alt ty::get(ty::node_id_to_type(cx.tcx, base.id)).struct {
ty::ty_class(parent_id, ts) {
/* ...and if it has a class type, prepend the
class bounds onto the method bounds */
bounds =
@(*ty::lookup_item_type(cx.tcx, parent_id).bounds
+ *bounds);
}
_ { }
}
bounds
}
typeck::method_param(ifce_id, n_mth, _, _) |
typeck::method_iface(ifce_id, n_mth) {

View File

@ -950,7 +950,7 @@ fn iter_structural_ty(cx: block, av: ValueRef, t: ty::t,
ty::ty_class(did, tps) {
// a class is like a record type
let mut i: int = 0;
for vec::each(ty::class_items_as_fields(cx.tcx(), did)) {|fld|
for vec::each(ty::class_items_as_fields(cx.tcx(), did, tps)) {|fld|
let llfld_a = GEPi(cx, av, [0, i]);
cx = f(cx, llfld_a, fld.mt.ty);
i += 1;
@ -1983,8 +1983,13 @@ fn monomorphic_fn(ccx: @crate_ctxt, fn_id: ast::def_id, real_substs: [ty::t],
set_inline_hint(lldecl);
trans_res_ctor(ccx, pt, decl, fn_id.node, psubsts, lldecl);
}
ast::item_class(_, _, ctor) {
ccx.sess.unimpl("monomorphic class constructor");
ast::item_class(tps, _, ctor) {
set_inline_hint_if_appr(i.attrs, lldecl);
let tp_tys: [ty::t] = ty::ty_params_to_tys(ccx.tcx, tps);
trans_class_ctor(ccx, pt, ctor.node.dec, ctor.node.body, lldecl,
option::get_or_default(psubsts,
{tys:tp_tys, vtables: none, bounds: @[]}),
fn_id.node, i.id, ctor.span);
}
}
}
@ -2238,7 +2243,8 @@ fn trans_rec_field_inner(bcx: block, val: ValueRef, ty: ty::t,
field: ast::ident, sp: span) -> lval_result {
let fields = alt ty::get(ty).struct {
ty::ty_rec(fs) { fs }
ty::ty_class(did,_) { ty::class_items_as_fields(bcx.tcx(), did) }
ty::ty_class(did,ts) {
ty::class_items_as_fields(bcx.tcx(), did, ts) }
// Constraint?
_ { bcx.tcx().sess.span_bug(sp, "trans_rec_field:\
base expr has non-record type"); }
@ -4255,6 +4261,61 @@ fn trans_const(ccx: @crate_ctxt, e: @ast::expr, id: ast::node_id) {
llvm::LLVMSetGlobalConstant(g, True);
}
fn trans_class_ctor(ccx: @crate_ctxt, path: path, decl: ast::fn_decl,
body: ast::blk, llctor_decl: ValueRef,
psubsts: param_substs, ctor_id: ast::node_id,
parent_id: ast::node_id, sp: span) {
// Add ctor to the ctor map
ccx.class_ctors.insert(ctor_id, parent_id);
// Translate the ctor
// Set up the type for the result of the ctor
// kludgy -- this wouldn't be necessary if the typechecker
// special-cased constructors, then we could just look up
// the ctor's return type.
let rslt_ty = ty::mk_class(ccx.tcx, local_def(parent_id),
psubsts.tys);
// Make the fn context
let fcx = new_fn_ctxt_w_id(ccx, path, llctor_decl, ctor_id,
some(psubsts), some(sp));
// FIXME: need to substitute into the fn arg types too?
create_llargs_for_fn_args(fcx, no_self, decl.inputs);
let mut bcx_top = top_scope_block(fcx, some(sp));
let lltop = bcx_top.llbb;
bcx_top = copy_args_to_allocas(fcx, bcx_top, decl.inputs,
ty::ty_fn_args(node_id_type(bcx_top, ctor_id)));
// We *don't* want self to be passed to the ctor -- that
// wouldn't make sense
// So we initialize it here
let selfptr = alloc_ty(bcx_top, rslt_ty);
// initialize fields to zero
let fields = ty::class_items_as_fields(bcx_top.tcx(),
local_def(parent_id),
psubsts.tys);
let mut bcx = bcx_top;
// Initialize fields to zero so init assignments can validly
// drop their LHS
for field in fields {
let ix = field_idx_strict(bcx.tcx(), sp, field.ident, fields);
bcx = zero_alloca(bcx, GEPi(bcx, selfptr, [0, ix]),
field.mt.ty);
}
// note we don't want to take *or* drop self.
fcx.llself = some({v: selfptr, t: rslt_ty});
// Translate the body of the ctor
bcx = trans_block(bcx_top, body, ignore);
let lval_res = {bcx: bcx, val: selfptr, kind: owned};
// Generate the return expression
bcx = store_temp_expr(bcx, INIT, fcx.llretptr, lval_res,
rslt_ty, true);
cleanup_and_leave(bcx, none, some(fcx.llreturn));
Unreachable(bcx);
finish_fn(fcx, lltop);
}
fn trans_item(ccx: @crate_ctxt, item: ast::item) {
let _icx = ccx.insn_ctxt("trans_item");
let path = alt check ccx.tcx.items.get(item.id) {
@ -4322,62 +4383,15 @@ fn trans_item(ccx: @crate_ctxt, item: ast::item) {
native::trans_native_mod(ccx, native_mod, abi);
}
ast::item_class(tps, items, ctor) {
// FIXME factor our ctor translation, call from monomorphic_fn
let llctor_decl = get_item_val(ccx, ctor.node.id);
// Add ctor to the ctor map
ccx.class_ctors.insert(ctor.node.id, item.id);
// Translate the ctor
// Set up the type for the result of the ctor
// kludgy -- this wouldn't be necessary if the typechecker
// special-cased constructors, then we could just look up
// the ctor's return type.
let ty_args = vec::from_fn(tps.len(), {|i|
ty::mk_param(ccx.tcx, i, local_def(tps[i].id))
});
let rslt_ty = ty::mk_class(ccx.tcx,
local_def(item.id),
ty_args);
// Make the fn context
let fcx = new_fn_ctxt_w_id(ccx, *path, llctor_decl, ctor.node.id,
// substs?
none, some(ctor.span));
create_llargs_for_fn_args(fcx, no_self, ctor.node.dec.inputs);
let mut bcx_top = top_scope_block(fcx, some(ctor.span));
let lltop = bcx_top.llbb;
bcx_top = copy_args_to_allocas(fcx, bcx_top, ctor.node.dec.inputs,
ty::ty_fn_args(node_id_type(bcx_top, ctor.node.id)));
// We *don't* want self to be passed to the ctor -- that
// wouldn't make sense
// So we initialize it here
let selfptr = alloc_ty(bcx_top, rslt_ty);
// initialize fields to zero
let fields = ty::class_items_as_fields(bcx_top.tcx(),
local_def(item.id));
let mut bcx = bcx_top;
// Initialize fields to zero so init assignments can validly
// drop their LHS
for field in fields {
let ix = field_idx_strict(bcx.tcx(), ctor.span, field.ident,
fields);
bcx = zero_alloca(bcx, GEPi(bcx, selfptr, [0, ix]),
field.mt.ty);
if tps.len() == 0u {
let psubsts = {tys: ty::ty_params_to_tys(ccx.tcx, tps),
vtables: none,
bounds: @[]};
trans_class_ctor(ccx, *path, ctor.node.dec, ctor.node.body,
get_item_val(ccx, ctor.node.id), psubsts,
ctor.node.id, item.id, ctor.span);
}
// note we don't want to take *or* drop self.
fcx.llself = some({v: selfptr, t: rslt_ty});
// Translate the body of the ctor
bcx = trans_block(bcx_top, ctor.node.body, ignore);
let lval_res = {bcx: bcx, val: selfptr, kind: owned};
// Generate the return expression
bcx = store_temp_expr(bcx, INIT, fcx.llretptr, lval_res,
rslt_ty, true);
cleanup_and_leave(bcx, none, some(fcx.llreturn));
Unreachable(bcx);
finish_fn(fcx, lltop);
// If there are ty params, the ctor will get monomorphized
// Translate methods
let (_, ms) = ast_util::split_class_items(items);

View File

@ -369,10 +369,10 @@ fn shape_of(ccx: @crate_ctxt, t: ty::t, ty_param_map: [uint]) -> [u8] {
s
}
ty::ty_iface(_, _) { [shape_box_fn] }
ty::ty_class(did, _) {
ty::ty_class(did, ts) {
// same as records
let mut s = [shape_struct], sub = [];
for f:field in ty::class_items_as_fields(ccx.tcx, did) {
for f:field in ty::class_items_as_fields(ccx.tcx, did, ts) {
sub += shape_of(ccx, f.mt.ty, ty_param_map);
}
add_substr(s, sub);

View File

@ -81,11 +81,11 @@ fn type_of(cx: @crate_ctxt, t: ty::t) -> TypeRef {
}
ty::ty_opaque_closure_ptr(_) { T_opaque_box_ptr(cx) }
ty::ty_constr(subt,_) { type_of(cx, subt) }
ty::ty_class(did, _) {
ty::ty_class(did, ts) {
// only instance vars are record fields at runtime
let fields = lookup_class_fields(cx.tcx, did);
let tys = vec::map(fields) {|f|
let t = ty::lookup_field_type(cx.tcx, did, f.id);
let t = ty::lookup_field_type(cx.tcx, did, f.id, ts);
type_of(cx, t)
};
T_struct(tys)

View File

@ -1,4 +1,4 @@
// Determines the ways in which a generic function body is dependant
// Determines the ways in which a generic function body depends
// on its type parameters. Used to aggressively reuse compiled
// function bodies for different types.
@ -80,7 +80,7 @@ fn type_uses_for(ccx: @crate_ctxt, fn_id: def_id, n_tps: uint)
}
}
ast_map::node_ctor(@{node: item_class(_, _, ctor), _}, _) {
ccx.sess.unimpl("type uses in class constructor");
handle_body(cx, ctor.node.body);
}
}
let uses = vec::from_mut(cx.uses);

View File

@ -197,6 +197,20 @@ type pred_args = spanned<pred_args_>;
// for this local.
type constr_arg_use = spanned<constr_arg_general_<inst>>;
/*
A constraint is either an init constraint, referring to the initialization
state of a variable (not initialized, definitely initialized, or maybe
initialized) or a predicate constraint, referring to the truth value of a
predicate on variables (definitely false, maybe true, or definitely true).
cinit and ninit represent init constraints, while cpred and npred
represent predicate constraints.
In a predicate constraint, the <path> field (and the <def_id> field
in the npred constructor) names a user-defined function that may
be the operator in a "check" expression in the source.
*/
enum constraint {
cinit(uint, span, ident),

View File

@ -82,7 +82,7 @@ export ty_vec, mk_vec, type_is_vec;
export ty_nil, mk_nil, type_is_nil;
export ty_iface, mk_iface;
export ty_res, mk_res;
export ty_param, mk_param;
export ty_param, mk_param, ty_params_to_tys;
export ty_ptr, mk_ptr, mk_mut_ptr, mk_imm_ptr, mk_nil_ptr, type_is_unsafe_ptr;
export ty_rptr, mk_rptr;
export ty_rec, mk_rec;
@ -742,6 +742,10 @@ fn fold_ty(cx: ctxt, fld: fold_mode, ty_0: t) -> t {
ty_constr(subty, cs) {
ty = mk_constr(cx, do_fold(cx, fld, subty, under_rptr), cs);
}
ty_class(did, ts) {
ty = mk_class(cx, did, vec::map(ts, {|t|
do_fold(cx, fld, t, under_rptr)}));
}
_ {
cx.sess.bug("unsupported sort of type in fold_ty");
}
@ -886,8 +890,8 @@ fn type_needs_drop(cx: ctxt, ty: t) -> bool {
for f in flds { if type_needs_drop(cx, f.mt.ty) { accum = true; } }
accum
}
ty_class(did,_) {
for f in ty::class_items_as_fields(cx, did)
ty_class(did, ts) {
for f in ty::class_items_as_fields(cx, did, ts)
{ if type_needs_drop(cx, f.mt.ty) { accum = true; } }
accum
}
@ -1996,7 +2000,8 @@ fn substitute_type_params(cx: ctxt, substs: [ty::t], typ: t) -> t {
fn def_has_ty_params(def: ast::def) -> bool {
alt def {
ast::def_fn(_, _) | ast::def_variant(_, _) { true }
ast::def_fn(_, _) | ast::def_variant(_, _) | ast::def_class(_)
{ true }
_ { false }
}
}
@ -2166,9 +2171,6 @@ fn enum_variant_with_id(cx: ctxt, enum_id: ast::def_id,
// If the given item is in an external crate, looks up its type and adds it to
// the type cache. Returns the type parameters and type.
fn lookup_item_type(cx: ctxt, did: ast::def_id) -> ty_param_bounds_and_ty {
/*
Are we putting class ids in the tcache (where does that happen?)
*/
alt cx.tcache.find(did) {
some(tpt) { ret tpt; }
none {
@ -2184,23 +2186,26 @@ fn lookup_item_type(cx: ctxt, did: ast::def_id) -> ty_param_bounds_and_ty {
}
// Look up a field ID, whether or not it's local
fn lookup_field_type(tcx: ctxt, class_id: def_id, id: def_id) -> ty::t {
if id.crate == ast::local_crate {
// Takes a list of type substs in case the class is generic
fn lookup_field_type(tcx: ctxt, class_id: def_id, id: def_id,
substs: [ty::t]) -> ty::t {
let t = if id.crate == ast::local_crate {
node_id_to_type(tcx, id.node)
}
else {
alt tcx.tcache.find(id) {
some(tpt) { ret tpt.ty; }
some(tpt) { tpt.ty }
none {
let tpt = csearch::get_field_type(tcx, class_id, id);
// ok b/c fields are monomorphic
// TODO: Comment might be a lie, what if it mentions
// class-bound ty params?
tcx.tcache.insert(id, tpt);
ret tpt.ty;
tpt.ty
}
}
}
};
substitute_type_params(tcx, substs, t)
}
// Look up the list of field names and IDs for a given class
@ -2298,13 +2303,16 @@ fn class_field_tys(items: [@class_member]) -> [field_ty] {
// Return a list of fields corresponding to the class's items
// (as if the class was a record). trans uses this
fn class_items_as_fields(cx:ctxt, did: ast::def_id) -> [field] {
// Takes a list of substs with which to instantiate field types
fn class_items_as_fields(cx:ctxt, did: ast::def_id, substs: [ty::t])
-> [field] {
let mut rslt = [];
for f in lookup_class_fields(cx, did) {
// consider all instance vars mut, because the
// constructor may mutate all vars
rslt += [{ident: f.ident, mt: {ty: lookup_field_type(cx, did, f.id),
mutbl: m_mutbl}}];
rslt += [{ident: f.ident, mt:
{ty: lookup_field_type(cx, did, f.id, substs),
mutbl: m_mutbl}}];
}
rslt
}
@ -2408,7 +2416,11 @@ fn ast_constr_to_constr<T>(tcx: ctxt, c: @ast::constr_general<T>) ->
}
}
fn ty_params_to_tys(tcx: ty::ctxt, tps: [ast::ty_param]) -> [t] {
vec::from_fn(tps.len(), {|i|
ty::mk_param(tcx, i, ast_util::local_def(tps[i].id))
})
}
// Local Variables:
// mode: rust
// fill-column: 78;

View File

@ -218,7 +218,7 @@ fn instantiate_path(fcx: @fn_ctxt, pth: @ast::path,
(sp, "this item does not take type parameters");
} else if ty_substs_len > ty_param_count {
fcx.ccx.tcx.sess.span_fatal
(sp, "too many type parameter provided for this item");
(sp, "too many type parameters provided for this item");
} else if ty_substs_len < ty_param_count {
fcx.ccx.tcx.sess.span_fatal
(sp, "not enough type parameters provided for this item");
@ -399,7 +399,7 @@ fn ast_ty_to_ty(tcx: ty::ctxt, mode: mode, &&ast_ty: @ast::ty) -> ty::t {
path_to_str(path))); }
some(d) { d }};
alt a_def {
ast::def_ty(did) {
ast::def_ty(did) | ast::def_class(did) {
instantiate(tcx, ast_ty.span, mode, did,
id, path.node.types)
}
@ -433,28 +433,6 @@ fn ast_ty_to_ty(tcx: ty::ctxt, mode: mode, &&ast_ty: @ast::ty) -> ty::t {
}
}
}
ast::def_class(class_id) {
if class_id.crate == ast::local_crate {
alt tcx.items.find(class_id.node) {
some(ast_map::node_item(
@{node: ast::item_class(tps, _, _), _}, _)) {
if vec::len(tps) != vec::len(path.node.types) {
tcx.sess.span_err(ast_ty.span, "incorrect number \
of type parameters to object type");
}
ty::mk_class(tcx, class_id, vec::map(path.node.types,
{|ast_ty| ast_ty_to_ty(tcx, mode, ast_ty)}))
}
_ {
tcx.sess.span_bug(ast_ty.span, #fmt("class id is \
unbound in items"));
}
}
}
else {
getter(tcx, mode, class_id).ty
}
}
_ {
tcx.sess.span_fatal(ast_ty.span,
"found type name used as a variable");
@ -2196,7 +2174,8 @@ fn lookup_method_inner_(tcx: ty::ctxt, ms: [ty::method],
tcx.sess.span_fatal(
sp, "can not call a method that contains a \
self type through a boxed iface");
} else if (*m.tps).len() > 0u {
} else if (*m.tps).len() > 0u &&
alt parent { an_iface(_) { true } cls(_) { false } } {
tcx.sess.span_fatal(
sp, "can not call a generic method through a \
boxed iface");
@ -2276,8 +2255,10 @@ fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr,
}
}
ty::ty_class(did, tps) {
alt lookup_method_inner_(tcx, *ty::iface_methods(tcx, did), tps,
cls(did), name, expr.span, include_private) {
alt lookup_method_inner_(tcx, *ty::iface_methods(tcx, did),
/* Need to include class tps so that the
indices for ty params work out right */
tps, cls(did), name, expr.span, include_private) {
some(r) { ret some(r); }
none { }
}
@ -2361,9 +2342,10 @@ fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr,
// Only for fields! Returns <none> for methods>
// FIXME: privacy flags
fn lookup_field_ty(tcx: ty::ctxt, class_id: ast::def_id,
items:[ty::field_ty], fieldname: ast::ident) -> option<ty::t> {
items:[ty::field_ty], fieldname: ast::ident, substs: [ty::t])
-> option<ty::t> {
option::map(vec::find(items, {|f| f.ident == fieldname}),
{|f| ty::lookup_field_type(tcx, class_id, f.id) })
{|f| ty::lookup_field_type(tcx, class_id, f.id, substs) })
}
/*
@ -3236,7 +3218,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
_ {}
}
}
ty::ty_class(base_id, _params) {
ty::ty_class(base_id, params) {
// This is just for fields -- the same code handles
// methods in both classes and ifaces
@ -3255,7 +3237,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
else {
lookup_public_fields(tcx, base_id)
};
alt lookup_field_ty(tcx, base_id, cls_items, field) {
alt lookup_field_ty(tcx, base_id, cls_items, field, params) {
some(field_ty) {
// (2) look up what field's type is, and return it
// FIXME: actually instantiate any type params

View File

@ -0,0 +1,28 @@
class cat<U> {
priv {
let mut info : [U];
let mut meows : uint;
}
let how_hungry : int;
new(in_x : uint, in_y : int, -in_info: [U])
{ self.meows = in_x; self.how_hungry = in_y;
self.info <- in_info; }
fn speak<T>(stuff: [T]) {
self.meows += stuff.len();
}
fn meow_count() -> uint { self.meows }
}
fn main() {
let nyan : cat<int> = cat::<int>(52u, 99, [9]);
let kitty = cat(1000u, 2, ["tabby"]);
assert(nyan.how_hungry == 99);
assert(kitty.how_hungry == 2);
nyan.speak([1,2,3]);
assert(nyan.meow_count() == 55u);
kitty.speak(["meow", "mew", "purr", "chirp"]);
assert(kitty.meow_count() == 1004u);
}

View File

@ -0,0 +1,19 @@
class cat<U> {
priv {
let mut meows : uint;
}
let how_hungry : int;
new(in_x : uint, in_y : int) { self.meows = in_x; self.how_hungry = in_y; }
fn speak() {
self.meows += 1u;
}
fn meow_count() -> uint { self.meows }
}
fn main() {
let _nyan : cat<int> = cat::<int>(52u, 99);
// let kitty = cat(1000u, 2);
}