nes-pascal

Milestone 0.5.12 — Functions: Completeness and Quality Audit

English Português (Brasil)

This document is an independent review artifact for milestone 0.5.12 (Functions). It is distinct from the implementation report functions-0.5.12.md, which is the implementer’s milestone snapshot. This audit records evidence-based verification, coverage findings, and a backlog for follow-up hardening.


1. Milestone requirements matrix

The contract is the Functions section of roadmap/0.md (identifier 0.5.12). All twelve requirements were verified against concrete repository evidence, not names or comments.

# Requirement Status Evidence
1 Function declarations Verified function grammar in parser.py (typed name, optional parameter list, optional return type); declarations accepted between var and the main block; test_parser_represents_typed_function_and_explicit_call, test_parser_rejects_malformed_function_declarations; declarations interleaved with procedures.
2 Function calls Verified FunctionCall AST node and ResolvedFunctionCall; every call requires parentheses, including parameterless calls; test_forward_calls_resolve_in_callee_first_order; nested calls in arguments/arithmetic/comparisons/conditions verified in runtime fixtures and probes.
3 byte return values Verified function_result_<name> regular-RAM backing byte; epilogue lda function_result_<name> + rts; test_return_storage_is_regular_ram_and_absent_without_functions; golden ABI; Mesen runtime (nested arithmetic results $06/$31/$66/$F3).
4 boolean return values Verified Canonical $00/$01 materialization; accumulator carries the result; the final lda leaves the Z flag valid so caller short-circuit branches directly; test_boolean_function_is_valid_in_short_circuit_expression; probe and Mesen short-circuit verification.
5 Function parameters Verified byte/boolean value parameters using the static regular-RAM procedure ABI (parameter_<name> symbols); type validation via E4005; probe-verified rejection of enum and color parameter types.
6 Return type validation Verified Only byte/boolean returns; E4026 for any other type (tests/fixtures/diagnostics/unsupported_function_return_type.nsp); wrong result literals rejected with E4004 (wrong_boolean_function_result.nsp, wrong_byte_function_result.nsp).
7 Function calls inside expressions Verified Calls resolve as value expressions in arithmetic, comparisons, array indexes, record field writes, if/while/for conditions, nes.* arguments, and short-circuit operands; verified at unit, golden, and Mesen-runtime layers.
8 Define return-value storage or calling convention Verified Documented ABI in docs/language/functions.md and functions-0.5.12.md: static regular-RAM result byte per function, result returned in A, A/X/Y/flags caller-clobbered, hardware-stack return addresses, no runtime frame; golden tests/golden/functions_abi.asm.
9 Preserve outer expression temporaries across nested function calls Verified callable_bases in TemporaryRequirements; codegen pre-acquires each callee’s base prefix so callee body temporaries sit above caller-live slots; _generate_call_arguments leases earlier argument results across later call-containing arguments; analysis and codegen agree; pressure golden (Leaf at base 2, peak 3 live bytes).
10 Support nested function-call expressions on top of the scoped temporary allocator Verified Nested calls reuse the 0.5.11 scoped pool without aliasing; 4-deep probe (max_call_depth 4, 3 live caller temps) verified via Mesen; nested call chains with sibling stacking verified.
11 Define evaluation order when function calls appear in larger expressions Verified Documented and verified: arguments left-to-right; and/or left-to-right short-circuit (skipped operands do not execute); binary arithmetic/comparison keeps right-operand-first when the right side requires evaluation (LeftCall() - RightCall() runs RightCall() first). Matches pre-existing lowering rules; no regression.
12 Reject direct and indirect recursion involving functions Verified E3014 for direct (recursive_function_call.nsp), indirect (recursive_function_call_indirect.nsp), and mixed procedure/function cycles (recursive_callable_mixed.nsp); test_direct_indirect_and_mixed_recursion_are_rejected; probe confirmed a cycle is rejected even when the recursive function is unreachable from the main block.

Uncovered roadmap requirements: none. All 12 requirements are implemented, documented, and exercised by tests, fixtures, goldens, or benchmarks.


2. Positive semantic coverage

Normal / common cases (covered)

Boundary conditions (covered)

Interactions (covered)

Cases only indirectly covered


3. Diagnostic coverage matrix

