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.