Skip to content

CG lane: shared_types consolidation + TLC-1 callable dispatch - #321

Merged
briansrls merged 11 commits into
mainfrom
cg-tlc1-unblock
Apr 6, 2026
Merged

briansrls merged 11 commits into
mainfrom
cg-tlc1-unblock

Conversation

@briansrls

Copy link
Copy Markdown
Contributor

Summary

  • TLC-1 zero-arg callable dispatch: Unify clone semantics in SharingStrategy, land callable dispatch for Rust emitter
  • shared_types consolidation: Eliminate ValueContext middleman type, fold sharing logic into single EmitGraphInfo.shared_types authority computed by build_shared_types
  • is_type_constant: Fixed-width carrier detection — primitive-only structs and unit-only enums skip Rc wrapping
  • L1 ratchet: Bump 30→31 for pre-existing TLC-1 violation, tracked as M4 Tier 2.6 (functional system modeling)
  • ROADMAP: Add Tier 2.6 functional system modeling item — ad-hoc callable/arity inspection dissolves once function application is modeled as a concept

Test plan

  • cargo test -p v2-compiler-tests — 283 passed
  • cargo test -p v2-compiler-tests full_dsl_compiles -- --ignored — passed
  • cargo test -p v2-compiler-tests strict_compile_diagnostic_count -- --ignored — 0 diagnostics
  • scripts/l1-ratchet.sh --check — 31 <= 31
  • Stage0 regeneration — 0 diagnostics

🤖 Generated with Claude Code

briansrls and others added 3 commits April 5, 2026 10:58
…e dispatch

CloneTemplates type removed — SharingStrategy is sole clone authority with
field_clone and clone_suffix fields. TLC-1: is_zero_arg_callable_ref checks
FunctionValueBinding + Callable(params=[]) to emit name() instead of bare name
in emit_var_ref (registry hit/miss) and emit_typed_expr_base.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Eliminates the ValueContext middleman type and 6 helper functions from
04_infer.dag. Sharing decisions now flow through a single authority:
EmitGraphInfo.shared_types, computed by build_shared_types in the Rust
emitter where the Rust-specific collection types live.

- Delete ValueContext type from 04_emit_info.dag
- Add has_fn_fields: Bool to TypeSummary (computed at build time)
- Replace value_contexts with shared_types in EmitGraphInfo
- Add is_type_constant for fixed-width carrier detection (no Rc needed)
- Bump L1 ratchet 30→31 (pre-existing TLC-1 violation, tracked in M4)
- Track functional system modeling gap as M4 Tier 2.6 in ROADMAP

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Catalog 10 categories of unmodeled compiler concepts where structural
properties are interrogated ad-hoc instead of through modeled facts.
Principle: improper modeling spawns downstream questions; model the
concept and the questions dissolve.

Maps each category to the milestone that dissolves it (M4, M7, or
independent). Continuous track — won't finish until the compiler
models its own domain in .dag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4cb6b73a6a

ℹ️ 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".

Comment thread src/v2/05_emit_rust.dag Outdated
Comment on lines +1665 to +1667
match map_get(emit_info.variant_to_enum, name) {
Some { value: parent } => Some { value: parent }
None =>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve resolved enum when qualifying variant references

effective_variant_parent now returns the cached variant_to_enum mapping unconditionally, so it no longer validates the parent against resolved_type/binding_kind. This regresses ambiguous variant handling (e.g. names reused across enums like Rust or NullCoalesce): derive_variant_to_enum is first-write-wins over hash-map iteration, so the chosen parent can be arbitrary, and this path will emit the wrong qualified enum variant (WrongEnum::Variant) even when type inference already knows the correct enum. That can produce nondeterministic or invalid Rust output depending on map iteration order.

Useful? React with 👍 / 👎.

@briansrls briansrls left a comment

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.

Review (INVARIANTS: 3, MODELING: 2+/2-, ROADMAP: 2✓/3!)

INVARIANTS — Violations (3)

MODELING — Strengths

  • src/v2/languages.dag Folding clone syntax and annotated-let syntax into LanguageSpec moves more target-language facts into shared data instead of hardcoded emitter branches.
  • src/v2/04_emit_info.dag Moving has_fn_fields onto TypeSummary is a good consolidation because the fact now lives with the type summary that derives it.

MODELING — Improvements

  • src/v2/languages.dag SharingStrategy is more compositional than the deleted CloneTemplates, but callability and clone behavior are still modeled as string templates plus emitter heuristics rather than as structural functional/sharing facts.
  • src/v2/04_emit_info.dag shared_types and variant_to_enum are still name-keyed emit-boundary caches, and the model would be more faithful if those decisions were carried as structural witnesses or Node-linked facts instead of Map<String, ...> proxies.

ROADMAP — Verified

  • CG-1 Clone semantics in LanguageSpec: This diff does remove CloneTemplates and routes Rust clone emission through SharingStrategy.
  • CG-2 TLC-4 Explicit annotation requirements: This diff adds let_binding_annotated to AnnotationRequirements and wires the Rust let-binding emit paths to emit_let_binding_annotated.

ROADMAP — Incomplete

  • CG-1 emit_primitive_type fail-closed: src/v2/05_emit.dag still implements emit_primitive_type as a plain target_primitive_type(...) passthrough, so the newly checked box is not supported by this PR.
  • CG-1 rc_types derived from ValueContext: The roadmap now checks a ValueContext-based sharing item even though this PR deletes ValueContext and still computes sharing in emit_rust, so the claimed authority is not the one the code uses.
  • CG-2 TLC-3 Indexing / character access semantics: src/v2/05_emit_go.dag and src/v2/05_emit_python.dag still route index/slice emission directly through spec.indexing.list_index and list_slice, so per-collection dispatch is not done in all three backends.

The PR improves data-driven codegen structure, but it introduces a new callable name/arity heuristic, promotes a lossy variant-name cache into emit authority, and overclaims roadmap progress on TLC-3 and fail-closed primitive type emission.

Comment thread src/v2/04_emit_info.dag
}
let gpn = item.params |> map(p => param_node_name(n: p))
let is_product = item.connective == Conj
let has_fn = item.children |> any(child =>

This comment was marked as resolved.

Comment thread src/v2/05_emit_rust.dag
@@ -1636,14 +1661,34 @@ fn emit_var_clone_or_move(ident: String, name: String, resolved_type: InferredNo
}

fn effective_variant_parent(name: String, binding_kind: VarBindingKind?, resolved_type: InferredNode?, emit_info: EmitGraphInfo) -> String? {

This comment was marked as resolved.

Comment thread src/v2/05_emit_rust.dag
}
}
}

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.

