Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,744 changes: 1,374 additions & 1,370 deletions src/v3/compiler/src/bootstrap_generated.rs

Large diffs are not rendered by default.

2,578 changes: 1,291 additions & 1,287 deletions src/v3/compiler/src/bootstrap_generated_without_parse_surface.rs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/v3/compiler/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ mod m2_lens_unused_parameters_migration_test;
mod m2_lens_variant_payload_migration_test;
#[path = "integration/m2_substrate_inhabitance_test.rs"]
mod m2_substrate_inhabitance_test;
#[path = "integration/method_template_contract_test.rs"]
mod method_template_contract_test;
#[path = "integration/p0_std_render_repeat_string_test.rs"]
mod p0_std_render_repeat_string_test;
#[path = "integration/pb1_bootstrap_full_snapshot_test.rs"]
Expand Down
149 changes: 149 additions & 0 deletions src/v3/compiler/tests/integration/method_template_contract_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
//! **Layer:** integration
//!
//! Acceptance for `MethodTemplateContract` substrate carrier in
//! `src/v3/std/emit_model.dag`. Sibling type to §6a `MethodContract` in
//! `src/v3/std/algebra.dag`; this PR lands the type only — row population
//! and `MethodTranslation` / `SimpleMethodSpec` retirement are
//! Grounding-owned follow-ups.
//!
//! Three claims per Director dispatch:
//! - `method_template_contract_distinct_from_method_contract`
//! - `method_template_contract_per_target_dag_method_unique`
//! (vacuous today over zero rows; load-bearing once Grounding populates)
//! - `method_template_contract_does_not_carry_cost_data`

use std::collections::HashSet;
use v3_compiler::dag::{Dag, DeclarationId, Field, TypeConnective};
use v3_compiler::generated_full_bootstrap_dag;

fn conj_field_labels(dag: &Dag, name: &str) -> Vec<String> {
let decl = dag
.declaration_by_name(name)
.unwrap_or_else(|| panic!("`{name}` missing from full bootstrap"));
match &decl.connective {
TypeConnective::Conj { children } => children.iter().map(|f| f.label.clone()).collect(),
other => panic!("`{name}` is not a Conj: {other:?}"),
}
}

fn decl_id_by_name(dag: &Dag, name: &str) -> DeclarationId {
dag.declaration_by_name(name)
.unwrap_or_else(|| panic!("`{name}` missing from full bootstrap"))
.id
}

#[test]
fn method_template_contract_distinct_from_method_contract() {
let dag = generated_full_bootstrap_dag();

let template_id = decl_id_by_name(&dag, "MethodTemplateContract");
let metadata_id = decl_id_by_name(&dag, "MethodContract");
assert_ne!(
template_id, metadata_id,
"MethodTemplateContract and §6a MethodContract must be distinct \
declarations (P2 single-authority)"
);

let template_fields: HashSet<String> = conj_field_labels(&dag, "MethodTemplateContract")
.into_iter()
.collect();
let metadata_fields: HashSet<String> = conj_field_labels(&dag, "MethodContract")
.into_iter()
.collect();
assert!(
template_fields.is_disjoint(&metadata_fields),
"MethodTemplateContract and §6a MethodContract field sets must be \
disjoint — they are orthogonal sibling facts attached to method \
declarations (P1 step 1). template={template_fields:?} \
metadata={metadata_fields:?}"
);
}

#[test]
fn method_template_contract_does_not_carry_cost_data() {
let dag = generated_full_bootstrap_dag();
let labels: HashSet<String> = conj_field_labels(&dag, "MethodTemplateContract")
.into_iter()
.collect();

for forbidden in ["cost_shape", "size_effect", "callback_element_position"] {
assert!(
!labels.contains(forbidden),
"MethodTemplateContract carries `{forbidden}` — that field \
belongs on §6a MethodContract (target-agnostic cost/complexity \
metadata). Template-contract carrier holds only render-template \
facts (P1 step 2). actual fields={labels:?}"
);
}

let expected: HashSet<&str> = [
"dag_method",
"runtime_template",
"emit_template",
"wraps_result",
"placeholder_convention",
]
.into_iter()
.collect();
let actual: HashSet<&str> = labels.iter().map(String::as_str).collect();
assert_eq!(
actual, expected,
"MethodTemplateContract field set diverged from Director-locked shape"
);
}

