aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 16:09:07 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 16:09:07 +0330
commite228e7609929763d5acddb728ba543832fcffc6a (patch)
tree9b4ecc4d46bf38e8d20234d631a6d41543daa211 /docs
parentmerge: memdebug obj_base via size classes (diff)
parentbounds checking: constant loop bound proof works, plain-op fixed-array check,... (diff)
merge bounds checking
Diffstat (limited to 'docs')
-rw-r--r--docs/SPEC.md17
1 files changed, 12 insertions, 5 deletions
diff --git a/docs/SPEC.md b/docs/SPEC.md
index 5618a5e..6df55f6 100644
--- a/docs/SPEC.md
+++ b/docs/SPEC.md
@@ -60,6 +60,7 @@ a name that looks like `x.y` where `x` is not a known module is the three tokens
| `memdebug` | S | T | | 4.11 |
| `hints` | S | T | | 4.12 |
| `unchecked` | S | | | 4.9, 5.1 |
+| `unbounded` | S | | | 4.15, 5.1 |
| `o3` | S | | | 4.10 |
| `asm` | S | | E | raw vm words (4.13) |
| `run.method` | S | T | | register a receiver method (7.7) |
@@ -190,7 +191,7 @@ u32 z = 0; z--; // z is 4294967295
### 2.6 arrays
-2.6.1 `T[n]`: `n` is a constant expression (3.14) and the array lives in place: in a frame, a global, or inside a struct. its value is its address. `sizeof(T[n])` is the words the elements take (packed for sized types). there is no run-time bound check; a constant index outside `0..n-1` is error: the index is out of range for this array.
+2.6.1 `T[n]`: `n` is a constant expression (3.14) and the array lives in place: in a frame, a global, or inside a struct. its value is its address. `sizeof(T[n])` is the words the elements take (packed for sized types). a variable index is checked at run time against `n` (3.7.4) unless the code is unbounded (4.15); a constant index outside `0..n-1` is error: the index is out of range for this array.
2.6.2 `T[]` is an in-place array whose length comes from its initializer: `int[] a = {1, 2, 3};` has length 3. without an initializer, error: T[] needs an initializer to determine its length.
@@ -426,7 +427,7 @@ pn((int)(p + 1) - (int)p); // 1: p + 1 moves one word
3.7.3 `&x` is the address of a variable, field or element. it applies to a local (which then lives in memory and cannot be an o3 variable), a global, a field and an element of a non-packed array. an element of a packed array has no address (2.3.3).
-3.7.4 indexing a new object (`rt.new(int)[0]`) is error: index a new object only after binding it to a name. indexing a `T[?]` is bounds checked (3.13). indexing a `T[n]` or a pointer is not checked at run time.
+3.7.4 indexing a new object (`rt.new(int)[0]`) is error: index a new object only after binding it to a name. indexing is bounds checked at run time unless the function, block, statement or unit is unbounded or the code is o3 (4.15). a `T[?]` is checked against its length (3.13), a `T[n]` against `n`, and a pointer only when it points at the first word of a live heap block, against that block's payload (a buddy block rounds the payload up: `rt.new(4)` has 11 words). a pointer into the middle of a block, a frame array, a global or memory managed by hand is not checked. a constant index 0, and an index a loop test `i < k` with constant `k` (or `i < len(a)`) has already proved, are not checked again. a failed check prints `zy: index I out of range (length N)` and stops, as in 3.13.4.
3.7.5 an index must be a number (typecheck on).
@@ -480,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: `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 (`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.
```
int[?] a = {1, 2};
push(a, 3); a.push(4);
@@ -611,7 +612,7 @@ switch (s) { case circle(r): pn(r); case rect(w, h): pn(w * h); case dot: ps("do
### 4.10 o3
-4.10.1 `o3 { ... }` marks a block, `o3 T x = e;` one variable, and `o3 T f(...) { ... }` / `o3 fn f(...)` a function, for the optimising tier. o3 changes speed, not meaning, subject to these restrictions on code in an o3 region:
+4.10.1 `o3 { ... }` marks a block, `o3 T x = e;` one variable, and `o3 T f(...) { ... }` / `o3 fn f(...)` a function, for the optimising tier. o3 changes speed, not meaning, except that o3 code is unbounded (4.15), subject to these restrictions on code in an o3 region:
- every variable and parameter must be typed (errors: o3 variables need a type, o3 parameters need a type).
- no address-of on an o3 variable and no naming one in `asm` (errors: an o3 variable has no address, asm can't reach an o3 variable).
- no closures (3.15.3).
@@ -655,11 +656,17 @@ int y = 7; asm { y @ 1 + y ! } // y is 8
4.14.8 defer works in `unchecked` code: the analyzer's cleanup is off there, but the deferred code runs. a defer keeps the checked or unchecked mode it was written in. a function that falls off its end runs its function-level defers before returning 0.
+### 4.15 unbounded
+
+4.15.1 `unbounded stmt` (a block or a single statement) compiles the statement without index bounds checks (3.7.4). `unbounded;` at top level does it for the rest of the unit. `unbounded fn f() {}` (also `unbounded T f(...)`, a method or a closure) makes one function unbounded; the prefix combines with `unchecked`, `immediate` and `o3` in any order. o3 code is unbounded. `unbounded` changes no result of a program whose indexes are in range.
+
+4.15.2 the runtime, the core, the compiler, the language server, `run.zc` and `build.zc` are unbounded; std, icon and actor are checked except where a function reads headers on purpose.
+
## 5. declarations at the top level
### 5.1 items
-5.1.1 a unit is a sequence of items. an item is: a function definition or prototype, a global variable, `const`, `struct`, `type`, `enum`, `include`, `embed`, `namespace`, `typecheck`, `docs`, `lines`, `memdebug`, `hints`, `enhance`, `extern`, `unchecked;`, or a use of a top-level parse word (7.2).
+5.1.1 a unit is a sequence of items. an item is: a function definition or prototype, a global variable, `const`, `struct`, `type`, `enum`, `include`, `embed`, `namespace`, `typecheck`, `docs`, `lines`, `memdebug`, `hints`, `enhance`, `extern`, `unchecked;`, `unbounded;`, or a use of a top-level parse word (7.2).
5.1.2 `struct`, `type`, `typecheck`, `docs`, `lines`, `namespace`, `enhance` and `extern` are valid only at the top level. `enum`, `embed`, `memdebug`, `hints` and `const` also appear as statements in functions. `include` is top-level only (5.12.7).