選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

7.1KB


story: E06-S07 owner: codebuff status: blocked started: 2026-11-10 depends-on: [E06-S05, E05-S04, E04-S06, E04-S07]

E06-S07 Claim

Intended change

Emit interface values from source rather than only from hand-written fixtures: store and copy all four bytes of an object/table pair in locals, parameters, fields and array elements; convert a class reference or null at a use site; and call an interface method through if_call with the compile-time slot. The emitted code reuses the E06-S02 trampoline, the E06-S04 conversions, and iface_slot, so no new runtime entry point or layout is introduced (D153).

File area

  • src/compiler/emit.s: four-byte value paths for a slot, a field and a call site; interface-aware em_target/em_store_target
  • src/compiler/members.s: four-byte member load/store between a field and if_dst/if_src
  • src/compiler/parser.s: pass-2 hooks for conversion assignment and interface call sites
  • src/compiler/iftables.s: the table lookup the conversion emission needs (it_find, already present)
  • src/common/emit.inc: new emitter error codes for an unsupported interface shape
  • tests/compiler/iface.n8, tests/compiler/iface-test.s
  • scripts/build-iface-test.ps1, scripts/verify-iface-test.ps1
  • tests/semantic/*.n8 for the misuse diagnostics
  • docs/decisions.md (D153), docs/architecture.md, docs/memory-map.md
  • planning/claims/E06-S07.md
  • generated files under build/iface-test/

Acceptance criteria (from the story)

  • Concrete-to-interface assignment in a statement, argument, return or initializer emits the E06-S04 conversion path and stores all four bytes.
  • null in an interface context produces the canonical null value and a call through it takes the fault path.
  • Interface-typed locals, parameters, returns and fields keep all four bytes, including array elements and copies.
  • A source-level interface call emits if_call with the value pointer in A/X and the slot in Y, with arguments and one-/two-byte returns.
  • Two classes dispatch one interface call differently, and one class dispatching two interfaces works, compiled from source.
  • Unrelated class assignment, undeclared interface member and private member through an interface each report their catalogue code and emit no PRG.
  • MAP output names the interface tables and slots the emitted code uses.

Blocking

Exact failed criteria. The argument, return and call criteria (1, 3, 4 and 5) cannot be met. The emitter has no calling-convention layer at all, so there is nothing for an interface value to travel through:

  • parse_args (parser.s, around line 2107) parses and type-checks each argument but emits no code; the only emitter hooks in the call path are em_member_receiver and em_call before the argument list.
  • em_call (emit.s) refuses C_MEM_PCOUNT != 0 with EM_ERR_CALL, so no emitted call takes arguments.
  • em_return emits a bare RTS; em_begin_func emits no prologue and fl_prologue/fl_ret/fl_ret_byte/fl_ret_word are never called by the parser, so parameters and returns have no emitted frame.
  • em_target, em_store_target, em_expr_name, em_push_left and em_bin reject every width but one byte, so interface locals/fields cannot even be loaded or stored yet.
  • it_class builds the per-class tables only in the output stage, after the code section's length and addresses exist. A conversion emits the table address as an immediate, so the table has to be placed (or its address fixed) before pass 2 can emit the conversion.

Evidence collected. src/compiler/emit.s (whole file), parser.s call/assignment/argument paths, flow.s, codegen.s, iftables.s, members.s, interfaces.s, and runtime/interfaces.s were read. No emitter hook for arguments, prologue or value returns exists in any of them; the calls-test.s fixture passes self in A/X and runs a method body directly, which is why the gap has not surfaced before.

Smallest decision or fact needed. A calling-convention emission story must land first (argument push in source order, argument byte count for the discard, frame_enter prologue sizing, return with 0/1/2-byte and interface results), together with a decision placing interface tables at a layout-time-known address so a conversion can embed the table pointer. That work is larger than this story and changes the generated ABI/section layout, so it needs its own story and a Dxxx record rather than being folded in here.

Safe work that remains possible. The storage half can be implemented and proven without the calling convention: four-byte em_expr_name/em_target/ em_store_target for locals and fields, four-byte member load/store in members.s, null as the canonical zero value, and copy-mode if_assign between two interface slots. It cannot satisfy the story's call and argument criteria on its own, so the claim stays blocked until the calling-convention story exists.

Coordination notes

  • The type checker is not the gap: types.s classifies class and interface references by name and ty_assign accepts a class for an interface it implements (tests/semantic/refs.n8).
  • if_assign takes the destination in A/X and the mode in Y (0 copies if_src, any other value converts if_obj with if_tab); if_call takes the value pointer in A/X and the slot in Y. Both are already proven by tests/runtime/convert-test.s and tests/runtime/ifcall-test.s.
  • Zero page has no free pair: $FB-$FE is FRAME_SP/FRAME_FP, $F5-$F8 is EM_LP/EM_TMP, $F9 is MB_SELF, $C0-$C5 are the print vectors and $C6+ is IF_RESULT. A second value pointer must live in BSS and be copied to if_src/if_dst with absolute stores.
  • E07-S04's interface sample depends on this story, so E07-S04 is not eligible work while this claim is blocked.

Work log

  • Read the emitter, parser call/argument paths, flow, codegen, iftables, member and interface runtime modules. Confirmed the emitter is byte-only end to end and that the parser's argument list emits no instructions.
  • Confirmed the only interface machinery that exists is the runtime (if_assign, if_call, if_ret_*, if_arg_push, if_elem_*) and the pass-3 table builder; nothing emits calls to them.
  • Confirmed interface tables are built after the code section, so a conversion cannot embed a table address during the emitting pass without a layout decision.
  • Re-read the story's dependency E04-S07: its claim covers sizing, pass-3 addresses, MAP method records and deterministic PRGs, not the argument and frame emission this story needs.

Completion

  • Tests run: none; no source change has landed for this story.
  • Results: not applicable. The claim is blocked rather than done because four acceptance criteria cannot be verified without the calling-convention layer.
  • Remaining risks: implementing the storage half alone would leave a compiler that can copy interface values but cannot call through them, which is worse than the current explicit refusal; the story should not be closed until the prerequisite story and the table-placement decision exist.

Powered by TurnKey Linux.