refactor(adr7): extract the per-boundary call-lowering seam (path-H inc 2) - #360
Conversation
…nc 2) Executes ADR-7 path-H, increment 2: lift the "how is this cross-component call boundary lowered" decision out of `FactStyleGenerator::generate_adapter` into a dedicated `adapter::call_lowering` module — behavior-preservingly. The three-way class dispatch (`crosses_memory` / `needs_transcoding` → Direct / MemoryCopy / Transcode) and the #304 inline-eligibility guard are decided together in `resolve_call_lowering_plan`, because they are coupled: an adapter may be inlined only when its class is Direct AND it carries no resource conversions AND no post-return, and that guard must stay a superset of `generate_direct_adapter`'s pure-trampoline branch. Computing both in one pure function makes the coupling explicit and turns the generator's call site into a lookup instead of a re-derivation. Async boundaries are unaffected — they are dispatched before generate_adapter (the is_async_lift branch in `generate`), so they never flow through the seam; the match arm is `unreachable!` documenting that. Future per-boundary ABI strategies (symmetric ABI, static-base PIC / #353) become new inputs to `resolve_call_lowering_plan` rather than new branches threaded through the adapter hot path — mirroring the address-strategy seam (#359, inc 1). - adapter/call_lowering.rs: BoundaryFacts + CallLoweringPlan + resolve_call_lowering_plan; 8 unit tests covering the full class + inline-eligibility truth table. - adapter/fact.rs: generate_adapter now resolves the plan, matches on the class for body generation, and reads inline_eligible for the #304 bypass. - adapter/mod.rs: register the module. No behavior change: full meld-core suite (481 lib + all integration incl. wit_bindgen_runtime, runtime_intra_adapter, transcoding, #304 inline) green; fmt + clippy clean. Tier-5 (adapter/fact.rs + new fusion-critical module) -> Mythos discover pass to follow. Refs #354 (ADR-7) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mythos delta-pass requiredThis PR modifies one or more Tier-5 source files (per Before merge, run the Mythos discover protocol on the
Why this gate exists: LS-A-10 The gate check on this PR will pass once the label is |
Mythos discover pass — NO FINDINGSThis PR is a single logical extraction (the per-boundary call-lowering decision) spanning the three touched Tier-5 files, so it was reviewed as one adversarial unit with the highest prior on the refactor failure mode: does the extracted
|
LS-N verification gate✅ 59/59 approved LS entries verified
Approved Failed LS entries(none) Missing regression tests(none) Updated automatically by |
Mythos delta-pass (auto)✅ NO FINDINGS across 3 Tier-5 file(s)
Auto-run via |
Triage of the auto-Mythos finding (
|
… seam (#361) The auto-Mythos delta-pass on the call-lowering seam surfaced a real, pre-existing latent bug (faithfully preserved by inc 2's behavior-preserving extraction, not introduced by it): a sync call that needs string transcoding but does NOT cross a memory boundary (`needs_transcoding && !crosses_memory` — e.g. `--memory shared` fusion of a UTF-8 component calling a UTF-16 one) routed to `AdapterClass::Direct`, a thin shim that does not transcode, so the string bytes were delivered verbatim in the wrong encoding — a silent miscompile. No upstream validation rejected mixed encodings under shared memory (only the LS-P-17 warning). meld already fails loud on the identical ASYNC case (`guard_async_cross_encoding_strings`, fact.rs:12139, #272). This adds the sync twin: `resolve_call_lowering_plan` now returns `Result` and hard-fails with `AdapterGeneration` on that boundary rather than emit a silently mis-transcoding Direct shim — the #351/#352/#355 silent-corruption-vs-loud- failure choice, applied consistently. Actually supporting same-memory transcoding (a Transcode-class adapter without the cross-memory realloc+copy assumption) is tracked in #361; the seam is the right home to resolve that boundary to a real adapter later. - call_lowering.rs: resolve_call_lowering_plan -> Result; guard + 9th unit test (same_memory_transcoding_hard_fails); the class + inline truth table is otherwise unchanged. - fact.rs: generate_adapter propagates the seam's Result via `?`. Full meld-core suite green (0 failures; guard fires on no existing fixture — the path was silently latent); fmt + clippy clean. Refs #360, #361, #272, ADR-7 (path-H inc 2) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mythos update — finding resolved by the #361 hard-failThe auto-gate finding is addressed at the source: Re-verified on the new commit (
The class + inline-eligibility decision is otherwise the same behavior-preserving extraction verified earlier (2⁶ truth-table equivalence). |
…-fail) (#367) #360 hard-failed a sync boundary that needs transcoding but does not cross memory (`--memory shared` fusion of a UTF-8 caller + UTF-16 callee) — the loud-not-silent placeholder for the gap the call-lowering seam surfaced. This lands the actual support. Root: the transcode adapter (`generate_transcoding_adapter`) is already memory-index-parameterised — source reads use `caller_memory`, destination writes use `callee_memory`, and it transcodes byte-by-byte (no cross-memory `memory.copy`). So when the two are the SAME memory (both 0 under `--memory shared`) it transcodes correctly within that one memory. Only the class dispatch was gating it: it reached `Transcode` only when memory was crossed. Fix = route `needs_transcoding` → `Transcode` regardless of `crosses_memory`, and drop the hard-fail. - call_lowering.rs: `resolve_call_lowering_plan` routes any transcoding boundary to `Transcode`; the `same_memory_transcoding_hard_fails` unit test becomes `..._routes_to_transcode`. - adapter_safety.rs: `test_361_same_memory_utf8_to_utf16_transcoding` — the SR-17 UTF-8→UTF-16 'Hello' fusion under `SharedMemory`, executed on wasmtime, returns 500 (correct transcode), not a verbatim mis-copy. (Confirmed it hard-failed before this change.) - verification-matrix: SR-17 gains the same-memory execution oracle. Full meld-core suite green (0 failures); fmt + clippy clean. Tier-5 (adapter/) → Mythos delta-pass to follow. Closes #361. Refs #360, #272, SR-17. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s + static-PIC + same-memory transcoding (#369) Ships the full ADR-7 path-H arc (address-strategy seam #359, call-lowering seam #360, multiply-instantiated modules #362/#363, static-PIC fold #365, ADR record #354) plus the extended-const fold completion (#368), the #364 safety fix (#366), and same-memory string transcoding (#367). New capabilities: multiply-instantiated module support (RFC-46 Q1, MultiMemory, SR-55), static-PIC data/element offset folding (#353), same-memory string transcoding (#361). Safety: multiply-instantiated gated to the execution- verified MultiMemory case (#364); the sync same-memory cross-encoding miscompile closed (#360/#361). Falsification statements in CHANGELOG. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Executes ADR-7 path-H, increment 2 (#354): lift the "how is this
cross-component call boundary lowered" decision out of
FactStyleGenerator::generate_adapterinto a dedicatedadapter::call_loweringmodule. The call-lowering counterpart of the address-strategy seam (#359, inc 1).
What
adapter/call_lowering.rs—BoundaryFacts+CallLoweringPlan+resolve_call_lowering_plan. The three-way class dispatch(
crosses_memory/needs_transcoding→Direct/MemoryCopy/Transcode) and the Fusion emits identity trampolines that never inline (dead inline_adapters flag); de-export fusion-vestigial symbols #304 inline-eligibility guard are resolved together(they are coupled — see the module doc). 9 unit tests cover the class + inline
truth table and the hard-fail below.
adapter/fact.rs—generate_adapterresolves the plan,matches on theclass for body generation, and reads
inline_eligiblefor the Fusion emits identity trampolines that never inline (dead inline_adapters flag); de-export fusion-vestigial symbols #304 bypass.adapter/mod.rs— register the module.One deliberate behavior change: a silent-corruption backstop (#361)
The extraction is otherwise behavior-preserving. But the auto-Mythos delta-pass
surfaced a real, pre-existing latent bug that the original inline dispatch
carried: a sync call that needs transcoding but does not cross memory
(
needs_transcoding && !crosses_memory— e.g.--memory sharedfusion of aUTF-8 caller and a UTF-16 callee) routed to
Direct, a shim that does nottranscode → string bytes delivered verbatim in the wrong encoding, silently.
meld already fails loud on the identical async case
(
guard_async_cross_encoding_strings,fact.rs:12139, #272). This PR adds thesync twin:
resolve_call_lowering_planhard-fails withAdapterGenerationrather than emit a silently mis-transcoding adapter — the #351/#352/#355
silent-corruption-vs-loud-failure choice, applied consistently. Actually
supporting same-memory transcoding is tracked in #361.
The guard fires on no existing fixture (the path was silently latent); full
meld-coresuite green (0 failures), fmt + clippy clean.Async is unaffected
Async boundaries are dispatched before
generate_adapter(theis_async_liftbranch ingenerate), so they never flow through this seam; theAdapterClass::Asyncmatch arm isunreachable!and documents that.Why this is the seam ADR-7 wants
Future per-boundary ABI strategies — symmetric ABI (cpetig/Aptiv) and
static-base PIC (#353) — and same-memory transcoding (#361) become new
inputs/outputs of
resolve_call_lowering_planrather than new branches threadedthrough the adapter hot path.
Tier-5 (
adapter/fact.rs+ new fusion-critical module) → Mythos delta-passbelow.
Refs #354 (ADR-7), #361, #272