From 31ae48845492c12f2745c7f4c0cd2ea448e0942a Mon Sep 17 00:00:00 2001 From: zinc Date: Thu, 15 Oct 2026 02:35:04 +0330 Subject: destructors: reassigning an m owner that still holds a value frees the old value first, running its drop (right side first, then the free, then the store), with hint: x: reassigning frees the old value; a new value that is part of the old one is an error; u, d and captured owners keep the leak error; the new value's size, leaf type and drop are recorded like a declaration's; spec 6.8.5, 6.15.2 --- tests/conformance/6-ownership/drop_reassign.zc | 25 ++++++++++++++++++++++ tests/conformance/6-ownership/overwrite_frees.zc | 6 ++++++ tests/conformance/6-ownership/overwrite_leaks.zc | 6 ------ .../6-ownership/reassign_moves_inner_owner.zc | 5 +++++ tests/data/reassign_hint.zc | 2 ++ tests/errors/leak_overwrite.zc | 4 +++- tests/errors/move_live_owner.zc | 5 ----- tests/errors/reassign_from_own_child.zc | 6 ++++++ tests/test.sh | 1 + tests/xref.baseline | 21 +++++++++--------- 10 files changed, 59 insertions(+), 22 deletions(-) create mode 100644 tests/conformance/6-ownership/drop_reassign.zc create mode 100644 tests/conformance/6-ownership/overwrite_frees.zc delete mode 100644 tests/conformance/6-ownership/overwrite_leaks.zc create mode 100644 tests/conformance/6-ownership/reassign_moves_inner_owner.zc create mode 100644 tests/data/reassign_hint.zc delete mode 100644 tests/errors/move_live_owner.zc create mode 100644 tests/errors/reassign_from_own_child.zc (limited to 'tests') diff --git a/tests/conformance/6-ownership/drop_reassign.zc b/tests/conformance/6-ownership/drop_reassign.zc new file mode 100644 index 0000000..89f233e --- /dev/null +++ b/tests/conformance/6-ownership/drop_reassign.zc @@ -0,0 +1,25 @@ +// expect: a drop1 b drop2 3 drop3 drop10 drop11 12 t:7 t:8 9 t:9 drop12 +// destructors 6.8.5, 6.15.2: reassigning an owner that still holds a value frees the old value first, running its drop; the right side is computed before the free +include rt; +struct t { int v; void drop() { rt.put_s("t:"); rt.put_n(this.v); rt.put_s(" "); } } +int* mk(int v) { return drop(heap(v), fn() { rt.put_s("drop"); rt.put_n(*this); rt.put_s(" "); }); } +fn main() { + int* p = heap(1); + p.drop = fn() { rt.put_s("drop"); rt.put_n(*this); rt.put_s(" "); }; + rt.put_s("a "); + p = heap(2); + p.drop = fn() { rt.put_s("drop"); rt.put_n(*this); rt.put_s(" "); }; + rt.put_s("b "); + p = mk(*p + 1); + rt.put_n(*p); rt.put_s(" "); + int i = 10; + while (i < 13) { p = mk(i); i++; } + rt.put_n(*p); rt.put_s(" "); + t* s = rt.new(t); s.v = 7; + s = rt.new(t); s.v = 8; + t* u = rt.new(t); u.v = 9; + s = u; + rt.put_n(s.v); rt.put_s(" "); + return 0; +} +// zc0: skip diff --git a/tests/conformance/6-ownership/overwrite_frees.zc b/tests/conformance/6-ownership/overwrite_frees.zc new file mode 100644 index 0000000..9ae60d0 --- /dev/null +++ b/tests/conformance/6-ownership/overwrite_frees.zc @@ -0,0 +1,6 @@ +// expect: 0/ +// spec 6.8.5: overwriting a variable that still owns frees the old object first +include rt; +void pn(int x) { rt.put_n(x); rt.put_c(10); } +fn main() { int* a = rt.new(1); a[0] = 1; a = rt.new(1); pn(a[0]); return 0; } +// zc0: skip diff --git a/tests/conformance/6-ownership/overwrite_leaks.zc b/tests/conformance/6-ownership/overwrite_leaks.zc deleted file mode 100644 index a9b0138..0000000 --- a/tests/conformance/6-ownership/overwrite_leaks.zc +++ /dev/null @@ -1,6 +0,0 @@ -// expect error: a: still owns an object; overwriting it would leak it -// spec 6.8.5 -include rt; -void pn(int x) { rt.put_n(x); rt.put_c(10); } -fn main() { int* a = rt.new(1); a[0] = 1; a = rt.new(1); pn(a[0]); return 0; } -// zc0: skip diff --git a/tests/conformance/6-ownership/reassign_moves_inner_owner.zc b/tests/conformance/6-ownership/reassign_moves_inner_owner.zc new file mode 100644 index 0000000..d66d886 --- /dev/null +++ b/tests/conformance/6-ownership/reassign_moves_inner_owner.zc @@ -0,0 +1,5 @@ +// expect: 1 2/ +// spec 6.8.5: r still owns; assigning n frees r's object and moves n's into r, so n's block end frees nothing +include rt; +fn main() { word r = rt.new(4); { word n = rt.new(8); n[0] = 1; r = n; } rt.put_n(r[0]); r[0] = 2; rt.put_c(32); rt.put_n(r[0]); rt.put_c(10); return 0; } +// zc0: skip diff --git a/tests/data/reassign_hint.zc b/tests/data/reassign_hint.zc new file mode 100644 index 0000000..6fd9a0c --- /dev/null +++ b/tests/data/reassign_hint.zc @@ -0,0 +1,2 @@ +include rt; +fn main() { int* p = rt.new(int); *p = 1; p = rt.new(int); return *p; } diff --git a/tests/errors/leak_overwrite.zc b/tests/errors/leak_overwrite.zc index 50fecfa..45febed 100644 --- a/tests/errors/leak_overwrite.zc +++ b/tests/errors/leak_overwrite.zc @@ -1,5 +1,7 @@ namespace; include rt; +// a u object still has to leave its block, so overwriting it is not a free (spec 6.8.5) // expect: x: still owns an object -fn main() { let x = rt.new(1); x = rt.new(2); return x[0]; } +int*.u mk() { int*.u x = rt.new(1); x = rt.new(2); return x; } +fn main() { let p = mk(); return p[0]; } diff --git a/tests/errors/move_live_owner.zc b/tests/errors/move_live_owner.zc deleted file mode 100644 index e36909c..0000000 --- a/tests/errors/move_live_owner.zc +++ /dev/null @@ -1,5 +0,0 @@ -namespace; -include rt; - -// expect: still owns an object; overwriting it would leak it -fn main() { word r = rt.new(4); { word n = rt.new(8); n[0] = 1; r = n; } r[0] = 2; return 0; } diff --git a/tests/errors/reassign_from_own_child.zc b/tests/errors/reassign_from_own_child.zc new file mode 100644 index 0000000..4a23e51 --- /dev/null +++ b/tests/errors/reassign_from_own_child.zc @@ -0,0 +1,6 @@ +namespace; +include rt; + +struct node { node* next; int v; } +// expect: a: reassigning frees the old value, and the new value is part of it +fn main() { node* a = rt.new(node); a.next = rt.new(node); a = a.next; return 0; } diff --git a/tests/test.sh b/tests/test.sh index e93472c..9aee6d0 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -579,6 +579,7 @@ s_lsp() { done chk "$($lsp < examples/struct_closure_opt.zc | grep -c error)" "0" "lsp: methods and closures check clean" case "$($lsp < tests/data/drop_hint.zc)" in *"value with drop is never bound; it drops at the end of this statement"*) chk 1 1 "lsp hint: a value with drop that is never bound";; *) chk 0 1 "lsp hint: a value with drop that is never bound";; esac + case "$($lsp < tests/data/reassign_hint.zc)" in *"p: reassigning frees the old value"*) chk 1 1 "lsp hint: reassigning an owner frees the old value";; *) chk 0 1 "lsp hint: reassigning an owner frees the old value";; esac # a file of a larger build checks on its own: its bodyless declarations are not errors in check mode for f in src/runtime/rt.zc src/runtime/run.zc src/lsp/server.zc; do chk "$($lsp < $f | grep -c ': error:')" "0" "lsp: $f checks alone"; done # the compiler is several files that only make sense together: check them as one document diff --git a/tests/xref.baseline b/tests/xref.baseline index b8b1e1f..1660302 100644 --- a/tests/xref.baseline +++ b/tests/xref.baseline @@ -25,6 +25,7 @@ argowner args argowner_fact args argtemporary args arrloc arrvalue chk_reset +asg_quiet assigncheck reassign_free asgcap reserve_assignment asm_fixes reserve_asm_references asm_labelcap reserve_asm_labels @@ -291,14 +292,14 @@ lkeycap local_bind lkeyn local_bind zc_init lkeys local_bind lkeys_initial -lleaf assigncheck embed_bind localdecl reserve_locals +lleaf assigncheck embed_bind expr localdecl reassign_free reserve_locals lleaf_initial lname newloc reserve_locals -lnsz assigncheck embed_bind localdecl reserve_locals +lnsz assigncheck embed_bind expr localdecl reassign_free reserve_locals lnsz_initial local_captured clocap_at newloc reserve_locals local_cell capture_cell newloc reserve_locals -local_dropset drop_attach localdecl newloc reserve_locals +local_dropset drop_attach expr localdecl newloc reassign_free reserve_locals local_end_info assigncheck end_owner newloc reserve_locals local_ended assigncheck end_owner newloc reserve_locals local_origin assigncheck newloc reserve_locals @@ -379,8 +380,8 @@ nclo chk_reset closure ndfr defer_drop kw_defer nenh blob_load build_image_grow chk_reset enhdef nerr chk_begin chk_reset errat prescan replace_function -newleaf array_dynamic_literal array_pop call heap_box localdecl newtyped -newsz array_dynamic_literal array_pop call chk_reset heap_box localdecl newtyped +newleaf array_dynamic_literal array_pop call expr heap_box localdecl newtyped +newsz array_dynamic_literal array_pop call chk_reset expr heap_box localdecl newtyped nfact chk_reset fdrop putfact nfixu fixnew zc_init ngbind call chk_reset primary @@ -691,8 +692,8 @@ uname add_unop reserve_unary uncap reserve_unary useq add_unop reserve_unary vislo build_image_grow chk_unit start syntax_resident -xaf af_forget args array_cap array_dynamic_literal array_len array_literal_tokens array_pop bin call chk_reset drop_attach drop_member expr field heap_box incdec kw_share_value kwcall methcall newtyped postfix_tail primary ternary unary -xbor array_cap array_dynamic_literal array_len array_literal_tokens array_pop assigncheck bin blockexpr call chk_reset closure discard drop_attach drop_member enhcall field fn_begin heap_box kw_share_value kwcall methcall postfix_tail primary ternary typearg unary +xaf af_forget args array_cap array_dynamic_literal array_len array_literal_tokens array_pop bin call chk_reset drop_attach drop_member expr field heap_box incdec kw_share_value kwcall methcall newtyped postfix_tail primary reassign_free ternary unary +xbor array_cap array_dynamic_literal array_len array_literal_tokens array_pop assigncheck bin blockexpr call chk_reset closure discard drop_attach drop_member enhcall field fn_begin heap_box kw_share_value kwcall methcall postfix_tail primary reassign_free ternary typearg unary xc_file xc_init xc_fn closure fndef xc_hook xc_init @@ -701,14 +702,14 @@ xc_src unit_stdin xcls array_cap array_len array_pop array_push array_resize chk_reset ctx_pop kw_asm kwcall quote_tokens xconst array_dynamic_literal array_literal_tokens bin call chk_reset closure drop_attach heap_box kw_share_value methcall postfix_tail primary unary xcval array_literal_tokens primary unary -xfloat array_dynamic_literal array_len array_literal_tokens array_pop array_push array_resize bin blockexpr call closure drop_attach expr fcoerce femit ftest heap_box incdec kw_cfunc kw_for methcall postfix_tail primary ternary unary +xfloat array_dynamic_literal array_len array_literal_tokens array_pop array_push array_resize bin blockexpr call closure drop_attach expr fcoerce femit ftest heap_box incdec kw_cfunc kw_for methcall postfix_tail primary reassign_free ternary unary xgive argcheck args chk_reset xlend assigncheck expr unary xlenloc array_len bin call closure enhcall methcall postfix_tail primary unary xprojection ctx_pop field postfix_tail primary xstamp array_dynamic_literal array_len bin call closure enhcall methcall newtyped postfix_tail primary unary -xstr array_dynamic_literal array_len array_literal_tokens bin blockexpr call closure drop_attach heap_box incdec methcall postfix_tail primary unary -xty args array_cap array_dynamic_literal array_len array_literal_tokens array_pop array_push array_resize bin blockexpr call closure ctx_pop drop_attach drop_member enhcall expr fcoerce field ftest heap_box kw_cfunc kw_share_value kw_sizeof kw_typeof kwcall methcall newtyped postfix_tail primary ternary unary +xstr array_dynamic_literal array_len array_literal_tokens bin blockexpr call closure drop_attach heap_box incdec methcall postfix_tail primary reassign_free unary +xty args array_cap array_dynamic_literal array_len array_literal_tokens array_pop array_push array_resize bin blockexpr call closure ctx_pop drop_attach drop_member enhcall expr fcoerce field ftest heap_box kw_cfunc kw_share_value kw_sizeof kw_typeof kwcall methcall newtyped postfix_tail primary reassign_free ternary unary xvk array_dynamic_literal array_literal_tokens bin blockexpr chk_reset ctx_pop expr field fn_begin incdec kw_share_value kwcall postfix_tail primary ternary unary vf_coerce vf_effect vf_forget xvv blockexpr primary unary vf_coerce xzero array_dynamic_literal array_literal_tokens bin blockexpr call closure drop_attach enhcall expr field heap_box incdec kwcall methcall newtyped postfix_tail primary ternary unary -- cgit v1.2.3