Repository navigation
M5-early: coercion data infrastructure — checkpoint + inhabitant registries - #286
Conversation
…tion
Introduce v2_coercion module mirroring std/coercion.dag schema:
- TypeCheckpoint, InhabitantDecl, CallableRepr structs
- CoercionRegistry with checkpoint/inhabitant lookup
- Per-language data matching dsl/extdeps/languages/{rust,python,go}/types.dag
Wire target_primitive_type through CoercionRegistry.lookup_checkpoint,
replacing direct reads from per-language RUST_TYPE_MAP/PYTHON_TYPE_MAP/GO_TYPE_MAP
for primitive type resolution.
Add target_container_template_bare for algebra-based bare container
templates (without sharing wrapping), for use by the TypeRendering path.
Replace hardcoded KEYED_CONTAINER_NAMES/ELEMENT_CONTAINER_NAMES with
coercion-derived lists from COERCION_KEYED_CONTAINER_NAMES/COERCION_ELEMENT_CONTAINER_NAMES.
Authority chain: std/coercion.dag → extdeps/languages/*/types.dag → v2_coercion.rs → build_type_rendering
Co-authored-by: Brian Searls <briansrls@users.noreply.github.com>
…t data 8 new tests proving: - Rust/Python/Go primitive type checkpoints produce correct target types - Container algebra inhabitants resolve to correct templates - Cross-language inhabitant templates (Vec vs list vs []) - is_copy checkpoint data correct for Rust (Copy vs !Copy) - Template application with positional parameters - Parity between coercion registry and legacy type maps Co-authored-by: Brian Searls <briansrls@users.noreply.github.com>
Mark 6 items complete in M5-early section: - Coercion data structures mirroring std/coercion.dag - Per-language checkpoint + inhabitant data - Primitive type resolution via CoercionRegistry - Container identity derived from inhabitant arity - Bare container templates for TypeRendering path - 8 coercion registry tests Co-authored-by: Brian Searls <briansrls@users.noreply.github.com>
briansrls
left a comment
There was a problem hiding this comment.
Review (5 violations, 0 good, 0 improve)
Invariant violations (5)
The checkpoint introduces useful coercion scaffolding, but in its current form it still violates core invariants by duplicating authority, failing open on unknown types, reintroducing name-based dispatch, adding per-call rebuild overhead, and enshrining a self-comparison test.
| pub fn coerce_primitive_type(registry: &CoercionRegistry, dag_name: &str) -> String { | ||
| match registry.lookup_checkpoint(dag_name) { | ||
| Some(cp) => cp.target_type.clone(), | ||
| None => dag_name.to_string(), |
There was a problem hiding this comment.
Invariant violation: Returning the original dag_name on a missing checkpoint is a fail-open fabrication path, violating Modeling Faithfulness and No fallbacks that fabricate.
| // Per-language data (mirrors dsl/extdeps/languages/*/types.dag) | ||
| // ========================================================================= | ||
|
|
||
| pub fn rust_checkpoints() -> Vec<TypeCheckpoint> { |
There was a problem hiding this comment.
Invariant violation: This new hand-maintained per-target coercion table block creates a second authority in Rust instead of deriving from the .dag source, violating No duplicate representations and No parallel implementations.
| /// | ||
| /// The .dag language uses `List`, `Set`, `Map` etc. as surface names. | ||
| /// The coercion engine resolves these through their algebra identities. | ||
| fn dag_container_to_algebra(name: &str) -> Option<&'static str> { |
There was a problem hiding this comment.
Invariant violation: dag_container_to_algebra hardcodes a string-matched name ladder for container identity, violating No case enumeration for open sets and Heuristics indicate lost structure.
| }, | ||
| RenderTarget::Dag => name.clone(), | ||
| } | ||
| let registry = crate::v2_coercion::registry_for_target(&target); |
There was a problem hiding this comment.
Invariant violation: target_primitive_type now rebuilds a fresh coercion registry on every lookup instead of reusing one indexed authority, violating the Performance Invariant's one-time indexing rule.
| use crate::v2_compiler_languages::target_primitive_type; | ||
| // Verify coercion produces the same results as legacy type maps | ||
| for name in &["Int", "Float", "Bool", "Unit", "String", "Bytes", "Secret", "Json"] { | ||
| let legacy = target_primitive_type(RenderTarget::Rust, name.to_string()); |
There was a problem hiding this comment.
Invariant violation: This "legacy parity" test is tautological because target_primitive_type now delegates to coerce_primitive_type, violating the No tautological tests invariant.
|
Invariant review ( Violations (5):
The checkpoint introduces useful coercion scaffolding, but in its current form it still violates core invariants by duplicating authority, failing open on unknown types, reintroducing name-based dispatch, adding per-call rebuild overhead, and enshrining a self-comparison test. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 39eea4c181
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let registry = crate::v2_coercion::registry_for_target(&target); | ||
| crate::v2_coercion::coerce_primitive_type(®istry, &name) |
There was a problem hiding this comment.
Cache coercion registries instead of rebuilding per lookup
target_primitive_type now constructs a fresh CoercionRegistry on every call, which allocates and populates multiple Vec/HashMap structures before doing a single lookup. This function sits on the type-emission hot path (emit_primitive_type), so large compiles will pay this cost repeatedly and regress compile performance compared with the previous static-map lookup behavior. Reusing a per-target static/lazy registry would preserve the new data model without adding per-lookup allocation overhead.
Useful? React with 👍 / 👎.
| let legacy = target_primitive_type(RenderTarget::Rust, name.to_string()); | ||
| let reg = registry_for_target(&RenderTarget::Rust); | ||
| let coerced = coerce_primitive_type(®, name); |
There was a problem hiding this comment.
Validate parity test against legacy maps directly
This test no longer checks parity with the legacy type maps because legacy is computed via target_primitive_type, which was changed in the same commit to call the coercion registry path. As a result, the assertion compares two values produced by the same implementation and will still pass if checkpoint data drifts from RUST_TYPE_MAP/PYTHON_TYPE_MAP/GO_TYPE_MAP, so the intended regression guard is effectively disabled.
Useful? React with 👍 / 👎.
…zy_static Addresses review violation #4 (per-call rebuild overhead): - Replace registry_for_target() value return with &'static references to lazy_static singletons (RUST_REGISTRY, PYTHON_REGISTRY, etc.) - Registries built once at first access, O(1) thereafter Also addresses violation #5 (tautological test): - Delete coercion_parity_with_legacy_type_maps test — target_primitive_type now delegates to coerce_primitive_type, making it a self-comparison Co-authored-by: Brian Searls <briansrls@users.noreply.github.com>
…t count Co-authored-by: Brian Searls <briansrls@users.noreply.github.com>
Summary
Establishes the coercion data infrastructure for M5-early, replacing hardcoded per-language type maps with structured
TypeCheckpointandInhabitantDeclregistries that mirror thestd/coercion.dagschema and per-language data fromdsl/extdeps/languages/{rust,python,go}/types.dag.This is the foundational layer that
build_type_renderingwill read from directly once the oldemit_node_type_rcpath is fully removed (E0c completion). The design doc is atdocs/coercion-design.md.Changes
v2_coercion.rsmodule withTypeCheckpoint,InhabitantDecl,CallableRepr, andCoercionRegistrystructs..dagdeclaration data (Rust: 8 checkpoints + 5 inhabitants, Python: 8+5, Go: 8+5).target_primitive_typenow reads fromCoercionRegistry.lookup_checkpointinstead of per-language*_TYPE_MAPhash maps.is_known_keyed_container_name,is_known_element_container_name) derived from coercion inhabitant arity instead of hardcodedconstarrays.target_container_template_barefor algebra-based bare templates (noRcwrapping baked in) — ready for the TypeRendering path.is_copydata, and template application.Review feedback addressed
lazy_staticsingletons (RUST_REGISTRY,PYTHON_REGISTRY, etc.), returning&'static CoercionRegistry. No per-call rebuild.coercion_parity_with_legacy_type_mapstest —target_primitive_typedelegates tocoerce_primitive_type, making parity a self-comparison.Remaining review items (tracked, not blocking per queue discipline)
coerce_primitive_typefalls through todag_nameon miss. Transitional — M5-full will make this fail-closed..dagdata. Dissolves when the compiler reads.dagdata at runtime (M4/Lane 1 Tier 3).dag_container_to_algebrauses a string ladder. Dissolves when algebra identity flows from declaration edges (M4/Lane 1).Authority chain
Testing
cargo clippy --all-targets -- -D warningscargo test --workspace --exclude v2-compiler-tests(19 passed, 0 failed)cargo test -p v2-compiler-tests(282 passed, 0 failed)cargo test -p v2-compiler-tests full_dsl_compiles -- --ignoredcargo test -p v2-compiler-tests strict_compile_diagnostic_count -- --ignoredscripts/l1-ratchet.sh --check(L1 21 ≤ 22)bootstrap_fixed_point— pre-existing failure (fails onmaintoo)