aboutsummaryrefslogtreecommitdiff
path: root/AGENTS/PITFALLS.md
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-07-26 18:10:10 +0000
committer_ov4 <ov4@ov4.org>2026-07-26 18:10:10 +0000
commitc2c2e1771bf9b747f38dc145b5201b45c579d465 (patch)
tree09c752f360e56f523eb87e4a6dc17751be56210b /AGENTS/PITFALLS.md
parenta44f1246dbe24fc813a212a5ef3f5811b1d6402a (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.md50
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`).