summary_step4.1.md 3.3 KB

V3 step 4.1 — procedure calls: emission, frames, recursion (done 2026-09-22)

Suite 79/79 (37 run — incl. 4 trap-aborts — + 42 reject). LL(1)-clean (only the known-benign TypeBlock advisory). Module-level procedures only; nested lowering = 4.2.

What lowers now

  • Calls as expressions and statements: value params (with INTEGER→REAL conversion), VAR params by address, function results, recursion, mutual recursion through FORWARD, open-array VAR formals over fixed actuals, string literals into open CHAR formals. Arity/type failures are 233 (invalid call, new message); proper-as- function and function-as-statement are 233; composite results stay 230; open value formals are 230.
  • Frames: value params copied to slots on entry (scalars, pointers, sets, arrays via CopyArray, records via CopyRecord); VAR params stay addresses; locals in stack slots (alloc4/8); named consts get function-mangled data. Same-named methods mangle by proc uid ($Init_3).
  • Bodies: RETURN emits (ret, converted when widening); fall-off-end gets a typed default; FORWARD headings emit nothing (buffered header discarded).

Backend lessons (all probed, not guessed)

  • QBE accepts statement-calls, 8-arg spill, l params, recursion, and forward references (all proven before use).
  • Reentrancy: nested calls (Acc(t, Double(5))) clobbered the shared call state (wrong target + merged args). Fixed with a 16-deep call stack; 64-arg/level cap → 233.
  • Zero-init locals: the frontend's dead target-load (Test2 heritage, harmless for globals) reads never-stored stack slots, which QBE rejects. Scalar/pointer/set slots now zero at alloc — consistent with the zero-initialized globals (arrays/records need no loads: AddrOf only).
  • Single-letter SSA temps mislink (3.1 rule, re-observed).

SymTab (additive)

ProcUid (mangled names), ProcNPar/ParamType/ParamIsVar (call checking), ProcDepth (nesting detection), VarParamOk (same type, fixed→open, literal→open-CHAR). NIL (FNil, 3.6) needed zero changes — p := NIL, p = NIL and the 233s all worked through the new paths untouched.

Grammar notes

  • NIL exempted from the named-const 230 (else nothing with NIL compiles); bare proc names shed their eager 230 (the following ArgList decides; Fact reports 230 when none).
  • AssOrCall merges assignment and statement calls (:= vs ( are LL(1)-distinct); bare P; is now a syntax error (was silently accepted — strictly better).
  • VAR actuals resolve through a temp→address note table (ring of 256; exact because SSA temps never repeat), so no expression-rule signatures changed. WITH-field actuals work (un-deferring the step-3 note).
  • F()^ result suffixes are still syntax errors (deferred).

Tests

t_call → 125 (results, recursion), t_varpar → 40 (swap, address semantics), t_mutual → 11, t_withfield → 13, t_valarray → 6 (copy proven: aliasing would give 104), t_openarr → 7, t_nestedproc → 230; t_proc/t_forward promoted to run (0); t_bad_arity/t_bad_actual/ t_bad_funstmt → 233; showcase9 → 140. Total 79.

Deferred: nested/display (4.2), separate compilation + opaque completion + HIGH/LEN (4.3), proc-type vars, F()^ suffixes, >64 params, recursive DISPOSE.