Invalid condition Expected code Existing test / fixture Status
Unknown function name E3059 tests/fixtures/diagnostics/unknown_function.nsp + unit case Missing() Covered
Wrong argument count E3060 tests/fixtures/diagnostics/function_argument_count.nsp + unit case One() Covered
Function used as statement E3061 tests/fixtures/diagnostics/function_used_as_statement.nsp + unit case Covered
Procedure used as expression E3062 tests/fixtures/diagnostics/procedure_used_as_expression.nsp + unit case Value := Work() Covered
Result read before assigned / not on every path E3063 tests/fixtures/diagnostics/undefined_function_result.nsp + unit definite-assignment tests Covered
Wrong result type / argument type E4004 wrong_byte_function_result.nsp, wrong_boolean_function_result.nsp (result); argument type only unit-tested (Enabled($01)) Covered (result); P3-1 (argument)
Unsupported return type E4026 tests/fixtures/diagnostics/unsupported_function_return_type.nsp Covered
Unsupported parameter type E4005 pre-existing parameter fixtures exercised for enum/color via probes; enum-specific fixture enum_procedure_parameter.nsp Covered
Direct / indirect / mixed recursion E3014 recursive_function_call.nsp, recursive_function_call_indirect.nsp, recursive_callable_mixed.nsp Covered
Function registered as frame callback E3018 probe-verified only; function is declared yet reported “Unknown callback procedure” (P3-3) P3-3
Function called without parentheses E3005 probe-verified; “Unknown identifier” (P3-4) P3-4
Bare statement call with wrong arg count E3061 before E3060 function_used_as_statement.nsp (correct count); wrong-count statement probe reports E3061 (by design, P3-5) P3-5

All six new function-specific diagnostics (E3059E3063, E4026) are registered in the canonical catalog (docs/DIAGNOSTICS.md, docs/reference/diagnostics/index.md, semantic.md/type-system.md, and the maintained PT-BR counterparts), validated by the diagnostic-catalog test, and have focused negative fixtures. Messages are specific and actionable with suggestion text.


4. Parser / semantic / backend layer coverage

Layer Evidence Assessment
Lexer / Parser function keyword, optional parameter list, optional : type, begin/end body; statement-level F(...) parsed as a procedure call so E3061 fires; test_parser_represents_typed_function_and_explicit_call, test_parser_rejects_malformed_function_declarations Covered
Semantic FunctionDeclaration/FunctionCall resolution, ResolvedFunction, ResolvedFunctionCall, ResolvedFunctionResultAssignment; definite-result analysis with short-circuit-aware path rules; recursion-cycle detection; shared global namespace; function/callback placement rules Covered
AST FunctionDeclaration, FunctionCall, ResolvedFunction* nodes; dedicated types; catalog of resolved nodes updated Covered
Memory layout function_result_* regular-RAM symbols in FUNCTION_RESULTS; zero cost when no functions (test_return_storage_is_regular_ram_and_absent_without_functions, benchmark identity); parameters reuse the procedure static-RAM ABI Covered
Backend codegen_analysis.py computes callable_bases + max_call_depth; backend_ca65.py pre-acquires base slots, leases argument results across call-containing arguments, emits lda function_result_X; rts epilogue; _zero_flag_is_valid enables direct Z branching for boolean results; analysis and codegen lease behavior agree (probe-verified across 20+ scenarios) Covered
Runtime support No new runtime code; hardware stack reserved in full for JSR/RTS return addresses; benchmarks unchanged Covered

5. Golden Assembly audit

tests/golden/functions_abi.asm records the return-ABI contract: epilogue lda function_result_X / rts, caller jsr, and result staging.

tests/golden/functions_temporary_pressure.asm is the critical temporary-safety golden: the caller holds expression_temporary_0, function_Middle acquires expression_temporary_1 (base 1), and function_Leaf acquires expression_temporary_2 (base 2) — a verified peak of three simultaneously live bytes at source call depth two (four hardware-stack bytes). This is exactly the boundary the milestone must not regress.

Classification: required — exists. The focused-golden convention is followed; no full-file golden is warranted for Functions.


6. Toolchain validation


7. Mesen runtime coverage

tests/mesen/verify_functions.lua runs tests/fixtures/runtime/functions.nsp and asserts concrete runtime memory: nested static-parameter safety, left-to-right arguments, right-first complex arithmetic, comparisons, Boolean normalization, short-circuit side effects, procedure/function interaction, and 8-bit wraparound. It is wired as test_functions_preserve_nested_static_parameters_and_short_circuits; all 29 MesenIntegrationTests pass locally.

The independent probe battery adds runtime verification beyond the shipped fixture (each address cross-checked against its memory map):

This is behavioral verification of call-safe temporaries, evaluation order, and nested parameter safety, not a ROM-boots-only check.


