aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/SPEC.md2
-rw-r--r--src/compiler/stmt.zc12
-rw-r--r--tests/conformance/8-runtime/o3_break_continue_leave.zc28
-rw-r--r--tests/xref.baseline2
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