Invariant violation: is_zero_arg_callable_ref decides call-vs-value by checking rt.name == "Callable" and zero params, which is exactly the downstream name/arity heuristic the boundary is supposed to eliminate, violating Boundary sufficiency and Heuristics indicate lost structure.

@briansrls

briansrls commented Apr 5, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

I reviewed the attached diff rather than the GitHub page directly; the PR URL itself wasn’t accessible from here. Overall: good direction, but I would not merge this as-is.

The strongest issues I see are:

  1. has_fn_fields misses zero-arg callables

In src/v2/04_emit_info.dag, has_fn_fields is now derived with rt.params |> count > 0 in both build_type_summary and add_emit_item_summary. Then src/v2/05_emit_rust.dag uses TypeSummary.has_fn_fields in emit_struct_from_children to decide whether to suppress Debug/PartialEq derives.

That means a field of type Callable with zero params is treated as “not function-typed”, even though dyn Fn() still does not implement those derives. So a struct or fielded variant containing a zero-arg callable can still get derives that Rust will reject.

This looks like a real correctness bug, not just debt. The fix should be a structural callable predicate, not an arity check.

  1. Re-enabling annotated let bindings looks premature relative to TLC-2

This PR turns annotated Rust lets back on in all three sites:

  • emit_func_body
  • emit_typed_let
  • emit_tco_init_stmt

Each now computes a rendered type through build_type_rendering(...) and emits let x: T = ....

But the same PR’s ROADMAP.md still says TLC-2 is unresolved: runtime helper return/wrap conventions are not yet derived from the same authority as emission, with the explicit v2_rt::map_keys / Vec<K> vs Rc<Vec<K>> example. If that is still true, this change can turn previously-inferred code into explicit emitted-Rust type mismatches at the let site.

So even if the model is directionally right, I’d treat this as a likely regression risk until there is a fixture proving annotated lets across runtime-helper paths and empty-collection sites.

  1. variant_to_enum looks lossy unless variant names are globally unique

EmitGraphInfo now carries:

  • variant_to_enum: Map<String, String>

and effective_variant_parent(...) now prefers that precomputed map before falling back to resolved type / binding kind.

That is only safe if variant names are globally unique across all enums in the compilation unit. If they are not, a Map<String, String> collapses distinct variants into one parent and the emitter can qualify with the wrong enum.

So either:

  • the language guarantees global uniqueness, in which case I’d want that asserted/documented at map construction time, or
  • this is a correctness bug and the key needs to be more specific than bare variant name.

From an invariants perspective, this is exactly the kind of “lossy fact table” the attached INVARIANTS.md warns about.

  1. The languages.dag changes are good; the callable dispatch change is not modeled enough

The best part of this PR is the languages.dag cleanup:

  • deleting CloneTemplates
  • moving clone semantics into SharingStrategy
  • adding let_binding_annotated to AnnotationRequirements

That is a net improvement in compositional modeling and reduces parallel representation. This is aligned with the repo’s “single authority / language facts in LanguageSpec” direction.

The weak spot is TLC-1 itself. is_zero_arg_callable_ref(...) in 05_emit_rust.dag decides call-vs-value by checking:

  • binding_kind == FunctionValueBinding
  • rt.name == "Callable"
  • rt.params |> count == 0

That is still a type-name / arity heuristic in the emitter. The PR is honest about that — it raises L1_RATCHET from 30 to 31 and adds ROADMAP Tier 2.6 specifically to delete this later — but architecturally I would describe TLC-1 as a tactical patch, not a completed structural solution.

  1. Roadmap text is internally inconsistent after this diff

A couple roadmap items need cleanup:

  • In the status paragraph near the top, ROADMAP.md still says annotated-let has “consumer pending”, but later TLC-4 is marked complete and this diff does add the consumer. Those now contradict each other.
  • It marks “rc_types derived from ValueContext” as done, but this PR deletes ValueContext. That checkbox text is now stale and should be rewritten in terms of TypeSummary / shared_types / boundary sharing authority.
  • The new Tier 2.6 section is the right correction. It matches what the code actually does.
  1. shared_types is better than ValueContext, but the comment overstates where the authority lives

EmitGraphInfo now comments that shared_types is the “SINGLE authority” for sharing decisions. But build_emit_graph_info(...) in 04_infer.dag still populates it as empty_map(), and 05_emit_rust.dag synthesizes it later with build_shared_types(...).

So this is improved centralization, but it is not yet a fully end-to-end infer→emit boundary fact. I would not call that a merge blocker by itself, because it does match the current short-term Rust-emit direction, but the comment is stronger than the implementation.

What I’d ask for before merge:

  • Fix has_fn_fields so zero-arg callables count as function-typed.
  • Either prove/assert global uniqueness for variant names or change variant_to_enum keying.
  • Add emitted-Rust regression tests for:
    • zero-arg callable fields
    • zero-arg callable refs vs values
    • annotated-let with runtime helpers / empty collection initializers

Net: I’d block on items 1 and 2, and I’d want item 3 resolved or explicitly proven impossible.


View conversation

