Implement an initial version of placement new.

This commit is contained in:
Niko Matsakis 2012-03-14 15:16:46 -04:00
parent ba39e277e2
commit cfcbec3cc3
22 changed files with 266 additions and 87 deletions

View File

@ -434,8 +434,8 @@ fn for_each_package(c: cargo, b: fn(source, package)) {
// Runs all programs in directory <buildpath>
fn run_programs(buildpath: str) {
let new = os::list_dir(buildpath);
for ct: str in new {
let newv = os::list_dir(buildpath);
for ct: str in newv {
run::run_program(ct, []);
}
}
@ -471,9 +471,9 @@ fn install_one_crate(c: cargo, path: str, cf: str) {
none { ret; }
some(bp) { bp }
};
let new = os::list_dir(buildpath);
let newv = os::list_dir(buildpath);
let exec_suffix = os::exe_suffix();
for ct: str in new {
for ct: str in newv {
if (exec_suffix != "" && str::ends_with(ct, exec_suffix)) ||
(exec_suffix == "" && !str::starts_with(path::basename(ct),
"lib")) {

View File

@ -488,23 +488,23 @@ fn file_might_not_converge(filename: str) -> bool {
fn check_roundtrip_convergence(code: @str, maxIters: uint) {
let i = 0u;
let new = code;
let old = code;
let newv = code;
let oldv = code;
while i < maxIters {
old = new;
if content_might_not_converge(*old) { ret; }
new = @parse_and_print(old);
if old == new { break; }
oldv = newv;
if content_might_not_converge(*oldv) { ret; }
newv = @parse_and_print(oldv);
if oldv == newv { break; }
i += 1u;
}
if old == new {
if oldv == newv {
#error("Converged after %u iterations", i);
} else {
#error("Did not converge after %u iterations!", i);
write_file("round-trip-a.rs", *old);
write_file("round-trip-b.rs", *new);
write_file("round-trip-a.rs", *oldv);
write_file("round-trip-b.rs", *newv);
run::run_program("diff",
["-w", "-u", "round-trip-a.rs",
"round-trip-b.rs"]);

View File

@ -266,30 +266,30 @@ fn normalize(p: path) -> path {
#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
#[cfg(target_os = "freebsd")]
fn reabsolute(orig: path, new: path) -> path {
fn reabsolute(orig: path, n: path) -> path {
if path_is_absolute(orig) {
path_sep() + new
path_sep() + n
} else {
new
n
}
}
#[cfg(target_os = "win32")]
fn reabsolute(orig: path, new: path) -> path {
fn reabsolute(orig: path, newp: path) -> path {
if path_is_absolute(orig) && orig[0] == consts::path_sep as u8 {
str::from_char(consts::path_sep) + new
str::from_char(consts::path_sep) + newp
} else {
new
newp
}
}
fn reterminate(orig: path, new: path) -> path {
fn reterminate(orig: path, newp: path) -> path {
let last = orig[str::len(orig) - 1u];
if last == consts::path_sep as u8
|| last == consts::path_sep as u8 {
ret new + path_sep();
ret newp + path_sep();
} else {
ret new;
ret newp;
}
}
}

View File

@ -468,22 +468,22 @@ Concatenate a vector of vectors.
Flattens a vector of vectors of T into a single vector of T.
"]
fn concat<T: copy>(v: [const [const T]]) -> [T] {
let mut new: [T] = [];
for inner: [T] in v { new += inner; }
ret new;
let mut r: [T] = [];
for inner: [T] in v { r += inner; }
ret r;
}
#[doc = "
Concatenate a vector of vectors, placing a given separator between each
"]
fn connect<T: copy>(v: [const [const T]], sep: T) -> [T] {
let mut new: [T] = [];
let mut r: [T] = [];
let mut first = true;
for inner: [T] in v {
if first { first = false; } else { push(new, sep); }
new += inner;
if first { first = false; } else { push(r, sep); }
r += inner;
}
ret new;
ret r;
}
#[doc = "Reduce a vector from left to right"]

View File

@ -2308,6 +2308,9 @@ fn resolve_impl_in_expr(e: @env, x: @ast::expr, sc: iscopes, v: vt<iscopes>) {
ast::expr_assign_op(_, _, _) | ast::expr_index(_, _) {
e.impl_map.insert(x.id, sc);
}
ast::expr_new(p, _, _) {
e.impl_map.insert(p.id, sc);
}
_ {}
}
visit::visit_expr(x, sc, v);

View File

@ -1261,9 +1261,9 @@ fn trans_unary(bcx: block, op: ast::unop, e: @ast::expr,
some(origin) {
let callee_id = ast_util::op_expr_callee_id(un_expr);
let fty = node_id_type(bcx, callee_id);
ret trans_call_inner(bcx, fty, {|bcx|
ret trans_call_inner(bcx, fty, expr_ty(bcx, un_expr), {|bcx|
impl::trans_method_callee(bcx, callee_id, e, origin)
}, [], un_expr.id, dest);
}, arg_exprs([]), dest);
}
_ {}
}
@ -1450,10 +1450,10 @@ fn trans_assign_op(bcx: block, ex: @ast::expr, op: ast::binop,
some(origin) {
let callee_id = ast_util::op_expr_callee_id(ex);
let fty = node_id_type(bcx, callee_id);
ret trans_call_inner(bcx, fty, {|bcx|
ret trans_call_inner(bcx, fty, expr_ty(bcx, ex), {|bcx|
// FIXME provide the already-computed address, not the expr
impl::trans_method_callee(bcx, callee_id, dst, origin)
}, [src], ex.id, save_in(lhs_res.val));
}, arg_exprs([src]), save_in(lhs_res.val));
}
_ {}
}
@ -1561,9 +1561,9 @@ fn trans_binary(bcx: block, op: ast::binop, lhs: @ast::expr,
some(origin) {
let callee_id = ast_util::op_expr_callee_id(ex);
let fty = node_id_type(bcx, callee_id);
ret trans_call_inner(bcx, fty, {|bcx|
ret trans_call_inner(bcx, fty, expr_ty(bcx, ex), {|bcx|
impl::trans_method_callee(bcx, callee_id, lhs, origin)
}, [rhs], ex.id, dest);
}, arg_exprs([rhs]), dest);
}
_ {}
}
@ -2498,6 +2498,10 @@ fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
ret rslt(bcx, val);
}
enum call_args {
arg_exprs([@ast::expr]),
arg_vals([ValueRef])
}
// NB: must keep 4 fns in sync:
//
@ -2505,12 +2509,12 @@ fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
// - create_llargs_for_fn_args.
// - new_fn_ctxt
// - trans_args
fn trans_args(cx: block, llenv: ValueRef, es: [@ast::expr], fn_ty: ty::t,
fn trans_args(cx: block, llenv: ValueRef, args: call_args, fn_ty: ty::t,
dest: dest, generic_intrinsic: bool)
-> {bcx: block, args: [ValueRef], retslot: ValueRef} {
let temp_cleanups = [];
let args = ty::ty_fn_args(fn_ty);
let arg_tys = ty::ty_fn_args(fn_ty);
let llargs: [ValueRef] = [];
let ccx = cx.ccx();
@ -2546,13 +2550,21 @@ fn trans_args(cx: block, llenv: ValueRef, es: [@ast::expr], fn_ty: ty::t,
// First we figure out the caller's view of the types of the arguments.
// This will be needed if this is a generic call, because the callee has
// to cast her view of the arguments to the caller's view.
let arg_tys = type_of_explicit_args(ccx, args);
let i = 0u;
for e: @ast::expr in es {
let r = trans_arg_expr(bcx, args[i], arg_tys[i], e, temp_cleanups);
bcx = r.bcx;
llargs += [r.val];
i += 1u;
alt args {
arg_exprs(es) {
let llarg_tys = type_of_explicit_args(ccx, arg_tys);
let i = 0u;
for e: @ast::expr in es {
let r = trans_arg_expr(bcx, arg_tys[i], llarg_tys[i],
e, temp_cleanups);
bcx = r.bcx;
llargs += [r.val];
i += 1u;
}
}
arg_vals(vs) {
llargs += vs;
}
}
// now that all arguments have been successfully built, we can revoke any
@ -2568,15 +2580,15 @@ fn trans_args(cx: block, llenv: ValueRef, es: [@ast::expr], fn_ty: ty::t,
}
fn trans_call(in_cx: block, f: @ast::expr,
args: [@ast::expr], id: ast::node_id, dest: dest)
args: call_args, id: ast::node_id, dest: dest)
-> block {
trans_call_inner(in_cx, expr_ty(in_cx, f),
{|cx| trans_callee(cx, f)}, args, id, dest)
trans_call_inner(in_cx, expr_ty(in_cx, f), node_id_type(in_cx, id),
{|cx| trans_callee(cx, f)}, args, dest)
}
fn trans_call_inner(in_cx: block, fn_expr_ty: ty::t,
fn trans_call_inner(in_cx: block, fn_expr_ty: ty::t, ret_ty: ty::t,
get_callee: fn(block) -> lval_maybe_callee,
args: [@ast::expr], id: ast::node_id, dest: dest)
args: call_args, dest: dest)
-> block {
with_scope(in_cx, "call") {|cx|
let f_res = get_callee(cx);
@ -2603,9 +2615,10 @@ fn trans_call_inner(in_cx: block, fn_expr_ty: ty::t,
}
};
let ret_ty = node_id_type(bcx, id);
let args_res = trans_args(bcx, llenv, args, fn_expr_ty, dest,
option::is_some(f_res.tds));
let args_res = {
trans_args(bcx, llenv, args, fn_expr_ty, dest,
option::is_some(f_res.tds))
};
bcx = args_res.bcx;
let llargs = args_res.args;
option::may(f_res.tds) {|vals|
@ -2923,7 +2936,7 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
}
ast::expr_cast(val, _) { ret trans_cast(bcx, val, e.id, dest); }
ast::expr_call(f, args, _) {
ret trans_call(bcx, f, args, e.id, dest);
ret trans_call(bcx, f, arg_exprs(args), e.id, dest);
}
ast::expr_field(base, _, _) {
if dest == ignore { ret trans_expr(bcx, base, ignore); }
@ -2937,9 +2950,9 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
let origin = bcx.ccx().maps.method_map.get(e.id);
let callee_id = ast_util::op_expr_callee_id(e);
let fty = node_id_type(bcx, callee_id);
ret trans_call_inner(bcx, fty, {|bcx|
ret trans_call_inner(bcx, fty, expr_ty(bcx, e), {|bcx|
impl::trans_method_callee(bcx, callee_id, base, origin)
}, [idx], e.id, dest);
}, arg_exprs([idx]), dest);
}
// These return nothing
@ -3037,6 +3050,45 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
assert dest == ignore;
ret trans_assign_op(bcx, e, op, dst, src);
}
ast::expr_new(pool, alloc_id, val) {
// First, call pool->alloc(sz, align) to get back a void*. Then, cast
// this memory to the required type and evaluate value into it.
let ccx = bcx.ccx();
// Allocate space for the ptr that will be returned from
// `pool.alloc()`:
let ptr_ty = expr_ty(bcx, e);
let {bcx, val: ptr_ptr_val} = alloc_ty(bcx, ptr_ty);
#debug["ptr_ty = %s", ty_to_str(tcx, ptr_ty)];
#debug["ptr_ptr_val = %s", val_str(ccx.tn, ptr_ptr_val)];
let void_ty = ty::mk_ptr(tcx, {ty: ty::mk_nil(tcx),
mutbl: ast::m_imm});
let voidval = {
let llvoid_ty = type_of(ccx, void_ty);
PointerCast(bcx, ptr_ptr_val, T_ptr(llvoid_ty))
};
#debug["voidval = %s", val_str(ccx.tn, voidval)];
let llval_ty = type_of(ccx, expr_ty(bcx, val));
let args = [llsize_of(ccx, llval_ty), llalign_of(ccx, llval_ty)];
let origin = bcx.ccx().maps.method_map.get(alloc_id);
let bcx = trans_call_inner(
bcx,
node_id_type(bcx, alloc_id),
void_ty,
{|bcx| impl::trans_method_callee(bcx, alloc_id, pool, origin) },
arg_vals(args),
save_in(voidval));
#debug["dest = %s", dest_str(ccx, dest)];
let ptr_val = Load(bcx, ptr_ptr_val);
#debug["ptr_val = %s", val_str(ccx.tn, ptr_val)];
let bcx = trans_expr(bcx, val, save_in(ptr_val));
store_in_dest(bcx, ptr_val, dest)
}
_ { bcx.tcx().sess.span_bug(e.span, "trans_expr reached \
fall-through case"); }

View File

@ -111,27 +111,28 @@ fn classify_ty(ty: TypeRef) -> [x86_64_reg_class] {
}
}
fn unify(cls: [mut x86_64_reg_class], i: uint,
new: x86_64_reg_class) {
if cls[i] == new {
fn unify(cls: [mut x86_64_reg_class],
i: uint,
newv: x86_64_reg_class) {
if cls[i] == newv {
ret;
} else if cls[i] == no_class {
cls[i] = new;
} else if new == no_class {
cls[i] = newv;
} else if newv == no_class {
ret;
} else if cls[i] == memory_class || new == memory_class {
} else if cls[i] == memory_class || newv == memory_class {
cls[i] = memory_class;
} else if cls[i] == integer_class || new == integer_class {
} else if cls[i] == integer_class || newv == integer_class {
cls[i] = integer_class;
} else if cls[i] == x87_class ||
cls[i] == x87up_class ||
cls[i] == complex_x87_class ||
new == x87_class ||
new == x87up_class ||
new == complex_x87_class {
newv == x87_class ||
newv == x87up_class ||
newv == complex_x87_class {
cls[i] = memory_class;
} else {
cls[i] = new;
cls[i] = newv;
}
}

View File

@ -168,6 +168,9 @@ fn mark_for_expr(cx: ctx, e: @expr) {
expr_log(_, _, val) {
node_type_needs(cx, use_tydesc, val.id);
}
expr_new(_, _, v) {
node_type_needs(cx, use_repr, v.id);
}
expr_for(_, _, _) | expr_do_while(_, _) | expr_alt(_, _, _) |
expr_block(_) | expr_if(_, _, _) | expr_while(_, _) |
expr_fail(_) | expr_break | expr_cont | expr_unary(_, _) |

View File

@ -183,14 +183,14 @@ fn set_poststate(a: ts_ann, p: poststate) -> bool {
// Set all the bits in p that are set in new
fn extend_prestate(p: prestate, new: poststate) -> bool {
ret tritv_union(p, new);
fn extend_prestate(p: prestate, newv: poststate) -> bool {
ret tritv_union(p, newv);
}
// Set all the bits in p that are set in new
fn extend_poststate(p: poststate, new: poststate) -> bool {
ret tritv_union(p, new);
fn extend_poststate(p: poststate, newv: poststate) -> bool {
ret tritv_union(p, newv);
}
// Sets the given bit in p to "don't care"

View File

@ -709,8 +709,8 @@ fn find_instances(_fcx: fn_ctxt, subst: subst, c: constraint) ->
for d: pred_args in *descs {
if args_mention(d.node.args, find_in_subst_bool, subst) {
let old_bit_num = d.node.bit_num;
let new = replace(subst, d);
alt find_instance_(new, *descs) {
let newv = replace(subst, d);
alt find_instance_(newv, *descs) {
some(d1) { rslt += [{from: old_bit_num, to: d1}]; }
_ { }
}
@ -753,7 +753,7 @@ fn replace(subst: subst, d: pred_args) -> [constr_arg_general_<inst>] {
alt c.node {
carg_ident(p) {
alt find_in_subst(p.node, subst) {
some(new) { rslt += [carg_ident(new)]; }
some(newv) { rslt += [carg_ident(newv)]; }
_ { rslt += [c.node]; }
}
}

View File

@ -325,6 +325,9 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) {
clear_pp(rslt);
handle_var(fcx, rslt, e.id, path_to_ident(p));
}
expr_new(p, _, v) {
find_pre_post_exprs(fcx, [p, v], e.id);
}
expr_log(_, lvl, arg) {
find_pre_post_exprs(fcx, [lvl, arg], e.id);
}

View File

@ -355,6 +355,9 @@ fn find_pre_post_state_expr(fcx: fn_ctxt, pres: prestate, e: @expr) -> bool {
let num_constrs = num_constraints(fcx.enclosing);
alt e.node {
expr_new(p, _, v) {
ret find_pre_post_state_two(fcx, pres, p, v, e.id, oper_pure);
}
expr_vec(elts, _) {
ret find_pre_post_state_exprs(fcx, pres, e.id,
vec::from_elem(vec::len(elts),

View File

@ -155,8 +155,8 @@ fn trit_and(a: trit, b: trit) -> trit {
// a and b were both dont_care
}
fn change(changed: bool, old: trit, new: trit) -> bool {
changed || new != old
fn change(changed: bool, old: trit, newv: trit) -> bool {
changed || newv != old
}
fn tritv_difference(p1: t, p2: t) -> bool {
@ -166,9 +166,9 @@ fn tritv_difference(p1: t, p2: t) -> bool {
let changed = false;
while i < sz {
let old = tritv_get(p1, i);
let new = trit_minus(old, tritv_get(p2, i));
changed = change(changed, old, new);
tritv_set(i, p1, new);
let newv = trit_minus(old, tritv_get(p2, i));
changed = change(changed, old, newv);
tritv_set(i, p1, newv);
i += 1u;
}
ret changed;
@ -181,9 +181,9 @@ fn tritv_union(p1: t, p2: t) -> bool {
let changed = false;
while i < sz {
let old = tritv_get(p1, i);
let new = trit_or(old, tritv_get(p2, i));
changed = change(changed, old, new);
tritv_set(i, p1, new);
let newv = trit_or(old, tritv_get(p2, i));
changed = change(changed, old, newv);
tritv_set(i, p1, newv);
i += 1u;
}
ret changed;
@ -196,9 +196,9 @@ fn tritv_intersect(p1: t, p2: t) -> bool {
let changed = false;
while i < sz {
let old = tritv_get(p1, i);
let new = trit_and(old, tritv_get(p2, i));
changed = change(changed, old, new);
tritv_set(i, p1, new);
let newv = trit_and(old, tritv_get(p2, i));
changed = change(changed, old, newv);
tritv_set(i, p1, newv);
i += 1u;
}
ret changed;

View File

@ -1888,6 +1888,12 @@ fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr,
origin: method_origin,
self_sub: option<self_subst>}> {
let tcx = fcx.ccx.tcx;
#debug["lookup_method_inner: expr=%s name=%s ty=%s",
expr_to_str(expr),
name,
ty_to_str(fcx.ccx.tcx, ty)];
// First, see whether this is an interface-bounded parameter
alt ty::get(ty).struct {
ty::ty_param(n, did) {
@ -2870,6 +2876,51 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
}
}
}
ast::expr_new(p, alloc_id, v) {
bot |= check_expr(fcx, p);
bot |= check_expr(fcx, v);
let p_ty = expr_ty(tcx, p);
alt lookup_method(fcx, p, alloc_id, "alloc", p_ty, []) {
some(origin) {
fcx.ccx.method_map.insert(alloc_id, origin);
// Check that the alloc() method has the expected type, which
// should be fn(sz: uint, align: uint) -> *().
let expected_ty = {
let ty_uint = ty::mk_uint(tcx);
let ty_nilp = ty::mk_ptr(tcx, {ty: ty::mk_nil(tcx),
mutbl: ast::m_imm});
let m = ast::expl(ty::default_arg_mode_for_ty(ty_uint));
ty::mk_fn(tcx, {proto: ast::proto_any,
inputs: [{mode: m, ty: ty_uint},
{mode: m, ty: ty_uint}],
output: ty_nilp,
ret_style: ast::return_val,
constraints: []})
};
demand::simple(fcx, expr.span,
expected_ty, node_id_to_type(tcx, alloc_id));
}
none {
let t_err = resolve_type_vars_if_possible(fcx, p_ty);
let msg = #fmt["no `alloc()` method found for type `%s`",
ty_to_str(tcx, t_err)];
tcx.sess.span_err(expr.span, msg);
}
}
// The region value must have a type like &r.T. The resulting
// memory will be allocated into the region `r`.
let pool_region = region_of(fcx, p);
let v_ty = expr_ty(tcx, v);
let res_ty = ty::mk_rptr(tcx, pool_region, {ty: v_ty,
mutbl: ast::m_imm});
write_ty(tcx, expr.id, res_ty);
}
}
if bot { write_ty(tcx, expr.id, ty::mk_bot(tcx)); }

View File

@ -325,6 +325,10 @@ enum expr_ {
expr_be(@expr),
expr_log(int, @expr, @expr),
expr_new(/* arena */ @expr,
/* id for the alloc() call */ node_id,
/* value */ @expr),
/* just an assert, no significance to typestate */
expr_assert(@expr),

View File

@ -382,6 +382,11 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ {
let fold_mac = bind fold_mac_(_, fld);
ret alt e {
expr_new(p, i, v) {
expr_new(fld.fold_expr(p),
fld.new_id(i),
fld.fold_expr(v))
}
expr_vec(exprs, mutt) {
expr_vec(fld.map_exprs(fld.fold_expr, exprs), mutt)
}

View File

@ -150,7 +150,7 @@ fn bad_expr_word_table() -> hashmap<str, ()> {
"enum", "export", "fail", "fn", "for", "if", "iface",
"impl", "import", "let", "log", "loop", "mod", "mut",
"mutable", "native", "pure", "resource", "ret", "trait",
"type", "unchecked", "unsafe", "while"] {
"type", "unchecked", "unsafe", "while", "new"] {
words.insert(word, ());
}
words
@ -852,6 +852,13 @@ fn parse_bottom_expr(p: parser) -> pexpr {
let blk = parse_block_tail(p, lo, ast::default_blk);
ret mk_pexpr(p, blk.span.lo, blk.span.hi, ast::expr_block(blk));
}
} else if eat_word(p, "new") {
expect(p, token::LPAREN);
let r = parse_expr(p);
expect(p, token::RPAREN);
let v = parse_expr(p);
ret mk_pexpr(p, lo, p.span.hi,
ast::expr_new(r, p.get_id(), v));
} else if eat_word(p, "if") {
ret pexpr(parse_if_expr(p));
} else if eat_word(p, "for") {

View File

@ -1089,6 +1089,13 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
word_nbsp(s, "assert");
print_expr(s, expr);
}
ast::expr_new(p, _, v) {
word_nbsp(s, "new");
popen(s);
print_expr(s, p);
pclose(s);
print_expr(s, v);
}
ast::expr_mac(m) { print_mac(s, m); }
}
s.ann.post(ann_node);

View File

@ -315,6 +315,10 @@ fn visit_mac<E>(m: mac, e: E, v: vt<E>) {
fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) {
alt ex.node {
expr_new(pool, _, val) {
v.visit_expr(pool, e, v);
v.visit_expr(val, e, v);
}
expr_vec(es, _) { visit_exprs(es, e, v); }
expr_rec(flds, base) {
for f: field in flds { v.visit_expr(f.node.expr, e, v); }

View File

@ -0,0 +1,15 @@
import libc, unsafe;
enum malloc_pool = ();
impl methods for malloc_pool {
fn alloc(sz: int, align: int) -> *() {
fail;
}
}
fn main() {
let p = &malloc_pool(());
let x = new(*p) 4u;
//!^ ERROR mismatched types: expected `fn(uint, uint) -> *()`
}

View File

@ -21,7 +21,7 @@ fn request_task(c: chan<ctx>) {
req = recv(p);
}
fn new() -> ctx {
fn new_cx() -> ctx {
let p = port();
let ch = chan(p);
let t = task::spawn {|| request_task(ch); };
@ -31,7 +31,7 @@ fn new() -> ctx {
}
fn main() {
let cx = new();
let cx = new_cx();
let p = port::<bool>();
send(cx, close(chan(p)));

View File

@ -0,0 +1,21 @@
import libc, unsafe;
enum malloc_pool = ();
impl methods for malloc_pool {
fn alloc(sz: uint, align: uint) -> *() {
unsafe {
unsafe::reinterpret_cast(libc::malloc(sz))
}
}
}
fn main() {
let p = &malloc_pool(());
let x = new(*p) 4u;
io::print(#fmt["%u", *x]);
assert *x == 4u;
unsafe {
libc::free(unsafe::reinterpret_cast(x));
}
}