Skip to content

lever: cross-module recursive_type_set local-restrict + import-authority union (+ floor witness) - #5624

Closed
gunbai-bot[bot] wants to merge 4 commits into
mainfrom
session/sleek-owl-775-xmod
Closed

gunbai-bot[bot] wants to merge 4 commits into
mainfrom
session/sleek-owl-775-xmod

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

What

The cross-module recursive-set fix (the lever), plus a permanent floor witness. Stacked on #5623 (the 04_resolve (Y') prerequisite) — base will retarget to main when #5623 merges.

04_infer (both build_type_env_resolved + build_type_env_unresolved): compute the type-cycle set over local bindings only, then union the imported modules' recursive-type authority:

cross_type_all_names = concat(concat(local_cycle, import_recursive_names), kernel)

Why

Imported type bindings arrive as self-ref stubs (node_type_deps returns [own_name]), so detect_type_cycles_kahn over the merged bindings re-marked every imported type recursive — spuriously boxing ~non-recursive concrete types in the emitted Rust (the E0308 value-wrapping long pole) and, where it failed open, masking real resolution defects.

Single-authority fix: recursion is decided once, in the defining module. Local cycle detection stays local; imported types carry their defining-module recursive status via import_recursive_set (previously computed-then-discarded — now used). Sound + complete because cross-module value cycles are forbidden (§4 acyclic import DAG toward std) → every genuine cycle is intra-module → caught locally + carried by the import authority.