Mechanical rename of ~423 occurrences in 05_emit.dag, 05_emit_rust.dag,
their stage0 counterparts, and hand-written tests. Aligns parameter names
with the consolidated EmitGraphInfo.shared_types authority from 4cb6b73.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@briansrls briansrls left a comment

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.

Review (INVARIANTS: 3, MODELING: 1+/2-, ROADMAP: 2✓/4!)

INVARIANTS — Violations (3)

MODELING — Strengths

  • src/v2/languages.dag Moving clone and wrap templates into LanguageSpec.sharing makes more of Rust emission data-driven instead of hardcoded emitter logic.

MODELING — Improvements

  • src/v2/languages.dag AnnotationRequirements should compose from std.languages statement syntax instead of extending a parallel v2-only schema.
  • src/v2/04_emit_info.dag The emit boundary is moving in the right direction, but shared_types and variant_to_enum should become structural witnesses rather than string-keyed tables to stay faithful to M4.

ROADMAP — Verified

  • CG-1 clone semantics unified in SharingStrategy: CloneTemplates is removed, Rust clone data moves into SharingStrategy, and Rust emission now reads clone operations through sharing_for_target(...).
  • CG-2 TLC-4 explicit annotation requirements: The diff adds let_binding_annotated, introduces emit_let_binding_annotated, and wires the Rust let-binding emit paths to it.

ROADMAP — Incomplete

  • CG-2 TLC-1 call syntax / reference distinction: The diff adds a Rust-side is_zero_arg_callable_ref heuristic, but the callable-vs-value fact still does not survive structurally from resolution through emit as the roadmap item says.
  • CG-2 TLC-3 indexing / character access semantics: ROADMAP marks Go/Python per-collection dispatch done, but this diff contains no Go or Python emitter changes to substantiate that completion.
  • CG-1 emit_primitive_type fail-closed: ROADMAP checks this item off, but this diff does not change emit_primitive_type, so the claimed completion is not evidenced here.
  • Ratchet Counts / L1 type knowledge: scripts/l1-ratchet.sh raises the enforced L1 ratchet to 31, but ROADMAP still reports the current L1 count as 30.

The PR improves the data-driven codegen shape, but it still adds proxy-string boundary state and over-claims roadmap progress on TLC-1, TLC-3, and the L1 ratchet.