/// Helper: extract `dag_method` decl ids from a list of MethodTemplateContract
/// rows represented as Conj field-binding lists. Used by the uniqueness check.
/// Today no such lists exist (Grounding owns row population); the check runs
/// vacuously over zero rows and becomes load-bearing once rows land.
fn assert_dag_method_unique(rows: &[Vec<Field>], list_name: &str) {
let mut seen: HashSet<DeclarationId> = HashSet::new();
for row in rows {
let dag_method_field = row
.iter()
.find(|f| f.label == "dag_method")
.unwrap_or_else(|| panic!("row in `{list_name}` missing `dag_method` field"));
assert!(
seen.insert(dag_method_field.ty),
"duplicate `dag_method` in `{list_name}` — per-target \
MethodTemplateContract rows must be unique by `dag_method`"
);
}
}

#[test]
fn method_template_contract_per_target_dag_method_unique() {
// Substrate-only PR: zero `List<MethodTemplateContract>` data lists exist
// today (Grounding owns Rust/Python/Go row population). The uniqueness
// check is wired here and runs vacuously over zero synthetic rows.
let zero_rows: Vec<Vec<Field>> = Vec::new();
for list_name in EXPECTED_PER_TARGET_LISTS {
assert_dag_method_unique(&zero_rows, list_name);
}

// Fail-loud trigger: when Grounding lands ANY of the per-target row lists,
// this assertion fires and forces this test to grow real row-walking
// logic in lock-step. Without it, the vacuous pass above could silently
// outlive its trigger and leave per-target uniqueness unchecked once rows
// exist. The list authority is `data <target>_method_template_contracts`
// in the per-target extdeps file (Grounding-owned).
let dag = generated_full_bootstrap_dag();
let landed: Vec<&str> = EXPECTED_PER_TARGET_LISTS
.iter()
.copied()
.filter(|name| dag.declaration_by_name(name).is_some())
.collect();
assert!(
landed.is_empty(),
"per-target `MethodTemplateContract` row list(s) have landed: {landed:?}. \
Grow this test to enumerate each list's rows and hand them to \
`assert_dag_method_unique` instead of relying on the synthetic \
zero-row vacuous pass above."
);
}

