From 8a2d8ce312f694ecfeae4ca52c7b035bfc18c16a Mon Sep 17 00:00:00 2001 From: Daniel Covington Date: Tue, 10 Nov 2026 17:08:43 -0500 Subject: [PATCH] Record E06-S07 as blocked on calling-convention emission. The story needs argument, return and interface-call emission, but the emitter has no argument push, prologue or value return at all, and interface tables are only placed after the code section. Document the exact gap and the decision needed so the prerequisite work is not mistaken for this story. Generated with Codebuff Co-Authored-By: Codebuff --- newLang/planning/claims/E06-S07.md | 132 +++++++++++++++++------------ 1 file changed, 77 insertions(+), 55 deletions(-) diff --git a/newLang/planning/claims/E06-S07.md b/newLang/planning/claims/E06-S07.md index af46f6f..a52ff63 100644 --- a/newLang/planning/claims/E06-S07.md +++ b/newLang/planning/claims/E06-S07.md @@ -1,7 +1,7 @@ --- story: E06-S07 owner: codebuff -status: in-progress +status: blocked started: 2026-11-10 depends-on: [E06-S05, E05-S04, E04-S06, E04-S07] --- @@ -52,67 +52,89 @@ emitted code reuses the E06-S02 trampoline, the E06-S04 conversions, and 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` already classifies class and - interface references by name and `ty_assign` accepts a class for an interface - it implements (`tests/semantic/refs.n8`). Interface slots therefore allocate - four bytes through the existing size argument; the byte-only emitter is what - refuses them (`EM_ERR_SIZE` from `em_expr_name`, `em_target`, `em_bin`). +- 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 and reports through `if_error`. - Both are already proven by `tests/runtime/convert-test.s` and - `tests/runtime/ifcall-test.s`. -- Zero page has no room for another pointer: `$F2-$F4` is the only free gap, so - D153 keeps one value address (`EM_LP`) plus the runtime's own module - pointers. -- E04-S07 owns frame sizing, pass-3 addresses and argument push/discard, so - interface calls with arguments cannot be emitted before it; that is why this - claim lists it as a dependency. The zero-argument call and all storage and - conversion shapes do not need it. + 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 `emit.s`, `members.s`, `codegen.s`, `emit.inc`, `interfaces.inc`, - `types.s` and the runtime interface API. The emitter is byte-only end to end: - `em_size` is compared with 1 in load, push, combine, target and store, and - `em_target` accepts only a one-byte local or a one-byte self field. -- `mb_ld_field`/`mb_st_field` accept `MB_BYTE` or `MB_WORD` only, so a - four-byte interface field needs its own member transfer; `mb_point` already - loads any object reference into `MB_SELF`, which a receiver expression can - reuse. -- `iface_slot` returns the slot from the interface's signature rows and is the - only correct source of the call's Y; `it_find` returns the class's table for a - given interface, which is the compile-time half of a conversion. -- Emitter state has no four-byte temp: `em_size` is a single byte count and the - left-operand path pushes exactly one byte. Conversions and copies must move - all four bytes through the runtime routines rather than a new temp. -- Story dependency corrected: E04-S07 added, because argument push/discard and - word results are its work, and the story's argument criterion cannot be met - without it. +- 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 yet. This claim records analysis and the emission decision; - no emitter, parser or fixture change has landed, so there is no behaviour to - verify and the claim stays `in-progress` rather than `done`. -- Results: not applicable. -- Remaining risks: pass 2 emits as it type-checks, so a four-byte target must be - known before the operand is emitted, which is a different ordering from the - byte path where the target is captured and consumed afterwards; a conversion - site must also decide between `if_assign` mode 0 and 1 from the source type - that `types.s` already computed. - -## Next steps - -1. Add the four-byte slot load/store and `em_target`/`em_store_target` cases - with `IF_VALUE_SIZE`, plus `EM_ERR_SIZE` only for shapes that are still - unsupported. -2. Add `mb_ld_iface`/`mb_st_iface` and use them for interface fields. -3. Emit conversion assignment at a use site: `it_find` for `if_tab`, source - pointer or zero into `if_obj`, destination slot pointer into `if_dst`, - `JSR if_assign` with mode 0 or 1. -4. Emit `if_call` from an interface receiver with `iface_slot` as Y, then the - `iface.n8` fixture with two classes and two interfaces, and the misuse - diagnostics in the semantic suite. +- 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.