Comment thread src/v2/languages.dag
// Ref: std.languages StatementSyntax (let_binding / let_binding_inferred).
type AnnotationRequirements {
let_binding_inferred: String // e.g. "let {0} = {1};" / "{0} := {1}" / "{0} = {1}"
let_binding_annotated: String // e.g. "let {0}: {1} = {2};" / "var {0} {1} = {2}" / "{0}: {1} = {2}"

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.

Invariant violation: let_binding_annotated adds a second authority for let-binding syntax alongside std.languages StatementSyntax.let_binding, violating No duplicate representations and No parallel implementations.

Comment thread src/v2/04_emit_info.dag
@@ -97,6 +88,9 @@ type EmitGraphInfo {
// dispatching to emit_fn_def/emit_func_def. Bindings in this set have
// fan-out=1 and can be moved instead of cloned.

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.

Invariant violation: variant_to_enum: Map<String, String> stores semantic identity in name-keyed proxies instead of structural references, violating No duplicate representations and the M4 structural-identity direction.

Comment thread src/v2/04_infer.dag
let fielded = build_fielded_variants(modules: modules, type_summaries: built.type_summaries)
let value_ctxs = build_value_contexts(modules: modules, recursive_type_set: all_recursive)
let vtoe = derive_variant_to_enum(type_summaries: built.type_summaries)
EmitGraphInfo {

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.

Invariant violation: shared_types: empty_map() introduces a new emit-boundary fact table without an upstream derivation, so the emitter still recomputes sharing itself, violating Explicit boundary contracts and Emission is translation, not decision-making.

@briansrls

briansrls commented Apr 5, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

Verdict: good direction, but I would not merge this as-is.

The patch makes a real improvement in a couple of places: removing Rust-specific CloneTemplates in favor of SharingStrategy, and adding emit_let_binding_annotated() in shared emit, both move syntax decisions toward LanguageSpec instead of hardcoding them in Rust-only code. That is directionally aligned with the invariant that emission should be translation, not decision-making. Pasted markdown

The two review comments I’d treat as blocking are these.

First, has_fn_fields now under-classifies zero-arg callables. In 04_emit_info.dag, both build_type_summary() and the variant summary path define “function-typed field” as rt.params |> count > 0. But this PR’s whole TLC-1 change is about zero-arg Callable being semantically callable. Those fields are still function fields, and Rust derive selection should still treat them as such. As written, a struct or fielded variant containing a zero-arg callable can now be misclassified as derive-safe, which is exactly the case the surrounding comment says should be avoided. I’d change this to an explicit callable predicate, not an arity check.

Second, variant_to_enum is only safe if derive_variant_to_enum() is ambiguity-aware. effective_variant_parent() now makes the flat variant_to_enum: Map<String, String> the primary authority. That is a nice fast path, but only if ambiguous variant names are either impossible or intentionally omitted from the map. The emitter comments already acknowledge real variant-name collisions. With a flat map, any duplicated variant name, or even a local binding that happens to share a variant spelling, can be misrendered as Enum::Variant. I’d want either collision-proof keys, or a rule that ambiguous names are excluded and resolved only via local type context.

On invariants, the main issue is that “shared_types consolidation” is only partial. The boundary now has EmitGraphInfo.shared_types, and its comment says this is the “single authority,” but build_emit_graph_info() still fills that field with empty_map(), and the real map is still derived later inside 05_emit_rust.dag by build_shared_types(). So the fact is not actually flowing across the infer→emit boundary yet; Rust emit is still computing it locally. That conflicts with the invariant language around explicit boundary contracts, exact fact tables, and facts flowing forward instead of being re-derived downstream. INVARIANTS

Pasted markdown

211

Related to that, build_shared_types() is still doing emitter-side semantic classification by string/type-name heuristics: is_dag_value_type_name() hardcodes Int|Bool|Float, and the shared set is extended from Rust container template names. That may be acceptable as tactical debt, but it is still exactly the class of “emitter branches on type names / hardcoded lists because a fact was lost upstream” that INVARIANTS calls out. Pasted markdown

INVARIANTS

TLC-1 also lands tactically, not compositionally. is_zero_arg_callable_ref() decides call-vs-value from binding_kind == FunctionValueBinding, rt.name == "Callable", and zero arity. That is a pragmatic bootstrap fix, but it is still “arity as semantics,” and the updated roadmap itself now adds Tier 2.6 precisely to dissolve this heuristic later. So I would not describe TLC-1 as conceptually done; I’d describe it as “landed with tracked L1 debt.” The ratchet bump to 31 is honest and matches that story.

For compositional modeling quality of the .dag changes, I’d split it this way:

The good part is emit_let_binding_annotated() and the move from local child inspection to precomputed TypeSummary.has_fn_fields. Those are the right shape: derive a fact once, then consume it. The less-good part is deleting ValueContext entirely and replacing it with shared_types + has_fn_fields + local is_type_constant(). That narrows the model from a target-agnostic value-context abstraction to something much more Rust-centric. The earlier roadmap direction for E0b explicitly framed this as ValueContext × LanguageSpec, including non-Rust contexts like SpecificationWitness and CallableValue; this PR is a design pivot, not just a cleanup. If that pivot is intentional, I’d want the roadmap text to say so explicitly rather than presenting it as straightforward completion of the old E0b plan. ROADMAP

On roadmap alignment, I see two places where the doc seems ahead of the code.

The biggest one is TLC-3. ROADMAP now says “TLC-3 Go/Python per-collection dispatch done” and “all three backends dispatch on collection type,” but this diff only touches ROADMAP.md, Rust extdeps, 04_emit_info.dag, 04_infer.dag, 05_emit.dag, and 05_emit_rust.dag. I do not see any Go or Python emitter changes, and I do not see shared indexing-dispatch logic changing in this patch either. The most recent roadmap material I found still described Go/Python indexing as pending. Pasted markdown

TLC-4 also looks slightly overstated. The prior roadmap text said emit_let_binding_annotated existed but had no consumer; this PR adds exactly one new direct consumer in emit_func_body(). That may be enough if emit_func_body() is now the single funnel for all Rust let emission, but the diff alone does not prove the broader “done” claim. I would soften the roadmap wording unless there are tests showing all Rust let-binding paths now route through that helper. Pasted markdown

A few smaller concerns:

shared_types is now both a field on EmitGraphInfo and a separately threaded parameter through a long Rust emit call chain. If EmitGraphInfo.shared_types is truly the authority, I’d stop threading the extra parameter and read from emit_info directly. Otherwise the code is carrying the same fact twice.

The comment in emit_typed_expr_base() says “check binding_kind first, then fall back to resolved function signatures for imported …”, but the new helper still requires FunctionValueBinding. The comment suggests a broader fallback than the code currently implements.

I also don’t see tests in this diff for the new behavior. Given the T11 guidance, I’d want at least one Tier 1 structure/codegen test and one Tier 2 emitted-Rust compile test for: zero-arg callable refs, a struct/variant with a zero-arg callable field, ambiguous variant names, and the claimed Go/Python indexing completion. Pasted markdown

Net: I like the direction, especially the CloneTemplates removal and the annotated-let helper, but I’d stop this PR on:

  1. zero-arg callable fields not counting as has_fn_fields, and
  2. variant_to_enum needing explicit ambiguity handling.

Everything else I’d treat as important follow-up debt rather than immediate merge blockers.


View conversation

Replace 4 hardcoded emitters (filter/any/all/flat_map) with a single
shared emit_rust_higher_order_method function dispatched through
HigherOrderMethodSpec data in Rust extdeps. Each spec carries
inline_template, fn_ref_template, and wraps_in_sharing — the emitter
reads structural facts instead of encoding per-method logic.

Net: -93 lines, 4 functions → 1, method addition = data change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@briansrls

briansrls commented Apr 5, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

I reviewed the visible uploaded patch, but one caveat up front: the diff file is truncated at the tail of src/v2/05_emit_rust.dag (it ends mid-emit_rust_expr_slice with a literal ... (truncated)), so anything after that point is unverifiable from the artifact you attached.

My take: good direction overall, but I would not stamp this as-is.

Likely blockers

  • ROADMAP claims are internally inconsistent and at least partly unsupported by the visible diff.
    • The status paragraph says “TLC-4 data model landed, annotated-let consumer pending” (diff lines 22–27), but the checklist later marks TLC-4 done and says “All three Rust emitter let-binding sites consume emit_let_binding_annotated” (lines 91–97).
    • In the visible code, I can confirm exactly one new consumer in emit_func_body (lines 1174–1176).
    • The roadmap table still says the L1 ratchet is 30 (line 13), but scripts/l1-ratchet.sh is changed to 31 (lines 241–243).
    • TLC-3 is marked complete in ROADMAP (lines 84–90), but this diff does not touch 05_emit_go.dag or 05_emit_python.dag in the visible file list.
  • That makes the roadmap update read as overstated. I’d mark TLC-1/TLC-3/TLC-4 as partial unless the missing truncated tail really contains the rest.
  • effective_variant_parent now trusts variant_to_enum too eagerly.
    • New primary path is map_get(emit_info.variant_to_enum, name) (lines 1524–1545).
    • That is only safe if derive_variant_to_enum is guaranteed to return an exact, ambiguity-free mapping.
    • Earlier emit-info logic in this codebase used an ambiguity sentinel for duplicate variant names. If derive_variant_to_enum still follows that convention, this function will happily treat the sentinel as a parent and emit nonsense qualification.
  • At minimum, this needs a regression test with duplicate variant names across two enums/modules. Better: make ambiguity unrepresentable here, or drop ambiguous entries from the map entirely.

Invariant / modeling read

  • TLC-1 is still a heuristic, not a modeled fact.
    • is_zero_arg_callable_ref checks FunctionValueBinding plus rt.name == "Callable" plus params |> count == 0 (lines 1549–1559).
    • The ratchet update explicitly admits this adds a new L1 violation (lines 241–243).
    • The roadmap itself also now says this should eventually dissolve into a modeled “function application” concept (lines 120–126).
  • Against INVARIANTS.md, this is squarely in “heuristics indicate lost structure” / “boundary sufficiency” territory. It may be a reasonable stopgap, but it is not “done” in the invariant sense.
  • shared_types is not actually an upstream/shared authority yet.
    • EmitGraphInfo now carries shared_types and calls it the “SINGLE authority” (lines 291–297).
    • But 04_infer.dag still fills it with empty_map() (lines 458–468), and 05_emit_rust.dag computes it locally with build_shared_types(...) (lines 783–817).
    • There is even a comment acknowledging it is “still derived inside the Rust emitter entry points” (lines 735–739).
  • So this is cleaner than ValueContext, but it still violates the intended “emit consumes facts, it does not re-derive them” boundary.
  • is_dag_value_type_name is a fragile emitter-side classifier.
    • It only recognizes Int, Bool, and Float (lines 755–759).
    • That is another name-based semantic rule in the Rust emitter. Even if it is conservative, it is not compositional modeling.
  • I would expect this to drift as soon as another fixed-width/value-like kernel type matters.

Compositional modeling quality of the .dag changes

Mixed.

What I like:

  • HigherOrderMethodSpec in dsl/extdeps/languages/rust/emit.dag is the right shape of change: moving target-language method facts into data instead of keeping four bespoke emit paths.
  • Moving clone behavior behind sharing_for_target(...) is also the right direction.

What still feels unmodeled:

  • is_zero_arg_callable_ref is arity/type-name interrogation instead of a first-class call/reference fact.
  • shared_types is a Rust-local derivation, not a resolve/infer fact.
  • The new roadmap section on “functional system modeling” is actually the most honest description of the current state.

General code review notes

  • has_fn_fields looks brittle.
    • In build_type_summary, it checks direct child inferred.params |> count > 0 (lines 328–333).
    • For structs, that may work. For enums, direct children are variants, not variant field types, so this may under-detect function-typed fields inside fielded variants.
    • I do not see an immediate visible bug from this in the shown diff, but it is a shaky summary fact.
  • The annotated-let work is good, but I cannot verify end-to-end coverage.
    • The helper is added in shared emit (line 636).
    • I can see one Rust consumer in emit_func_body (lines 1174–1176).
    • I cannot verify the claimed “all three let-binding sites” from the truncated artifact.

What I’d ask for before merge

  • Fix ROADMAP to match what actually landed in this PR.
  • Add a regression test for duplicate variant names across enums/modules.
  • Add zero-arg callable tests for more than the happy path:
    • local function value
    • imported function
    • any callable-shaped value that is not obviously FunctionValueBinding
  • Add coverage proving all intended annotated-let sites now consume emit_let_binding_annotated.
  • Either soften the shared_types “single authority” wording, or move the derivation out of Rust emit.

The strongest positive is that this PR does move some Rust-specific behavior into better data shapes. The strongest negative is that TLC-1 and shared-type classification are still emitter heuristics, while the ROADMAP text currently describes them as more complete than the visible patch justifies.


View conversation

@briansrls briansrls left a comment

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.

Review (INVARIANTS: 1, MODELING: 3+/2-, ROADMAP: 3✓/3!)

INVARIANTS — Violations (1)

MODELING — Strengths

  • src/v2/04_emit_info.dag Adding has_fn_fields to TypeSummary moves a derive-relevant fact toward the reconcile→emit boundary instead of recomputing it ad hoc in Rust emit.
  • src/v2/languages.dag Expanding SharingStrategy to include clone, deref, field, and iterator ownership syntax makes sharing behavior compose from one backend spec instead of a parallel helper type.
  • dsl/extdeps/languages/rust/emit.dag HigherOrderMethodSpec sits at the right Rust-specific layer and faithfully captures the two real Rust iterator cases here: inline lambdas and function references.

MODELING — Improvements

  • src/v2/languages.dag Higher-order method rendering is still split between shared LanguageSpec and a Rust-only side table, so method rendering is not yet a single cross-backend authority.
  • dsl/extdeps/languages/rust/emit.dag Using method_name: String keeps intrinsic identity as a Rust-local string key; a more compositional model would reuse the shared intrinsic/algebra authority rather than another name table.

ROADMAP — Verified

  • CG-1 clone semantics unified in SharingStrategy: The diff removes CloneTemplates, adds clone operations to SharingStrategy, and rewires Rust clone helpers through sharing_for_target.
  • CG-3 higher-order method templates: The diff adds HigherOrderMethodSpec in Rust extdeps and routes filter/any/all/flat_map through emit_rust_higher_order_method.
  • CG-2 TLC-4 explicit annotation requirements: The diff adds let_binding_annotated templates and switches the three Rust let-emission sites to emit_let_binding_annotated.

ROADMAP — Incomplete

  • CG-1 emit_primitive_type fail-closed: ROADMAP checks this off, but src/v2/05_emit.dag still returns target_primitive_type(...) directly instead of failing closed on an unmapped primitive.
  • CG-2 TLC-3 indexing / character access semantics: ROADMAP says Go/Python per-collection dispatch is done, but this diff contains no Go/Python indexing-dispatch change to support that completion.
  • CG status paragraph for TLC-4: The updated status text still says the annotated-let consumer is pending, but this same diff wires the Rust annotated-let consumer end to end.

The PR improves data-driven codegen structure, but it still reintroduces a new downstream name-based type authority in Rust emit and overclaims roadmap completion on fail-closed primitive rendering and Go/Python indexing dispatch.

Comment thread src/v2/05_emit_rust.dag

// A type is constant (fixed-width carrier) when it needs no shared-pointer
// wrapping: not recursive, no generics, no fn-typed fields, and every field
// resolves to a DAG value type. Unit-only enums are trivially constant.

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.

Invariant violation: The new is_dag_value_type_name hardcodes Int/Bool/Float inside Rust emit to decide sharing, reintroducing a duplicate kernel authority downstream instead of importing it from the model, violating No duplicate representations and Root-Cause Depth.

…ackends

Replace 35+ hardcoded field strings in Rust/Go/Python emitters with a single
ServiceFieldTemplates type in LanguageSpec. Two shared functions
(service_field_decls, service_field_ctors) apply templates against ServiceFieldSet,
returning List<String> so each backend joins with its own separator.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@briansrls briansrls left a comment

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.

Review (INVARIANTS: 1, MODELING: 3+/1-, ROADMAP: 3✓/5!)

INVARIANTS — Violations (1)

MODELING — Strengths

  • src/v2/04_emit_info.dag Moving has_fn_fields onto TypeSummary is more compositional than keeping a separate ValueContext fact table.
  • src/v2/languages.dag Folding clone operations into SharingStrategy is a cleaner composition because sharing syntax now lives under one rendering concept.
  • dsl/extdeps/languages/rust/emit.dag HigherOrderMethodSpec moves Rust-only higher-order rendering into extdep data, which is the right layer for backend-specific syntax.

MODELING — Improvements

  • dsl/extdeps/languages/rust/emit.dag It would compose more faithfully if higher-order method identity came from the compiler’s intrinsic authority instead of free-form method_name strings.

ROADMAP — Verified

  • CG-1 clone semantics unified in SharingStrategy: CloneTemplates are removed from LanguageSpec and Rust now reads clone forms from SharingStrategy.
  • CG-2 TLC-4 explicit annotation requirements: Rust let-binding emission now routes the let-binding sites through emit_let_binding_annotated with rendered types.
  • CG-3 higher-order method templates: HigherOrderMethodSpec plus emit_rust_higher_order_method replaces the four hardcoded Rust higher-order emitters with data-driven templates.

ROADMAP — Incomplete

  • Bootstrap tests gate count 283 pass / 39 ignored: This diff only adjusts existing test code and does not show the additional passing and ignored tests claimed in the table.
  • CG-1 sharing authority consolidated into EmitGraphInfo.shared_types: build_emit_graph_info still seeds shared_types as empty_map and Rust emit rebuilds sharing locally, so the boundary is not yet authoritative.
  • CG-1 explicit parent-enum ownership facts through resolve/infer/emit: Emit still consults the lossy variant_to_enum name cache first, so parent-enum identity is not yet carried structurally end-to-end.
  • CG-2 TLC-1 call syntax / reference distinction: The new dispatch still relies on is_zero_arg_callable_ref’s Callable-name/zero-arity heuristic, so the claimed boundary fix is not complete.
  • CG-2 TLC-3 indexing / character access semantics: The diff does not modify the Go/Python indexing emit paths, so this PR does not substantiate the roadmap claim that their per-collection dispatch is done.

The PR improves emitter parameterization, but it adds a new duplicate language authority and overclaims roadmap progress while key name-based boundary heuristics remain in place.

Comment thread src/v2/languages.dag
}

// -- Service field templates: per-language rendering of transport config fields --
// Each service struct needs config fields driven by which transports it uses.

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.

Invariant violation: ServiceFieldTemplates creates a second compiler-local authority for service field syntax and field names instead of reusing shared language syntax data, violating No duplicate representations and No parallel implementations.

briansrls and others added 2 commits April 5, 2026 20:58
# Conflicts:
#	ROADMAP.md
#	src/v2/04_emit_info.dag
#	src/v2/04_infer.dag
#	src/v2/05_emit_rust.dag
#	src/v2/stage0/src/v2_compiler_emit_rust.rs
#	src/v2/stage0/src/v2_compiler_infer.rs
#	src/v2/stage0/src/v2_compiler_infer_emit_info.rs
…enum collision handling

Two blocking review items:
- has_fn_fields: switch from arity check (rt.params |> count > 0) to structural
  callable predicate (rt.name == "Callable") — fixes zero-arg Callable types
- derive_variant_to_enum: empty-string sentinel on name collision instead of
  first-write-wins; effective_variant_parent filters sentinel and falls through
  to structural check

Also: L1 ratchet 31→33 (two new Callable checks), ROADMAP text fixes,
owned_bindings field added to emit_module EmitGraphInfo construction.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@briansrls

briansrls commented Apr 6, 2026 •

Copy link
Copy Markdown
Contributor Author

ChatGPT Review

Reviewed the attached diff. Overall: good direction, but I would leave request changes on this as written.

Blockers

1) TLC-1 is still an emitter-side heuristic, and the PR explicitly ratchets in that debt.

The new Rust helper is_zero_arg_callable_ref() decides call-vs-value by checking:

  • binding_kind == FunctionValueBinding
  • resolved_type is a Resolved node
  • rt.name == "Callable"
  • param_count == 0

That is exactly the sort of downstream heuristic / type-name-string escape hatch that INVARIANTS.md warns against. The roadmap itself now adds a Tier 2.6 item to “dissolve is_zero_arg_callable_ref and rt.name == "Callable",” which means the same PR is both claiming TLC-1 “landed” and acknowledging the implementation is still an L1 violation.

Relatedly, scripts/l1-ratchet.sh is bumped 30 → 31 with the comment that the increase is specifically for this new rt.name=="Callable" check. I would not describe that as “landed”; it is a tactical workaround plus an admitted ratchet increase.

My ask:

  • either reframe TLC-1 in ROADMAP.md as partial/guardrail, not done,
  • or move the callable/call distinction into an upstream modeled fact instead of an emit-time guess.

2) There is a real correctness hole in the zero-arg callable fallback path.