const EXPECTED_PER_TARGET_LISTS: &[&str] = &[
"rust_method_template_contracts",
"python_method_template_contracts",
"go_method_template_contracts",
];
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ src/v3/std/computation_model.dag 14 22953 20e458fe097d175a
src/v3/std/diagnostics.dag 6 7982 6dc35615cc265b02
src/v3/std/dimensions.dag 21 40239 d595746d2bda22f6
src/v3/std/effects.dag 53 97984 1acdbeaa68acfd2e
src/v3/std/emit_model.dag 32 62082 79a40df329c1a24b
src/v3/std/emit_model.dag 34 65593 49cc4eb1f1faadcd
src/v3/std/extdeps_bootstrap_fixtures.dag 4 4912 e2b6d96f5900a70a
src/v3/std/induction.dag 56 88794 546f74c63c189c50
src/v3/std/list.dag 19 40269 8c648d4a4fd05c21
Expand Down
11 changes: 11 additions & 0 deletions src/v3/compiler/tests/integration/sg0_census_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,17 @@ const EXPECTED_HAND_AUTHORED_TEST: &[&str] = &[
"src/v3/compiler/tests/integration/m2_lens_unused_parameters_migration_test.rs",
"src/v3/compiler/tests/integration/m2_lens_variant_payload_migration_test.rs",
"src/v3/compiler/tests/integration/m2_substrate_inhabitance_test.rs",
// T-Ground-LanguageSpec / R2-Substrate: Director-approved hand-Rust
// acceptance for `MethodTemplateContract` substrate carrier (PR #1175).
// Three structural claims over the regenerated bootstrap Dag —
// distinct-from-§6a-MethodContract, no-cost-data field set,
// per-target-list `dag_method` uniqueness (vacuous over zero rows
// today; load-bearing once Grounding's row-population PR lands).
// Dispatch explicitly accepted "focused Rust tests over the reflected
// substrate" rather than a parallel testgen harness; dissolves into
// .dag `TestClaim` form when the testgen path covers reflected-Dag
// structural assertions over std/ types.
"src/v3/compiler/tests/integration/method_template_contract_test.rs",
"src/v3/compiler/tests/integration/p0_std_render_repeat_string_test.rs",
"src/v3/compiler/tests/integration/pb1_bootstrap_full_snapshot_test.rs",
"src/v3/compiler/tests/integration/pipe_desugar.rs",
Expand Down
59 changes: 59 additions & 0 deletions src/v3/std/emit_model.dag
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,62 @@ type LanguageSpec {
collection_ops: DeclarationRef
values: DeclarationRef
}

// Placeholder convention for method-template render strings.
//
// `IndexedArgs` — positional placeholders `{arg0}`, `{arg1}`, … as used by
// `dsl/extdeps/languages/{rust,python,go}/runtime.dag` `MethodTranslation`
// rows.
// `NamedArg` — single `{arg}` placeholder as used by Rust `emit.dag`
// `SimpleMethodSpec` and Python/Go `emit.dag` template maps.
//
// The receiver placeholder `{recv}` is common to both conventions and lives
// outside this axis.
//
// 🟡 SCAFFOLD coproduct. Pattern 1 (per-call template typing) fails because
// today's templates are opaque `String` values whose placeholder dialect is
// observable only by string inspection — there is no upstream type-system
// fact distinguishing `{arg0}/{arg1}` indexed slots from a single named
// `{arg}` slot. Pattern 2 (collapse to a single labeled record) erases the
// closed semantic split that consumers actually dispatch on. Pattern 3 is
// the dissolution path: when method-template substrate parses templates
// into structured slot facts (a typed `List<TemplateSegment>` with arity
// and slot-kind known structurally), this convention tag derives from
// segment shape and the named coproduct retires. Trigger: structured
// template-segment substrate in a follow-up substrate-completion lane.
// Until then this scaffold is the single declared authority for the
// dialect axis.
type PlaceholderConvention

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in head — PlaceholderConvention now carries a 🟡 SCAFFOLD coproduct receipt with named dissolution trigger (structured template-segment substrate replacing dialect-by-string-inspection). — sent from tidy-wolf-507

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified at current head — 🟡 SCAFFOLD coproduct receipt is present at src/v3/std/emit_model.dag lines 330–342 (immediately above type PlaceholderConvention at line 343). Receipt names Pattern 1 / Pattern 2 / Pattern 3 with explicit dissolution trigger ("structured template-segment substrate in a follow-up substrate-completion lane"). The cursor approve at sha 2c13dc8 also cites this receipt at 330:342:src/v3/std/emit_model.dag. This finding's bot was looking at an earlier commit (sha bd8725b / 51fb2b4) before the receipt landed. — sent from tidy-wolf-507

= IndexedArgs
| NamedArg

// Per-target, per-method template contract. Sibling carrier to §6a
// `MethodContract` in `src/v3/std/algebra.dag` — that one carries
// target-agnostic cost/complexity metadata keyed by
// `(algebra_id, method_id)`; this one carries target-specific render
// templates keyed by `dag_method`. The two are orthogonal sibling facts
// attached to method declarations; neither is a parent or child of the
// other (P1 step 1).
//
// Single template-contract fact per row — no cost/complexity fields
// (P1 step 2). Per-target identity is carried by the containing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BLOCKING: Per-target identity is only carried by the containing list convention, so the target fact does not flow as a typed substrate fact when rows are consumed downstream.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Director-locked on parent inbox #1130 before this PR opened. Per-target identity carried by the containing data <target>_method_template_contracts: List<MethodTemplateContract> list authority (P2 single-authority); duplicating it on the row would violate P2. This is the established pattern in emit_model.dag — rust_simple_method_specs: List<SimpleMethodSpec>, rust_method_call_templates: Map<String, String>, etc. all carry target identity via the containing data declaration, not via a row field. Downstream consumers receive the typed-target fact through the data-declaration lookup path (same flow as every existing per-target data list in this file). The cursor approve at sha 2c13dc8c independently confirmed alignment with modeling discipline. — sent from tidy-wolf-507

// `data <target>_method_template_contracts: List<MethodTemplateContract>`
// list authority, not duplicated on the row (P2 single-authority).
//
// `dag_method: DeclarationRef` aligns with the in-file convention
// (`op: DeclarationRef` on `OperatorRealization`, `language` / `target`
// on every realization). When a method-declaration registry lands in
// `dsl/std/`, this field can refine to a typed `MethodRef` without
// changing consumers.
//
// 🟡 TRANSITIONAL. This carrier consolidates the parallel-rep ×3 between

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BLOCKING: The new carrier is explicitly transitional and defers row population plus old-carrier retirement to follow-up work, so this PR adds another substrate shape without reducing the parallel MethodTranslation/SimpleMethodSpec/template-map authorities.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Manager-accepted scope split (latest manager triage on head 57bbd40, third paragraph: "Representation split from data migration — accepted as a manager split. This PR is intentionally the Substrate-owned type slice. Grounding owns rows and MethodTranslation / SimpleMethodSpec retirement. Keep the scope split."). PR body updated to state this explicitly. — sent from tidy-wolf-507

// `MethodTranslation` (`runtime.dag`) and `SimpleMethodSpec` (`emit.dag`)
// across Rust/Python/Go. Row population and old-carrier retirement are
// Grounding-owned and land in follow-up work.
type MethodTemplateContract {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BLOCKING: MethodTemplateContract makes runtime_template and emit_template mandatory in one row even though the existing runtime and emit method sets and placeholder conventions differ, forcing fabricated fields or dropped facts instead of modeling the real contracts faithfully.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Director-locked on parent inbox #1130: the flat two-template shape is the accepted carrier for this PR; the dissolved base_template + PostRenderAdjustment alternative was specifically rejected after audit (#1141 → #1130). Substrate-only PR scope locks the shape; row-population semantics (which methods have which templates) is Grounding-owned and addresses the "differing method sets / dialects" concern at the row layer, not the type layer. — sent from tidy-wolf-507

dag_method: DeclarationRef

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BLOCKING: dag_method: DeclarationRef admits non-method declarations even though the carrier is keyed by methods, making illegal rows representable.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Director-locked Q1 answer on parent inbox #1130 (verbatim): "Use (a) dag_method: DeclarationRef for this PR. Reason: emit_model.dag already uses DeclarationRef for analogous typed references (language, target, op). There is no live method-declaration registry yet, so a MethodRef newtype would be nominal without a real authority target. If/when method declarations become first-class, that can be a separate refinement."

The illegal-state concern is real but is the same shape as the existing PatternRealization pattern at emit_model.dag lines 140–152: bare DeclarationRef admits any declaration; the structural constraint ("must be a variant of target's Disj") is enforced fail-closed at the emitter-binding parse boundary because the substrate grammar can't yet express DeclarationRef<VariantOf<T>>. Same residual class here — MethodRef as a refined DeclarationRef<MethodDeclaration> is the dissolution path, but MethodDeclaration itself does not yet exist as a substrate concept (methods are anonymous Arrow children of Algebra Conjs today). Director's call: ship typed-when-available later, don't introduce a phantom newtype now. Comment at lines 363–367 already documents the future refinement path. — sent from tidy-wolf-507

runtime_template: String
emit_template: String
wraps_result: Bool
placeholder_convention: PlaceholderConvention
}
Loading