aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/LEARN.md38
-rw-r--r--docs/SPEC.md19
2 files changed, 57 insertions, 0 deletions
diff --git a/docs/LEARN.md b/docs/LEARN.md
index a3588e7..d514d85 100644
--- a/docs/LEARN.md
+++ b/docs/LEARN.md
@@ -174,6 +174,44 @@ output:
0 5 6
```
+### defer
+
+`defer expr;` or `defer { statements }` runs code when the enclosing block ends: after the last statement, and also on `return`, `break` and `continue` out of it. defers in a block run last registered first. the deferred code is compiled at each exit, so it sees the variables as they are when it runs, and `return e;` computes `e` before any defer runs. `defer rt.free(p);` hands the owner `p` over at that point: `p` stays usable until the block ends and is not freed twice. `return`, `break` and `continue` are not allowed inside a deferred statement.
+
+[example source](../examples/learn/34-defer.zc):
+
+```zc
+namespace;
+include rt;
+fn say(n) { rt.put_n(n); rt.put_c(32); return 0; }
+int work(int n) {
+ int* p = rt.new(int);
+ defer rt.free(p);
+ defer say(100);
+ *p = n * 2;
+ say(*p);
+ if (n > 2) return *p + 1;
+ say(1);
+ return 0;
+}
+fn main() {
+ for (let i = 0; i < 4; i++) {
+ defer { say(i); rt.put_c(10); }
+ if (i == 1) continue;
+ say(work(i));
+ }
+}
+```
+
+output:
+
+```text
+0 1 100 0 0
+1
+4 1 100 0 2
+6 100 7 3
+```
+
## numbers and strings
### sized integers
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