In src/v2/05_emit_rust.dag, the comment in emit_typed_expr_base() says:

check binding_kind first, then fall back to resolved function signatures for imported functions not marked FunctionValueBinding

But is_zero_arg_callable_ref() has no such fallback. It only returns true when binding_kind is FunctionValueBinding.

So imported / extern / otherwise non-FunctionValueBinding zero-arg function refs still look likely to emit as bare name instead of name() on that path.

I would want at least one regression test for each of these before merging:

  • local zero-arg function ref
  • imported zero-arg function ref
  • registry-hit and registry-miss paths
  • negative case: non-zero-arg callable ref stays bare unless actually called

Invariant / architecture concerns

3) shared_types is not actually boundary-owned yet.

This refactor improves naming, but it does not fully move sharing authority upstream.

What the code does now:

  • EmitGraphInfo grows shared_types
  • build_emit_graph_info() still fills it with empty_map()
  • Rust later computes build_shared_types(...) inside emit_rust() / emit_module() and injects a fresh EmitGraphInfo

So the field exists, but the authority is still Rust-emitter-local. That makes roadmap text like:

  • “sharing authority consolidated into EmitGraphInfo.shared_types”
  • “build_rc_types eliminated”

read too strongly.

What actually happened is closer to:

  • rc_types renamed/refactored to shared_types
  • computation reorganized as build_shared_types
  • but the fact still originates in Rust emission, not at the infer→emit boundary

