diff options
| author | 2026-10-14 21:15:13 +0330 | |
|---|---|---|
| committer | 2026-10-14 21:15:13 +0330 | |
| commit | 3405cb8a34b8706ee2de3f110aeced3b7de2a890 (patch) | |
| tree | 45fca828bd71ae55df4e60aa7d89b0cf09f4f291 | |
| parent | detach doc comments on internal bounds helpers (genome size); todo: reuse cap... (diff) | |
zy: a runtime trap exits 2 (rt.trap: index out of range, frame overflow, out of heap, bad pointer, use after free); compile errors stay 1; spec 8.1.3; exitstatus.sh checks exit 2
| -rw-r--r-- | docs/SPEC.md | 4 | ||||
| -rw-r--r-- | src/runtime/rt.zc | 13 | ||||
| -rw-r--r-- | tests/exitstatus.sh | 8 |
3 files changed, 15 insertions, 10 deletions
diff --git a/docs/SPEC.md b/docs/SPEC.md index 4936856..32afdfb 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -481,7 +481,7 @@ pn((int)(p + 1) - (int)p); // 1: p + 1 moves one word 3.13.3 `push(a, x)` appends and grows; `pop(a)` removes and returns the last element; `resize(a, n)` sets the length, zero-filling new elements and dropping the rest (freeing owned elements). `push` and `pop` on aggregate element types are errors (push takes scalar or reference elements; use resize for aggregates). `push`, `pop`, `resize` on a `fixed` array: error: a fixed array can't grow or shrink. -3.13.4 indexing (outside unbounded and o3 code, 4.15): `a[i]` with `i < 0` or `i >= len(a)` prints `zy: index I out of range (length N)` on the output and stops the program (`rt.halt`, exit status 0). `pop` on an empty array reports the same with index -1. a constant index known to be out of range also produces a compile-time warning: `zc: line N: warning: out-of-bounds access: index 2 is outside length 2 (storage declared or allocated on line N)`, and the program still runs. +3.13.4 indexing (outside unbounded and o3 code, 4.15): `a[i]` with `i < 0` or `i >= len(a)` prints `zy: index I out of range (length N)` on the output and stops the program (a runtime trap, exit status 2, 8.1.3). `pop` on an empty array reports the same with index -1. a constant index known to be out of range also produces a compile-time warning: `zc: line N: warning: out-of-bounds access: index 2 is outside length 2 (storage declared or allocated on line N)`, and the program still runs. ``` int[?] a = {1, 2}; push(a, 3); a.push(4); @@ -1046,7 +1046,7 @@ changes to the operator and keyword tables take effect for the text read after t 8.1.2 after a unit compiles with no errors, the runtime calls `std.libinit()` if the unit includes `std`, then `main()` if a function `main` is defined (no arguments; it works in a namespaced file too), then stops the machine. a unit with no `main` runs nothing. a unit with errors does not run. -8.1.3 the value `main` returns is discarded. the process exit status of `zy` is 0 in all cases, including compile errors and `rt.halt()`. +8.1.3 the value `main` returns is discarded. the process exit status of `zy` (and `zym`) is 1 after compile errors, 2 after a runtime trap (an index out of range, a stack or frame overflow, out of heap memory, a bad pointer or use after free under memdebug), and 0 otherwise, including `rt.halt()`. 8.1.4 compile errors are printed on standard output (not standard error), the same stream as the program's output. diff --git a/src/runtime/rt.zc b/src/runtime/rt.zc index d6dcfb6..ad18f85 100644 --- a/src/runtime/rt.zc +++ b/src/runtime/rt.zc @@ -169,6 +169,9 @@ int get_c() { return asm { , }; } int eof() { return asm { ? }; } // stop the machine now. nothing after it runs, and the exit status is 0. int halt() { asm { q } return 0; } +// stop after a runtime trap (a bounds error, an overflow, out of memory, a bad +// pointer) with exit status 2. a compile error exits 1, a normal halt 0. +int trap() { syscall(7, 2, 0, 0); return 0; } // 7 is SYS_EXIT, defined further down // write a zero-terminated string, one character at a time. it is the same as // put_c on each byte of s, so a capture sees the text too. int put_s(char* s) { @@ -686,7 +689,7 @@ int scan_ptr(int p, int n) { int frame_overflow() { put_s("zy: frame stack overflow\n"); trace_at(1); - halt(); + trap(); return 0; } // choose the minimum size of subsequent frame segments, in words. @@ -731,7 +734,7 @@ int oom() { outmode = 0; outgrow = 0; put_s("zc: out of heap memory\n"); trace_at(1); - halt(); + trap(); return 0; } // n words, untyped and not zeroed. the type word keeps -(n + 1), so fatten @@ -1103,7 +1106,7 @@ int oob(int i, int n) { put_n(n); put_s(")\n"); trace_at(1); - halt(); + trap(); return 0; } // element i of a growable array h. indexing with [] does the same, with the bounds @@ -1417,11 +1420,11 @@ int checkptr(int w) { if (!(dbg & 1) || !w) return 0; if (w < HEAP_LO || w >= hstop) return 0; p = obj_base(w); - if (!p) { put_s("zy: bad heap pointer\n"); trace_at(1); halt(); return 0; } + if (!p) { put_s("zy: bad heap pointer\n"); trace_at(1); trap(); return 0; } if (!(p[-4] & 1)) { put_s("zy: use after free from line "); put_n(site_line_of(p)); put_c(10); trace_at(1); - halt(); + trap(); } return 0; } diff --git a/tests/exitstatus.sh b/tests/exitstatus.sh index d48ad1f..064aa1a 100644 --- a/tests/exitstatus.sh +++ b/tests/exitstatus.sh @@ -1,8 +1,9 @@ #!/bin/bash # exit status of zy: a program that fails to compile (syntax, type or analyzer # error, or too many errors) exits 1; a good program exits 0; a runtime trap -# keeps its status (0 today). usage: tests/exitstatus.sh [VM]. prints one line -# per case and exits 1 when any case is wrong. +# (bounds, overflow, out of memory, bad pointer) exits 2. usage: +# tests/exitstatus.sh [VM]. prints one line per case and exits 1 when any +# case is wrong. vm=${1:-build/bin/zy} T=$(mktemp -d); trap 'rm -rf "$T"' EXIT fail=0 @@ -17,5 +18,6 @@ want 1 "an undefined name" <<< $'namespace;\ninclude rt;\nfn main() { rt.put_n(n want 1 "a type error" < tests/typeerrors/arg.zc want 1 "an analyzer error" < tests/errors/alias_field.zc want 1 "twenty errors" < <(printf 'namespace;\ninclude rt;\nfn main() {\n'; for i in $(seq 25); do echo " int a$i = ;"; done; echo ' return 0; }') -want 0 "a runtime trap keeps its status" < tests/programs/bounds_heap.zc +want 2 "a bounds trap" < tests/programs/bounds_heap.zc +want 0 "an explicit halt" <<< $'namespace;\ninclude rt;\nfn main() { rt.put_n(1); rt.halt(); return 3; }' exit $fail |
