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). |
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