Against the invariants, this still looks partial rather than resolved.

4) variant_to_enum is a good move, but I’d want a duplicate-name test.

Precomputing variant_to_enum is much better than the old emit-side lookup heuristics. Good change.

But it is a flat Map<String, String>. If the language permits two enums with the same variant name, effective_variant_parent() now trusts that map first, and I did not see collision handling or diagnostics in this diff.

If duplicate variant names are impossible by language rule, document that. If they are possible, add a failing test or collision diagnostic.

Compositional modeling quality (.dag)

This part is mostly strong.

Good changes

  • SharingStrategy absorbing clone/deref/field/iterator clone behavior is a real modeling improvement over the old CloneTemplates split.
  • ServiceFieldTemplates + shared service_field_decls() / service_field_ctors() is exactly the right shape: structural fact in one place, target syntax in LanguageSpec.
  • HigherOrderMethodSpec is a solid reduction of Rust hardcoding for filter/any/all/flat_map.
  • has_fn_fields moving into TypeSummary is better than carrying a heavier ValueContext wrapper when only that fact is needed.
  • variant_to_enum is directionally correct: explicit precomputed fact beats emitter lookup.

Still partial

  • HigherOrderMethodSpec is still Rust-only and string-template-based. That is fine as a step, but it does not close the higher-order modeling gap; roadmap is right to keep CallableOf open.
  • sharing remains Rust-centric because build_shared_types() lives in 05_emit_rust.dag and folds in Rust container-template keys

