aboutsummaryrefslogtreecommitdiff
path: root/docs/SPEC.md
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-15 18:29:55 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-15 18:29:55 +0330
commite7fe081efaae893ed46548fdbc3bfa39b2879878 (patch)
treead2e3804bcbc6493d7152f68e91f17d4b357c73e /docs/SPEC.md
parentconstant expressions: c precedence and / % ^ (b.1.3); local const usable in c... (diff)
duplicate case labels are an error (b.2.15); unterminated string literal reported where it starts (b.2.11); an unreachable droppable cycle drops whole in one collection (6.15.3)
Diffstat (limited to 'docs/SPEC.md')
-rw-r--r--docs/SPEC.md10
1 files changed, 5 insertions, 5 deletions
diff --git a/docs/SPEC.md b/docs/SPEC.md
index 9d860fe..a58b59a 100644
--- a/docs/SPEC.md
+++ b/docs/SPEC.md
@@ -91,7 +91,7 @@ a keyword marked only S used inside an expression is the error `NAME: can't be u
1.4.3 float literals are ieee-754 binary64 values stored in one word. forms: `1.5`, `1.` , `.5`, `1e3`, `1.5e-3`, `1E+3`. the lexer reads the digits, optional `.digits`, and an optional exponent `e|E [+-] digits`; an exponent letter without digits after it is error: bad float. a dot after digits is a fraction when a digit, `e` or `E` follows it, is a field access when a letter or `_` (other than `e`/`E`) follows (`1.field`), and otherwise ends the literal (`1.` is a float). the type is `float`.
-1.4.4 word strings. `"..."` is a string of type `char*`: the bytes are stored one per word in the code area, followed by a 0 word. escapes are as for character literals. a `\0` (or any byte 0) inside the literal ends the string there (`"ab\0cd"` is `"ab"`). a literal may contain a raw line feed. an unterminated string runs to the end of the unit and then error: expected ')' (or the like) at the end of the file. adjacent literals are not concatenated. the literal is static storage that is not a heap object; it is read-only by convention (writing through it is unspecified).
+1.4.4 word strings. `"..."` is a string of type `char*`: the bytes are stored one per word in the code area, followed by a 0 word. escapes are as for character literals. a `\0` (or any byte 0) inside the literal ends the string there (`"ab\0cd"` is `"ab"`). a literal may contain a raw line feed. a string with no closing quote is error: unterminated string literal, reported on the line where it starts. adjacent literals are not concatenated. the literal is static storage that is not a heap object; it is read-only by convention (writing through it is unspecified).
1.4.5 packed byte strings. `b"..."`, where `b` touches the opening quote, has type `bstr`: a pointer to a block whose word 0 is the length in bytes, followed by the bytes eight to a word, little-endian, followed by a 0 byte. escapes and the `\0` truncation rule are as for word strings. `b "x"` (with a space) is the name `b` then a string.
```
@@ -595,7 +595,7 @@ pn(n); // 13
switch (k) { case 1, 2: ps("one-two"); case 3: ps("three"); break; default: ps("d"); }
// k = 2: prints one-two only. no fallthrough into case 3.
```
-4.7.2 duplicate labels are not an error: the first matching case in source order runs. a non-constant label is error: expected a constant. the statements of a case form their own block.
+4.7.2 a label value that appears twice in one switch (two equal constants, or a tagged member twice) is error: duplicate case label. a non-constant label is error: expected a constant. the statements of a case form their own block.
4.7.3 tagged enums. when `e` has type pointer-to-tagged-enum, `case member(a, b):` matches the member and binds each payload field, in order, to a new local scoped to that case; the name count must equal the member's field count (error: a case binds every field of its member). `case member:` or `case member():` with no names matches by tag. a switch on a tagged enum with no `default` that omits a member prints a warning `zc: line N: warning: this switch doesn't cover M1, M2` (not an error; compile continues).
```
@@ -1028,7 +1028,7 @@ rt.put_n(*heap(15).drop(fn() { rt.put_s(" bye "); rt.put_n(*this); })); // 15
let guard = drop(heap(0), fn() { unlock(); }); // unlocks at block end
```
6.15.2 a value dies, and its drop runs, where 6.5 frees it: at the end of its owner's block or an early exit, at `rt.free`, when its owner is reassigned (6.8.5), or with its container, whose drop runs before its children's. a new value assigned to the owner can have its own drop set on the next statement, like a declaration's. a value with a drop that nothing holds (a temporary) dies at the end of its full statement, with hint: value with drop is never bound; it drops at the end of this statement. an owned local with a drop is not "never used" (6.5.2): holding it is its use.
-6.15.3 the collector runs the drop of an unreachable value after its sweep, never during it, and at most once: the value is kept until a later collection finds it unreachable again. a drop that stores `this` somewhere reachable keeps the value alive, and its drop does not run again. among unreachable values, one reached by another waits until that one has dropped (parent first); values that reach each other (a cycle, or a drop that captures its own value) have nothing outside to drop first, so one of them drops per collection, in an unspecified order, each once. there is no collection at exit, so collector-owned values are not dropped then. a freed block forgets its drop, so a block reused at the same address has none.
+6.15.3 the collector runs the drop of an unreachable value after its sweep, never during it, and at most once: the value is kept until a later collection finds it unreachable again. a drop that stores `this` somewhere reachable keeps the value alive, and its drop does not run again. among unreachable values, one reached by another waits until that one has dropped (parent first); values that reach each other (a cycle, or a drop that captures its own value) have nothing outside to drop first, so they all drop in the same collection, in an unspecified order, each once. there is no collection at exit, so collector-owned values are not dropped then. a freed block forgets its drop, so a block reused at the same address has none.
6.15.4 copying a value with a drop into an actor message is error: x: has drop and can't be copied into a message. `spawn` copies the whole vm into the worker process, so both processes hold an owned value that is live at the call and both run its drop: hint: x: has drop and is live at spawn; the worker's copy drops it too. the hint is not given for structs declared with `actor`: one that declares no `drop` gets one that waits for the replies its receiver is owed, closes the endpoint, and does nothing in the worker (docs/ACTOR.md).
6.15.5 nothing moves out of a value with a drop, whose drop may need what it holds. a field or element of it is a borrowed projection and can't be given away (error: a borrowed projection cannot transfer ownership; transfer its owner), and `pop` on an array that has a drop, or on an array field of a value with a drop, is error: a: can't move out of a value with drop. there is no `take`. borrowing a child is fine, and an array without a drop hands out its elements with `pop` as before, drops and all.
@@ -1168,11 +1168,11 @@ each item gives a minimal program, the expected behavior (per this spec) and the
8. not a bug: a global initializer that names another global uses the other global's address (5.3.2, 3.14.2): `int a = 5; int b = a + 1;` gives `b` the value `&a + 1`.
9. fixed (5.12.7): `include std;` inside a function body gave `return outside a function`; it is now error: include is top level only.
10. a syntax error hides errors in the statements skipped by recovery: `int a = ; int b = 3 +;` reports only the first error (8.2). by design, but easy to miss.
-11. fixed (1.2.2): an unterminated `/* ...` comment was accepted silently; it is error: unterminated comment. (an unterminated string literal is still only reported as a later syntax error.)
+11. fixed (1.2.2): an unterminated `/* ...` comment was accepted silently; it is error: unterminated comment. an unterminated string literal is error: unterminated string literal (1.4.4), fixed too.
12. settled (3.2.3): `x as int * y` parses `int*` as the type. this is the rule (the type is read greedily, as zc0 does too), so the program is a syntax error.
13. fixed (5.2.1): a prototype's parameters may be unnamed (`int f(int, int*);`). a lone type name counts as a type only in a prototype; with a body it is still the parameter's name, and an unnamed typed parameter there is `a parameter needs a name`.
14. fixed (3.13.5): `len(s)` on a `char*` used to return word 0 of the string when `typecheck` was off; it is now a compile error.
-15. duplicate `case` labels are not diagnosed (4.7.2).
+15. fixed (4.7.2): duplicate `case` labels were not diagnosed; they are error: duplicate case label.
16. fixed (3.14.2): a local `const` could not be used in a constant expression (b.1.7).
17. fixed: a bad float or character literal in a function's first statement was not reported (1.4).
18. fixed: `u*d` with a visible variable `u` (local or global) was read as a recast; it is a product (1.6.3).