diff options
| author | _ov4 <ov4@ov4.org> | 2026-07-26 18:10:10 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-07-26 18:10:10 +0000 |
| commit | c2c2e1771bf9b747f38dc145b5201b45c579d465 (patch) | |
| tree | 09c752f360e56f523eb87e4a6dc17751be56210b /AGENTS/PITFALLS.md | |
| parent | a44f1246dbe24fc813a212a5ef3f5811b1d6402a (diff) | |
fix: close type-safety holes found by adversarial review; update docs
Review findings (all reproduced, then fixed and re-verified):
- '^x' leaked as a raw address into any builtin context (s <- ^x printed
the variable's bytes as a C string; NOT ^x, '&', and ^x = ^x compiled).
Address-of is now rejected everywhere except a pointer-typed target.
- A whole-array variable passed the nominal gate as a value of its
ELEMENT type (s <- arrayOfSeason stored the raw data pointer; = on it
emitted invalid IR). Whole arrays are now rejected as operands and as
coercion sources; array parameters are unaffected.
- Enum values smuggled into INTEGER through array subscripts and DECLARE
bounds (a[Autumn], ARRAY[Spring:Winter]); both now go through
emitCoercedExpr like every other INTEGER context.
- An array DECLAREd inside an IF/WHILE/REPEAT/FOR body and used after the
block was a wild-pointer write (constant bounds) or a verifier dump
(runtime bounds). Arrays are now block-scoped: control-flow bodies
snapshot and restore the ArrayTable, so escape is a clean compile error.
- Duplicate parameter names silently shadowed (BYREF write-back defeated);
now a parse error. Trailing comma in parameter lists accepted; now a
parse error consistent with every other list.
- A variable/array/parameter/function could reuse a TYPE name; reserved.
- RETURN with a value inside a PROCEDURE was silently discarded (control
did not return); now an error, as is a bare RETURN in a FUNCTION.
- INPUT r.f mis-parsed into a three-error cascade; now one clear error.
- Assigning to an enum constant now says so instead of 'Unknown variable'.
Docs: ARCH/CONVENTIONS/EXTENDING/SUBSYSTEMS/PITFALLS rewritten for the
TYPE system (ABI additions, nominal-typing invariants, admission-gate
doors, array-parameter ABI, new recipes, new sharp edges); README
features/TODO updated — random files are unblocked.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'AGENTS/PITFALLS.md')
| -rw-r--r-- | AGENTS/PITFALLS.md | 50 |
1 files changed, 40 insertions, 10 deletions
diff --git a/AGENTS/PITFALLS.md b/AGENTS/PITFALLS.md index e24b4bc..7fddfff 100644 --- a/AGENTS/PITFALLS.md +++ b/AGENTS/PITFALLS.md @@ -11,10 +11,24 @@ test programs. stray `, ", ", b` produces parse errors (recovery resyncs at the next statement start, so following statements survive; the compile still fails). Concatenate with `&`. Similarly INPUT only accepts a plain identifier — no `INPUT arr[i]`. -- **Arrays cannot cross function boundaries.** `ArrayTable` is never scoped; metadata - SSA values belong to the declaring function, so referencing another function's array - produces invalid IR. Passing a whole array as a call argument is rejected with a - compile error. +- **Arrays cross function boundaries only as parameters.** `ArrayTable` is scoped per + function now, so referencing another function's array fails cleanly as + "Undeclared array". Whole arrays pass as `ARRAY OF T` / `ARRAY[l:u,...] OF T` + parameters (BYVAL copies the entire buffer and never frees it); a whole-array + argument against a scalar parameter is still a compile error. FUNCTIONs cannot + RETURN arrays. A whole-array variable is also rejected as an operand or as a + value of its element type (it used to leak its raw data pointer). +- **Arrays are additionally block-scoped.** An array DECLAREd inside an + IF/WHILE/REPEAT/FOR body is not visible after the block ("Undeclared array") — + its buffer setup and bound SSA values live inside the branch, so letting it + escape meant wild-pointer writes or invalid IR. Scalars remain function-scoped + (their alloca is hoisted to the entry block); declare at the top of the + function like the exam board intends and neither matters. +- **Parameter modes are sticky and BYREF is FUNCTION-forbidden (§8.3).** + `(BYREF a : T, b : T)` makes `b` BYREF too — programs that relied on the old + per-parameter reset (where `b` was silently BYVAL) change meaning. Literal + arguments against an accidentally-BYREF parameter now fail with "BYREF argument + must be a variable"; annotate `BYVAL` explicitly. - **`/` always yields REAL** (double FDiv), even for two INTEGERs; integer division is DIV/MOD only. CHR returns a STRING (2-byte heap buffer), not CHAR. STR_TO_NUM always returns REAL. NUM_TO_STR formats i64 with sprintf `"%d"` — corrupts values over 32 @@ -43,11 +57,27 @@ test programs. the already-open check and get two independent buffered streams — reads through the alias can see stale/empty data instead of a Fatal. +- **TYPE-system sharp edges (§4).** `TYPE`/`ENDTYPE`/`SET`/`DEFINE` are reserved + keywords now. Typing is *nominal*: two pointer types with the same pointee, or two + records with the same layout, are incompatible. Strict pointee matching rejects the + official guide's own worked example (`^INTEGER` pointed at a `Season` variable) — + deliberate deviation. Enum OUTPUT prints the 0-based ordinal; a DECLAREd-but-unset + enum *is* the first enumerator (no uninitialized state). `E ± INTEGER` needs the + enum on the left; out-of-range results die `[Fatal]` at run time. There is no NULL + literal: pointers zero-init to null, dereferencing one dies `[Fatal]`, and unset-ness + is deliberately untestable. `RETURN ^localVar` dangles — documented, unchecked. + Record STRING fields stay null until read (normalized to `""` per-load, not at copy + time). Enum value names are globally reserved (they block DECLARE/parameter/function + names). TYPE must be top-level; array fields inside records and bounded array + parameters with non-literal bounds are dedicated compile errors. A digit run + followed by `.` is swallowed into a REAL literal, so `1.foo` mislexes (only + already-invalid programs can hit it). + ## Not implemented (9618 features) -CASE OF, CONSTANT, TYPE/ENDTYPE (records/enums/pointers), random files -(OPENFILE...FOR RANDOM/SEEK/GETRECORD/PUTRECORD — blocked on TYPE; the RANDOM keyword -is reserved and gives a dedicated parse error), DATE, multi-expression OUTPUT, INPUT -into array elements, string escape sequences. `README.md` lists `type` as the intended -next work. No tests and no CI exist; verify changes by compiling sample programs -end-to-end (command sequence in `ARCH.md`). +CASE OF, CONSTANT, SET types (`SET`/`DEFINE` are reserved with dedicated errors), +random files (OPENFILE...FOR RANDOM/SEEK/GETRECORD/PUTRECORD — TYPE is done, so these +are unblocked now; the RANDOM keyword is reserved and gives a dedicated parse error), +DATE, multi-expression OUTPUT, INPUT into array elements, string escape sequences, +array-typed record fields, whole-array RETURN. No tests and no CI exist; verify +changes by compiling sample programs end-to-end (command sequence in `ARCH.md`). |