So my compositional read is: good local modeling improvements, but not yet the full invariant-aligned end state.

Roadmap alignment

There are several doc mismatches in this PR.

1) TLC-4 contradicts itself inside the same roadmap edit.

  • The status paragraph says annotated-let consumer is still pending.
  • Later the TLC-4 checklist says all three Rust let-binding sites now consume emit_let_binding_annotated.
  • The code matches the checklist, not the status paragraph.

2) TLC-1 is overstated.

  • The roadmap says TLC-1 landed.
  • The same roadmap also adds a new open item to dissolve the exact helper and heuristic it just introduced.

That should be marked partial, not done.

3) L1 ratchet drift.

  • ROADMAP.md still shows L1 ratchet = 30 near the top.
  • scripts/l1-ratchet.sh is bumped to 31 in this PR.

Those need to match.

4) TLC-3 “Go/Python per-collection dispatch done” is not evidenced by this diff.

The Go/Python changes I see here are service-field templating and annotation templates. I do not see corresponding Go/Python indexing/slicing dispatch changes in this patch. Maybe that landed elsewhere, but as a PR-scoped changelog this line reads ahead of the code.

Smaller notes

  • The service-field refactor is a keeper.
  • The rc_types → shared_types rename is worthwhile, but I would tone down the roadmap wording from “authority moved” to “mechanical cleanup + partial consolidation.”
  • The higher-order refactor is a nice shrink in emitter surface area.

My merge recommendation: not yet. The main issue is that TLC-1 is currently implemented as exactly the kind of emit-time heuristic the invariants are trying to eliminate, and there’s also a likely imported-zero-arg-function correctness gap.


View conversation

# Conflicts:
#	src/v2/04_emit_info.dag
#	src/v2/04_infer.dag
#	src/v2/05_emit_rust.dag
#	src/v2/stage0/src/v2_compiler_emit.rs
#	src/v2/stage0/src/v2_compiler_emit_rust.rs

@briansrls briansrls left a comment

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.

Review (INVARIANTS: 1, MODELING: 3+/3-, ROADMAP: 3✓/2!)

INVARIANTS — Violations (1)

MODELING — Strengths

  • src/v2/04_emit_info.dag Moving has_fn_fields into TypeSummary turns a Rust derive decision into boundary data with an immediate consumer instead of another emitter-local scan.
  • src/v2/languages.dag Extending SharingStrategy and AnnotationRequirements keeps clone and annotation syntax in LanguageSpec, which is the right target-specific layer.
  • dsl/extdeps/languages/rust/emit.dag rust_higher_order_methods makes the repeated Rust higher-order method shapes data-driven instead of keeping four separate emitter functions.

MODELING — Improvements

  • src/v2/04_emit_info.dag has_fn_fields still derives callable-ness from rt.name == "Callable" rather than a structural callable witness, so the boundary remains name-dependent.
  • src/v2/languages.dag The model still stops at syntax templates because the actual shared_types decision is recomputed in Rust emit instead of being carried as an upstream language-model fact.
  • dsl/extdeps/languages/rust/emit.dag HigherOrderMethodSpec should be keyed by the existing intrinsic authority rather than method_name: String, so the model composes from the closed method set instead of another string layer.

