From 26fe441cce7ea72e4b2f215ccbf144181f634b64 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 19 Apr 2026 03:01:07 -0400 Subject: [PATCH 1/3] WIP: 552 followup --- src/v3/compiler/src/emit.rs | 14 ++------------ src/v3/compiler/src/emit/python_target.rs | 20 ++++---------------- src/v3/compiler/src/emit/rust_target.rs | 22 ++++++---------------- src/v3/spec/go.dag | 1 + src/v3/spec/python.dag | 1 + src/v3/spec/rust.dag | 3 +++ src/v3/std/emit_model.dag | 13 +++++++++---- 7 files changed, 26 insertions(+), 48 deletions(-) diff --git a/src/v3/compiler/src/emit.rs b/src/v3/compiler/src/emit.rs index 7ca03127950..61bae027f48 100644 --- a/src/v3/compiler/src/emit.rs +++ b/src/v3/compiler/src/emit.rs @@ -83,14 +83,8 @@ enum CallableStrategyBinding { ListContains, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum PatternStrategyBinding { - VectorList, -} - #[derive(Debug, Clone)] struct PatternRealizationBinding { - strategy: PatternStrategyBinding, scrutinee: String, empty_pattern: String, cons_pattern: String, @@ -1208,11 +1202,8 @@ impl<'a> Ctx<'a> { let Some(binding) = self.indexes.patterns.get(&disj_id) else { return Ok(None); }; - match binding.strategy { - PatternStrategyBinding::VectorList => self - .render_vector_list_pattern_branch(branch, disj_id, binding, locals) - .map(Some), - } + self.render_vector_list_pattern_branch(branch, disj_id, binding, locals) + .map(Some) } fn render_vector_list_pattern_branch( @@ -2436,7 +2427,6 @@ fn require_pattern_realization( }); } Ok(PatternRealizationBinding { - strategy: PatternStrategyBinding::VectorList, scrutinee: require_field_string(fields, "scrutinee", declaration)?, empty_pattern: require_field_string(fields, "empty_pattern", declaration)?, cons_pattern: require_field_string(fields, "cons_pattern", declaration)?, diff --git a/src/v3/compiler/src/emit/python_target.rs b/src/v3/compiler/src/emit/python_target.rs index b5f8be25db5..b8311c3f0f1 100644 --- a/src/v3/compiler/src/emit/python_target.rs +++ b/src/v3/compiler/src/emit/python_target.rs @@ -122,14 +122,8 @@ enum PatternBindingRuleBinding { NotApplicable, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum PatternStrategyBinding { - VectorList, -} - #[derive(Debug, Clone)] struct PatternRealizationBinding { - strategy: PatternStrategyBinding, scrutinee: String, empty_pattern: String, cons_pattern: String, @@ -825,11 +819,8 @@ impl<'a> Ctx<'a> { let Some(binding) = self.indexes.patterns.get(&disj_id) else { return Ok(None); }; - match binding.strategy { - PatternStrategyBinding::VectorList => self - .render_vector_list_pattern_branch(branch, disj_id, binding, locals) - .map(Some), - } + self.render_vector_list_pattern_branch(branch, disj_id, binding, locals) + .map(Some) } fn render_vector_list_pattern_branch( @@ -1596,16 +1587,13 @@ fn parse_pattern_realization( detail: "PatternStrategy variants must not carry payload", }); } - let strategy = if constructor == named_variant_id(dag, "PatternStrategy", "VectorList")? { - PatternStrategyBinding::VectorList - } else { + if constructor != named_variant_id(dag, "PatternStrategy", "VectorList")? { return Err(EmitPythonError::MalformedSpec { declaration, detail: "unsupported PatternStrategy variant", }); - }; + } Ok(PatternRealizationBinding { - strategy, scrutinee: require_field_string(fields, "scrutinee", declaration)?, empty_pattern: require_field_string(fields, "empty_pattern", declaration)?, cons_pattern: require_field_string(fields, "cons_pattern", declaration)?, diff --git a/src/v3/compiler/src/emit/rust_target.rs b/src/v3/compiler/src/emit/rust_target.rs index 39125c52cf1..06b3f766139 100644 --- a/src/v3/compiler/src/emit/rust_target.rs +++ b/src/v3/compiler/src/emit/rust_target.rs @@ -301,14 +301,8 @@ impl ParameterDispositionBinding { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum RustPatternStrategyBinding { - VectorList, -} - #[derive(Debug, Clone)] struct PatternRealizationBinding { - strategy: RustPatternStrategyBinding, scrutinee: String, empty_pattern: String, cons_pattern: String, @@ -1987,29 +1981,28 @@ fn require_pattern_realization( else { return Err(EmitError::MalformedRealization { declaration, - detail: "PatternRealization.strategy must be a RustPatternStrategy variant", + detail: "PatternRealization.strategy must be a PatternStrategy variant", }); }; if !payload.is_empty() { return Err(EmitError::MalformedRealization { declaration, - detail: "RustPatternStrategy variants must not carry payload fields", + detail: "PatternStrategy variants must not carry payload fields", }); } let vector_list = named_variant_id(dag, "PatternStrategy", "VectorList").ok_or( EmitError::MalformedRealization { declaration, - detail: "RustPatternStrategy.VectorList declaration was not found", + detail: "PatternStrategy.VectorList declaration was not found", }, )?; if *constructor != vector_list { return Err(EmitError::MalformedRealization { declaration, - detail: "RustPatternStrategy constructor must be VectorList", + detail: "PatternStrategy constructor must be VectorList", }); } Ok(PatternRealizationBinding { - strategy: RustPatternStrategyBinding::VectorList, scrutinee: require_field_string(fields, "scrutinee", declaration)?, empty_pattern: require_field_string(fields, "empty_pattern", declaration)?, cons_pattern: require_field_string(fields, "cons_pattern", declaration)?, @@ -3388,11 +3381,8 @@ impl<'a> Ctx<'a> { let Some(binding) = self.indexes.patterns.get(&disj_id) else { return Ok(None); }; - match binding.strategy { - RustPatternStrategyBinding::VectorList => self - .render_vector_list_pattern_branch(branch, disj_id, binding, locals) - .map(Some), - } + self.render_vector_list_pattern_branch(branch, disj_id, binding, locals) + .map(Some) } fn render_vector_list_pattern_branch( diff --git a/src/v3/spec/go.dag b/src/v3/spec/go.dag index aba30ede29e..caac42c2083 100644 --- a/src/v3/spec/go.dag +++ b/src/v3/spec/go.dag @@ -794,6 +794,7 @@ data go_contains_callable: CallableRealization = { cost: 1 } +// List structural-sum → slice carrier; same pattern-role contract as Rust/Python. data go_list_pattern: PatternRealization = { language: go_language target: List diff --git a/src/v3/spec/python.dag b/src/v3/spec/python.dag index d84b5f24459..7b83d963c01 100644 --- a/src/v3/spec/python.dag +++ b/src/v3/spec/python.dag @@ -449,6 +449,7 @@ data python_list_instantiation: TypeInstantiationRealization = { cost: 1 } +// List structural-sum → Python list surface; pattern-role + templates are spec-owned. data python_list_pattern: PatternRealization = { language: python_language target: List diff --git a/src/v3/spec/rust.dag b/src/v3/spec/rust.dag index f440f40cb89..abc49eb2727 100644 --- a/src/v3/spec/rust.dag +++ b/src/v3/spec/rust.dag @@ -881,6 +881,9 @@ data rust_contains_callable: CallableRealization = { cost: 1 } +// List structural-sum (`Empty`/`Cons`) → carrier patterns. `strategy` is the +// pattern-role tag (validated at emitter load); templates below are the +// emission bundle — no strategy dispatch in emitters post Stage 1e.0. data rust_list_pattern: PatternRealization = { language: rust_language target: List diff --git a/src/v3/std/emit_model.dag b/src/v3/std/emit_model.dag index cc6703a9a2a..ab9cf9c37de 100644 --- a/src/v3/std/emit_model.dag +++ b/src/v3/std/emit_model.dag @@ -112,6 +112,12 @@ type CallableStrategy // `List -> Vec` or `[]T`. The terminal target is the same authority // expressed as target-language facts consumed by `.dag` emission; this record // remains the single authority until that stage lands. +// +// **Pattern-role authority (Stage 1e.0):** `strategy` names which lowering +// recipe applies (`VectorList` today). Emitters validate it fail-closed at +// index build and **do not** branch on it when rendering — the role is fully +// expressed by the spec data item (templates + target ref); rendering follows +// that bundle alone. type PatternRealization { language: DeclarationRef target: DeclarationRef @@ -124,10 +130,9 @@ type PatternRealization { cost: Int } -// Dissolution receipt — 🟡 mixed lifecycle. The current staged emitters need a -// small structural strategy tag to decide which pattern-lowering path to take. -// It dissolves with `PatternRealization` when the emitters themselves become -// data. +// List-pattern role tag — single authority lives in each target's +// `data _list_pattern: PatternRealization` in `spec/*.dag`. +// Emitters map validated records to emission without per-role `match` arms. type PatternStrategy = PlaceholderPatternStrategy | VectorList From 5d0a98551fca28970516faf2687faf305aca93a5 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 19 Apr 2026 03:02:40 -0400 Subject: [PATCH 2/3] WIP: 552 followup --- ROADMAP.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ROADMAP.md b/ROADMAP.md index b49737d59d3..de099bca8e7 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -640,6 +640,8 @@ Cleared (prior PR #521): `DerivedOpEffect { method, path_template, shape }` coll **In progress on the current branch (pending merge):** `src/v3/compiler/src/emit.rs` now exists as the shared emit entrypoint with `EmitTarget`, `EmitMode`, `EmittedSource`, `emit(...)`, and `emit_module(...)`. The first migrated target is **Go**: its hand-written renderer moved under `emit.rs`, and `src/v3/compiler/src/emit_go.rs` is reduced to a compatibility adapter that forwards into the shared path. Coverage is locked by `m1_3_emit_go_test` plus `emit::tests::*`, including a wrapper-parity test proving `emit_go` / `emit_go_module` are thin adapters over the shared entrypoint. The authoritative scaffold receipt now lives in `docs/phase1-lane3-consolidation-build-plan.md` §"Wrapper exception receipt": Go's adapter is allowed only because its body already moved under `emit.rs`; Rust/Python do not get wrapper copies in advance, and all wrappers delete at Stage 1e.6. Follow-up namespace unification moved the remaining per-target monoliths under `src/v3/compiler/src/emit/*_target.rs`, with `emit.rs` staying the single dispatch surface and the old `emit_.rs` files reduced to compatibility adapters only after their bodies moved in the same diff. **Python's Stage 1e.0 bridge is now cleared:** `spec/python.dag` authors shared `LanguageSpec`/`TypeRealization`/`OperatorRealization`/`CallableRealization`/`TypeInstantiationRealization`/`PatternRealization` data, `Dag` caches `python_language` + `python_target`, and `emit/python_target.rs` filters shared realizations by `language: python_language` while loading shared `PatternRealization` for list-pattern lowering. Rust and Python still remain target-monolithic under the shared namespace; full recursive walker unification is still the remaining Stage 1e work. +**Stage 1e.0 closeout — pattern-role authority structural** → ✅ List-pattern lowering no longer dispatches on a host `PatternStrategy` enum in emitters: each `spec/{rust,go,python}.dag` `*_list_pattern` declares `strategy: VectorList` plus the template bundle; Rust/Go/Python emitters validate that tag fail-closed when indexing realizations and render from the loaded templates only (`src/v3/std/emit_model.dag` documents the split). Further roles add new `PatternStrategy` variants in the shared schema and new spec data — not new match arms in `emit/*_target.rs` / `emit.rs`. + ### Cross-cutting — performance **Deferral: self-compile perf ratchet investigation (M, not on any critical path but compounding).** Self-compile time drifted from ~60s to ~70s in recent cycles (~16% growth). The ratchet keeps getting bumped without a root-cause investigation; each bump normalizes the regression. Scope: (1) profile a single `cargo test -p v3-compiler-tests` run, identify the top hot paths; (2) measure where the 10s came from across recent PRs (bisect across #479, #489, #490 if signal is unclear); (3) either fix the regression or document it as an accepted cost with a new ratchet ceiling. **Yellow-flag threshold: 90s.** If self-compile exceeds that before this deferral is scheduled, it preempts other work. No design doc needed; profiling is a data-gathering exercise. From 5883a3a986f6c487ba62007590ea1df986106826 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Sun, 19 Apr 2026 03:08:49 -0400 Subject: [PATCH 3/3] WIP: 552 followup --- ROADMAP.md | 2 +- src/v3/std/emit_model.dag | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index de099bca8e7..10155cfc960 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -640,7 +640,7 @@ Cleared (prior PR #521): `DerivedOpEffect { method, path_template, shape }` coll **In progress on the current branch (pending merge):** `src/v3/compiler/src/emit.rs` now exists as the shared emit entrypoint with `EmitTarget`, `EmitMode`, `EmittedSource`, `emit(...)`, and `emit_module(...)`. The first migrated target is **Go**: its hand-written renderer moved under `emit.rs`, and `src/v3/compiler/src/emit_go.rs` is reduced to a compatibility adapter that forwards into the shared path. Coverage is locked by `m1_3_emit_go_test` plus `emit::tests::*`, including a wrapper-parity test proving `emit_go` / `emit_go_module` are thin adapters over the shared entrypoint. The authoritative scaffold receipt now lives in `docs/phase1-lane3-consolidation-build-plan.md` §"Wrapper exception receipt": Go's adapter is allowed only because its body already moved under `emit.rs`; Rust/Python do not get wrapper copies in advance, and all wrappers delete at Stage 1e.6. Follow-up namespace unification moved the remaining per-target monoliths under `src/v3/compiler/src/emit/*_target.rs`, with `emit.rs` staying the single dispatch surface and the old `emit_.rs` files reduced to compatibility adapters only after their bodies moved in the same diff. **Python's Stage 1e.0 bridge is now cleared:** `spec/python.dag` authors shared `LanguageSpec`/`TypeRealization`/`OperatorRealization`/`CallableRealization`/`TypeInstantiationRealization`/`PatternRealization` data, `Dag` caches `python_language` + `python_target`, and `emit/python_target.rs` filters shared realizations by `language: python_language` while loading shared `PatternRealization` for list-pattern lowering. Rust and Python still remain target-monolithic under the shared namespace; full recursive walker unification is still the remaining Stage 1e work. -**Stage 1e.0 closeout — pattern-role authority structural** → ✅ List-pattern lowering no longer dispatches on a host `PatternStrategy` enum in emitters: each `spec/{rust,go,python}.dag` `*_list_pattern` declares `strategy: VectorList` plus the template bundle; Rust/Go/Python emitters validate that tag fail-closed when indexing realizations and render from the loaded templates only (`src/v3/std/emit_model.dag` documents the split). Further roles add new `PatternStrategy` variants in the shared schema and new spec data — not new match arms in `emit/*_target.rs` / `emit.rs`. +**Stage 1e.0 closeout — pattern-role authority structural** → ✅ Structurally resolved for the live single-role state (`VectorList` only): list-pattern lowering does not dispatch on a host `PatternStrategy` enum in emitters — each `spec/{rust,go,python}.dag` `*_list_pattern` declares `strategy: VectorList` plus the template bundle; Rust/Go/Python emitters validate that tag fail-closed when indexing realizations and render from the loaded templates only (`src/v3/std/emit_model.dag` documents the split). When a second distinct lowering path ships, restore strategy on the emitter binding and branch in render (still fail-closed from spec rows), and extend `PatternStrategy` + `spec/*.dag` data — YAGNI until then. ### Cross-cutting — performance diff --git a/src/v3/std/emit_model.dag b/src/v3/std/emit_model.dag index ab9cf9c37de..dfc417d45dd 100644 --- a/src/v3/std/emit_model.dag +++ b/src/v3/std/emit_model.dag @@ -133,6 +133,10 @@ type PatternRealization { // List-pattern role tag — single authority lives in each target's // `data _list_pattern: PatternRealization` in `spec/*.dag`. // Emitters map validated records to emission without per-role `match` arms. +// +// `PlaceholderPatternStrategy` keeps the coproduct non-singleton (substrate +// discipline: avoid a one-variant sum) until a second real role lands; it +// then dissolves in favor of that role (e.g. tuple-list lowering). type PatternStrategy = PlaceholderPatternStrategy | VectorList