You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5.1KB


story: E06-S05 owner: codebuff status: done started: 2026-11-10 completed: 2026-11-10 depends-on: [E06-S02, E06-S04]

E06-S05 Claim

Intended change

Make an interface method call resolvable from both halves it needs: the slot that the interface declaration fixes at compile time, and the table pointer the value carries at run time. iface_slot resolves the slot from the parser tables and refuses a name the interface does not declare, so emission never invents an offset. The call itself stays if_call (E06-S02); this story proves it against the story's four behaviours, including self identity, two classes per call site, two interfaces per object, arguments, and one-/two-byte returns.

File area

  • src/common/ifcalls.inc (new): IS_OK, IS_ERR_METHOD
  • src/compiler/interfaces.s: iface_slot, is_error
  • tests/compiler/iftable-test.s: slot-resolution checks in phase 7
  • tests/runtime/ifcall-test.s (new)
  • scripts/build-ifcall-test.ps1, scripts/verify-ifcall-test.ps1 (new)
  • docs/decisions.md (D146), docs/architecture.md (status, 7.5), docs/memory-map.md (call rows)
  • planning/claims/E06-S05.md
  • generated files under build/

Acceptance evidence

  • Call uses the compile-time interface slot and runtime table pointer: iface_slot returns 0 for IFOO/ALPHA, 1 for IFOO/BETA, and 0 for IBAR/ALPHA in the table fixture, so the slot is the declaration order assign_slots recorded; the call fixture then passes those slots as constants to if_call, which reads the table pointer from the value at run time. Phase 2 swaps only the table pointer under one slot and reaches a different method for the same object.
  • self is the underlying object, not the fat-reference address: every fixture method compares the incoming A/X against its class object ($1111/$2222) while the value records live elsewhere, and any miss counts self_bad; all phases require self_bad = 0.
  • Two classes dispatch the same interface call to different methods: phase 1 calls slot 0 of IFOO on object $1111 (returns $A0) and on $2222 (returns $B0), with each class's call counter at 1.
  • One class implements and dispatches at least two interfaces: phase 2 calls slot 0 on object $1111 through the IFOO table (m_a0, $A0) and through the IBAR table (m_abar, $C0).
  • Arguments and one-/two-byte returns work through interface calls: phase 3 calls slot 1, whose method makes its own interface call and returns $5A+1 in A; phase 4 pushes a byte and a word, the method reads them at FP+2..FP+4 and returns $1234+7 in A/X ($3B/$12), and the caller's frame stack returns to $C000 after discarding three argument bytes.
  • A call site that names a method the interface does not declare is refused at compile time: iface_slot reports IS_ERR_METHOD for IBAR/GAMMA and for a class ID, verified in the table fixture.

Coordination notes

  • Depends on E06-S02 (if_call) and E06-S04 (values); both are complete. The trampoline and frame ABI were reused unchanged, so this story adds no runtime entry point and cannot regress dispatch behaviour.
  • iface_slot is the lookup emission will call; codegen still handles only byte scalars and zero-argument self calls, so wiring interface expressions into emit.s remains open compiler work rather than part of this story.
  • Interface lifetime, deletion through an interface, and null-call policy are E06-S06.

Work log

  • Added iface_slot beside assign_slots so the slot's writer and reader live in one module; it walks the interface's signature rows and returns C_MEM_SLOT rather than recomputing the index, which keeps a call site correct if slot assignment ever changes.
  • The call fixture reuses the E06-S02 dispatch conventions (A/X = value pointer, Y = slot, results in A/X, status in if_error) so it tests the call path rather than a private copy of it.
  • First build failed the .assert on the IFOO table: * - tab_a_foo spanned all four tables. The assertion now subtracts the next label, and a second one covers the one-slot IBAR table.

Completion

  • Tests run: scripts/verify-iftable-test.ps1 (x128, fresh D81, now including the slot-resolution checks); scripts/verify-ifcall-test.ps1 (x128, fresh D81); scripts/verify-parser-test.ps1 (42 semantic/parser fixtures); git diff --check.
  • Results: the table fixture still reports PASS with all eight flags, NOVA128 IFTABLE TEST OK, and its four MAP records (stub addresses moved to 7573/7574/7576/7577 because interfaces.s grew). The call fixture reports PASS with all four flags and prints IFCALL OK; its PRG ends $2354.
  • Remaining risks: the trampoline still trusts the value's object and table pointers, so a forged value can reach any address; if_call uses one shared if_vec, safe only because the jump happens before a nested call can reuse it; and argument pushes are byte-wise, so a stack refusal mid-push leaves the caller's arguments to be discarded by the caller. Generated-code emission of interface calls and lifetime remain E06-S06 and later compiler work, and physical C128 testing remains P2 validation.

Powered by TurnKey Linux.