diff options
| author | 2026-10-14 10:39:47 +0000 | |
|---|---|---|
| committer | 2026-10-14 10:39:47 +0000 | |
| commit | 54b8a8918bb63c98a8f58e823033232243647562 (patch) | |
| tree | e0608f7dab1f568c22390262e5a6d39966519760 /docs/SPEC.md | |
| parent | todo: handoff section with state, conventions, in-flight work and architectur... (diff) | |
zc: add the defer statement
defer expr; and defer { ... } register code that runs when the enclosing
block ends: at the end of the block, and at return, break and continue
that leave it. the tokens are kept at the defer and compiled again at each
exit (block_close, body, kw_return, kw_break, kw_continue), newest first,
before the block's variables are released, so the analyzer sees a deferred
rt.free(p) as the transfer of p where it runs. names mean what they meant
at the defer; values are read when it runs. return, break and continue
inside a deferred statement are errors, reported once. works in unchecked
code and closures (own base per function context).
Diffstat (limited to 'docs/SPEC.md')
| -rw-r--r-- | docs/SPEC.md | 19 |
1 files changed, 19 insertions, 0 deletions
diff --git a/docs/SPEC.md b/docs/SPEC.md index 51ed5d1..f3e47a7 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 |