8. Benchmark / resource coverage


9. Documentation coverage

Checked: docs/language/functions.md, docs/pt-BR/language/functions.md, docs/runtime/cpu-memory.md, docs/pt-BR/runtime/cpu-memory.md, docs/reference/unsupported-features.md, docs/reference/diagnostics/{index,semantic,type-system}.md + PT-BR, docs/compiler/functions-0.5.12.md, docs/compiler/test-coverage-map.md + PT-BR, docs/index.md, docs/DIAGNOSTICS.md, docs/getting-started/*, roadmap 0.md + README.md, README, and docs/reference/compiler-pipeline.md.


10. Test coverage map

docs/compiler/test-coverage-map.md (EN/PT-BR) adds subsystem 31 “Functions” with Strong across every applicable tier (Semantic, Memory Layout, Backend ASM, Golden ASM, Toolchain, Mesen Runtime, Benchmark Corpus) and N/A for Lexer/Parser (grammar is exercised via unit tests but the coverage map marks it N/A per the established convention). This classification is defensible: focused unit tests for all 12 requirements, negative fixtures for every new diagnostic, a focused ABI golden plus a temporary-pressure golden, toolchain assembly/linking of the 21-benchmark corpus, a Mesen runtime test, and exact benchmark accounting exist for every applicable tier. No map correction is warranted.


11. Regression and interaction audit


12. Coverage gaps and findings

Severity Finding
P0 None found. No correctness or semantic defect identified in the implemented, documented scope.
P1 None. All 12 documented milestone requirements are implemented and have focused regression protection.
P2-1 No compile-time guard on maximum source-call depth vs the 256-byte hardware stack Resolved on fix/function-call-depth-stack-guard: a derived budget reserves 10 bytes beyond the two bytes per active JSR return address (4 bytes for runtime-internal JSR frames reachable from user statements, 6 bytes for NMI headroom), giving a supported maximum callable depth of (256 - 10) / 2 = 123. E5007 (HARDWARE_STACK_CALL_DEPTH_EXHAUSTED) rejects deeper acyclic chains at compile time; the 124-chain boundary is covered by focused tests in tests/test_functions.py.
P3-1 Function argument type mismatch (E4004) has no dedicated negative fixture; only an in-process unit assertion (Enabled($01)) and CLI probes. Same code path as the pre-existing E4004; low incremental value.
P3-2 docs/pt-BR/compiler/functions-0.5.12.md missing; breaks the per-milestone PT-BR design-doc pattern (all prior milestone design docs are translated). All user-facing 0.5.12 docs are translated.
P3-3 A function registered as a frame callback (nes.on_update(SomeFunction)) is rejected with E3018 “Unknown callback procedure: SomeFunction” although the function is declared; the rejection is correct, but the message does not explain that functions cannot be callbacks.
P3-4 A function called without parentheses (Value := CurrentScore;) reports E3005 “Unknown identifier: CurrentScore”; accurate but does not suggest the required ().
P3-5 A bare statement call with the wrong argument count (F($01, $02);) reports E3061 (function used as a statement) rather than E3060 (argument count). By design — statement-level calls parse as procedure calls; the primary error surfaces first.

13. Local validation results

14. GitHub Actions run

Pushed audit/0.5.12-validation. The authoritative CI pipeline (.github/workflows/ci.yml) ran against the pushed branch. The final validation run (after the record-keeping commit a5add38) is GitHub Actions run 31772718704 (run number 56, event push, head a5add38).

15. Final ci-gate

The aggregated ci-gate job passed for the pushed branch, confirming the compiler-toolchain and Mesen runtime jobs and the overall gate remotely. Local evidence (section 13) agrees with the remote result.


Recommendation

All twelve milestone 0.5.12 requirements are verified against concrete evidence (implementation, unit tests, negative fixtures, two focused goldens, toolchain assembly/linking, 29 headless Mesen runs, and exact benchmark accounting). The feature is layered correctly across parser, semantic, memory layout, and backend; analysis and codegen agree on temporary liveness, so call-safety divergence would be a loud compile-time failure rather than silent corruption. Evaluation order, Boolean materialization, short-circuit lowering, and all size/cycle invariants are preserved (pre/post benchmark identity across the corpus). No P0, P1, or P2 findings remain: the single P2 hardening item (maximum source-call depth guard, E5007) is resolved on fix/function-call-depth-stack-guard with a derived 123-call budget and focused boundary coverage. The remaining P3 items are documentation polish and minor diagnostic wording, none of which block the milestone’s acceptance criteria.

READY