diff --git a/ROADMAP.md b/ROADMAP.md index 8ab4ce40b91..e1a813f0eff 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -370,7 +370,7 @@ Per-row tagging is a scheduled follow-up sweep — **trigger:** post-merge of th ### P3 — modeling gaps -- **`declaration_by_name(...)` pattern in emit** — 20 sites on `origin/main`, including `emit.rs`, `emit/rust_target.rs`, `emit/python_target.rs` looking up `"OrderedRing"`, `"SubstrateAccessorBinding"`, `"Dag"`, `"fold"`, `"id"`. Violates Layer Opacity / Semantic Authority (post-lowering). Dissolution: typed substrate access via cached declaration-id (`algebra_field_for_operator` pattern) at every call site. +- **~~`declaration_by_name(...)` pattern in emit~~** — **RETIRED 2026-05-04 (PR #1638)**. Audit found the production emit callers were fixed substrate / emit-model anchors, not caller-provided program strings: `MethodEmitTemplate`, `MethodTemplateContract`, collection method anchors (`concat_method`, `length_method`, `fold_method`, `is_empty_method`), and fixed emit-model coproduct variants (`PatternStrategy`, `FieldAccess`, `ParameterDisposition`, `MemoryModel`, `ScopeModel`, Rust rendering/computation-model variants). Dissolved by extending the bootstrap-populated `Dag` typed caches (`EmitAnchorCache` / `EmitModelVariants`) and replacing emit-time string lookups with cached `DeclarationId` reads. Ratchet: `emit_production_code_has_no_declaration_by_name_calls` fails closed if production `src/v3/compiler/src/emit*` code reintroduces `Dag::declaration_by_name`; test-only fixture lookups remain outside the row scope. - **`pipeline_authority.rs` (case 2c):** runtime stage order is **structural** (`PipelineStageBinding` declaration order only). `fn compile` body vs binding drift check is **suspended** (PR #1171): `compile` stays `ArrowBody::Unparsed` — no lowered ordered stage list without a source-text channel; neither `include_str!` nor `read_to_string`+slice satisfies R3 for that reconcile. Dissolution: single typed carrier or structural compile body. - **LLM service flattening**: `dsl/extdeps/llm/llm.dag` declares `Role`, `ContentBlock`, `LlmMessage`, `StopReason`; Anthropic/OpenAI service operations still take `model: String`, `messages: Json`, and extract outputs by string path. Dissolution: service operations consume the typed carriers; outputs returned as typed responses. **Typed request JSON vs wire (e2e risk):** closure trigger identifier `rest_request_wire_serde_alignment` — Anthropic request payload coproducts carry source-level `CoproductWireContract` rows for message `role` and content-block `type` tagging, ratcheted by `anthropic_request_coproduct_wire_contracts_emit_targeted_serde` and `anthropic_messages_request_body_json_matches_messages_wire_tags`; `structural_coverage_gap_anthropic_messages_request_wire` is retired for those outer request coproduct discriminators. Nested Anthropic tool-result `content` still needs scalar-text vs content-block-array wire serde and remains tracked by `structural_coverage_gap_anthropic_tool_result_content_wire_shape`; additive document/search/tool-reference nested blocks remain tracked separately by `structural_coverage_gap_anthropic_tool_result_additive_blocks`. OpenAI Chat Completions narrow-row `messages[].role` for `OpenAiChatMessageRole` is ratcheted by pipeline test `openai_chat_message_role_wire_matches_llm_snake_contract` (`src/v2/tests/src/pipeline.rs`) against `data wire_contract: VariantEncoding = llm_snake_wire_contract` in `dsl/extdeps/llm/openai.dag` (same M2 pattern as Anthropic: module-local `wire_contract` names the single `llm_snake_wire_contract` authority in `extdeps.llm.llm`; emitter derefs `init.inferred` / `Resolved` per `resolve_wire_serde_policy_for_coproduct` in `05_emit_rust.dag`). `v2.compiler.emit_rust` authorizes coproduct serde from `CoproductWireContract` rows targeting local coproduct declarations, then module `wire_contract`, then the tagged-object default; stale local contract targets emit `compile_error!`. The old `structural_coverage_gap_openai_chat_request_messages_wire` row was removed when that receipt landed; tracker [#987](https://github.com/gunb-ai/gunbc/issues/987). - **GitHub auth model bypass**: `dsl/extdeps/github/github.dag:42-46` declares `GitHubAuthToken { token, scopes, expires_at }` but `dsl/extdeps/github/auth.dag:13-23` returns only `{ token: Secret }` and hardcodes GCP secret-manager policy. Dissolution: `github_token()` returns the full typed token; remove the hardcoded provider policy. diff --git a/src/v3/compiler/src/bootstrap_generated.rs b/src/v3/compiler/src/bootstrap_generated.rs index d4bda3806e2..67b2c2761d1 100644 --- a/src/v3/compiler/src/bootstrap_generated.rs +++ b/src/v3/compiler/src/bootstrap_generated.rs @@ -21,6 +21,7 @@ pub(crate) fn bootstrapped_fixture_dag() -> Dag { ), verifier_output_policy_variants: VerifierOutputPolicyVariants::default(), callable_strategy_variants: CallableStrategyVariants::default(), + emit_model_variants: EmitModelVariants::default(), clusters: bootstrapped_fixture_dag_clusters(), optional_match_disjs: bootstrapped_fixture_dag_optional_match_disjs(), declaration_append_begin_after_bootstrap: 2317, diff --git a/src/v3/compiler/src/bootstrap_generated_without_parse_surface.rs b/src/v3/compiler/src/bootstrap_generated_without_parse_surface.rs index 89a3c38cc93..6745a6cdb6c 100644 --- a/src/v3/compiler/src/bootstrap_generated_without_parse_surface.rs +++ b/src/v3/compiler/src/bootstrap_generated_without_parse_surface.rs @@ -21,6 +21,7 @@ pub(crate) fn bootstrapped_fixture_without_parse_surface_dag() -> Dag { ), verifier_output_policy_variants: VerifierOutputPolicyVariants::default(), callable_strategy_variants: CallableStrategyVariants::default(), + emit_model_variants: EmitModelVariants::default(), clusters: bootstrapped_fixture_without_parse_surface_dag_clusters(), optional_match_disjs: bootstrapped_fixture_without_parse_surface_dag_optional_match_disjs(), declaration_append_begin_after_bootstrap: 2234, diff --git a/src/v3/compiler/src/bootstrap_std_generated.rs b/src/v3/compiler/src/bootstrap_std_generated.rs index faac9372bcb..88593bc6862 100644 --- a/src/v3/compiler/src/bootstrap_std_generated.rs +++ b/src/v3/compiler/src/bootstrap_std_generated.rs @@ -21,6 +21,7 @@ pub(crate) fn bootstrapped_std_fixture_dag() -> Dag { ), verifier_output_policy_variants: VerifierOutputPolicyVariants::default(), callable_strategy_variants: CallableStrategyVariants::default(), + emit_model_variants: EmitModelVariants::default(), clusters: bootstrapped_std_fixture_dag_clusters(), optional_match_disjs: bootstrapped_std_fixture_dag_optional_match_disjs(), declaration_append_begin_after_bootstrap: 607, diff --git a/src/v3/compiler/src/dag.rs b/src/v3/compiler/src/dag.rs index 5c66b37cc41..276aa5d9a25 100644 --- a/src/v3/compiler/src/dag.rs +++ b/src/v3/compiler/src/dag.rs @@ -2276,6 +2276,104 @@ pub(crate) struct EmitAnchorCache { /// `src/v3/spec/rust.dag` (bootstrap name resolution prefers `src/v3/` /// over the duplicate carrier in `dsl/std/languages.dag`). pub rust_functions: Option, + /// `MethodEmitTemplate` coproduct parent used to map a method-template + /// constructor id back to its variant label during target emission. + pub method_emit_template: Option, + /// `MethodTemplateContract` meta-type used by fold-method contract checks. + pub method_template_contract: Option, + /// `std.list.concat_method` method declaration. + pub concat_method: Option, + /// `std.list.length_method` method declaration. + pub length_method: Option, + /// `std.list.fold_method` method declaration. + pub fold_method: Option, + /// `std.list.is_empty_method` method declaration. + pub is_empty_method: Option, +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct PatternStrategyVariants { + pub vector_list: Option, +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct FieldAccessVariants { + pub direct_field: Option, + pub accessor_method: Option, +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct ParameterDispositionVariants { + pub borrowed: Option, + pub consumed: Option, +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct MemoryModelVariants { + pub value_only: Option, + pub garbage_collected: Option, + pub ref_counted: Option, + pub ownership_based: Option, +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct ScopeModelVariants { + pub lexical_scoping: Option, + pub dynamic_scoping: Option, +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct ReadStrategyVariants { + pub borrow: Option, + pub pass_by_value: Option, +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct ConstructStrategyVariants { + pub copy_or_clone: Option, + pub pass_by_value: Option, +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct MutabilityVariants { + pub immutable: Option, + pub mutable: Option, +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct PurityVariants { + pub pure: Option, + pub effectful: Option, +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct StructureVariants { + pub explicit_dag: Option, + pub arbitrary: Option, +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct IterationVariants { + pub bounded: Option, + pub unbounded: Option, +} + +/// Cached variant DeclarationIds for fixed emit-model coproducts. +/// Populated once at bootstrap end so emitters dispatch on typed +/// constructors instead of resolving parent/variant names at parse time. +#[derive(Debug, Default, Clone)] +pub(crate) struct EmitModelVariants { + pub pattern_strategy: PatternStrategyVariants, + pub field_access: FieldAccessVariants, + pub parameter_disposition: ParameterDispositionVariants, + pub memory_model: MemoryModelVariants, + pub scope_model: ScopeModelVariants, + pub read_strategy: ReadStrategyVariants, + pub construct_strategy: ConstructStrategyVariants, + pub mutability: MutabilityVariants, + pub purity: PurityVariants, + pub structure: StructureVariants, + pub iteration: IterationVariants, } #[derive(Debug, Default, Clone)] @@ -2474,6 +2572,9 @@ pub struct Dag { /// emitter plus Rust/Python target emitters when parsing /// `CallableRealization.strategy`. callable_strategy_variants: CallableStrategyVariants, + /// Cached fixed emit-model coproduct variant DeclarationIds used by + /// emit target parsers. + emit_model_variants: EmitModelVariants, /// Sidecar structural facts for mutually-recursive SCCs. clusters: Vec, /// Synthetic match carriers for anonymous `T?` cardinalities. Used when @@ -2553,6 +2654,7 @@ impl Dag { VariantPayloadFieldAccessRuleVariants::default(), verifier_output_policy_variants: VerifierOutputPolicyVariants::default(), callable_strategy_variants: CallableStrategyVariants::default(), + emit_model_variants: EmitModelVariants::default(), clusters: Vec::new(), optional_match_disjs: HashMap::new(), declaration_append_begin_after_bootstrap: 0, @@ -3019,6 +3121,36 @@ impl Dag { self.emit_anchors.rust_functions } + /// `MethodEmitTemplate` coproduct parent. + pub(crate) fn method_emit_template_decl(&self) -> Option { + self.emit_anchors.method_emit_template + } + + /// `MethodTemplateContract` meta-type. + pub(crate) fn method_template_contract_decl(&self) -> Option { + self.emit_anchors.method_template_contract + } + + /// `fold_method` method declaration. + pub(crate) fn fold_method_decl(&self) -> Option { + self.emit_anchors.fold_method + } + + /// `concat_method` method declaration. + pub(crate) fn concat_method_decl(&self) -> Option { + self.emit_anchors.concat_method + } + + /// `length_method` method declaration. + pub(crate) fn length_method_decl(&self) -> Option { + self.emit_anchors.length_method + } + + /// `is_empty_method` method declaration. + pub(crate) fn is_empty_method_decl(&self) -> Option { + self.emit_anchors.is_empty_method + } + /// Typed accessor for the cached `PatternBindingRule` variant /// handles resolved from `src/v3/std/clean_emission.dag` at /// bootstrap end. Consumed by per-target emitters when @@ -3056,6 +3188,11 @@ impl Dag { &self.callable_strategy_variants } + /// Typed accessor for fixed emit-model coproduct variant handles. + pub(crate) fn emit_model_variants(&self) -> &EmitModelVariants { + &self.emit_model_variants + } + pub fn nodes(&self) -> &[Behavior] { &self.nodes } @@ -3652,6 +3789,16 @@ impl Dag { self.emit_anchors.dag_type = self.declaration_by_name("Dag").map(|d| d.id); self.emit_anchors.std_list_fold = self.declaration_by_name("fold").map(|d| d.id); self.emit_anchors.rust_functions = self.declaration_by_name("rust_functions").map(|d| d.id); + self.emit_anchors.method_emit_template = + self.declaration_by_name("MethodEmitTemplate").map(|d| d.id); + self.emit_anchors.method_template_contract = self + .declaration_by_name("MethodTemplateContract") + .map(|d| d.id); + self.emit_anchors.concat_method = self.declaration_by_name("concat_method").map(|d| d.id); + self.emit_anchors.length_method = self.declaration_by_name("length_method").map(|d| d.id); + self.emit_anchors.fold_method = self.declaration_by_name("fold_method").map(|d| d.id); + self.emit_anchors.is_empty_method = + self.declaration_by_name("is_empty_method").map(|d| d.id); // `PatternBindingRule` variant resolution. Walks the // `std/clean_emission.dag` declaration's `Disj` variants @@ -3778,6 +3925,174 @@ impl Dag { } } self.callable_strategy_variants = callable_strategy_variants; + + let mut emit_model_variants = EmitModelVariants::default(); + if let Some(parent) = self.declaration_by_name("PatternStrategy") { + if let TypeConnective::Disj { variants } = &parent.connective { + for variant in variants { + if variant.label == "VectorList" { + emit_model_variants.pattern_strategy.vector_list = Some(variant.ty); + } + } + } + } + if let Some(parent) = self.declaration_by_name("FieldAccess") { + if let TypeConnective::Disj { variants } = &parent.connective { + for variant in variants { + match variant.label.as_str() { + "DirectField" => { + emit_model_variants.field_access.direct_field = Some(variant.ty); + } + "AccessorMethod" => { + emit_model_variants.field_access.accessor_method = Some(variant.ty); + } + _ => {} + } + } + } + } + if let Some(parent) = self.declaration_by_name("ParameterDisposition") { + if let TypeConnective::Disj { variants } = &parent.connective { + for variant in variants { + match variant.label.as_str() { + "Borrowed" => { + emit_model_variants.parameter_disposition.borrowed = Some(variant.ty); + } + "Consumed" => { + emit_model_variants.parameter_disposition.consumed = Some(variant.ty); + } + _ => {} + } + } + } + } + if let Some(parent) = self.declaration_by_name("MemoryModel") { + if let TypeConnective::Disj { variants } = &parent.connective { + for variant in variants { + match variant.label.as_str() { + "ValueOnly" => { + emit_model_variants.memory_model.value_only = Some(variant.ty); + } + "GarbageCollected" => { + emit_model_variants.memory_model.garbage_collected = Some(variant.ty); + } + "RefCounted" => { + emit_model_variants.memory_model.ref_counted = Some(variant.ty); + } + "OwnershipBased" => { + emit_model_variants.memory_model.ownership_based = Some(variant.ty); + } + _ => {} + } + } + } + } + if let Some(parent) = self.declaration_by_name("ScopeModel") { + if let TypeConnective::Disj { variants } = &parent.connective { + for variant in variants { + match variant.label.as_str() { + "LexicalScoping" => { + emit_model_variants.scope_model.lexical_scoping = Some(variant.ty); + } + "DynamicScoping" => { + emit_model_variants.scope_model.dynamic_scoping = Some(variant.ty); + } + _ => {} + } + } + } + } + if let Some(parent) = self.declaration_by_name("ReadStrategy") { + if let TypeConnective::Disj { variants } = &parent.connective { + for variant in variants { + match variant.label.as_str() { + "Borrow" => { + emit_model_variants.read_strategy.borrow = Some(variant.ty); + } + "PassByValue" => { + emit_model_variants.read_strategy.pass_by_value = Some(variant.ty); + } + _ => {} + } + } + } + } + if let Some(parent) = self.declaration_by_name("ConstructStrategy") { + if let TypeConnective::Disj { variants } = &parent.connective { + for variant in variants { + match variant.label.as_str() { + "CopyOrClone" => { + emit_model_variants.construct_strategy.copy_or_clone = Some(variant.ty); + } + "PassByValue" => { + emit_model_variants.construct_strategy.pass_by_value = Some(variant.ty); + } + _ => {} + } + } + } + } + if let Some(parent) = self.declaration_by_name("Mutability") { + if let TypeConnective::Disj { variants } = &parent.connective { + for variant in variants { + match variant.label.as_str() { + "Immutable" => { + emit_model_variants.mutability.immutable = Some(variant.ty); + } + "Mutable" => { + emit_model_variants.mutability.mutable = Some(variant.ty); + } + _ => {} + } + } + } + } + if let Some(parent) = self.declaration_by_name("Purity") { + if let TypeConnective::Disj { variants } = &parent.connective { + for variant in variants { + match variant.label.as_str() { + "Pure" => { + emit_model_variants.purity.pure = Some(variant.ty); + } + "Effectful" => { + emit_model_variants.purity.effectful = Some(variant.ty); + } + _ => {} + } + } + } + } + if let Some(parent) = self.declaration_by_name("Structure") { + if let TypeConnective::Disj { variants } = &parent.connective { + for variant in variants { + match variant.label.as_str() { + "ExplicitDAG" => { + emit_model_variants.structure.explicit_dag = Some(variant.ty); + } + "Arbitrary" => { + emit_model_variants.structure.arbitrary = Some(variant.ty); + } + _ => {} + } + } + } + } + if let Some(parent) = self.declaration_by_name("Iteration") { + if let TypeConnective::Disj { variants } = &parent.connective { + for variant in variants { + match variant.label.as_str() { + "Bounded" => { + emit_model_variants.iteration.bounded = Some(variant.ty); + } + "Unbounded" => { + emit_model_variants.iteration.unbounded = Some(variant.ty); + } + _ => {} + } + } + } + } + self.emit_model_variants = emit_model_variants; } fn populate_target_clean_emission_bindings(&mut self) { @@ -4117,6 +4432,21 @@ mod tests { dag.rust_functions_syntax_decl().is_some(), "rust_functions syntax anchor" ); + assert!( + dag.method_emit_template_decl().is_some(), + "MethodEmitTemplate anchor" + ); + assert!( + dag.method_template_contract_decl().is_some(), + "MethodTemplateContract anchor" + ); + assert!(dag.fold_method_decl().is_some(), "fold_method anchor"); + assert!(dag.concat_method_decl().is_some(), "concat_method anchor"); + assert!(dag.length_method_decl().is_some(), "length_method anchor"); + assert!( + dag.is_empty_method_decl().is_some(), + "is_empty_method anchor" + ); } #[test] @@ -4153,6 +4483,92 @@ mod tests { ); } + #[test] + fn emit_model_variants_populated_after_bootstrap() { + let dag = Dag::new(); + let variants = dag.emit_model_variants(); + assert!( + variants.pattern_strategy.vector_list.is_some(), + "PatternStrategy.VectorList" + ); + assert!( + variants.field_access.direct_field.is_some(), + "FieldAccess.DirectField" + ); + assert!( + variants.field_access.accessor_method.is_some(), + "FieldAccess.AccessorMethod" + ); + assert!( + variants.parameter_disposition.borrowed.is_some(), + "ParameterDisposition.Borrowed" + ); + assert!( + variants.parameter_disposition.consumed.is_some(), + "ParameterDisposition.Consumed" + ); + assert!( + variants.memory_model.value_only.is_some(), + "MemoryModel.ValueOnly" + ); + assert!( + variants.memory_model.garbage_collected.is_some(), + "MemoryModel.GarbageCollected" + ); + assert!( + variants.memory_model.ref_counted.is_some(), + "MemoryModel.RefCounted" + ); + assert!( + variants.memory_model.ownership_based.is_some(), + "MemoryModel.OwnershipBased" + ); + assert!( + variants.scope_model.lexical_scoping.is_some(), + "ScopeModel.LexicalScoping" + ); + assert!( + variants.scope_model.dynamic_scoping.is_some(), + "ScopeModel.DynamicScoping" + ); + assert!( + variants.read_strategy.borrow.is_some(), + "ReadStrategy.Borrow" + ); + assert!( + variants.read_strategy.pass_by_value.is_some(), + "ReadStrategy.PassByValue" + ); + assert!( + variants.construct_strategy.copy_or_clone.is_some(), + "ConstructStrategy.CopyOrClone" + ); + assert!( + variants.construct_strategy.pass_by_value.is_some(), + "ConstructStrategy.PassByValue" + ); + assert!( + variants.mutability.immutable.is_some(), + "Mutability.Immutable" + ); + assert!(variants.mutability.mutable.is_some(), "Mutability.Mutable"); + assert!(variants.purity.pure.is_some(), "Purity.Pure"); + assert!(variants.purity.effectful.is_some(), "Purity.Effectful"); + assert!( + variants.structure.explicit_dag.is_some(), + "Structure.ExplicitDAG" + ); + assert!( + variants.structure.arbitrary.is_some(), + "Structure.Arbitrary" + ); + assert!(variants.iteration.bounded.is_some(), "Iteration.Bounded"); + assert!( + variants.iteration.unbounded.is_some(), + "Iteration.Unbounded" + ); + } + #[test] fn bridge_mark_bootstrap_secret_nominal_opacity_retired() { fn assert_secret_marked(dag: Dag, label: &str) { diff --git a/src/v3/compiler/src/emit.rs b/src/v3/compiler/src/emit.rs index d91a0d69a53..16eab7cf42b 100644 --- a/src/v3/compiler/src/emit.rs +++ b/src/v3/compiler/src/emit.rs @@ -74,7 +74,7 @@ pub(crate) fn method_emit_template_variant_label( dag: &Dag, constructor: DeclarationId, ) -> Option<&str> { - let parent = dag.declaration_by_name("MethodEmitTemplate")?; + let parent = dag.declaration(dag.method_emit_template_decl()?); let TypeConnective::Disj { variants } = &parent.connective else { return None; }; @@ -1268,7 +1268,10 @@ pub(crate) fn parse_pattern_strategy( if !payload.is_empty() { return Err("PatternStrategy variants must not carry payload fields"); } - let vector_list = named_variant_id(dag, "PatternStrategy", "VectorList") + let vector_list = dag + .emit_model_variants() + .pattern_strategy + .vector_list .ok_or("PatternStrategy.VectorList declaration was not found")?; if *constructor != vector_list { return Err("PatternStrategy constructor must be VectorList"); @@ -2769,18 +2772,23 @@ fn parse_field_access( }); } }; - let direct_field = named_variant_id(dag, "FieldAccess", "DirectField").ok_or( - EmitError::MalformedRealization { - declaration, - detail: "FieldAccess.DirectField declaration was not found", - }, - )?; - let accessor_method = named_variant_id(dag, "FieldAccess", "AccessorMethod").ok_or( - EmitError::MalformedRealization { - declaration, - detail: "FieldAccess.AccessorMethod declaration was not found", - }, - )?; + let variants = dag.emit_model_variants(); + let direct_field = + variants + .field_access + .direct_field + .ok_or(EmitError::MalformedRealization { + declaration, + detail: "FieldAccess.DirectField declaration was not found", + })?; + let accessor_method = + variants + .field_access + .accessor_method + .ok_or(EmitError::MalformedRealization { + declaration, + detail: "FieldAccess.AccessorMethod declaration was not found", + })?; if *constructor == direct_field { Ok(GoFieldAccessBinding::DirectField(name)) } else if *constructor == accessor_method { @@ -2981,18 +2989,23 @@ fn parse_parameter_disposition( detail: "ParameterDisposition variants must not carry payload fields", }); } - let borrowed = named_variant_id(dag, "ParameterDisposition", "Borrowed").ok_or( - EmitError::MalformedRealization { - declaration, - detail: "ParameterDisposition.Borrowed declaration was not found", - }, - )?; - let consumed = named_variant_id(dag, "ParameterDisposition", "Consumed").ok_or( - EmitError::MalformedRealization { - declaration, - detail: "ParameterDisposition.Consumed declaration was not found", - }, - )?; + let variants = dag.emit_model_variants(); + let borrowed = + variants + .parameter_disposition + .borrowed + .ok_or(EmitError::MalformedRealization { + declaration, + detail: "ParameterDisposition.Borrowed declaration was not found", + })?; + let consumed = + variants + .parameter_disposition + .consumed + .ok_or(EmitError::MalformedRealization { + declaration, + detail: "ParameterDisposition.Consumed declaration was not found", + })?; if *constructor == borrowed { Ok(ParameterDispositionBinding::Borrowed) } else if *constructor == consumed { @@ -3079,12 +3092,14 @@ fn require_pattern_realization( detail: "PatternStrategy variants must not carry payload fields", }); } - let vector_list = named_variant_id(dag, "PatternStrategy", "VectorList").ok_or( - EmitError::MalformedRealization { + let vector_list = dag + .emit_model_variants() + .pattern_strategy + .vector_list + .ok_or(EmitError::MalformedRealization { declaration, detail: "PatternStrategy.VectorList declaration was not found", - }, - )?; + })?; if *constructor != vector_list { return Err(EmitError::MalformedRealization { declaration, @@ -3131,14 +3146,27 @@ fn require_memory_model( detail: "MemoryModel variants must not carry payload fields", }); } - let variants = [ - ("ValueOnly", MemoryModelBinding::ValueOnly), - ("GarbageCollected", MemoryModelBinding::GarbageCollected), - ("RefCounted", MemoryModelBinding::RefCounted), - ("OwnershipBased", MemoryModelBinding::OwnershipBased), + let variants = dag.emit_model_variants(); + let memory_variants = [ + ( + variants.memory_model.value_only, + MemoryModelBinding::ValueOnly, + ), + ( + variants.memory_model.garbage_collected, + MemoryModelBinding::GarbageCollected, + ), + ( + variants.memory_model.ref_counted, + MemoryModelBinding::RefCounted, + ), + ( + variants.memory_model.ownership_based, + MemoryModelBinding::OwnershipBased, + ), ]; - for (label, binding) in variants { - let Some(variant_id) = named_variant_id(dag, "MemoryModel", label) else { + for (variant_id, binding) in memory_variants { + let Some(variant_id) = variant_id else { return Err(EmitError::MalformedTargetSyntax { declaration, detail: "MemoryModel variant declaration was not found", @@ -3184,12 +3212,19 @@ fn require_scope_model( detail: "ScopeModel variants must not carry payload fields", }); } - let variants = [ - ("LexicalScoping", ScopeModelBinding::LexicalScoping), - ("DynamicScoping", ScopeModelBinding::DynamicScoping), + let variants = dag.emit_model_variants(); + let scope_variants = [ + ( + variants.scope_model.lexical_scoping, + ScopeModelBinding::LexicalScoping, + ), + ( + variants.scope_model.dynamic_scoping, + ScopeModelBinding::DynamicScoping, + ), ]; - for (label, binding) in variants { - let Some(variant_id) = named_variant_id(dag, "ScopeModel", label) else { + for (variant_id, binding) in scope_variants { + let Some(variant_id) = variant_id else { return Err(EmitError::MalformedTargetSyntax { declaration, detail: "ScopeModel variant declaration was not found", @@ -3303,17 +3338,6 @@ fn normalize_source_filter_path(file: &str) -> String { parts.join("/") } -fn named_variant_id(dag: &Dag, parent_name: &str, variant_label: &str) -> Option { - let parent = dag.declaration_by_name(parent_name)?; - let TypeConnective::Disj { variants } = &parent.connective else { - return None; - }; - variants - .iter() - .find(|variant| variant.label == variant_label) - .map(|variant| variant.ty) -} - fn render_named_template(template: &str, bindings: &[(&str, &str)]) -> String { let bindings: HashMap<&str, &str> = bindings.iter().copied().collect(); let chars: Vec = template.chars().collect(); diff --git a/src/v3/compiler/src/emit/collection_ops_method_contract.rs b/src/v3/compiler/src/emit/collection_ops_method_contract.rs index 5a1e3913c2e..d4a1f3fdd5f 100644 --- a/src/v3/compiler/src/emit/collection_ops_method_contract.rs +++ b/src/v3/compiler/src/emit/collection_ops_method_contract.rs @@ -14,9 +14,8 @@ pub(crate) fn require_method_template_contract_dag_method( expected_method_decl: DeclarationId, ) -> Result<(), &'static str> { let mtc_type_id = dag - .declaration_by_name("MethodTemplateContract") - .ok_or("internal: MethodTemplateContract type missing from dag")? - .id; + .method_template_contract_decl() + .ok_or("internal: MethodTemplateContract type missing from dag")?; let decl = dag.declaration(contract_decl); let template = match &decl.connective { diff --git a/src/v3/compiler/src/emit/python_target.rs b/src/v3/compiler/src/emit/python_target.rs index 238f7439d55..33050bbae06 100644 --- a/src/v3/compiler/src/emit/python_target.rs +++ b/src/v3/compiler/src/emit/python_target.rs @@ -349,18 +349,18 @@ impl PythonIndexes { )?, concat: { let cfields = structural_fields_for_decl(dag, collections)?; - let concat_method_decl = dag.declaration_by_name("concat_method").ok_or( - EmitPythonError::MalformedSpec { - declaration: collections, - detail: "internal: concat_method missing from std.methods registry", - }, - )?; + let concat_method_decl = + dag.concat_method_decl() + .ok_or(EmitPythonError::MalformedSpec { + declaration: collections, + detail: "internal: concat_method missing from std.methods registry", + })?; let id = require_field_decl_ref(cfields, "concat_contract", collections)?; require_method_template_contract_dag_method( dag, id, "concat_contract", - concat_method_decl.id, + concat_method_decl, ) .map_err(|detail| EmitPythonError::MalformedSpec { declaration: id, @@ -370,18 +370,18 @@ impl PythonIndexes { }, length: { let cfields = structural_fields_for_decl(dag, collections)?; - let length_method_decl = dag.declaration_by_name("length_method").ok_or( - EmitPythonError::MalformedSpec { - declaration: collections, - detail: "internal: length_method missing from std.methods registry", - }, - )?; + let length_method_decl = + dag.length_method_decl() + .ok_or(EmitPythonError::MalformedSpec { + declaration: collections, + detail: "internal: length_method missing from std.methods registry", + })?; let id = require_field_decl_ref(cfields, "length_contract", collections)?; require_method_template_contract_dag_method( dag, id, "length_contract", - length_method_decl.id, + length_method_decl, ) .map_err(|detail| EmitPythonError::MalformedSpec { declaration: id, @@ -391,18 +391,18 @@ impl PythonIndexes { }, is_empty: { let cfields = structural_fields_for_decl(dag, collections)?; - let is_empty_method_decl = dag.declaration_by_name("is_empty_method").ok_or( - EmitPythonError::MalformedSpec { - declaration: collections, - detail: "internal: is_empty_method missing from std.methods registry", - }, - )?; + let is_empty_method_decl = + dag.is_empty_method_decl() + .ok_or(EmitPythonError::MalformedSpec { + declaration: collections, + detail: "internal: is_empty_method missing from std.methods registry", + })?; let id = require_field_decl_ref(cfields, "is_empty_contract", collections)?; require_method_template_contract_dag_method( dag, id, "is_empty_contract", - is_empty_method_decl.id, + is_empty_method_decl, ) .map_err(|detail| EmitPythonError::MalformedSpec { declaration: id, @@ -412,18 +412,18 @@ impl PythonIndexes { }, fold: { let cfields = structural_fields_for_decl(dag, collections)?; - let fold_method_decl = dag.declaration_by_name("fold_method").ok_or( - EmitPythonError::MalformedSpec { - declaration: collections, - detail: "internal: fold_method missing from std.methods registry", - }, - )?; + let fold_method_decl = + dag.fold_method_decl() + .ok_or(EmitPythonError::MalformedSpec { + declaration: collections, + detail: "internal: fold_method missing from std.methods registry", + })?; let fold_contract = require_field_decl_ref(cfields, "fold_contract", collections)?; require_method_template_contract_dag_method( dag, fold_contract, "fold_contract", - fold_method_decl.id, + fold_method_decl, ) .map_err(|detail| EmitPythonError::MalformedSpec { declaration: fold_contract, @@ -1928,14 +1928,27 @@ fn require_memory_model( detail: "MemoryModel variants must not carry payload fields", }); } - let variants = [ - ("ValueOnly", MemoryModelBinding::ValueOnly), - ("GarbageCollected", MemoryModelBinding::GarbageCollected), - ("RefCounted", MemoryModelBinding::RefCounted), - ("OwnershipBased", MemoryModelBinding::OwnershipBased), + let variants = dag.emit_model_variants(); + let memory_variants = [ + ( + variants.memory_model.value_only, + MemoryModelBinding::ValueOnly, + ), + ( + variants.memory_model.garbage_collected, + MemoryModelBinding::GarbageCollected, + ), + ( + variants.memory_model.ref_counted, + MemoryModelBinding::RefCounted, + ), + ( + variants.memory_model.ownership_based, + MemoryModelBinding::OwnershipBased, + ), ]; - for (label, binding) in variants { - let variant_id = named_variant_id(dag, "MemoryModel", label)?; + for (variant_id, binding) in memory_variants { + let variant_id = variant_id.ok_or(EmitPythonError::MissingMeta("MemoryModel variant"))?; if constructor == variant_id { return Ok(binding); } @@ -1961,12 +1974,19 @@ fn require_scope_model( detail: "ScopeModel variants must not carry payload fields", }); } - let variants = [ - ("LexicalScoping", ScopeModelBinding::LexicalScoping), - ("DynamicScoping", ScopeModelBinding::DynamicScoping), + let variants = dag.emit_model_variants(); + let scope_variants = [ + ( + variants.scope_model.lexical_scoping, + ScopeModelBinding::LexicalScoping, + ), + ( + variants.scope_model.dynamic_scoping, + ScopeModelBinding::DynamicScoping, + ), ]; - for (label, binding) in variants { - let variant_id = named_variant_id(dag, "ScopeModel", label)?; + for (variant_id, binding) in scope_variants { + let variant_id = variant_id.ok_or(EmitPythonError::MissingMeta("ScopeModel variant"))?; if constructor == variant_id { return Ok(binding); } @@ -1977,26 +1997,6 @@ fn require_scope_model( }) } -fn named_variant_id( - dag: &Dag, - parent_name: &str, - variant_label: &str, -) -> Result { - let parent = dag - .declaration_by_name(parent_name) - .ok_or(EmitPythonError::MissingMeta("variant parent"))?; - let TypeConnective::Disj { variants } = &parent.connective else { - return Err(EmitPythonError::Unsupported(format!( - "{parent_name} is not a disjunction" - ))); - }; - variants - .iter() - .find(|variant| variant.label == variant_label) - .map(|variant| variant.ty) - .ok_or(EmitPythonError::MissingMeta("variant")) -} - fn render_named_template(template: &str, bindings: &[(&str, &str)]) -> String { let mut rendered = template.to_string(); for (name, value) in bindings { diff --git a/src/v3/compiler/src/emit/rust_target.rs b/src/v3/compiler/src/emit/rust_target.rs index 6a63e619af2..fb502207749 100644 --- a/src/v3/compiler/src/emit/rust_target.rs +++ b/src/v3/compiler/src/emit/rust_target.rs @@ -1464,26 +1464,26 @@ fn parse_collection_ops( ) -> Result { let fields = structural_fields_for_decl(dag, declaration)?; - let concat_method_decl = - dag.declaration_by_name("concat_method") - .ok_or(EmitError::MalformedTargetSyntax { - declaration, - detail: "internal: concat_method missing from std.methods registry", - })?; - let length_method_decl = - dag.declaration_by_name("length_method") - .ok_or(EmitError::MalformedTargetSyntax { - declaration, - detail: "internal: length_method missing from std.methods registry", - })?; - let fold_method_decl = - dag.declaration_by_name("fold_method") - .ok_or(EmitError::MalformedTargetSyntax { - declaration, - detail: "internal: fold_method missing from std.methods registry", - })?; + let concat_method_decl = dag + .concat_method_decl() + .ok_or(EmitError::MalformedTargetSyntax { + declaration, + detail: "internal: concat_method missing from std.methods registry", + })?; + let length_method_decl = dag + .length_method_decl() + .ok_or(EmitError::MalformedTargetSyntax { + declaration, + detail: "internal: length_method missing from std.methods registry", + })?; + let fold_method_decl = dag + .fold_method_decl() + .ok_or(EmitError::MalformedTargetSyntax { + declaration, + detail: "internal: fold_method missing from std.methods registry", + })?; let is_empty_method_decl = - dag.declaration_by_name("is_empty_method") + dag.is_empty_method_decl() .ok_or(EmitError::MalformedTargetSyntax { declaration, detail: "internal: is_empty_method missing from std.methods registry", @@ -1494,7 +1494,7 @@ fn parse_collection_ops( dag, concat_contract, "concat_contract", - concat_method_decl.id, + concat_method_decl, ) .map_err(|detail| EmitError::MalformedTargetSyntax { declaration: concat_contract, @@ -1507,7 +1507,7 @@ fn parse_collection_ops( dag, length_contract, "length_contract", - length_method_decl.id, + length_method_decl, ) .map_err(|detail| EmitError::MalformedTargetSyntax { declaration: length_contract, @@ -1520,7 +1520,7 @@ fn parse_collection_ops( dag, is_empty_contract, "is_empty_contract", - is_empty_method_decl.id, + is_empty_method_decl, ) .map_err(|detail| EmitError::MalformedTargetSyntax { declaration: is_empty_contract, @@ -1533,7 +1533,7 @@ fn parse_collection_ops( dag, fold_contract, "fold_contract", - fold_method_decl.id, + fold_method_decl, ) .map_err(|detail| EmitError::MalformedTargetSyntax { declaration: fold_contract, @@ -1874,18 +1874,23 @@ fn parse_rust_field_access( }); } }; - let direct_field = named_variant_id(dag, "FieldAccess", "DirectField").ok_or( - EmitError::MalformedRealization { - declaration, - detail: "FieldAccess.DirectField declaration was not found", - }, - )?; - let accessor_method = named_variant_id(dag, "FieldAccess", "AccessorMethod").ok_or( - EmitError::MalformedRealization { - declaration, - detail: "FieldAccess.AccessorMethod declaration was not found", - }, - )?; + let variants = dag.emit_model_variants(); + let direct_field = + variants + .field_access + .direct_field + .ok_or(EmitError::MalformedRealization { + declaration, + detail: "FieldAccess.DirectField declaration was not found", + })?; + let accessor_method = + variants + .field_access + .accessor_method + .ok_or(EmitError::MalformedRealization { + declaration, + detail: "FieldAccess.AccessorMethod declaration was not found", + })?; if *constructor == direct_field { Ok(RustFieldAccessBinding::DirectField(name)) } else if *constructor == accessor_method { @@ -2101,18 +2106,23 @@ fn parse_parameter_disposition( detail: "ParameterDisposition variants must not carry payload fields", }); } - let borrowed = named_variant_id(dag, "ParameterDisposition", "Borrowed").ok_or( - EmitError::MalformedRealization { - declaration, - detail: "ParameterDisposition.Borrowed declaration was not found", - }, - )?; - let consumed = named_variant_id(dag, "ParameterDisposition", "Consumed").ok_or( - EmitError::MalformedRealization { - declaration, - detail: "ParameterDisposition.Consumed declaration was not found", - }, - )?; + let variants = dag.emit_model_variants(); + let borrowed = + variants + .parameter_disposition + .borrowed + .ok_or(EmitError::MalformedRealization { + declaration, + detail: "ParameterDisposition.Borrowed declaration was not found", + })?; + let consumed = + variants + .parameter_disposition + .consumed + .ok_or(EmitError::MalformedRealization { + declaration, + detail: "ParameterDisposition.Consumed declaration was not found", + })?; if *constructor == borrowed { Ok(ParameterDispositionBinding::Borrowed) } else if *constructor == consumed { @@ -2242,18 +2252,22 @@ fn require_read_strategy( detail: "ReadStrategy variants must not carry payload fields", }); } - let borrow_variant = named_variant_id(dag, "ReadStrategy", "Borrow").ok_or( - EmitError::MalformedTargetSyntax { + let variants = dag.emit_model_variants(); + let borrow_variant = variants + .read_strategy + .borrow + .ok_or(EmitError::MalformedTargetSyntax { declaration, detail: "ReadStrategy.Borrow declaration was not found", - }, - )?; - let pass_variant = named_variant_id(dag, "ReadStrategy", "PassByValue").ok_or( - EmitError::MalformedTargetSyntax { - declaration, - detail: "ReadStrategy.PassByValue declaration was not found", - }, - )?; + })?; + let pass_variant = + variants + .read_strategy + .pass_by_value + .ok_or(EmitError::MalformedTargetSyntax { + declaration, + detail: "ReadStrategy.PassByValue declaration was not found", + })?; if *constructor == borrow_variant { Ok(ReadStrategyBinding::Borrow) } else if *constructor == pass_variant { @@ -2296,18 +2310,23 @@ fn require_construct_strategy( detail: "ConstructStrategy variants must not carry payload fields", }); } - let copy_or_clone = named_variant_id(dag, "ConstructStrategy", "CopyOrClone").ok_or( - EmitError::MalformedTargetSyntax { - declaration, - detail: "ConstructStrategy.CopyOrClone declaration was not found", - }, - )?; - let pass_variant = named_variant_id(dag, "ConstructStrategy", "PassByValue").ok_or( - EmitError::MalformedTargetSyntax { - declaration, - detail: "ConstructStrategy.PassByValue declaration was not found", - }, - )?; + let variants = dag.emit_model_variants(); + let copy_or_clone = + variants + .construct_strategy + .copy_or_clone + .ok_or(EmitError::MalformedTargetSyntax { + declaration, + detail: "ConstructStrategy.CopyOrClone declaration was not found", + })?; + let pass_variant = + variants + .construct_strategy + .pass_by_value + .ok_or(EmitError::MalformedTargetSyntax { + declaration, + detail: "ConstructStrategy.PassByValue declaration was not found", + })?; if *constructor == copy_or_clone { Ok(ConstructStrategyBinding::CopyOrClone) } else if *constructor == pass_variant { @@ -2326,14 +2345,18 @@ fn require_source_mutability( declaration: DeclarationId, ) -> Result { let value = require_unit_variant_field(fields, "mutability", declaration)?; - let immutable = named_variant_id(dag, "Mutability", "Immutable").ok_or( - EmitError::MalformedTargetSyntax { + let variants = dag.emit_model_variants(); + let immutable = variants + .mutability + .immutable + .ok_or(EmitError::MalformedTargetSyntax { declaration, detail: "Mutability.Immutable declaration was not found", - }, - )?; - let mutable = - named_variant_id(dag, "Mutability", "Mutable").ok_or(EmitError::MalformedTargetSyntax { + })?; + let mutable = variants + .mutability + .mutable + .ok_or(EmitError::MalformedTargetSyntax { declaration, detail: "Mutability.Mutable declaration was not found", })?; @@ -2355,12 +2378,18 @@ fn require_source_purity( declaration: DeclarationId, ) -> Result { let value = require_unit_variant_field(fields, "purity", declaration)?; - let pure = named_variant_id(dag, "Purity", "Pure").ok_or(EmitError::MalformedTargetSyntax { - declaration, - detail: "Purity.Pure declaration was not found", - })?; - let effectful = - named_variant_id(dag, "Purity", "Effectful").ok_or(EmitError::MalformedTargetSyntax { + let variants = dag.emit_model_variants(); + let pure = variants + .purity + .pure + .ok_or(EmitError::MalformedTargetSyntax { + declaration, + detail: "Purity.Pure declaration was not found", + })?; + let effectful = variants + .purity + .effectful + .ok_or(EmitError::MalformedTargetSyntax { declaration, detail: "Purity.Effectful declaration was not found", })?; @@ -2382,18 +2411,21 @@ fn require_source_structure( declaration: DeclarationId, ) -> Result { let value = require_unit_variant_field(fields, "structure", declaration)?; - let explicit = named_variant_id(dag, "Structure", "ExplicitDAG").ok_or( - EmitError::MalformedTargetSyntax { + let variants = dag.emit_model_variants(); + let explicit = variants + .structure + .explicit_dag + .ok_or(EmitError::MalformedTargetSyntax { declaration, detail: "Structure.ExplicitDAG declaration was not found", - }, - )?; - let arbitrary = named_variant_id(dag, "Structure", "Arbitrary").ok_or( - EmitError::MalformedTargetSyntax { + })?; + let arbitrary = variants + .structure + .arbitrary + .ok_or(EmitError::MalformedTargetSyntax { declaration, detail: "Structure.Arbitrary declaration was not found", - }, - )?; + })?; if value == explicit { Ok(SourceStructureBinding::ExplicitDag) } else if value == arbitrary { @@ -2413,16 +2445,21 @@ fn require_source_iteration( ) -> Result { let value = require_unit_variant_field(fields, "iteration", declaration)?; let bounded = - named_variant_id(dag, "Iteration", "Bounded").ok_or(EmitError::MalformedTargetSyntax { - declaration, - detail: "Iteration.Bounded declaration was not found", - })?; - let unbounded = named_variant_id(dag, "Iteration", "Unbounded").ok_or( - EmitError::MalformedTargetSyntax { - declaration, - detail: "Iteration.Unbounded declaration was not found", - }, - )?; + dag.emit_model_variants() + .iteration + .bounded + .ok_or(EmitError::MalformedTargetSyntax { + declaration, + detail: "Iteration.Bounded declaration was not found", + })?; + let unbounded = + dag.emit_model_variants() + .iteration + .unbounded + .ok_or(EmitError::MalformedTargetSyntax { + declaration, + detail: "Iteration.Unbounded declaration was not found", + })?; if value == bounded { Ok(SourceIterationBinding::Bounded) } else if value == unbounded { @@ -2441,35 +2478,34 @@ fn require_memory_model( declaration: DeclarationId, ) -> Result { let value = require_unit_variant_field(fields, "memory", declaration)?; - let variants = [ + let variants = dag.emit_model_variants(); + let memory_variants = [ ( - "ValueOnly", + variants.memory_model.value_only, MemoryModelBinding::ValueOnly, "MemoryModel.ValueOnly declaration was not found", ), ( - "GarbageCollected", + variants.memory_model.garbage_collected, MemoryModelBinding::GarbageCollected, "MemoryModel.GarbageCollected declaration was not found", ), ( - "RefCounted", + variants.memory_model.ref_counted, MemoryModelBinding::RefCounted, "MemoryModel.RefCounted declaration was not found", ), ( - "OwnershipBased", + variants.memory_model.ownership_based, MemoryModelBinding::OwnershipBased, "MemoryModel.OwnershipBased declaration was not found", ), ]; - for (variant_name, binding, detail) in variants { - let variant = named_variant_id(dag, "MemoryModel", variant_name).ok_or( - EmitError::MalformedTargetSyntax { - declaration, - detail, - }, - )?; + for (variant, binding, detail) in memory_variants { + let variant = variant.ok_or(EmitError::MalformedTargetSyntax { + declaration, + detail, + })?; if value == variant { return Ok(binding); } @@ -2487,25 +2523,24 @@ fn require_scope_model( declaration: DeclarationId, ) -> Result { let value = require_unit_variant_field(fields, "scope", declaration)?; - let variants = [ + let variants = dag.emit_model_variants(); + let scope_variants = [ ( - "LexicalScoping", + variants.scope_model.lexical_scoping, ScopeModelBinding::LexicalScoping, "ScopeModel.LexicalScoping declaration was not found", ), ( - "DynamicScoping", + variants.scope_model.dynamic_scoping, ScopeModelBinding::DynamicScoping, "ScopeModel.DynamicScoping declaration was not found", ), ]; - for (variant_name, binding, detail) in variants { - let variant = named_variant_id(dag, "ScopeModel", variant_name).ok_or( - EmitError::MalformedTargetSyntax { - declaration, - detail, - }, - )?; + for (variant, binding, detail) in scope_variants { + let variant = variant.ok_or(EmitError::MalformedTargetSyntax { + declaration, + detail, + })?; if value == variant { return Ok(binding); } @@ -2548,17 +2583,6 @@ fn require_unit_variant_field( Ok(*constructor) } -fn named_variant_id(dag: &Dag, parent_name: &str, variant_label: &str) -> Option { - let parent = dag.declaration_by_name(parent_name)?; - let TypeConnective::Disj { variants } = &parent.connective else { - return None; - }; - variants - .iter() - .find(|variant| variant.label == variant_label) - .map(|variant| variant.ty) -} - fn derive_callable_dispositions( dag: &Dag, external: &HashMap>, @@ -6016,9 +6040,15 @@ fn classify(s: Sign) -> Int = match s { Plus => 0, Minus => 1 }", ) .expect("compiles"); let rendering_decl = dag.rust_rendering_spec().expect("rust_rendering cached"); - let pass_by_value = named_variant_id(&dag, "ReadStrategy", "PassByValue") + let pass_by_value = dag + .emit_model_variants() + .read_strategy + .pass_by_value .expect("ReadStrategy.PassByValue exists"); - let copy_or_clone = named_variant_id(&dag, "ConstructStrategy", "CopyOrClone") + let copy_or_clone = dag + .emit_model_variants() + .construct_strategy + .copy_or_clone .expect("ConstructStrategy.CopyOrClone exists"); dag.declaration_mut(rendering_decl).value_body = Some(ValueBody::Structural { fields: vec![ @@ -6198,10 +6228,16 @@ fn use_callback(base: Int) -> Int = apply_to_three(|x| base + x)", fn parameter_dispositions_reject_arity_drift_and_slot_collisions() { let dag = compile_to_dag("fn id(x: Int) -> Int = x", "arity_drift.v3").expect("compiles"); let bogus_decl = dag.declaration_by_name("id").expect("id decl").id; - let borrowed = - named_variant_id(&dag, "ParameterDisposition", "Borrowed").expect("Borrowed"); - let consumed = - named_variant_id(&dag, "ParameterDisposition", "Consumed").expect("Consumed"); + let borrowed = dag + .emit_model_variants() + .parameter_disposition + .borrowed + .expect("Borrowed"); + let consumed = dag + .emit_model_variants() + .parameter_disposition + .consumed + .expect("Consumed"); let entry = |slot: i64, ctor: DeclarationId| { FieldValue::Record(vec![ ( diff --git a/src/v3/compiler/src/regen_bootstrap_emit.rs b/src/v3/compiler/src/regen_bootstrap_emit.rs index 4922efeca05..38dafa6fd62 100644 --- a/src/v3/compiler/src/regen_bootstrap_emit.rs +++ b/src/v3/compiler/src/regen_bootstrap_emit.rs @@ -91,6 +91,7 @@ fn emit_bootstrap_module(dag: &Dag, function_name: &str) -> String { " verifier_output_policy_variants: VerifierOutputPolicyVariants::default(),\n", ); out.push_str(" callable_strategy_variants: CallableStrategyVariants::default(),\n"); + out.push_str(" emit_model_variants: EmitModelVariants::default(),\n"); push_field(&mut out, "clusters", &format!("{clusters_fn}()"), 2); push_field( &mut out, diff --git a/src/v3/compiler/tests/integration/sg0_census_test.rs b/src/v3/compiler/tests/integration/sg0_census_test.rs index d87bb95fb24..9f094f5741e 100644 --- a/src/v3/compiler/tests/integration/sg0_census_test.rs +++ b/src/v3/compiler/tests/integration/sg0_census_test.rs @@ -40,6 +40,42 @@ use v3_compiler::generated_files::GENERATED_FILES; // informally named in `dsl/gunbc/compiler.dag`. const CENSUS_ROOT: &str = "src/v3/compiler"; +#[test] +fn emit_production_code_has_no_declaration_by_name_calls() { + let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); + let emit_root = manifest_dir.join("src").join("emit"); + let mut files = vec![manifest_dir.join("src").join("emit.rs")]; + for entry in fs::read_dir(&emit_root).expect("read src/emit") { + let path = entry.expect("emit dir entry").path(); + if path.extension() == Some(OsStr::new("rs")) { + files.push(path); + } + } + files.sort(); + + let mut offenders = Vec::new(); + for path in files { + let source = fs::read_to_string(&path).expect("read emit source"); + let production_source = source + .split("\n#[cfg(test)]") + .next() + .expect("split always yields a prefix"); + if production_source.contains(".declaration_by_name(") { + offenders.push( + path.strip_prefix(manifest_dir) + .unwrap_or(&path) + .display() + .to_string(), + ); + } + } + + assert!( + offenders.is_empty(), + "emit production modules must use cached DeclarationId accessors, not Dag::declaration_by_name. Offenders: {offenders:#?}" + ); +} + // All non-test .rs files under `src/v3/compiler` that are currently // hand-authored. Sorted; one path per line, relative to the // workspace root. **Every SG-1..SG-7 PR shortens this list.**