ROADMAP — Verified

  • CG-1 clone semantics unified in SharingStrategy: The diff removes CloneTemplates, extends SharingStrategy, and routes Rust clone emission through sharing_for_target as claimed.
  • CG-2 TLC-4 explicit annotation requirements: The diff adds let_binding_annotated to AnnotationRequirements and wires emit_let_binding_annotated into the three Rust let-binding emit sites the roadmap names.
  • CG-3 higher-order method templates: The diff adds HigherOrderMethodSpec plus emit_rust_higher_order_method, replacing the dedicated Rust emitters for filter, any, all, and flat_map.

ROADMAP — Incomplete

  • CG-1 sharing authority consolidated into EmitGraphInfo.shared_types: build_emit_graph_info still emits shared_types: empty_map() and Rust recomputes sharing in build_shared_types, so the boundary is not yet the authority the roadmap claims.
  • CG-1 explicit parent-enum ownership facts through resolve/infer/emit: The new parent-enum path is still a name-keyed variant_to_enum: Map<String, String> cache rather than an explicit structural fact carried end-to-end through the boundary.

The PR improves emitter parameterization, but it still introduces a new string-keyed method authority and overstates how much boundary authority has actually moved upstream.

// {arg} — rendered first argument (fn_ref case)

type HigherOrderMethodSpec {
method_name: String

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.

Invariant violation: HigherOrderMethodSpec.method_name: String adds a new string-keyed authority for a closed intrinsic method set instead of reusing the existing method authority, violating No duplicate representations and No case enumeration for open sets.

Three fixes after merging origin/main:
1. language_spec is a function (takes target:), not a bare value — fix
   service_fields access in Rust/Go/Python emitters
2. Add fold_eligible_index/fold_eligible fields to EmitGraphInfo in emit_module
3. Revert emit_let_binding_annotated → emit_let_binding at 3 sites (bootstrap
   convergence: .dag type inference emits () for empty collections)

Regenerate stage0 — 0 diagnostics, round-2 self-freshness verified.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@briansrls briansrls left a comment

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.

Review (INVARIANTS: 1, MODELING: 2+/2-, ROADMAP: 2✓/4!)

INVARIANTS — Violations (1)

MODELING — Strengths

  • src/v2/languages.dag Folding clone operations into SharingStrategy improves harmony by making sharing and cloning one per-language authority instead of split template tables.
  • src/v2/04_emit_info.dag Moving has_fn_fields onto TypeSummary is the right layer because derive eligibility is a per-type summary fact rather than a repeated emitter scan.

MODELING — Improvements

  • src/v2/languages.dag ServiceFieldTemplates duplicates service-field syntax as compiler-local strings instead of composing from the shared language syntax model, so it sits too low in LanguageSpec.
  • dsl/extdeps/languages/rust/emit.dag HigherOrderMethodSpec usefully data-drives repeated Rust leaf syntax, but method_name: String keeps a closed intrinsic set string-keyed instead of grounded in the existing intrinsic authority.

ROADMAP — Verified

  • CG-1 clone semantics in LanguageSpec: The diff removes CloneTemplates, extends SharingStrategy, and rewires Rust clone sites to read sharing_for_target.
  • CG-3 higher-order method templates: The diff adds HigherOrderMethodSpec data plus a shared Rust higher-order renderer that replaces four dedicated emitters.

ROADMAP — Incomplete

  • CG-2 TLC-1 call syntax / reference distinction: The new is_zero_arg_callable_ref still reconstructs callable identity from FunctionValueBinding plus rt.name == "Callable" and arity, so the fact still does not survive structurally from resolution through emit.
  • CG-2 TLC-3 indexing / character access semantics: This diff shows no Go/Python index or slice emission changes, so the claim that Go/Python now dispatch per collection type is not supported here.
  • CG-2 TLC-4 explicit annotation requirements: The diff adds emit_let_binding_annotated, but Rust let emission still calls emit_let_binding, so annotated lets are not wired end-to-end.
  • CG-1 emit_primitive_type fail-closed: ROADMAP marks this item complete, but the diff contains no corresponding emit_primitive_type change to verify the claim.

The PR improves data-driven emission and fixes two previously flagged issues, but it still depends on a new name-based callable proxy and it overclaims several CG-lane completions in ROADMAP.

Comment thread src/v2/04_emit_info.dag
let has_fn = item.children |> any(child =>
match child.inferred {
Some { value: Resolved { node: rt } } => rt.name == "Callable"
_ => false

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.

Invariant violation: has_fn_fields still infers callable structure via rt.name == "Callable" instead of a structural callable witness, violating Boundary sufficiency / Heuristics indicate lost structure.

@briansrls

Copy link
Copy Markdown
Contributor Author

ChatGPT review in progress... (view conversation)

- TLC-1: mark partial — is_zero_arg_callable_ref is an L1 violation
  (emitter-side guardrail), not an upstream modeled fact
- TLC-3: mark partial — Go/Python still use list_index unconditionally
- TLC-4: mark partial — emit_let_binding_annotated disabled for bootstrap
  convergence (type inference produces () for empty collections)
- shared_types: "renamed and reorganized", not "authority consolidated"
- Fix stale rc_types reference in symptom description

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@briansrls
briansrls merged commit da2757d into main Apr 6, 2026
1 check passed
briansrls added a commit that referenced this pull request Apr 6, 2026
…eFieldTemplates)

Resolve conflicts by taking main's .dag and stage0 versions — main's
architecture diverged (TypedItemTypeDef, shared_types rename, data-driven
service fields). cm-track's unit_only wiring for Go/Python is deferred
to holistic CM work tracked in src/v2/CM.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@briansrls
briansrls deleted the cg-tlc1-unblock branch June 1, 2026 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant