diff options
| author | 2026-10-15 02:39:49 +0330 | |
|---|---|---|
| committer | 2026-10-15 02:40:51 +0330 | |
| commit | 7ff224faa88725fe59441e73c70dfa4eedd76345 (patch) | |
| tree | 2c8af51b9fc3ea3efe45e43ac0c8ab38ab709fe8 | |
| parent | rt: memreport marks a leaked value whose drop never ran with (drop); spec 4.1... (diff) | |
o3: break and continue out of an o3 { } block call rt.o3_leave for each block they leave (the loop records the o3 depth it began at), so the depth returns to 0 and pending collections and drops run; spec 4.10.3, o3_break_continue_leave.zc
| -rw-r--r-- | docs/SPEC.md | 2 | ||||
| -rw-r--r-- | src/compiler/stmt.zc | 12 | ||||
| -rw-r--r-- | tests/conformance/8-runtime/o3_break_continue_leave.zc | 28 | ||||
| -rw-r--r-- | tests/xref.baseline | 2 |
4 files changed, 43 insertions, 1 deletions
diff --git a/docs/SPEC.md b/docs/SPEC.md index bb0f44f..c80b220 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -624,7 +624,7 @@ switch (s) { case circle(r): pn(r); case rect(w, h): pn(w * h); case dot: ps("do o3 int sum(fixed int[?] a) { int s = 0; for (int i = 0; i < len(a); i++) s += a[i]; return s; } ``` -4.10.3 no collection runs inside an o3 region, from any code path. an allocation that would collect grows the heap instead and leaves a collection pending; an explicit `rt.gc()` inside the region is pending too. when the outermost region is left (the end of an `o3 { }` block, a `return` out of it, or the return of an o3 function called from normal code), the pending collection runs once. if the heap can't grow, the allocation is a runtime trap (out of heap memory, exit status 2). +4.10.3 no collection runs inside an o3 region, from any code path. an allocation that would collect grows the heap instead and leaves a collection pending; an explicit `rt.gc()` inside the region is pending too. when the outermost region is left (the end of an `o3 { }` block, a `return`, `break` or `continue` out of it, or the return of an o3 function called from normal code), the pending collection runs once. if the heap can't grow, the allocation is a runtime trap (out of heap memory, exit status 2). 4.10.4 drops (6.15) don't run inside o3: setting a drop inside an o3 region is error: a drop can't be set inside an o3 region, and ending the lifetime of a value with a drop inside an o3 region, by `rt.free`, at a scope end, or as a temporary, is error: x: has drop and can't end inside an o3 region. a drop the compiler can't see (unchecked code) that comes to run inside o3 stops the program: zy: a drop can't run inside an o3 region. ### 4.11 memdebug diff --git a/src/compiler/stmt.zc b/src/compiler/stmt.zc index 6d5abe7..703f2db 100644 --- a/src/compiler/stmt.zc +++ b/src/compiler/stmt.zc @@ -232,8 +232,18 @@ int kw_if() { // loops and switches share a frame stack (R_LOOP, 8 words each). a break adds // an exit record (the analyzer state at that point) with a jump to patch; // loop_end joins all of them with the normal fall-out and patches the jumps. +// lpo3[k]: how many o3 blocks were open when loop or switch k began; a break or +// continue out of an o3 { } block to it leaves those blocks (rt.o3_leave), as a return does +word lpo3; int lpo3cap; +int o3_leave_to(int k) { + int n = o3blk - lpo3[k]; + while (n > 0) { callname(intern("rt.o3_leave")); op(O_DROP); n--; } + return 0; +} int loop_push(int cont, int s0) { word f; + if (lsp >= lpo3cap) { lpo3 = chk_resize(lpo3, lpo3cap, lsp * 2 + 16); lpo3cap = lsp * 2 + 16; } + lpo3[lsp] = o3blk; reserve_R_LOOP((lsp + 1) * 8); if (lsp >= loopfacts_cap) { lpfact = chk_resize(lpfact, loopfacts_cap, loopfacts_cap * 2); loopfacts_cap = loopfacts_cap * 2; } f = R_LOOP + lsp * 8; @@ -412,6 +422,7 @@ int kw_break() { if (dfr_on && dfr_ctx == ctxsp && lsp <= dfr_lsp) { serr("break can't leave a deferred statement"); return 0; } defer_run(f[2] + 1, 0); exit_vars(f[2] + 1, 0); + o3_leave_to(lsp - 1); if (live) exit_rec(hole()); else hole(); op(O_JMP); live = 0; @@ -430,6 +441,7 @@ int kw_continue() { defer_run(f[2] + 1, 0); backedge(f[3]); exit_vars(f[2] + 1, 0); + o3_leave_to(j); if (f[0] == -2) { f[7] = fixnew(hole(), f[7]); op(O_JMP); } // do/while: the condition isn't placed yet else jmp(f[0]); live = 0; diff --git a/tests/conformance/8-runtime/o3_break_continue_leave.zc b/tests/conformance/8-runtime/o3_break_continue_leave.zc new file mode 100644 index 0000000..8cd9647 --- /dev/null +++ b/tests/conformance/8-runtime/o3_break_continue_leave.zc @@ -0,0 +1,28 @@ +// expect: 0 0 3 0 dropped/ +// spec 4.10.3: break and continue out of an o3 { } block leave the region like its end or a return, so the o3 depth is back to 0 after the loop and a drop can run again +include rt; +fn main() { + int i = 0, n = 0; + while (i < 10) { + o3 { + if (i == 4) break; + i++; + } + } + rt.put_n(rt.o3_depth); rt.put_c(32); + i = 0; + while (i < 6) { + i++; + o3 { + if (i % 2 == 0) continue; + n++; + } + } + rt.put_n(rt.o3_depth); rt.put_c(32); rt.put_n(n); rt.put_c(32); + for (int k = 0; k < 3; k++) { o3 { if (k == 1) continue; if (k == 2) break; } } + rt.put_n(rt.o3_depth); rt.put_c(32); + { int* p = drop(heap(1), fn() { rt.put_s("dropped"); }); } + rt.put_c(10); + return 0; +} +// zc0: skip diff --git a/tests/xref.baseline b/tests/xref.baseline index e83798d..e7533fa 100644 --- a/tests/xref.baseline +++ b/tests/xref.baseline @@ -313,6 +313,8 @@ lowner assigncheck join newloc reserve_locals snap_restore lpfact bpoison kw_for loop_push reserve_R_LOOP lpfact_initial lpinfo linfo_new loop_push +lpo3 loop_push +lpo3cap loop_push lpos closure embed_bind fndef localdecl newloc reserve_locals lpos_initial lprev local_bind reserve_locals |
