mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-11 07:21:51 +00:00
Remove support for obj dtors
This commit is contained in:
parent
30d2e358be
commit
b1d2a00b4a
@ -1327,11 +1327,10 @@ fn make_free_glue(cx: &@block_ctxt, v0: ValueRef, t: &ty::t) {
|
||||
cx.build.GEP(body,
|
||||
~[C_int(0), C_int(abi::obj_body_elt_tydesc)]);
|
||||
let tydesc = cx.build.Load(tydescptr);
|
||||
let cx_ = maybe_call_dtor(cx, v0);
|
||||
let ti = none[@tydesc_info];
|
||||
call_tydesc_glue_full(cx_, body, tydesc,
|
||||
call_tydesc_glue_full(cx, body, tydesc,
|
||||
abi::tydesc_field_drop_glue, ti);
|
||||
trans_non_gc_free(cx_, b)
|
||||
trans_non_gc_free(cx, b)
|
||||
}
|
||||
ty::ty_fn(_, _, _, _, _) {
|
||||
let box_cell =
|
||||
@ -2318,28 +2317,6 @@ fn call_tydesc_glue(cx: &@block_ctxt, v: ValueRef, t: &ty::t, field: int) ->
|
||||
ret rslt(td.bcx, C_nil());
|
||||
}
|
||||
|
||||
fn maybe_call_dtor(cx: &@block_ctxt, v: ValueRef) -> @block_ctxt {
|
||||
let vtbl = cx.build.GEP(v, ~[C_int(0), C_int(abi::obj_field_vtbl)]);
|
||||
vtbl = cx.build.Load(vtbl);
|
||||
let vtbl_type = T_ptr(T_array(T_ptr(T_nil()), 1u));
|
||||
vtbl = cx.build.PointerCast(vtbl, vtbl_type);
|
||||
|
||||
let dtor_ptr = cx.build.GEP(vtbl, ~[C_int(0), C_int(0)]);
|
||||
dtor_ptr = cx.build.Load(dtor_ptr);
|
||||
dtor_ptr = cx.build.BitCast(dtor_ptr, T_ptr(T_dtor(bcx_ccx(cx), cx.sp)));
|
||||
let dtor_cx = new_sub_block_ctxt(cx, "dtor");
|
||||
let after_cx = new_sub_block_ctxt(cx, "after_dtor");
|
||||
let test =
|
||||
cx.build.ICmp(lib::llvm::LLVMIntNE, dtor_ptr,
|
||||
C_null(val_ty(dtor_ptr)));
|
||||
cx.build.CondBr(test, dtor_cx.llbb, after_cx.llbb);
|
||||
let me = dtor_cx.build.Load(v);
|
||||
dtor_cx.build.FastCall(dtor_ptr,
|
||||
~[C_null(T_ptr(T_nil())), cx.fcx.lltaskptr, me]);
|
||||
dtor_cx.build.Br(after_cx.llbb);
|
||||
ret after_cx;
|
||||
}
|
||||
|
||||
fn call_cmp_glue(cx: &@block_ctxt, lhs: ValueRef, rhs: ValueRef, t: &ty::t,
|
||||
llop: ValueRef) -> result {
|
||||
// We can't use call_tydesc_glue_full() and friends here because compare
|
||||
@ -3943,8 +3920,7 @@ fn trans_for_each(cx: &@block_ctxt, local: &@ast::local, seq: &@ast::expr,
|
||||
ast::expr_call(f, args) {
|
||||
let pair =
|
||||
create_real_fn_pair(cx, iter_body_llty, lliterbody, llenv.ptr);
|
||||
r =
|
||||
trans_call(cx, f, some[ValueRef](cx.build.Load(pair)), args,
|
||||
r = trans_call(cx, f, some[ValueRef](cx.build.Load(pair)), args,
|
||||
seq.id);
|
||||
ret rslt(r.bcx, C_nil());
|
||||
}
|
||||
@ -4177,11 +4153,10 @@ fn trans_field(cx: &@block_ctxt, sp: &span, v: ValueRef, t0: &ty::t,
|
||||
r.bcx.build.GEP(r.val, ~[C_int(0), C_int(abi::obj_field_vtbl)]);
|
||||
vtbl = r.bcx.build.Load(vtbl);
|
||||
|
||||
let vtbl_type = T_ptr(T_array(T_ptr(T_nil()), ix + 2u));
|
||||
let vtbl_type = T_ptr(T_array(T_ptr(T_nil()), ix + 1u));
|
||||
vtbl = cx.build.PointerCast(vtbl, vtbl_type);
|
||||
|
||||
// +1 because slot #0 contains the destructor
|
||||
let v = r.bcx.build.GEP(vtbl, ~[C_int(0), C_int(ix + 1u as int)]);
|
||||
let v = r.bcx.build.GEP(vtbl, ~[C_int(0), C_int(ix as int)]);
|
||||
let fn_ty: ty::t = ty::method_ty_to_fn_ty(bcx_tcx(cx), methods.(ix));
|
||||
let tcx = bcx_tcx(cx);
|
||||
let ll_fn_ty =
|
||||
@ -5701,8 +5676,7 @@ fn trans_anon_obj(bcx: @block_ctxt, sp: &span, anon_obj: &ast::anon_obj,
|
||||
{fields:
|
||||
std::ivec::map(ast::obj_field_from_anon_obj_field,
|
||||
additional_fields),
|
||||
methods: anon_obj.methods,
|
||||
dtor: none[@ast::method]};
|
||||
methods: anon_obj.methods};
|
||||
|
||||
let inner_obj_ty: ty::t;
|
||||
let vtbl;
|
||||
@ -6666,13 +6640,12 @@ fn process_fwding_mthd(cx: @local_ctxt, sp: &span, m: @ty::method,
|
||||
}
|
||||
}
|
||||
|
||||
// Pick out the original method from the vtable. The +1 is because slot
|
||||
// #0 contains the destructor.
|
||||
let vtbl_type = T_ptr(T_array(T_ptr(T_nil()), ix + 2u));
|
||||
// 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 + 1u as int)]);
|
||||
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);
|
||||
@ -6756,16 +6729,7 @@ fn create_vtbl(cx: @local_ctxt, sp: &span, outer_obj_ty: ty::t,
|
||||
inner_obj_ty: option::t[ty::t],
|
||||
additional_field_tys: &ty::t[]) -> ValueRef {
|
||||
|
||||
let dtor = C_null(T_ptr(T_i8()));
|
||||
alt ob.dtor {
|
||||
some(d) {
|
||||
let dtor_1 = trans_dtor(cx, outer_obj_ty, ty_params, d);
|
||||
dtor = llvm::LLVMConstBitCast(dtor_1, val_ty(dtor));
|
||||
}
|
||||
none. { }
|
||||
}
|
||||
|
||||
let llmethods: ValueRef[] = ~[dtor];
|
||||
let llmethods: ValueRef[] = ~[];
|
||||
let meths: vtbl_mthd[] = ~[];
|
||||
let backwarding_vtbl: option::t[ValueRef] = none;
|
||||
|
||||
@ -6911,19 +6875,6 @@ fn create_vtbl(cx: @local_ctxt, sp: &span, outer_obj_ty: ty::t,
|
||||
ret gvar;
|
||||
}
|
||||
|
||||
fn trans_dtor(cx: @local_ctxt, outer_obj_ty: ty::t,
|
||||
ty_params: &ast::ty_param[],
|
||||
dtor: &@ast::method) -> ValueRef {
|
||||
let llfnty = T_dtor(cx.ccx, dtor.span);
|
||||
let s: str = mangle_internal_name_by_path(cx.ccx, cx.path + ~["drop"]);
|
||||
let llfn: ValueRef = decl_internal_fastcall_fn(cx.ccx.llmod, s, llfnty);
|
||||
cx.ccx.item_ids.insert(dtor.node.id, llfn);
|
||||
cx.ccx.item_symbols.insert(dtor.node.id, s);
|
||||
trans_fn(cx, dtor.span, dtor.node.meth, llfn, some(outer_obj_ty),
|
||||
ty_params, dtor.node.id);
|
||||
ret llfn;
|
||||
}
|
||||
|
||||
fn create_backwarding_vtbl(cx: @local_ctxt, sp: &span, inner_obj_ty: ty::t,
|
||||
outer_obj_ty: ty::t) -> ValueRef {
|
||||
|
||||
@ -6931,8 +6882,7 @@ fn create_backwarding_vtbl(cx: @local_ctxt, sp: &span, inner_obj_ty: ty::t,
|
||||
// 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 dtor = C_null(T_ptr(T_i8()));
|
||||
let llmethods: ValueRef[] = ~[dtor];
|
||||
let llmethods: ValueRef[] = ~[];
|
||||
let meths: vtbl_mthd[]= ~[];
|
||||
|
||||
// Gather up methods on the inner object.
|
||||
|
@ -542,11 +542,6 @@ fn T_glue_fn(cx: &crate_ctxt) -> TypeRef {
|
||||
ret t;
|
||||
}
|
||||
|
||||
fn T_dtor(ccx: &@crate_ctxt, sp: &span) -> TypeRef {
|
||||
ret type_of_fn_full(ccx, sp, ast::proto_fn, true, ~[],
|
||||
ty::mk_nil(ccx.tcx), 0u);
|
||||
}
|
||||
|
||||
fn T_cmp_glue_fn(cx: &crate_ctxt) -> TypeRef {
|
||||
let s = "cmp_glue_fn";
|
||||
if cx.tn.name_has_type(s) { ret cx.tn.get_type(s); }
|
||||
|
@ -67,7 +67,6 @@ fn find_pre_post_obj(ccx: &crate_ctxt, o: _obj) {
|
||||
find_pre_post_fn(fcx, m.node.meth);
|
||||
}
|
||||
for m: @method in o.methods { do_a_method(ccx, m); }
|
||||
option::map[@method, ()](bind do_a_method(ccx, _), o.dtor);
|
||||
}
|
||||
|
||||
fn find_pre_post_item(ccx: &crate_ctxt, i: &item) {
|
||||
|
@ -805,18 +805,6 @@ mod collect {
|
||||
write::ty_only(cx.tcx, fld.id, args.(i).ty);
|
||||
i += 1u;
|
||||
}
|
||||
|
||||
|
||||
// Finally, write in the type of the destructor.
|
||||
alt object.dtor {
|
||||
none. {/* nothing to do */ }
|
||||
some(m) {
|
||||
let t =
|
||||
ty::mk_fn(cx.tcx, ast::proto_fn, ~[], ty::mk_nil(cx.tcx),
|
||||
ast::return, ~[]);
|
||||
write::ty_only(cx.tcx, m.node.id, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::item_res(f, dtor_id, tps, ctor_id) {
|
||||
let t_arg = ty_of_arg(cx, f.decl.inputs.(0));
|
||||
@ -2624,7 +2612,6 @@ fn check_item(ccx: @crate_ctxt, it: &@ast::item) {
|
||||
|
||||
// Typecheck the methods.
|
||||
for method: @ast::method in ob.methods { check_method(ccx, method); }
|
||||
option::may[@ast::method](bind check_method(ccx, _), ob.dtor);
|
||||
|
||||
// Now remove the info from the stack.
|
||||
ivec::pop[obj_info](ccx.obj_infos);
|
||||
|
@ -524,8 +524,7 @@ type obj_field = {mut: mutability, ty: @ty, ident: ident, id: node_id};
|
||||
type anon_obj_field =
|
||||
{mut: mutability, ty: @ty, expr: @expr, ident: ident, id: node_id};
|
||||
|
||||
type _obj =
|
||||
{fields: obj_field[], methods: (@method)[], dtor: option::t[@method]};
|
||||
type _obj = {fields: obj_field[], methods: (@method)[]};
|
||||
|
||||
type anon_obj =
|
||||
// New fields and methods, if they exist.
|
||||
@ -590,16 +589,10 @@ tag item_ {
|
||||
item_ty(@ty, ty_param[]);
|
||||
item_tag(variant[], ty_param[]);
|
||||
item_obj(_obj, ty_param[], /* constructor id */node_id);
|
||||
item_res(
|
||||
/* dtor */
|
||||
_fn,
|
||||
|
||||
/* dtor id */
|
||||
node_id,
|
||||
item_res(_fn, /* dtor */
|
||||
node_id, /* dtor id */
|
||||
ty_param[],
|
||||
|
||||
/* ctor id */
|
||||
node_id);
|
||||
node_id /* ctor id */);
|
||||
}
|
||||
|
||||
type native_item =
|
||||
|
@ -232,8 +232,8 @@ fn noop_fold_item_underscore(i: &item_, fld: ast_fold) -> item_ {
|
||||
}
|
||||
item_obj(o, typms, d) {
|
||||
item_obj({fields: ivec::map(fold_obj_field, o.fields),
|
||||
methods: ivec::map(fld.fold_method, o.methods),
|
||||
dtor: option::map(fld.fold_method, o.dtor)}, typms, d)
|
||||
methods: ivec::map(fld.fold_method, o.methods)},
|
||||
typms, d)
|
||||
}
|
||||
item_res(dtor, did, typms, cid) {
|
||||
item_res(fld.fold_fn(dtor), did, typms, cid)
|
||||
|
@ -1816,24 +1816,6 @@ fn parse_method(p: &parser) -> @ast::method {
|
||||
ret @spanned(lo, f.body.span.hi, meth);
|
||||
}
|
||||
|
||||
fn parse_dtor(p: &parser) -> @ast::method {
|
||||
let lo = p.get_last_lo_pos();
|
||||
let b: ast::blk = parse_block(p);
|
||||
let inputs: ast::arg[] = ~[];
|
||||
let output: @ast::ty = @spanned(lo, lo, ast::ty_nil);
|
||||
// I guess dtors can't have constraints?
|
||||
let d: ast::fn_decl = {
|
||||
inputs: inputs,
|
||||
output: output,
|
||||
purity: ast::impure_fn,
|
||||
cf: ast::return,
|
||||
constraints: ~[]
|
||||
};
|
||||
let f: ast::_fn = {decl: d, proto: ast::proto_fn, body: b};
|
||||
let m: ast::method_ = {ident: "drop", meth: f, id: p.get_id()};
|
||||
ret @spanned(lo, f.body.span.hi, m);
|
||||
}
|
||||
|
||||
fn parse_item_obj(p: &parser, attrs: &ast::attribute[]) ->
|
||||
@ast::item {
|
||||
let lo = p.get_last_lo_pos();
|
||||
@ -1843,16 +1825,13 @@ fn parse_item_obj(p: &parser, attrs: &ast::attribute[]) ->
|
||||
parse_seq(token::LPAREN, token::RPAREN, some(token::COMMA),
|
||||
parse_obj_field, p);
|
||||
let meths: (@ast::method)[] = ~[];
|
||||
let dtor: option::t[@ast::method] = none;
|
||||
expect(p, token::LBRACE);
|
||||
while p.peek() != token::RBRACE {
|
||||
if eat_word(p, "drop") {
|
||||
dtor = some(parse_dtor(p));
|
||||
} else { meths += ~[parse_method(p)]; }
|
||||
meths += ~[parse_method(p)];
|
||||
}
|
||||
let hi = p.get_hi_pos();
|
||||
expect(p, token::RBRACE);
|
||||
let ob: ast::_obj = {fields: fields.node, methods: meths, dtor: dtor};
|
||||
let ob: ast::_obj = {fields: fields.node, methods: meths};
|
||||
ret mk_item(p, lo, hi, ident, ast::item_obj(ob, ty_params, p.get_id()),
|
||||
attrs);
|
||||
}
|
||||
|
@ -510,14 +510,6 @@ fn print_item(s: &ps, item: &@ast::item) {
|
||||
word(s.s, " ");
|
||||
print_block(s, meth.node.meth.body);
|
||||
}
|
||||
alt _obj.dtor {
|
||||
some(dtor) {
|
||||
space(s.s);
|
||||
head(s, "drop");
|
||||
print_block(s, dtor.node.meth.body);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
bclose(s, item.span);
|
||||
}
|
||||
ast::item_res(dt, dt_id, tps, ct_id) {
|
||||
|
@ -106,13 +106,6 @@ fn visit_item[E](i: &@item, e: &E, v: &vt[E]) {
|
||||
v.visit_fn(m.node.meth, ~[], m.span, some(m.node.ident),
|
||||
m.node.id, e, v);
|
||||
}
|
||||
alt ob.dtor {
|
||||
none. { }
|
||||
some(m) {
|
||||
v.visit_fn(m.node.meth, ~[], m.span, some(m.node.ident),
|
||||
m.node.id, e, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,13 +118,6 @@ fn walk_item(v: &ast_visitor, i: @ast::item) {
|
||||
m.node.id);
|
||||
v.visit_method_post(m);
|
||||
}
|
||||
alt ob.dtor {
|
||||
none. { }
|
||||
some(m) {
|
||||
walk_fn(v, m.node.meth, ~[], m.span, some(m.node.ident),
|
||||
m.node.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
v.visit_item_post(i);
|
||||
|
@ -1,14 +0,0 @@
|
||||
// -*- rust -*-
|
||||
|
||||
// error-pattern: attempted dynamic environment-capture
|
||||
|
||||
fn f(x: bool) { }
|
||||
|
||||
obj foobar(x: bool)
|
||||
{drop {
|
||||
let y = x;
|
||||
fn test() { f(y); }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() { }
|
@ -1,6 +0,0 @@
|
||||
// error-pattern:Unsatisfied precondition
|
||||
|
||||
fn main() {
|
||||
obj foo(x: int) {drop { let baz: int; log baz; } }
|
||||
fail;
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
// xfail-stage0
|
||||
// xfail-stage1
|
||||
// xfail-stage2
|
||||
// xfail-stage3
|
||||
// This test checks that destructors run in the right order. Because
|
||||
// stateful objects can't have destructors, we have the destructors
|
||||
// record their expected order into a channel when they execute (so
|
||||
// the object becomes 'io' rather than 'state'). Then each test case
|
||||
// asserts that the channel produces values in ascending order.
|
||||
//
|
||||
// FIXME: Write an int->str function and concatenate the whole failure
|
||||
// message into a single log statement (or, even better, a print).
|
||||
//
|
||||
// FIXME: check_order should take only 1 line in a test, not 2+a block
|
||||
// block. Since destructor-having objects can't refer to mutable state
|
||||
// (like the port), we'd need a with-like construct to do the same for
|
||||
// stateful objects within a scope.
|
||||
//
|
||||
// FIXME #21: Each test should execute in its own task, so it can fail
|
||||
// independently, writing its error message to a channel that the
|
||||
// parent task aggregates.
|
||||
|
||||
type order_info = rec(int order, str msg);
|
||||
|
||||
io fn check_order(port[order_info] expected_p) {
|
||||
chan(expected_p) <| rec(order=-1, msg="");
|
||||
let mutable int actual = 0;
|
||||
// FIXME #121: Workaround for while(true) bug.
|
||||
auto expected; expected_p |> expected;
|
||||
auto done = -1; // FIXME: Workaround for typechecking bug.
|
||||
while(expected.order != done) {
|
||||
if (expected.order != actual) {
|
||||
log expected.order;
|
||||
log " != ";
|
||||
log actual;
|
||||
log expected.msg;
|
||||
fail;
|
||||
}
|
||||
actual += 1;
|
||||
expected_p |> expected;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
obj dorder(chan[order_info] expected, int order, str message) {
|
||||
drop {
|
||||
expected <| rec(order=order, msg=message);
|
||||
}
|
||||
}
|
||||
|
||||
io fn test_simple() {
|
||||
let port[order_info] tracker_p = port();
|
||||
auto tracker = chan(tracker_p);
|
||||
dorder(tracker, 1, "Reverse decl order");
|
||||
dorder(tracker, 0, "Reverse decl order");
|
||||
check_order(tracker_p);
|
||||
}
|
||||
|
||||
io fn test_block() {
|
||||
let port[order_info] tracker_p = port();
|
||||
auto tracker = chan(tracker_p);
|
||||
{
|
||||
dorder(tracker, 2, "Before block");
|
||||
{
|
||||
dorder(tracker, 0, "Inside block");
|
||||
}
|
||||
dorder(tracker, 1, "After block");
|
||||
}
|
||||
check_order(tracker_p);
|
||||
}
|
||||
|
||||
io fn test_decl_v_init() {
|
||||
let port[order_info] tracker_p = port();
|
||||
auto tracker = chan(tracker_p);
|
||||
{
|
||||
auto var1;
|
||||
auto var2;
|
||||
var2 = dorder(tracker, 0, "decl, not init");
|
||||
var1 = dorder(tracker, 1, "decl, not init");
|
||||
}
|
||||
check_order(tracker_p);
|
||||
}
|
||||
|
||||
io fn test_overwritten_obj() {
|
||||
let port[order_info] tracker_p = port();
|
||||
auto tracker = chan(tracker_p);
|
||||
{
|
||||
auto var1 = dorder(tracker, 0, "overwritten object destroyed first");
|
||||
auto var2 = dorder(tracker, 2, "destroyed at end of scope");
|
||||
var1 = dorder(tracker, 3, "overwriter deleted in rev decl order");
|
||||
{
|
||||
dorder(tracker, 1, "overwritten object destroyed before end of scope");
|
||||
}
|
||||
}
|
||||
check_order(tracker_p);
|
||||
}
|
||||
|
||||
// Used to embed dorder objects into an expression. Note that the
|
||||
// parameters don't get destroyed.
|
||||
fn combine_dorders(dorder d1, dorder d2) -> int {
|
||||
ret 1;
|
||||
}
|
||||
io fn test_expression_destroyed_right_to_left() {
|
||||
let port[order_info] tracker_p = port();
|
||||
auto tracker = chan(tracker_p);
|
||||
{
|
||||
combine_dorders(dorder(tracker, 4, ""), dorder(tracker, 3, ""))
|
||||
/ combine_dorders(dorder(tracker, 2, ""), dorder(tracker, 1, ""));
|
||||
{
|
||||
dorder(tracker, 0,
|
||||
"expression objects live to end of block, not statement");
|
||||
}
|
||||
}
|
||||
check_order(tracker_p);
|
||||
}
|
||||
|
||||
io fn main() {
|
||||
test_simple();
|
||||
test_block();
|
||||
test_decl_v_init();
|
||||
test_overwritten_obj();
|
||||
test_expression_destroyed_right_to_left();
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
|
||||
|
||||
obj foo(x: @mutable int) {drop { log "running dtor"; *x = *x + 1; } }
|
||||
|
||||
fn main() {
|
||||
let mbox = @mutable 10;
|
||||
{ let x = foo(mbox); }
|
||||
assert (*mbox == 11);
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
|
||||
|
||||
|
||||
// xfail-stage0
|
||||
// xfail-stage1
|
||||
// xfail-stage2
|
||||
// xfail-stage3
|
||||
obj worker(c: chan[int]) {drop { log "in dtor"; c <| 10; } }
|
||||
|
||||
fn do_work(c: chan[int]) {
|
||||
log "in child task";
|
||||
{ let w: worker = worker(c); log "constructed worker"; }
|
||||
log "destructed worker";
|
||||
while true {
|
||||
// Deadlock-condition not handled properly yet, need to avoid
|
||||
// exiting the child early.
|
||||
|
||||
c <| 11;
|
||||
yield;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let p: port[int] = port();
|
||||
log "spawning worker";
|
||||
let w = spawn do_work(chan(p));
|
||||
let i: int;
|
||||
log "parent waiting for shutdown";
|
||||
p |> i;
|
||||
log "received int";
|
||||
assert (i == 10);
|
||||
log "int is OK, child-dtor ran as expected";
|
||||
}
|
Loading…
Reference in New Issue
Block a user