The prerequisite (#5623)

Correctly un-marking imported-recursive-arg coproduct aliases (e.g. Witness<StructurallyEqual>) as non-recursive exposed a latent collapse defect: those aliases lost their Holds/Violates arms during resolution → VariantNotFound. #5623 fixes that at the resolution layer (preserve the peelable form). This PR depends on it.

Floor witness (permanent, this PR)

src/v2/test/claim/algebra/witness_alias_recursion_independent_test.dag — NodePairEquality = Witness<NodePair> (a nullary Witness alias over a struct whose only recursion is the imported recursive Node, structurally identical to the production StageEmissionEquality = Witness<StructurallyEqual>). The test fn asserts both coproduct arms resolve (construction-stage; VariantNotFound = RED, fail-closed). Auto-enrolled by the *_test.dag + test fn naming.

It rides this PR (not #5623) because on main + (Y')-alone the alias is still recursive (non-discriminating); only with the lever does it exercise the (Y') resolved-alias path.

Proof (by execution)

  • lever + (Y') full-corpus emit = 836 files, 0 diagnostics (835 + this floor witness). The 4 production Witness VariantNotFound gone; zero net-new.
  • Floor-witness green: gunbc run --claim-run --entry … --function witness_alias_resolves_holds_violates_independent_of_recursion → true.
  • Floor-witness red-on-revert: lever held + (Y') reverted → variant 'Holds'/'Violates' not found in type 'NodePairEquality'; restored → true. Discriminates precisely.
  • req-3 (De-fork grounding keystone: fix v2 generic type-alias instantiation so FreeMonoid aliases resolve - brief in docs plans dsl-v2-defork-audit section 3b #5552) non-regression: 32 v1-compiler-tests green incl. generic_alias_field_access_resolves_through_expansion, list_and_freemonoid_* alias-compat, *coproduct*; generic_alias_coproduct_resolves() → true.
  • Production StageEmissionEquality resolution is permanently floor-gated by self_host/fixed_point_test.dag test fns (predates this work) + the corpus compile-clean gate.

Seed mirror (v1_compiler_infer.rs) hand-edited (no regen); gunbc builds clean.

Note: a follow-up (the lever's full E0308 carrier delta + 3-way termination-delta witness) lands after this; the de-nickname of is_witness_type_name (the witness-synthesis path) is filed as a separate grounded lever.

🤖 Generated with Claude Code

Brian Searls and others added 2 commits June 23, 2026 12:54
…ner aliases

A nullary alias to a generic coproduct container (e.g. Witness<C> = Holds|Violates)
whose only recursion is an imported recursive type is correctly un-marked
non-recursive once cross-module recursion-marking is fixed. Such an alias was then
COLLAPSED during resolution, dropping its coproduct arms -> VariantNotFound at match
sites (e.g. self_host.dag StageEmissionEquality = Witness<StructurallyEqual>).

Fix: the children==0 lazy-return branch already preserves the peelable form for
recursive types (they cannot be eagerly expanded). Extend that SAME "do not collapse
a peelable form" behavior to a second structural inhabitant: a resolved alias whose
target is a registered container type with a Disj (coproduct) declaration. Name-free
(is_container_type = container-registry membership; resolve_generic_use_decl(...)
.connective == Disj); does not touch is_witness_type_name.

This is recursion-INDEPENDENCE: the non-recursive coproduct-container alias now
resolves to the same peelable form the recursive case produced, so the existing
downstream variant resolution fires regardless of recursion-marking.

No-op on main (835/0 emit == pristine); enables the cross-module recursive-set fix
(separate PR) which exposes the previously recursion-masked sites.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ity union; floor witness

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gunbai-bot
gunbai-bot Bot force-pushed the session/sleek-owl-775-xmod branch from b62bb70 to 1daaeeb Compare June 23, 2026 12:58
Base automatically changed from session/sleek-owl-775-resolve to main June 23, 2026 13:35
@gunbai-bot

gunbai-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Closing as SUPERSEDED — the v3 lever shape (filter-to-local + import-authority union) was refuted by execution and abandoned per parent's signed reorientation.

Why it's dead:

  • The v3 filter+union regressed target_model (cross-module field-access cascade to bare T) while import_recursive_names is an incomplete authority — an architectural fail, not a tunable.
  • Root-caused by execution: main's detect_type_cycles_kahn over-marks because node_type_deps reports a LEAF NOMINAL's own identity (Symbol deps=[Symbol], opaque/alias/branded leaves) as a structural self-dependency. The kahn algorithm and import handling are correct; only the dependency-extraction input was wrong.

Replacement (parent-signed reshape): a minimal de-conflation at the dependency-extraction call site (all_deps_map) — a binding whose resolved form is a bare leaf and whose only dep is its own name contributes no self-dependency. kahn unchanged; no filter; no import-union; no 517 change. Genuine structural recursion (GrammarExpr etc.) is untouched.

Validated (apples-to-apples on identical sources): Box< 445→308, Box::new 258→123, 0 diagnostics both (no net-new); target_model 4/4; anthropic non-regression green; smoking gun (50 leaf/type-param marks dropped, 0 newly, genuine preserved). Fix-#1 is orphaned under this shape and dropped.

Replacement PR will be opened from branch session/identity-deconflate.

@gunbai-bot gunbai-bot Bot closed this Jun 23, 2026
briansrls pushed a commit that referenced this pull request Jun 24, 2026
…lf-containment (#5686)

node_type_deps reports a leaf nominal's own identity (Symbol -> [Symbol]; opaque
`type Symbol`, alias `type Email = String`, branded `type Char = Int where brand`)
as a structural self-dependency, so detect_type_cycles_kahn's self_ref check
spuriously marks every leaf nominal recursive -> spurious Box<> in emitted Rust
(the cargo-green E0308 class). The kahn algorithm and import handling are correct;
only the dependency-extraction input conflated identity with structural
self-containment.

Fix at the all_deps_map call site (both build_type_env copies, + seed mirror):
a binding whose resolved form is a bare leaf (NoConnective, 0 children) and whose
deps are exactly [own_name] contributes no self-dependency. Genuine structural
recursion (GrammarExpr, whose self-name comes from variant children) flows the
unchanged structural path and stays marked. node_type_deps and kahn unchanged;
no filter, no import-union, no resolve-stage change.

Validated apples-to-apples on identical sources: Box< 445->308, Box::new 258->123,
0 diagnostics before and after (no net-new); target_model 4/4; anthropic
non-regression green; full v1-compiler-tests net-new=0 vs plain-main baseline.

Supersedes #5624 (v3 filter+union lever, refuted by execution).

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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