aboutsummaryrefslogtreecommitdiff
path: root/docs/SPEC.md
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 10:47:30 +0000
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 10:47:30 +0000
commit4a22cf28742bf33577678de494aa09ab60a3c820 (patch)
treed9133c7ab431fd3f41ef7713a869d23be4539244 /docs/SPEC.md
parentMerge four small fixes: std hovers, capture terminator, bare static calls, vo... (diff)
parentzc: add the defer statement (diff)
Merge the defer statement; todo: tick it, add its follow-ups
Diffstat (limited to 'docs/SPEC.md')
-rw-r--r--docs/SPEC.md19
1 files changed, 19 insertions, 0 deletions
diff --git a/docs/SPEC.md b/docs/SPEC.md
index 8a6d089..3ad93c8 100644
--- a/docs/SPEC.md
+++ b/docs/SPEC.md
@@ -53,6 +53,7 @@ a name that looks like `x.y` where `x` is not a known module is the three tokens
| `while` `for` `do` | S | | | loops (4.4 to 4.6) |
| `switch` | S | | | 4.7 |
| `break` `continue` `return` | S | | | 4.8 |
+| `defer` | S | | | 4.14 |
| `enum` | S | T | | 5.8 |
| `include` | S | T | | 5.12 (a statement only by accident, 5.12.7) |
| `embed` | S | T | | 5.11 |
@@ -636,6 +637,24 @@ int x = asm { 2 3 + }; // 5
int y = 7; asm { y @ 1 + y ! } // y is 8
```
+### 4.14 defer
+
+4.14.1 `defer expr;` and `defer { statements }` register a statement that runs when the block that contains the `defer` ends. the tokens are kept, not compiled, at the `defer`; the compiler compiles them again, in place, at every point where control leaves the block: the end of the block, and a `return`, `break` or `continue` that leaves it. (a function body is a block; so are a loop or `if` body, a `switch` case and a block expression.) the code is therefore duplicated at each exit.
+
+4.14.2 several defers in one block run in reverse order of registration. an early exit runs the defers of every block it leaves, innermost block first, and within a block newest first. `return` runs those of the whole function, `break` and `continue` only those inside the loop or switch they leave.
+
+4.14.3 the deferred code is evaluated when it runs, not when it is registered (as in the c defer proposal): it reads the current values of the variables it names. which variable a name means is fixed where the `defer` is written: only the locals declared before the `defer` are visible to it, so a variable that an inner block declares again, or one declared later in the block, is not seen (naming one that does not exist yet is error: y is used but never defined).
+
+4.14.4 `return e;` evaluates `e` first, then runs the defers, then returns the value; a defer that changes a variable `e` read does not change the result.
+
+4.14.5 a `defer` registers when control reaches it. a `defer` in an `if` branch belongs to that branch's block and ends with it; an early `return` before a `defer` in the same block does not run it.
+
+4.14.6 `return`, `break` and `continue` that would leave a deferred statement are errors: return can't be used in a deferred statement, break can't leave a deferred statement, continue can't leave a deferred statement. loops, `switch` and closures inside the deferred statement may use their own. a deferred statement is compiled once per exit, but an error in it is reported once.
+
+4.14.7 ownership (6): the analyzer sees the deferred statement at the exit, before the block's locals are released. `defer rt.free(p);` is the transfer of `p` at that point: `p` stays alive and usable from the `defer` until the block ends, it is not freed again nor reported as never freed, and if `p` was given away meanwhile (a second `rt.free(p)`, a call to a `.d` parameter, a store into a global) the deferred `rt.free(p)` is error: p: used after it was given away.
+
+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.
+
## 5. declarations at the top level
### 5.1 items