on master help sign in
Infer unit patterns and recursive Hoon identities
e878edciko
docs/compiler.md+5−1
@@ -56,14 +56,18 @@ The core is split into chapters, `+| %name`, in this order:
56| `%span-check` | `+check-spans`, which `+golden` runs on every tree, and `+check-spans-test` |
56| `%span-check` | `+check-spans`, which `+golden` runs on every tree, and `+check-spans-test` |
57| `%type-inference` | The initial inference fragment and its unification helpers; not yet a module type checker |
57| `%type-inference` | The initial inference fragment and its unification helpers; not yet a module type checker |
58
58
59−The inference fragment supports fixed auras, unrestricted `@`, one-way widening into it and toward aura-family ancestors, generic variables and gates, faced and unfaced cells, cell sample binders, known record field reads, a two-binder cell pattern, a complete two-arm loobean branch, closed local mold aliases and acyclic forward value references. It instantiates generalized top-level definitions when they are used. Acyclic local value definitions can refer forward and generalize variables that are free of their enclosing environment; captured variables and explicit capitals remain shared. Acyclic simple local mold aliases can also refer forward, and are scoped to the body, not its gate's sample. Nonrecursive generic aliases of supported molds can be applied at exact arity, including locally and through library interfaces; their parameters are lexical. Closed, provably disjoint nonrecursive unions and tagged single-case molds support contextual term values, `~` and `|=%tag` constructors. A null-headed cell can match a union case but does not supply a `|=%$` constructor; unresolved tags and nulls remain pending, with distinct partial-inference diagnostics. Nonrecursive top-level generic union molds with provably disjoint cases also support contextual tags and constructors at exact arity; their arguments are retained through generalization and supported library interfaces. Nonrecursive generic aliases can reorder, repeat or omit parameters while preserving a supported union’s origin, including through local scopes and library interfaces. A generic union declared directly inside a body remains unsupported, as do recursive local groups and recursive molds. A value with type variable `A` can fit a field whose type is that same `A`, including in generic union payloads and nested or repeated record fields. A differing-variable face constraint remains unsupported. It can infer and generalize atom constraints arising from explicit capitals or bare sample binders, preserve those constraints through aliases, instantiate them independently, and print multiple constraints. Unresolved decimal variables default to `@ud` for inferred types without gates; defaulting inside gate types and other constraint cases remain incomplete. It does not yet check a module, complete imports, general patterns, general deferred constraints, recursive molds, dependency cycles or most errors with both conflicting excerpts. Definite cast and call mismatches, incompatible branches and overlapping union cases report both sites and excerpts. Concrete branch results can join at an explicitly present parent aura or `@`. Some known tuple and record positions acquire matching faces; other variable-dependent face merges, general branch tag context and ambiguous call-result propagation remain unsupported; function compatibility involving nested `@` and variable-dependent joins remain unsupported except for equal types. The current dependency pass can take quadratic time on long forward chains; the milestone's speed target remains unverified. A separate conservative pass reports unions whose cases are provably disjoint or overlapping and leaves other cases unresolved; it does not claim the mold is checked. Unsupported forms are reported as such, and the partial output says the module has not been checked.
59+The inference fragment supports fixed auras, unrestricted `@`, one-way widening into it and toward aura-family ancestors, generic variables and gates, faced and unfaced cells, cell sample binders, known record field reads, a two-binder cell pattern, a complete two-arm loobean branch, a complete two-arm unit-shaped branch, closed local mold aliases and acyclic forward value references. It instantiates generalized top-level definitions when they are used. Acyclic local value definitions can refer forward and generalize variables that are free of their enclosing environment; captured variables and explicit capitals remain shared. Acyclic simple local mold aliases can also refer forward, and are scoped to the body, not its gate's sample. Nonrecursive generic aliases of supported molds can be applied at exact arity, including locally and through library interfaces; their parameters are lexical. Closed, provably disjoint nonrecursive unions and tagged single-case molds support contextual term values, `~` and `|=%tag` constructors. A null-headed cell can match a union case but does not supply a `|=%$` constructor; unresolved tags and nulls remain pending, with distinct partial-inference diagnostics. Nonrecursive top-level generic union molds with provably disjoint cases also support contextual tags and constructors at exact arity; their arguments are retained through generalization and supported library interfaces. Nonrecursive generic aliases can reorder, repeat or omit parameters while preserving a supported union’s origin, including through local scopes and library interfaces. A generic union declared directly inside a body remains unsupported, as do recursive local groups and recursive molds. A value with type variable `A` can fit a field whose type is that same `A`, including in generic union payloads and nested or repeated record fields. A differing-variable face constraint remains unsupported. It can infer and generalize atom constraints arising from explicit capitals or bare sample binders, preserve those constraints through aliases, instantiate them independently, and print multiple constraints. Unresolved decimal variables default to `@ud` for inferred types without gates; defaulting inside gate types and other constraint cases remain incomplete. It does not yet check a module, complete imports, general patterns, general deferred constraints, recursive molds, dependency cycles or most errors with both conflicting excerpts. Definite cast and call mismatches, incompatible branches and overlapping union cases report both sites and excerpts. Concrete branch results can join at an explicitly present parent aura or `@`. Some known tuple and record positions acquire matching faces; other variable-dependent face merges, general branch tag context and ambiguous call-result propagation remain unsupported; function compatibility involving nested `@` and variable-dependent joins remain unsupported except for equal types. The current dependency pass can take quadratic time on long forward chains; the milestone's speed target remains unverified. A separate conservative pass reports unions whose cases are provably disjoint or overlapping and leaves other cases unresolved; it does not claim the mold is checked. Unsupported forms are reported as such, and the partial output says the module has not been checked.
60
60
61For supported closed unions, the type printer keeps the union’s origin separate from its displayed name. It prints an in-scope name such as `msg` or `msg.lib`, including in records and gates; an accessible imported nongeneric alias may name a transitive union. A nongeneric body-local union that escapes its scope, or an origin without an accessible name, prints its cases separated by spaces, parenthesized when nested. That case-list form is print-only. A supported generic union prints its name and all arguments, including unused arguments; when its name is unavailable, the partial printer reports that limitation instead of dropping its arguments. Two faces for the same imported origin remain compatible; compatibility between distinct union origins, including in gate types, remains unsupported. Some comparisons of generic union arguments also remain unsupported rather than deciding equality or mismatch. The Hoon bridge also translates some imported unnamed unions into this representation; unsupported cases remain errors.
61For supported closed unions, the type printer keeps the union’s origin separate from its displayed name. It prints an in-scope name such as `msg` or `msg.lib`, including in records and gates; an accessible imported nongeneric alias may name a transitive union. A nongeneric body-local union that escapes its scope, or an origin without an accessible name, prints its cases separated by spaces, parenthesized when nested. That case-list form is print-only. A supported generic union prints its name and all arguments, including unused arguments; when its name is unavailable, the partial printer reports that limitation instead of dropping its arguments. Two faces for the same imported origin remain compatible; compatibility between distinct union origins, including in gate types, remains unsupported. Some comparisons of generic union arguments also remain unsupported rather than deciding equality or mismatch. The Hoon bridge also translates some imported unnamed unions into this representation; unsupported cases remain errors.
62
62
63+A known union of `~` and a null-headed cell can be matched by `~` and `[~ value]` arms in either order. The payload binder is checked for shadowing. Unknown subjects, missing or duplicate arms, and unresolved branch results remain unsupported; this fragment infers the prelude’s `fall` and `need` signatures.
64+
63A known closed union can also provide context to each arm of a complete loobean branch in a cast or a call with a known union sample. Nested branches and transparent groups work; a mismatch reports the expected union and the offending arm. General patterns and context through bodies with local definitions remain unsupported, and unrelated unresolved tags stay pending.
65A known closed union can also provide context to each arm of a complete loobean branch in a cast or a call with a known union sample. Nested branches and transparent groups work; a mismatch reports the expected union and the offending arm. General patterns and context through bodies with local definitions remain unsupported, and unrelated unresolved tags stay pending.
64
66
65The separate `jibb-dev/lib/jibb-import.hoon` is an unfinished reader for selected Hoon type graphs. It handles named recursion, nested map, set, queue, list, unit, jar and jug arguments, direct ordered maps, and direct `$^` unions whose source branches can be proved disjoint. It also reads ordered map fields in records when the map arguments are syntactically fixed atom molds and other fields are fixed atom molds or standard collections of static atom molds. Collection names must not be shadowed. The reader can also import a mark door sample, removing its outer sample face and validating its build subject. The reader checks explicit Hoon `$%`, `$?` and `$^` unions against the Grammar’s disjointness rule (doc question 22). It re-evaluates statically recognizable source branches before Hoon can collapse them, checks materialized fork cases, and rejects shapes it cannot prove disjoint. Static records may also have verified standard-collection and `path` siblings. One direct explicit union argument in a standard list, set, map or unit is accepted when its call wrapper and collection provenance are verified and its static cases are pairwise disjoint. A synthetic regression checks that forged wrapper subjects, samples and tails are rejected. Other collection-union forms, nested ordered maps, general source-level disjointness and complete integration with the checker remain unfinished. `+golden-types-imports` can consume fixed-aura atoms, valid term singletons, nested faced or unfaced cells, unions of supported, pairwise-disjoint cases, and opaque standard collections with supported arguments from caller-supplied reader results for declared `/-` imports; `+golden-types-with-marks` accepts the same shapes for declared marks. The caller must pass unchanged results from the source-aware reader: the bridge validates the supplied graph but cannot detect source cases Hoon collapsed before that graph was built. The opaque collection types retain their names and arguments for identity, unification and printing; Jibb-written collection molds and their prelude operations are not yet connected. Other forms remain unsupported. Its disjointness proof is conservative and can reject a valid union it cannot prove disjoint. Collection recognition requires the originating `/sur` core. The reader validates direct zuse subjects and Ford subjects consisting of faced imported cores and a validated tail, checking imported cores transitively; it rejects shadowed names, inconsistent core subjects and other subject shapes. For a recursive reference, the reader matches the exact Hoon hold against named arms in validated cores, including transitive Ford imports; it rejects missing, unlabelled or ambiguous origins. Its fixtures include three real Tlon files; `tools/importer-test` compares the internal projection under eval, and the ship test compares the same golden output. `tools/type-import-test` and `tools/type-mark-test` compare bridge reports under eval and on the ship. `tools/clay-fixtures` also passes Clay-built `/sur` molds and mark samples through the Clay-built importer and partial checker.
67The separate `jibb-dev/lib/jibb-import.hoon` is an unfinished reader for selected Hoon type graphs. It handles named recursion, nested map, set, queue, list, unit, jar and jug arguments, direct ordered maps, and direct `$^` unions whose source branches can be proved disjoint. It also reads ordered map fields in records when the map arguments are syntactically fixed atom molds and other fields are fixed atom molds or standard collections of static atom molds. Collection names must not be shadowed. The reader can also import a mark door sample, removing its outer sample face and validating its build subject. The reader checks explicit Hoon `$%`, `$?` and `$^` unions against the Grammar’s disjointness rule (doc question 22). It re-evaluates statically recognizable source branches before Hoon can collapse them, checks materialized fork cases, and rejects shapes it cannot prove disjoint. Static records may also have verified standard-collection and `path` siblings. One direct explicit union argument in a standard list, set, map or unit is accepted when its call wrapper and collection provenance are verified and its static cases are pairwise disjoint. A synthetic regression checks that forged wrapper subjects, samples and tails are rejected. Other collection-union forms, nested ordered maps, general source-level disjointness and complete integration with the checker remain unfinished. `+golden-types-imports` can consume fixed-aura atoms, valid term singletons, nested faced or unfaced cells, unions of supported, pairwise-disjoint cases, and opaque standard collections with supported arguments from caller-supplied reader results for declared `/-` imports; `+golden-types-with-marks` accepts the same shapes for declared marks. The caller must pass unchanged results from the source-aware reader: the bridge validates the supplied graph but cannot detect source cases Hoon collapsed before that graph was built. The opaque collection types retain their names and arguments for identity, unification and printing; Jibb-written collection molds and their prelude operations are not yet connected. Other forms remain unsupported. Its disjointness proof is conservative and can reject a valid union it cannot prove disjoint. Collection recognition requires the originating `/sur` core. The reader validates direct zuse subjects and Ford subjects consisting of faced imported cores and a validated tail, checking imported cores transitively; it rejects shadowed names, inconsistent core subjects and other subject shapes. For a recursive reference, the reader matches the exact Hoon hold against named arms in validated cores, including transitive Ford imports; it rejects missing, unlabelled or ambiguous origins. Its fixtures include three real Tlon files; `tools/importer-test` compares the internal projection under eval, and the ship test compares the same golden output. `tools/type-import-test` and `tools/type-mark-test` compare bridge reports under eval and on the ship. `tools/clay-fixtures` also passes Clay-built `/sur` molds and mark samples through the Clay-built importer and partial checker.
66
68
69+Self-recursive `/sur` molds can cross the bridge as opaque module-and-name identities when their supported source-aware graph passes validation. Aliases, foreign references, recursive marks, unfolding, construction and field access remain unsupported; the partial printer declines recursive names that are not in scope.
70+
67Callers that have a `/sur` core and a name can use `+read-named`: it checks for a locally declared arm before asking Hoon to evaluate the mold, and reports evaluation failures with the final Hoon trace line. `+read-mold-in` remains available when the caller already has the selected type. The checker currently treats a failed import as unsupported rather than rendering the importer's error.
71Callers that have a `/sur` core and a name can use `+read-named`: it checks for a locally declared arm before asking Hoon to evaluate the mold, and reports evaluation failures with the final Hoon trace line. `+read-mold-in` remains available when the caller already has the selected type. The checker currently treats a failed import as unsupported rather than rendering the importer's error.
68
72
69`+golden-types-with-prelude` accepts case-local Jibb module sources alongside `/sur` and mark results and the vendored prelude source. It resolves reachable ordinary library modules in dependency order, caching completed interfaces per invocation and exporting supported closed value schemes, direct generic unions, and simple or nonrecursive generic mold aliases whose bodies the fragment supports. It reserves names from the supplied prelude, detects missing sources and cycles, and rejects a dependency if its interface cannot be inferred by the fragment. API files, modules with `main`, unsupported definitions and many other features remain outside this subset; the prelude inventory itself is incomplete. The older `+golden-types-with-modules` entry point fails closed for imports because it has no prelude scope. Neither entry point claims the module has been checked.
73`+golden-types-with-prelude` accepts case-local Jibb module sources alongside `/sur` and mark results and the vendored prelude source. It resolves reachable ordinary library modules in dependency order, caching completed interfaces per invocation and exporting supported closed value schemes, direct generic unions, and simple or nonrecursive generic mold aliases whose bodies the fragment supports. It reserves names from the supplied prelude, detects missing sources and cycles, and rejects a dependency if its interface cannot be inferred by the fragment. API files, modules with `main`, unsupported definitions and many other features remain outside this subset; the prelude inventory itself is incomplete. The older `+golden-types-with-modules` entry point fails closed for imports because it has no prelude scope. Neither entry point claims the module has been checked.
docs/status.md+7−6
@@ -19,7 +19,7 @@ On 2026-10-30, @iko decided that Jibb drops `$?`, so a `+$` takes a union's case
19
19
20## Milestone 2: in progress
20## Milestone 2: in progress
21
21
22−The repository spec records the subsequent type-system and prelude decisions; those changes also need synchronization to the Claude document. An initial inference fragment and a standalone Hoon type reader are being tested. The fragment explicitly reports that it does not check a module; partial bridges pass selected Hoon atoms, term singletons, nested faced or unfaced cells, supported disjoint unions and opaque standard collections from `/sur` molds and marks into inference. Collections retain their names and supported arguments for printing and unification; nested supported union arguments are retained too. The bridge trusts unchanged results from the source-aware reader; a materialized type graph alone cannot reveal cases Hoon already collapsed. Jibb-written collection molds, prelude collection operations and recursive references are not yet connected to this representation; malformed names and arities fail closed. The reader handles mark samples, recursive molds whose origins can be verified through Ford imports, nested collections, direct ordered maps, certain ordered-map fields, direct `$^` unions whose cases can be proved disjoint, and one verified explicit union argument in a list, set, map or unit. Ambiguous recursive origins fail closed; recognizable explicit unions are checked from source before Hoon can collapse their cases (doc question 22), while other nested unions, nested ordered maps, general union disjointness and complete import integration remain unfinished. The partial checker resolves a subset of ordinary library imports in dependency order using supplied Jibb sources and the current prelude names, caches completed interfaces per run, and detects missing sources and cycles. It does not yet check API or kind semantics, and the prelude inventory is incomplete. The inference fragment supports unrestricted `@`, selected one-way widenings including aura-family ancestors, generalized atom constraints arising from explicit capitals or bare sample binders, acyclic local value definitions with captures, simple and nonrecursive generic mold aliases, selected faced record casts and field reads, and contextual tags, `~` and constructors for closed disjoint unions, including direct nonrecursive generic unions and propagation through complete loobean branches in casts and known union-sample calls. Contextual null-headed cells fit their cases, but do not supply a `|=%$` constructor; unresolved nulls remain pending and are reported separately from tags. A value with type variable `A` can fit a field whose type is that same `A`, including in generic union payloads and nested or repeated record fields. Other variable-dependent face merges remain unsupported. Supported closed unions print using an in-scope name or imported alias; an unnamed nongeneric union prints its cases, parenthesized when nested. The latter is print-only. Generic union types retain all arguments in their printed name; if that name is unavailable, the partial printer reports the limitation. Different import faces for the same origin remain compatible; compatibility across distinct union origins and some generic-argument comparisons remain unsupported. Nonrecursive generic aliases can reorder, repeat or omit parameters while retaining a union’s origin, including through local scopes and library interfaces. A generic union declared directly inside a body remains unsupported, as do differing-variable face constraints in a union payload. A transitive generic union whose name is unavailable is reported as unsupported instead of hiding its arguments. Unfixed decimal literals default to `@ud` in gate-free inferred types; recursive local groups and molds, general branch tag context, other constraint cases and defaulting inside gate types still need work. Collection recognition checks the originating core and fails closed on shadows or unsupported Ford subject shapes; a wrapper regression also rejects forged subjects, samples and tails. Its golden fixtures include three real Tlon files. A Clay test passes a Ford-built nested union through the reader into partial inference. The partial prelude includes `murn`, `join`, `snip` and `rear`, parsed but still awaiting checker and backend validation, and [prelude-inventory.md](prelude-inventory.md) is a source audit, not the complete inventory or signatures. The milestone remains unfinished.
22+The repository spec records the subsequent type-system and prelude decisions; those changes also need synchronization to the Claude document. An initial inference fragment and a standalone Hoon type reader are being tested. The fragment explicitly reports that it does not check a module; partial bridges pass selected Hoon atoms, term singletons, nested faced or unfaced cells, supported disjoint unions and opaque standard collections from `/sur` molds and marks into inference. Collections retain their names and supported arguments for printing and unification; nested supported union arguments are retained too. The bridge trusts unchanged results from the source-aware reader; a materialized type graph alone cannot reveal cases Hoon already collapsed. Jibb-written collection molds and prelude collection operations are not yet connected to this representation; malformed names and arities fail closed. Selected self-recursive `/sur` molds cross as opaque module-and-name identities; aliases, foreign references, recursive marks, unfolding and construction remain unsupported. The reader handles mark samples, recursive molds whose origins can be verified through Ford imports, nested collections, direct ordered maps, certain ordered-map fields, direct `$^` unions whose cases can be proved disjoint, and one verified explicit union argument in a list, set, map or unit. Ambiguous recursive origins fail closed; recognizable explicit unions are checked from source before Hoon can collapse their cases (doc question 22), while other nested unions, nested ordered maps, general union disjointness and complete import integration remain unfinished. The partial checker resolves a subset of ordinary library imports in dependency order using supplied Jibb sources and the current prelude names, caches completed interfaces per run, and detects missing sources and cycles. It does not yet check API or kind semantics, and the prelude inventory is incomplete. The inference fragment supports unrestricted `@`, selected one-way widenings including aura-family ancestors, generalized atom constraints arising from explicit capitals or bare sample binders, acyclic local value definitions with captures, simple and nonrecursive generic mold aliases, selected faced record casts and field reads, and contextual tags, `~` and constructors for closed disjoint unions, including direct nonrecursive generic unions and propagation through complete loobean branches in casts and known union-sample calls. Contextual null-headed cells fit their cases, but do not supply a `|=%$` constructor; unresolved nulls remain pending and are reported separately from tags. A known union of `~` and a null-headed cell supports a complete `~` / `[~ value]` branch in either order, including shadow checks; unknown subjects, missing or duplicate arms, and unresolved branch results remain unsupported. This fragment infers the prelude’s `fall` and `need` signatures. A value with type variable `A` can fit a field whose type is that same `A`, including in generic union payloads and nested or repeated record fields. Other variable-dependent face merges remain unsupported. Supported closed unions print using an in-scope name or imported alias; an unnamed nongeneric union prints its cases, parenthesized when nested. The latter is print-only. Generic union types retain all arguments in their printed name; if that name is unavailable, the partial printer reports the limitation. Different import faces for the same origin remain compatible; compatibility across distinct union origins and some generic-argument comparisons remain unsupported. Nonrecursive generic aliases can reorder, repeat or omit parameters while retaining a union’s origin, including through local scopes and library interfaces. A generic union declared directly inside a body remains unsupported, as do differing-variable face constraints in a union payload. A transitive generic union whose name is unavailable is reported as unsupported instead of hiding its arguments. Unfixed decimal literals default to `@ud` in gate-free inferred types; recursive local groups and molds, general branch tag context, other constraint cases and defaulting inside gate types still need work. Collection recognition checks the originating core and fails closed on shadows or unsupported Ford subject shapes; a wrapper regression also rejects forged subjects, samples and tails. Its golden fixtures include three real Tlon files. A Clay test passes a Ford-built nested union through the reader into partial inference. The partial prelude includes `murn`, `join`, `snip` and `rear`, parsed but still awaiting checker and backend validation, and [prelude-inventory.md](prelude-inventory.md) is a source audit, not the complete inventory or signatures. The milestone remains unfinished.
23
23
24## Milestone 1: parser and mark
24## Milestone 1: parser and mark
25
25
@@ -37,8 +37,8 @@ What the plan asks for, and where it is:
37
37
38Besides the plan's list:
38Besides the plan's list:
39
39
40−- 493 golden cases: 277 that parse and 216 that fail, each failure at a stated line and column. One failing case has an unclear expectation (corpus question 3). The newer type-output cases exercise only the partial inference fragment. Each case's `about.txt` quotes the doc's words it tests.
40+- 494 golden cases: 278 that parse and 216 that fail, each failure at a stated line and column. One failing case has an unclear expectation (corpus question 3). The newer type-output cases exercise only the partial inference fragment. Each case's `about.txt` quotes the doc's words it tests.
41−- The 493-case corpus passed under `urbit eval` in 59.0 s. The fake ship passed all 498 tests, including the importer, importer/checker and mark/checker bridges, module scope and unification checks, with 29.9 s for sync and 16.1 s for tests. The importer golden compares 141 projections; `tools/clay-fixtures` builds the example Hoon fixtures through Clay, checks a recursive Ford import's origin, and passes Clay-built types through the Clay-built importer and partial checker.
41+- The 494-case corpus passed under `urbit eval` in 59.4 s. The fake ship passed all 499 tests, including the importer, importer/checker and mark/checker bridges, module scope and unification checks, with 25.3 s for sync and 24.3 s for tests. The importer golden compares 141 projections; `tools/clay-fixtures` builds the example Hoon fixtures through Clay, checks a recursive Ford import's origin, and passes Clay-built types through the Clay-built importer and partial checker.
42- `tools/testdata/selftest` (22 checks) and `tools/testdata/ship-selftest` test the tools themselves.
42- `tools/testdata/selftest` (22 checks) and `tools/testdata/ship-selftest` test the tools themselves.
43- Two independent audits before 7c739b7, one of which compared the library with a reference parser written in Python from the doc over about 19,000 generated programs. Before ab797a9, a second Python parser, written from the decisions, agreed with the library on 8,000 generated programs and 900 hand-written probes.
43- Two independent audits before 7c739b7, one of which compared the library with a reference parser written in Python from the doc over about 19,000 generated programs. Before ab797a9, a second Python parser, written from the decisions, agreed with the library on 8,000 generated programs and 900 hand-written probes.
44- Speed: starting `urbit eval` and building the library takes about 8 s. After that, a 2,000-line module of 400 definitions parses in about a second. Loops over definitions, arms, lines and items grow no stack; nesting costs a few stack frames a level, and 100,000 nested brackets still parse (library section 3).
44- Speed: starting `urbit eval` and building the library takes about 8 s. After that, a 2,000-line module of 400 definitions parses in about a second. Loops over definitions, arms, lines and items grow no stack; nesting costs a few stack frames a level, and 100,000 nested brackets still parse (library section 3).
@@ -74,14 +74,15 @@ Besides the plan's list:
74| 250564c | 2026-10-30 | Infer selected named generic unions, test Hoon collection wrappers, and add list helpers |
74| 250564c | 2026-10-30 | Infer selected named generic unions, test Hoon collection wrappers, and add list helpers |
75| e95ef8f | 2026-10-30 | Bridge selected Hoon unions into partial inference and support generic union aliases |
75| e95ef8f | 2026-10-30 | Bridge selected Hoon unions into partial inference and support generic union aliases |
76| 56a899f | 2026-10-30 | Infer matching variable types in faced fields and bridge opaque Hoon collections |
76| 56a899f | 2026-10-30 | Infer matching variable types in faced fields and bridge opaque Hoon collections |
77−| This change | 2026-10-30 | Resolve selected contextual null literals |
77+| 4d45d2f | 2026-10-30 | Resolve selected contextual null literals |
78+| This change | 2026-10-30 | Infer complete unit-shaped branches and bridge selected recursive Hoon molds |
78
79
79## Checking it
80## Checking it
80
81
81On a machine set up as [development.md](development.md) says:
82On a machine set up as [development.md](development.md) says:
82
83
83```
84```
84−tools/golden # 493 cases under urbit eval
85+tools/golden # 494 cases under urbit eval
85tools/importer-test # the Hoon importer fixture projection
86tools/importer-test # the Hoon importer fixture projection
86tools/type-import-test # the importer/checker bridge fixture
87tools/type-import-test # the importer/checker bridge fixture
87tools/type-mark-test # the mark/checker bridge fixture
88tools/type-mark-test # the mark/checker bridge fixture
@@ -91,7 +92,7 @@ tools/testdata/selftest # the tools themselves
91tools/doc-blocks docs/spec # the doc's examples are golden cases
92tools/doc-blocks docs/spec # the doc's examples are golden cases
92tools/ship start
93tools/ship start
93tools/ship gen-tests --check # tests/lib/jibb.hoon is up to date
94tools/ship gen-tests --check # tests/lib/jibb.hoon is up to date
94−tools/ship test # the same 493 cases as -test arms on the ship
95+tools/ship test # the same 494 cases as -test arms on the ship
95tools/testdata/ship-selftest
96tools/testdata/ship-selftest
96tools/ship check-commit # |commit stores .jibb files
97tools/ship check-commit # |commit stores .jibb files
97tools/clay-fixtures # example /sur files and marks build through Clay
98tools/clay-fixtures # example /sur files and marks build through Clay
jibb-dev/lib/jibb.hoon+219−1
@@ -4611,6 +4611,7 @@
4611 [%tag id=@ud literal=lit constructor=?]
4611 [%tag id=@ud literal=lit constructor=?]
4612 [%const literal=lit]
4612 [%const literal=lit]
4613 [%collection name=term args=(list itype)]
4613 [%collection name=term args=(list itype)]
4614+ [%recursive module=term name=term label=(unit @t)]
4614 [%union cases=(list itype) origin=(unit [at=path name=term]) label=(unit @t) args=(list itype)]
4615 [%union cases=(list itype) origin=(unit [at=path name=term]) label=(unit @t) args=(list itype)]
4615 [%face name=term body=itype]
4616 [%face name=term body=itype]
4616 [%fn sample=itype product=itype]
4617 [%fn sample=itype product=itype]
@@ -4663,6 +4664,7 @@
4663 %tag =(n id.t)
4664 %tag =(n id.t)
4664 %const |
4665 %const |
4665 %union |((lien cases.t |=(c=itype (itype-occurs n c sub))) (lien args.t |=(c=itype (itype-occurs n c sub))))
4666 %union |((lien cases.t |=(c=itype (itype-occurs n c sub))) (lien args.t |=(c=itype (itype-occurs n c sub))))
4667+ %recursive |
4666 %collection (lien args.t |=(c=itype (itype-occurs n c sub)))
4668 %collection (lien args.t |=(c=itype (itype-occurs n c sub)))
4667 %face $(t body.t)
4669 %face $(t body.t)
4668 %fn |($(t sample.t) $(t product.t))
4670 %fn |($(t sample.t) $(t product.t))
@@ -4681,6 +4683,8 @@
4681 =. a (itype-walk a sub)
4683 =. a (itype-walk a sub)
4682 =. b (itype-walk b sub)
4684 =. b (itype-walk b sub)
4683 ?: =(a b) `sub
4685 ?: =(a b) `sub
4686+ ?: &(?=(%recursive -.a) ?=(%recursive -.b))
4687+ ?:(&(=(module.a module.b) =(name.a name.b)) `sub ~)
4684 ?: &(?=(%union -.a) ?=(%union -.b))
4688 ?: &(?=(%union -.a) ?=(%union -.b))
4685 ?. =(origin.a origin.b) ~
4689 ?. =(origin.a origin.b) ~
4686 ?: &(?=(~ args.a) ?=(~ args.b)) ?:(=(cases.a cases.b) `sub ~)
4690 ?: &(?=(~ args.a) ?=(~ args.b)) ?:(=(cases.a cases.b) `sub ~)
@@ -4756,6 +4760,7 @@
4756 %tag |
4760 %tag |
4757 %const |
4761 %const |
4758 %union |((lien cases.t |=(c=itype (itype-has-any-atom c sub))) (lien args.t |=(c=itype (itype-has-any-atom c sub))))
4762 %union |((lien cases.t |=(c=itype (itype-has-any-atom c sub))) (lien args.t |=(c=itype (itype-has-any-atom c sub))))
4763+ %recursive |
4759 %collection (lien args.t |=(c=itype (itype-has-any-atom c sub)))
4764 %collection (lien args.t |=(c=itype (itype-has-any-atom c sub)))
4760 %face $(t body.t)
4765 %face $(t body.t)
4761 %fn |($(t sample.t) $(t product.t))
4766 %fn |($(t sample.t) $(t product.t))
@@ -4825,6 +4830,7 @@
4825 %tag &
4830 %tag &
4826 %const &
4831 %const &
4827 %union |((lien cases.typ |=(c=itype (itype-has-fixed-atom c))) (lien args.typ |=(c=itype (itype-has-fixed-atom c))))
4832 %union |((lien cases.typ |=(c=itype (itype-has-fixed-atom c))) (lien args.typ |=(c=itype (itype-has-fixed-atom c))))
4833+ %recursive |
4828 %collection (lien args.typ |=(c=itype (itype-has-fixed-atom c)))
4834 %collection (lien args.typ |=(c=itype (itype-has-fixed-atom c)))
4829 %face $(typ body.typ)
4835 %face $(typ body.typ)
4830 %fn |($(typ sample.typ) $(typ product.typ))
4836 %fn |($(typ sample.typ) $(typ product.typ))
@@ -4909,6 +4915,7 @@
4909 %var &
4915 %var &
4910 %cell &
4916 %cell &
4911 %union &
4917 %union &
4918+ %recursive &
4912 %collection &
4919 %collection &
4913 %face $(t body.t)
4920 %face $(t body.t)
4914 %fn |($(t sample.t) $(t product.t))
4921 %fn |($(t sample.t) $(t product.t))
@@ -4975,6 +4982,32 @@
4975 * |
4982 * |
4976 ==
4983 ==
4977::
4984::
4985+++ itype-has-recursive
4986+ |= t=itype
4987+ ^- ?
4988+ ?- -.t
4989+ %recursive &
4990+ %collection (lien args.t |=(c=itype (itype-has-recursive c)))
4991+ %union (lien (weld args.t cases.t) |=(c=itype (itype-has-recursive c)))
4992+ %face $(t body.t)
4993+ %cell |($(t hed.t) $(t tal.t))
4994+ %fn |($(t sample.t) $(t product.t))
4995+ * |
4996+ ==
4997+::
4998+++ itype-has-hidden-recursive
4999+ |= t=itype
5000+ ^- ?
5001+ ?- -.t
5002+ %recursive ?=(~ label.t)
5003+ %collection (lien args.t |=(c=itype (itype-has-hidden-recursive c)))
5004+ %union (lien (weld args.t cases.t) |=(c=itype (itype-has-hidden-recursive c)))
5005+ %face $(t body.t)
5006+ %cell |($(t hed.t) $(t tal.t))
5007+ %fn |($(t sample.t) $(t product.t))
5008+ * |
5009+ ==
5010+::
4978++ itype-fit-cases
5011++ itype-fit-cases
4979 |= [actual=itype cases=(list itype) sub=isub next=@ud]
5012 |= [actual=itype cases=(list itype) sub=isub next=@ud]
4980 ^- ifit
5013 ^- ifit
@@ -5022,6 +5055,9 @@
5022 =. actual (itype-walk actual sub)
5055 =. actual (itype-walk actual sub)
5023 =. expected (itype-walk expected sub)
5056 =. expected (itype-walk expected sub)
5024 ?: =(actual expected) [%ok sub next]
5057 ?: =(actual expected) [%ok sub next]
5058+ ?: &(?=(%recursive -.actual) ?=(%recursive -.expected))
5059+ ?:(&(=(module.actual module.expected) =(name.actual name.expected)) [%ok sub next] [%bad ~])
5060+ ?: &(|(?=(%recursive -.actual) ?=(%recursive -.expected)) !|(?=(%var -.actual) ?=(%var -.expected))) [%unknown ~]
5025 ?: &(?=(%union -.actual) ?=(%union -.expected))
5061 ?: &(?=(%union -.actual) ?=(%union -.expected))
5026 ?. =(origin.actual origin.expected) [%unknown ~]
5062 ?. =(origin.actual origin.expected) [%unknown ~]
5027 ?: &(?=(~ args.actual) ?=(~ args.expected))
5063 ?: &(?=(~ args.actual) ?=(~ args.expected))
@@ -5112,6 +5148,7 @@
5112 =/ unified=(unit isub) (itype-unify actual expected sub)
5148 =/ unified=(unit isub) (itype-unify actual expected sub)
5113 ?~ unified
5149 ?~ unified
5114 ?: |(?=(%face -.actual) ?=(%face -.expected)) [%unknown ~]
5150 ?: |(?=(%face -.actual) ?=(%face -.expected)) [%unknown ~]
5151+ ?: |((itype-has-recursive (itype-normalize actual sub)) (itype-has-recursive (itype-normalize expected sub))) [%unknown ~]
5115 ?: (itype-family-related actual expected sub) [%unknown ~]
5152 ?: (itype-family-related actual expected sub) [%unknown ~]
5116 [%bad ~]
5153 [%bad ~]
5117 [%ok u.unified next]
5154 [%ok u.unified next]
@@ -5126,6 +5163,7 @@
5126 =/ unified=(unit isub) (itype-unify actual expected sub)
5163 =/ unified=(unit isub) (itype-unify actual expected sub)
5127 ?~ unified
5164 ?~ unified
5128 ?: |((itype-has-union (itype-normalize actual sub)) (itype-has-union (itype-normalize expected sub))) [%unknown ~]
5165 ?: |((itype-has-union (itype-normalize actual sub)) (itype-has-union (itype-normalize expected sub))) [%unknown ~]
5166+ ?: |((itype-has-recursive (itype-normalize actual sub)) (itype-has-recursive (itype-normalize expected sub))) [%unknown ~]
5129 ?: (itype-family-related actual expected sub) [%unknown ~]
5167 ?: (itype-family-related actual expected sub) [%unknown ~]
5130 [%bad ~]
5168 [%bad ~]
5131 [%ok u.unified next]
5169 [%ok u.unified next]
@@ -5171,6 +5209,20 @@
5171 ["collection arguments should preserve aura-family uncertainty" problems]
5209 ["collection arguments should preserve aura-family uncertainty" problems]
5172 =? problems !(itype-face-asym [%collection %list ~[[%face %x txt]]] list-t ~)
5210 =? problems !(itype-face-asym [%collection %list ~[[%face %x txt]]] list-t ~)
5173 ["collection arguments should preserve face uncertainty" problems]
5211 ["collection arguments should preserve face uncertainty" problems]
5212+ =/ recursive=itype [%recursive %fixture %chain `'chain.fixture']
5213+ =/ same-recursive=itype [%recursive %fixture %chain ~]
5214+ =/ other-recursive=itype [%recursive %other %chain `'chain.other']
5215+ =? problems ?=(~ (itype-unify recursive same-recursive ~))
5216+ ["recursive identity should not depend on its visible name" problems]
5217+ =? problems ?=(^ (itype-unify recursive other-recursive ~))
5218+ ["recursive identity should include the source module" problems]
5219+ =/ recursive-fit=ifit (itype-fit recursive txt ~ 0)
5220+ =? problems !?=(%unknown -.recursive-fit)
5221+ ["opaque recursive types should not be unfolded by a fit" problems]
5222+ =/ nested-fit=ifit
5223+ (itype-fit [%collection %list ~[a]] list-t ~[[0 recursive]] 1)
5224+ =? problems !?=(%unknown -.nested-fit)
5225+ ["recursive types behind substitutions should remain uncertain" problems]
5174 problems
5226 problems
5175::
5227::
5176:: The expression inference below is deliberately a fragment: bindings,
5228:: The expression inference below is deliberately a fragment: bindings,
@@ -5231,6 +5283,7 @@
5231 ?- -.t
5283 ?- -.t
5232 %fn &
5284 %fn &
5233 %cell |($(t hed.t) $(t tal.t))
5285 %cell |($(t hed.t) $(t tal.t))
5286+ %recursive |
5234 %collection (lien args.t |=(c=itype (itype-has-gate c)))
5287 %collection (lien args.t |=(c=itype (itype-has-gate c)))
5235 %face $(t body.t)
5288 %face $(t body.t)
5236 %union (lien cases.t |=(c=itype (itype-has-gate c)))
5289 %union (lien cases.t |=(c=itype (itype-has-gate c)))
@@ -5269,6 +5322,7 @@
5269 %tag ~
5322 %tag ~
5270 %const ~
5323 %const ~
5271 %union (zing (turn (weld args.t cases.t) |=(c=itype (itype-vars c))))
5324 %union (zing (turn (weld args.t cases.t) |=(c=itype (itype-vars c))))
5325+ %recursive ~
5272 %collection (zing (turn args.t |=(c=itype (itype-vars c))))
5326 %collection (zing (turn args.t |=(c=itype (itype-vars c))))
5273 %face $(t body.t)
5327 %face $(t body.t)
5274 %fn (weld $(t sample.t) $(t product.t))
5328 %fn (weld $(t sample.t) $(t product.t))
@@ -5306,6 +5360,7 @@
5306 %tag t
5360 %tag t
5307 %const t
5361 %const t
5308 %union [%union (turn cases.t |=(c=itype (itype-rename c ren))) origin.t label.t (turn args.t |=(c=itype (itype-rename c ren)))]
5362 %union [%union (turn cases.t |=(c=itype (itype-rename c ren))) origin.t label.t (turn args.t |=(c=itype (itype-rename c ren)))]
5363+ %recursive t
5309 %collection [%collection name.t (turn args.t |=(c=itype (itype-rename c ren)))]
5364 %collection [%collection name.t (turn args.t |=(c=itype (itype-rename c ren)))]
5310 %face [%face name.t $(t body.t)]
5365 %face [%face name.t $(t body.t)]
5311 %fn [%fn $(t sample.t) $(t product.t)]
5366 %fn [%fn $(t sample.t) $(t product.t)]
@@ -5328,6 +5383,7 @@
5328 %tag |
5383 %tag |
5329 %const |
5384 %const |
5330 %union |((lien cases.t |=(c=itype (itype-var-is-atom n c))) (lien args.t |=(c=itype (itype-var-is-atom n c))))
5385 %union |((lien cases.t |=(c=itype (itype-var-is-atom n c))) (lien args.t |=(c=itype (itype-var-is-atom n c))))
5386+ %recursive |
5331 %collection (lien args.t |=(c=itype (itype-var-is-atom n c)))
5387 %collection (lien args.t |=(c=itype (itype-var-is-atom n c)))
5332 %face $(t body.t)
5388 %face $(t body.t)
5333 %fn |($(t sample.t) $(t product.t))
5389 %fn |($(t sample.t) $(t product.t))
@@ -5405,6 +5461,69 @@
5405 ?. (is-lower i.chars) |
5461 ?. (is-lower i.chars) |
5406 (levy t.chars |=(c=@tD (in c name-mask)))
5462 (levy t.chars |=(c=@tD (in c name-mask)))
5407::
5463::
5464+:: A source-aware reader result is trusted for origin. Validate every
5465+:: supplied branch and keep the recursive mold opaque: unfolding its
5466+:: root while naming only its tail would change its type identity.
5467++$ irec-pattern [pat=upat has=?]
5468+++ rec-pattern
5469+ |= [node=* module=term name=term]
5470+ ^- (unit irec-pattern)
5471+ ?@ node ~
5472+ ?+ -.node ~
5473+ %atom
5474+ ?: ?=([%atom %n [~ @]] node)
5475+ ?. =(0 +>+.node) ~
5476+ `[[%atom `0] |]
5477+ ?: ?=([%atom %tas [~ @]] node)
5478+ =/ value=@ +>+.node
5479+ ?. |(=(value 0) (jibb-face-name value)) ~
5480+ `[[%atom `value] |]
5481+ ?. ?=([%atom @ ~] node) ~
5482+ =/ aura=@ta (@ta +<.node)
5483+ ?. |(?=(?(%u %ud %ux %uv %t %ta %tas %p %da %dr) aura) =('tD' aura)) ~
5484+ `[[%atom ~] |]
5485+ %ref
5486+ ?. ?=([%ref @ @] node) ~
5487+ ?. &(=(module +<.node) =(name +>.node)) ~
5488+ `[[%unknown ~] &]
5489+ %face
5490+ ?. ?=([%face @ *] node) ~
5491+ ?. (jibb-face-name +<.node) ~
5492+ $(node +>.node)
5493+ %cell
5494+ ?. ?=([%cell * *] node) ~
5495+ =/ left $(node +<.node)
5496+ ?~ left ~
5497+ =/ right $(node +>.node)
5498+ ?~ right ~
5499+ `[[%cell pat.u.left pat.u.right] |(has.u.left has.u.right)]
5500+ %fork
5501+ =/ cases (rec-patterns +.node module name)
5502+ ?~ cases ~
5503+ ?~ pats.u.cases ~
5504+ ?~ t.pats.u.cases ~
5505+ ?. (rec-patterns-disjoint pats.u.cases) ~
5506+ `[[%or pats.u.cases] has.u.cases]
5507+ ==
5508+::
5509+++ rec-patterns
5510+ |= [nodes=* module=term name=term]
5511+ ^- (unit [pats=(list upat) has=?])
5512+ ?@ nodes ?:(=(nodes 0) `[~ |] ~)
5513+ =/ first (rec-pattern -.nodes module name)
5514+ ?~ first ~
5515+ =/ rest $(nodes +.nodes)
5516+ ?~ rest ~
5517+ `[[pat.u.first pats.u.rest] |(has.u.first has.u.rest)]
5518+::
5519+++ rec-patterns-disjoint
5520+ |= pats=(list upat)
5521+ ^- ?
5522+ ?~ pats &
5523+ ?. (levy t.pats |=(p=upat =(%disjoint (union-relate i.pats p)))) |
5524+ $(pats t.pats)
5525+::
5526+::
5408++ import-to-itype
5527++ import-to-itype
5409 |= node=*
5528 |= node=*
5410 ^- (unit itype)
5529 ^- (unit itype)
@@ -5496,7 +5615,14 @@
5496 =/ result=* result.i.found
5615 =/ result=* result.i.found
5497 ?@ result ~
5616 ?@ result ~
5498 ?. ?=(%& -.result) ~
5617 ?. ?=(%& -.result) ~
5499− (import-to-itype +.result)
5618+ =/ node=* +.result
5619+ ?@ node (import-to-itype node)
5620+ ?: ?=(%ref -.node) (import-to-itype node)
5621+ =/ pattern=(unit irec-pattern) (rec-pattern node module name)
5622+ ?~ pattern (import-to-itype node)
5623+ ?. has.u.pattern (import-to-itype node)
5624+ `[%recursive module name `(crip "{(trip name)}.{(trip module)}")]
5625+
5500::
5626::
5501++ collect-sur-imports
5627++ collect-sur-imports
5502 |= imports=(list import)
5628 |= imports=(list import)
@@ -5571,6 +5697,7 @@
5571 ?~ alias ~
5697 ?~ alias ~
5572 `(crip "{(trip u.alias)}.{(trip face)}")
5698 `(crip "{(trip u.alias)}.{(trip face)}")
5573 [%union (turn cases.t |=(c=itype (itype-in-import c iface face))) origin.t label (turn args.t |=(c=itype (itype-in-import c iface face)))]
5699 [%union (turn cases.t |=(c=itype (itype-in-import c iface face))) origin.t label (turn args.t |=(c=itype (itype-in-import c iface face)))]
5700+ %recursive [%recursive module.t name.t ~]
5574 %collection [%collection name.t (turn args.t |=(c=itype (itype-in-import c iface face)))]
5701 %collection [%collection name.t (turn args.t |=(c=itype (itype-in-import c iface face)))]
5575 %face [%face name.t $(t body.t)]
5702 %face [%face name.t $(t body.t)]
5576 %fn [%fn $(t sample.t) $(t product.t)]
5703 %fn [%fn $(t sample.t) $(t product.t)]
@@ -6006,6 +6133,85 @@
6006 [%| ["branch arm does not fit its expected union" ~[[context "expected union"] [(span-of e) "branch arm"]]]]
6133 [%| ["branch arm does not fit its expected union" ~[[context "expected union"] [(span-of e) "branch arm"]]]]
6007 [%& expected state.p.value(sub sub.fit, next next.fit)]
6134 [%& expected state.p.value(sub sub.fit, next next.fit)]
6008::
6135::
6136+:: The supported unit pattern covers exactly an atom zero and a cell
6137+:: headed by zero, with a single binder for its payload. Unresolved
6138+:: subjects and all other pattern matrices stay outside this fragment.
6139+::
6140+++ unit-empty-pattern
6141+ |= p=pattern
6142+ ^- ?
6143+ ?. ?=(%lit -.p) |
6144+ =(%null form.lit.p)
6145+::
6146+++ unit-payload-pattern
6147+ |= p=pattern
6148+ ^- (unit term)
6149+ ?. ?=(%cell -.p) ~
6150+ ?~ items.p ~
6151+ ?~ t.items.p ~
6152+ ?^ t.t.items.p ~
6153+ ?. (unit-empty-pattern i.items.p) ~
6154+ ?. ?=(%bind -.i.t.items.p) ~
6155+ `name.i.t.items.p
6156+::
6157+++ unit-empty-case
6158+ |= t=itype
6159+ ^- ?
6160+ ?. ?=(%const -.t) |
6161+ =(0 val.literal.t)
6162+::
6163+++ unit-payload-case
6164+ |= t=itype
6165+ ^- (unit itype)
6166+ ?. ?=(%cell -.t) ~
6167+ ?. (unit-empty-case hed.t) ~
6168+ =/ payload=itype tal.t
6169+ =? payload ?=(%face -.payload) body.payload
6170+ `payload
6171+::
6172+++ unit-union-payload
6173+ |= t=itype
6174+ ^- (unit itype)
6175+ ?. ?=(%union -.t) ~
6176+ ?~ cases.t ~
6177+ ?~ t.cases.t ~
6178+ ?^ t.t.cases.t ~
6179+ ?: (unit-empty-case i.cases.t) (unit-payload-case i.t.cases.t)
6180+ ?: (unit-empty-case i.t.cases.t) (unit-payload-case i.cases.t)
6181+ ~
6182+::
6183+++ infer-unit-branch
6184+ |= [e=expr env=ienv state=istate]
6185+ ^- iresult
6186+ ?> ?=(%branch -.e)
6187+ ?~ arms.e [%| ["unit branch is not supported by partial inference" ~]]
6188+ ?~ t.arms.e [%| ["unit branch is not supported by partial inference" ~]]
6189+ ?^ t.t.arms.e [%| ["unit branch is not supported by partial inference" ~]]
6190+ =/ x=arm i.arms.e
6191+ =/ y=arm i.t.arms.e
6192+ =/ first-empty=? (unit-empty-pattern pat.x)
6193+ =/ first-name=(unit term) (unit-payload-pattern pat.x)
6194+ =/ second-name=(unit term) (unit-payload-pattern pat.y)
6195+ ?. |(&(first-empty ?=(^ second-name)) &((unit-empty-pattern pat.y) ?=(^ first-name)))
6196+ [%| ["unit branch pattern is not supported by partial inference" ~]]
6197+ =/ binder=(unit term) ?:(first-empty second-name first-name)
6198+ ?~ binder [%| ["unit branch pattern is not supported by partial inference" ~]]
6199+ ?: |(?=(^ (ienv-lookup u.binder env)) (lien globals.state |=(n=term =(n u.binder))))
6200+ [%| ["pattern binder shadows a name" ~]]
6201+ =/ subject=iresult (infer-fragment subject.e env state)
6202+ ?: ?=(%| -.subject) subject
6203+ =/ typ=itype (itype-normalize typ.p.subject sub.state.p.subject)
6204+ =/ payload=(unit itype) (unit-union-payload typ)
6205+ ?~ payload [%| ["unit branch subject is not supported by partial inference" ~]]
6206+ =/ extended=ienv [[u.binder [u.payload ~]] env]
6207+ =/ left=iresult (infer-fragment val.x ?:(first-empty env extended) state.p.subject)
6208+ ?: ?=(%| -.left) left
6209+ =/ right=iresult (infer-fragment val.y ?:(first-empty extended env) state.p.left)
6210+ ?: ?=(%| -.right) right
6211+ =/ joined=(unit isub) (itype-unify typ.p.left typ.p.right sub.state.p.right)
6212+ ?~ joined [%| ["unit branch results need unsupported inference" ~]]
6213+ [%& typ.p.left state.p.right(sub u.joined)]
6214+::
6009++ infer-fragment
6215++ infer-fragment
6010 |= [e=expr env=ienv state=istate]
6216 |= [e=expr env=ienv state=istate]
6011 ^- iresult
6217 ^- iresult
@@ -6073,6 +6279,8 @@
6073 ?^ t.t.arms.e [%| ["branch is not supported by partial inference" ~]]
6279 ?^ t.t.arms.e [%| ["branch is not supported by partial inference" ~]]
6074 =/ x=arm i.arms.e
6280 =/ x=arm i.arms.e
6075 =/ y=arm i.t.arms.e
6281 =/ y=arm i.t.arms.e
6282+ ?: |((unit-empty-pattern pat.x) (unit-empty-pattern pat.y))
6283+ (infer-unit-branch e env state)
6076 ?. ?=(%lit -.pat.x) [%| ["branch pattern is not supported by partial inference" ~]]
6284 ?. ?=(%lit -.pat.x) [%| ["branch pattern is not supported by partial inference" ~]]
6077 ?. ?=(%lit -.pat.y) [%| ["branch pattern is not supported by partial inference" ~]]
6285 ?. ?=(%lit -.pat.y) [%| ["branch pattern is not supported by partial inference" ~]]
6078 ?. &(=(%loobean form.lit.pat.x) =(%loobean form.lit.pat.y))
6286 ?. &(=(%loobean form.lit.pat.x) =(%loobean form.lit.pat.y))
@@ -6093,6 +6301,8 @@
6093 [%| ["branch widening is not supported by partial inference" ~]]
6301 [%| ["branch widening is not supported by partial inference" ~]]
6094 =/ joined=(unit isub) (itype-unify typ.p.left typ.p.right sub.state.p.right)
6302 =/ joined=(unit isub) (itype-unify typ.p.left typ.p.right sub.state.p.right)
6095 ?~ joined
6303 ?~ joined
6304+ ?: |((itype-has-recursive ltype) (itype-has-recursive rtype))
6305+ [%| ["recursive branch matching is not supported by partial inference" ~]]
6096 ?: |((itype-has-tag ltype) (itype-has-tag rtype) (itype-has-union ltype) (itype-has-union rtype))
6306 ?: |((itype-has-tag ltype) (itype-has-tag rtype) (itype-has-union ltype) (itype-has-union rtype))
6097 [%| ["branch tag or union context is not supported by partial inference" ~]]
6307 [%| ["branch tag or union context is not supported by partial inference" ~]]
6098 =/ common=(unit itype) (itype-concrete-join typ.p.left typ.p.right sub.state.p.right)
6308 =/ common=(unit itype) (itype-concrete-join typ.p.left typ.p.right sub.state.p.right)
@@ -6197,6 +6407,8 @@
6197 =/ subject=iresult (infer-fragment subject.e env state)
6407 =/ subject=iresult (infer-fragment subject.e env state)
6198 ?: ?=(%| -.subject) subject
6408 ?: ?=(%| -.subject) subject
6199 =/ st=istate state.p.subject
6409 =/ st=istate state.p.subject
6410+ ?: (itype-has-recursive (itype-normalize typ.p.subject sub.st))
6411+ [%| ["recursive pattern is not supported by partial inference" ~]]
6200 ?: (itype-has-face typ.p.subject sub.st)
6412 ?: (itype-has-face typ.p.subject sub.st)
6201 [%| ["record patterns are not supported by partial inference" ~]]
6413 [%| ["record patterns are not supported by partial inference" ~]]
6202 =/ head=itype [%var next.st]
6414 =/ head=itype [%var next.st]
@@ -6228,6 +6440,7 @@
6228 ?~ label.t (show-itype-cases cases.t sub)
6440 ?~ label.t (show-itype-cases cases.t sub)
6229 ?~ args.t (trip u.label.t)
6441 ?~ args.t (trip u.label.t)
6230 "({(trip u.label.t)} {(show-itype-cases args.t sub)})"
6442 "({(trip u.label.t)} {(show-itype-cases args.t sub)})"
6443+ %recursive ?~(label.t "recursive mold" (trip u.label.t))
6231 %collection "({(trip name.t)} {(show-itype-cases args.t sub)})"
6444 %collection "({(trip name.t)} {(show-itype-cases args.t sub)})"
6232 %face "[{(trip name.t)}={(show-itype-context body.t sub)}]"
6445 %face "[{(trip name.t)}={(show-itype-context body.t sub)}]"
6233 %fn "$-({(show-itype-context sample.t sub)} {(show-itype-context product.t sub)})"
6446 %fn "$-({(show-itype-context sample.t sub)} {(show-itype-context product.t sub)})"
@@ -6271,6 +6484,7 @@
6271 %tag |
6484 %tag |
6272 %const |
6485 %const |
6273 %union |((lien cases.typ |=(c=itype (itype-has-nvar c))) (lien args.typ |=(c=itype (itype-has-nvar c))))
6486 %union |((lien cases.typ |=(c=itype (itype-has-nvar c))) (lien args.typ |=(c=itype (itype-has-nvar c))))
6487+ %recursive |
6274 %collection (lien args.typ |=(c=itype (itype-has-nvar c)))
6488 %collection (lien args.typ |=(c=itype (itype-has-nvar c)))
6275 %face $(typ body.typ)
6489 %face $(typ body.typ)
6276 %fn |($(typ sample.typ) $(typ product.typ))
6490 %fn |($(typ sample.typ) $(typ product.typ))
@@ -6282,6 +6496,7 @@
6282 ^- tape
6496 ^- tape
6283 =/ normalized=itype (itype-normalize typ sub)
6497 =/ normalized=itype (itype-normalize typ sub)
6284 ?: |(?=(%var -.normalized) ?=(%avar -.normalized) (itype-has-nvar normalized) (itype-has-tag normalized) (itype-has-union normalized)) label
6498 ?: |(?=(%var -.normalized) ?=(%avar -.normalized) (itype-has-nvar normalized) (itype-has-tag normalized) (itype-has-union normalized)) label
6499+ ?: (itype-has-hidden-recursive normalized) label
6285 =/ vars=(list @ud) (unique-type-vars (itype-vars normalized))
6500 =/ vars=(list @ud) (unique-type-vars (itype-vars normalized))
6286 ?: (gth (lent vars) 26) label
6501 ?: (gth (lent vars) 26) label
6287 =/ printable=itype (canonical-itype normalized vars)
6502 =/ printable=itype (canonical-itype normalized vars)
@@ -6348,6 +6563,7 @@
6348 %tag |
6563 %tag |
6349 %const |
6564 %const |
6350 %union |((lien cases.t |=(c=itype (has-atom-var n c))) (lien args.t |=(c=itype (has-atom-var n c))))
6565 %union |((lien cases.t |=(c=itype (has-atom-var n c))) (lien args.t |=(c=itype (has-atom-var n c))))
6566+ %recursive |
6351 %collection (lien args.t |=(c=itype (has-atom-var n c)))
6567 %collection (lien args.t |=(c=itype (has-atom-var n c)))
6352 %face $(t body.t)
6568 %face $(t body.t)
6353 %fn |($(t sample.t) $(t product.t))
6569 %fn |($(t sample.t) $(t product.t))
@@ -6820,6 +7036,8 @@
6820 $(ds t.ds, lines [(crip "++ {(trip name.d)}: `~` needs context in partial inference") lines])
7036 $(ds t.ds, lines [(crip "++ {(trip name.d)}: `~` needs context in partial inference") lines])
6821 ?: |((pending-tags tags.state.p.result sub.state.p.result) (itype-has-tag typ))
7037 ?: |((pending-tags tags.state.p.result sub.state.p.result) (itype-has-tag typ))
6822 $(ds t.ds, lines [(crip "++ {(trip name.d)}: tag needs context in partial inference") lines])
7038 $(ds t.ds, lines [(crip "++ {(trip name.d)}: tag needs context in partial inference") lines])
7039+ ?: (itype-has-hidden-recursive typ)
7040+ $(ds t.ds, lines [(crip "++ {(trip name.d)}: recursive mold name is not available in this scope") lines])
6823 ?: (itype-has-hidden-generic typ)
7041 ?: (itype-has-hidden-generic typ)
6824 $(ds t.ds, lines [(crip "++ {(trip name.d)}: generic union name is not available in this scope") lines])
7042 $(ds t.ds, lines [(crip "++ {(trip name.d)}: generic union name is not available in this scope") lines])
6825 ?: (unsupported-atom-vars typ caps.state.p.result binders.state.p.result sub.state.p.result)
7043 ?: (unsupported-atom-vars typ caps.state.p.result binders.state.p.result sub.state.p.result)
tests/clay-fixtures/ford-nested-union-bridge-expected.txt+1−1
@@ -1 +1 @@
1−'partial inference; module not checked\0a++ value: $-([id=@ud value=([a=@ud b=@t] @ud)] ([a=@ud b=@t] @ud))\0a++ values: $-([title=@t values=(list @ud)] (list @ud))'
1+'partial inference; module not checked\0a++ value: $-([id=@ud value=([a=@ud b=@t] @ud)] ([a=@ud b=@t] @ud))\0a++ values: $-([title=@t values=(list @ud)] (list @ud))\0a++ chain: $-(chain.blog chain.blog)'
tests/clay-fixtures/ford-nested-union-bridge.txt+4−2
@@ -2,11 +2,13 @@
2;< =bowl:spider bind:m get-bowl
2;< =bowl:spider bind:m get-bowl
3=/ now=@da now.bowl
3=/ now=@da now.bowl
4=/ src=vase .^(vase %ca /[(scot %p our.bowl)]/my-app/[(scot %da now)]/sur/import-fixture/hoon)
4=/ src=vase .^(vase %ca /[(scot %p our.bowl)]/my-app/[(scot %da now)]/sur/import-fixture/hoon)
5+=/ blog=vase .^(vase %ca /[(scot %p our.bowl)]/my-app/[(scot %da now)]/sur/blog/hoon)
5=/ reader=vase .^(vase %ca /[(scot %p our.bowl)]/my-app/[(scot %da now)]/lib/jibb-import/hoon)
6=/ reader=vase .^(vase %ca /[(scot %p our.bowl)]/my-app/[(scot %da now)]/lib/jibb-import/hoon)
6=/ jibb=vase .^(vase %ca /[(scot %p our.bowl)]/my-app/[(scot %da now)]/lib/jibb/hoon)
7=/ jibb=vase .^(vase %ca /[(scot %p our.bowl)]/my-app/[(scot %da now)]/lib/jibb/hoon)
7=/ read-gate=vase (slap reader (ream 'read-named'))
8=/ read-gate=vase (slap reader (ream 'read-named'))
8=/ result=vase (slym read-gate [%import-fixture %nested -.src])
9=/ result=vase (slym read-gate [%import-fixture %nested -.src])
9=/ sample-result=vase (slym read-gate [%import-fixture %sample -.src])
10=/ sample-result=vase (slym read-gate [%import-fixture %sample -.src])
11+=/ chain-result=vase (slym read-gate [%blog %chain -.blog])
10=/ check-gate=vase (slap jibb (ream 'golden-types-imports'))
12=/ check-gate=vase (slap jibb (ream 'golden-types-imports'))
11−=/ text=@t '/- import-fixture\0a++ value |= x=nested.import-fixture value.x\0a++ values |= x=sample.import-fixture values.x\0a'
13+=/ text=@t '/- import-fixture\0a/- blog\0a++ value |= x=nested.import-fixture value.x\0a++ values |= x=sample.import-fixture values.x\0a++ chain |= x=chain.blog x\0a'
12−(pure:m (slym check-gate [/ui/test/jibb text ~[[%import-fixture %nested +.result] [%import-fixture %sample +.sample-result]]]))
14+(pure:m (slym check-gate [/ui/test/jibb text ~[[%import-fixture %nested +.result] [%import-fixture %sample +.sample-result] [%blog %chain +.chain-result]]]))
tests/golden/prelude-1c-structural/types.txt+2−2
@@ -5,8 +5,8 @@ partial inference; module not checked
5++ not: $-(? ?)
5++ not: $-(? ?)
6+$ list: cases proven disjoint; mold not checked
6+$ list: cases proven disjoint; mold not checked
7+$ unit: cases proven disjoint; mold not checked
7+$ unit: cases proven disjoint; mold not checked
8−++ fall: branch pattern is not supported by partial inference
8+++ fall: $-([(unit A) A] A)
9−++ need: branch pattern is not supported by partial inference
9+++ need: $-((unit A) A)
10++ weld: sample mold is not supported by partial inference
10++ weld: sample mold is not supported by partial inference
11++ turn: sample mold is not supported by partial inference
11++ turn: sample mold is not supported by partial inference
12+$ dime: not checked
12+$ dime: not checked
tests/golden/prelude-3g-basic/types.txt+2−2
@@ -5,8 +5,8 @@ partial inference; module not checked
5++ not: $-(? ?)
5++ not: $-(? ?)
6+$ list: cases proven disjoint; mold not checked
6+$ list: cases proven disjoint; mold not checked
7+$ unit: cases proven disjoint; mold not checked
7+$ unit: cases proven disjoint; mold not checked
8−++ fall: branch pattern is not supported by partial inference
8+++ fall: $-([(unit A) A] A)
9−++ need: branch pattern is not supported by partial inference
9+++ need: $-((unit A) A)
10++ weld: sample mold is not supported by partial inference
10++ weld: sample mold is not supported by partial inference
11++ turn: sample mold is not supported by partial inference
11++ turn: sample mold is not supported by partial inference
12+$ dime: not checked
12+$ dime: not checked
tests/golden/prelude-3g-like/types.txt+2−2
@@ -5,8 +5,8 @@ partial inference; module not checked
5++ not: $-(? ?)
5++ not: $-(? ?)
6+$ list: cases proven disjoint; mold not checked
6+$ list: cases proven disjoint; mold not checked
7+$ unit: cases proven disjoint; mold not checked
7+$ unit: cases proven disjoint; mold not checked
8−++ fall: branch pattern is not supported by partial inference
8+++ fall: $-([(unit A) A] A)
9−++ need: branch pattern is not supported by partial inference
9+++ need: $-((unit A) A)
10++ weld: sample mold is not supported by partial inference
10++ weld: sample mold is not supported by partial inference
11++ turn: sample mold is not supported by partial inference
11++ turn: sample mold is not supported by partial inference
12+$ dime: not checked
12+$ dime: not checked
tests/golden/prelude-3g-structural/types.txt+2−2
@@ -5,8 +5,8 @@ partial inference; module not checked
5++ not: $-(? ?)
5++ not: $-(? ?)
6+$ list: cases proven disjoint; mold not checked
6+$ list: cases proven disjoint; mold not checked
7+$ unit: cases proven disjoint; mold not checked
7+$ unit: cases proven disjoint; mold not checked
8−++ fall: branch pattern is not supported by partial inference
8+++ fall: $-([(unit A) A] A)
9−++ need: branch pattern is not supported by partial inference
9+++ need: $-((unit A) A)
10++ weld: sample mold is not supported by partial inference
10++ weld: sample mold is not supported by partial inference
11++ turn: sample mold is not supported by partial inference
11++ turn: sample mold is not supported by partial inference
12+$ dime: not checked
12+$ dime: not checked
tests/golden/prelude-list-helpers/types.txt+2−2
@@ -5,8 +5,8 @@ partial inference; module not checked
5++ not: $-(? ?)
5++ not: $-(? ?)
6+$ list: cases proven disjoint; mold not checked
6+$ list: cases proven disjoint; mold not checked
7+$ unit: cases proven disjoint; mold not checked
7+$ unit: cases proven disjoint; mold not checked
8−++ fall: branch pattern is not supported by partial inference
8+++ fall: $-([(unit A) A] A)
9−++ need: branch pattern is not supported by partial inference
9+++ need: $-((unit A) A)
10++ weld: sample mold is not supported by partial inference
10++ weld: sample mold is not supported by partial inference
11++ turn: sample mold is not supported by partial inference
11++ turn: sample mold is not supported by partial inference
12+$ dime: not checked
12+$ dime: not checked
tests/golden/prelude-list-unit/types.txt+2−2
@@ -5,7 +5,7 @@ partial inference; module not checked
5++ not: $-(? ?)
5++ not: $-(? ?)
6+$ list: cases proven disjoint; mold not checked
6+$ list: cases proven disjoint; mold not checked
7+$ unit: cases proven disjoint; mold not checked
7+$ unit: cases proven disjoint; mold not checked
8−++ fall: branch pattern is not supported by partial inference
8+++ fall: $-([(unit A) A] A)
9−++ need: branch pattern is not supported by partial inference
9+++ need: $-((unit A) A)
10++ weld: sample mold is not supported by partial inference
10++ weld: sample mold is not supported by partial inference
11++ turn: sample mold is not supported by partial inference
11++ turn: sample mold is not supported by partial inference
tests/golden/prelude-unit-list-transforms/types.txt+6−6
@@ -5,8 +5,8 @@ partial inference; module not checked
5++ not: $-(? ?)
5++ not: $-(? ?)
6+$ list: cases proven disjoint; mold not checked
6+$ list: cases proven disjoint; mold not checked
7+$ unit: cases proven disjoint; mold not checked
7+$ unit: cases proven disjoint; mold not checked
8−++ fall: branch pattern is not supported by partial inference
8+++ fall: $-([(unit A) A] A)
9−++ need: branch pattern is not supported by partial inference
9+++ need: $-((unit A) A)
10++ weld: sample mold is not supported by partial inference
10++ weld: sample mold is not supported by partial inference
11++ turn: sample mold is not supported by partial inference
11++ turn: sample mold is not supported by partial inference
12+$ dime: not checked
12+$ dime: not checked
@@ -39,10 +39,10 @@ partial inference; module not checked
39++ lure: cast mold is not supported by partial inference
39++ lure: cast mold is not supported by partial inference
40++ snoc: sample mold is not supported by partial inference
40++ snoc: sample mold is not supported by partial inference
41++ flop: sample mold is not supported by partial inference
41++ flop: sample mold is not supported by partial inference
42−++ drop: branch pattern is not supported by partial inference
42+++ drop: unit branch results need unsupported inference
43−++ bind: branch pattern is not supported by partial inference
43+++ bind: expression is not supported by partial inference
44−++ biff: branch pattern is not supported by partial inference
44+++ biff: unit branch results need unsupported inference
45−++ both: branch pattern is not supported by partial inference
45+++ both: expression is not supported by partial inference
46++ clef: branch pattern is not supported by partial inference
46++ clef: branch pattern is not supported by partial inference
47++ skim: sample mold is not supported by partial inference
47++ skim: sample mold is not supported by partial inference
48++ skip: sample mold is not supported by partial inference
48++ skip: sample mold is not supported by partial inference
tests/golden/types-unit-patterns/about.txtnew file+4
@@ -0,0 +1,4 @@
1+source: Grammar, Patterns: "Every case needs an arm or a catch-all" and "A name in a pattern always binds anew"; Modules: no name may shadow one already in scope.
2+tests: A known unit-shaped union is matched in either arm order; shadowed binders, unknown subjects, missing or duplicate arms, unresolved result tags and a discarded unrelated tag are not silently accepted.
3+expect: parses
4+expect-types: a partial inference report
tests/golden/types-unit-patterns/source.jibbnew file+36
@@ -0,0 +1,36 @@
1++$ maybe
2+ ~
3+ [~ value=@t]
4+++ need |= x=maybe
5+ ?- x
6+ ~ !!
7+ [~ value] value
8+++ reverse |= x=maybe
9+ ?- x
10+ [~ value] value
11+ ~ !!
12+++ shadow |= x=maybe
13+ ?- x
14+ ~ !!
15+ [~ x] x
16+++ unknown |= x
17+ ?- x
18+ ~ !!
19+ [~ value] value
20+++ missing |= x=maybe
21+ ?- x
22+ ~ !!
23+++ duplicate |= x=maybe
24+ ?- x
25+ ~ !!
26+ ~ !!
27+ [~ value] value
28+++ pending |= x=maybe
29+ ?- x
30+ ~ %none
31+ [~ value] %some
32+++ unrelated |= x=maybe
33+ ~& %unknown
34+ ?- x
35+ ~ !!
36+ [~ value] value
tests/golden/types-unit-patterns/tokens.txtnew file+180
@@ -0,0 +1,180 @@
1+1:1 glyph +$
2+1:4 name maybe
3+2:3 INDENT
4+2:3 lit ~
5+3:3 NEWLINE
6+3:3 glyph [
7+3:4 tight lit ~
8+3:6 name value
9+3:11 tight glyph =
10+3:12 tight aura @t
11+3:14 tight glyph ]
12+4:1 DEDENT
13+4:1 NEWLINE
14+4:1 glyph ++
15+4:4 name need
16+4:9 glyph |=
17+4:12 name x
18+4:13 tight glyph =
19+4:14 tight name maybe
20+5:3 INDENT
21+5:3 glyph ?-
22+5:6 name x
23+6:5 INDENT
24+6:5 lit ~
25+6:7 glyph !!
26+7:5 NEWLINE
27+7:5 glyph [
28+7:6 tight lit ~
29+7:8 name value
30+7:13 tight glyph ]
31+7:15 name value
32+8:1 DEDENT
33+8:1 DEDENT
34+8:1 NEWLINE
35+8:1 glyph ++
36+8:4 name reverse
37+8:12 glyph |=
38+8:15 name x
39+8:16 tight glyph =
40+8:17 tight name maybe
41+9:3 INDENT
42+9:3 glyph ?-
43+9:6 name x
44+10:5 INDENT
45+10:5 glyph [
46+10:6 tight lit ~
47+10:8 name value
48+10:13 tight glyph ]
49+10:15 name value
50+11:5 NEWLINE
51+11:5 lit ~
52+11:7 glyph !!
53+12:1 DEDENT
54+12:1 DEDENT
55+12:1 NEWLINE
56+12:1 glyph ++
57+12:4 name shadow
58+12:11 glyph |=
59+12:14 name x
60+12:15 tight glyph =
61+12:16 tight name maybe
62+13:3 INDENT
63+13:3 glyph ?-
64+13:6 name x
65+14:5 INDENT
66+14:5 lit ~
67+14:7 glyph !!
68+15:5 NEWLINE
69+15:5 glyph [
70+15:6 tight lit ~
71+15:8 name x
72+15:9 tight glyph ]
73+15:11 name x
74+16:1 DEDENT
75+16:1 DEDENT
76+16:1 NEWLINE
77+16:1 glyph ++
78+16:4 name unknown
79+16:12 glyph |=
80+16:15 name x
81+17:3 INDENT
82+17:3 glyph ?-
83+17:6 name x
84+18:5 INDENT
85+18:5 lit ~
86+18:7 glyph !!
87+19:5 NEWLINE
88+19:5 glyph [
89+19:6 tight lit ~
90+19:8 name value
91+19:13 tight glyph ]
92+19:15 name value
93+20:1 DEDENT
94+20:1 DEDENT
95+20:1 NEWLINE
96+20:1 glyph ++
97+20:4 name missing
98+20:12 glyph |=
99+20:15 name x
100+20:16 tight glyph =
101+20:17 tight name maybe
102+21:3 INDENT
103+21:3 glyph ?-
104+21:6 name x
105+22:5 INDENT
106+22:5 lit ~
107+22:7 glyph !!
108+23:1 DEDENT
109+23:1 DEDENT
110+23:1 NEWLINE
111+23:1 glyph ++
112+23:4 name duplicate
113+23:14 glyph |=
114+23:17 name x
115+23:18 tight glyph =
116+23:19 tight name maybe
117+24:3 INDENT
118+24:3 glyph ?-
119+24:6 name x
120+25:5 INDENT
121+25:5 lit ~
122+25:7 glyph !!
123+26:5 NEWLINE
124+26:5 lit ~
125+26:7 glyph !!
126+27:5 NEWLINE
127+27:5 glyph [
128+27:6 tight lit ~
129+27:8 name value
130+27:13 tight glyph ]
131+27:15 name value
132+28:1 DEDENT
133+28:1 DEDENT
134+28:1 NEWLINE
135+28:1 glyph ++
136+28:4 name pending
137+28:12 glyph |=
138+28:15 name x
139+28:16 tight glyph =
140+28:17 tight name maybe
141+29:3 INDENT
142+29:3 glyph ?-
143+29:6 name x
144+30:5 INDENT
145+30:5 lit ~
146+30:7 lit %none
147+31:5 NEWLINE
148+31:5 glyph [
149+31:6 tight lit ~
150+31:8 name value
151+31:13 tight glyph ]
152+31:15 lit %some
153+32:1 DEDENT
154+32:1 DEDENT
155+32:1 NEWLINE
156+32:1 glyph ++
157+32:4 name unrelated
158+32:14 glyph |=
159+32:17 name x
160+32:18 tight glyph =
161+32:19 tight name maybe
162+33:3 INDENT
163+33:3 glyph ~&
164+33:6 lit %unknown
165+34:5 INDENT
166+34:5 glyph ?-
167+34:8 name x
168+35:7 INDENT
169+35:7 lit ~
170+35:9 glyph !!
171+36:7 NEWLINE
172+36:7 glyph [
173+36:8 tight lit ~
174+36:10 name value
175+36:15 tight glyph ]
176+36:17 name value
177+36:22 DEDENT
178+36:22 DEDENT
179+36:22 DEDENT
180+36:22 NEWLINE
tests/golden/types-unit-patterns/tree.txtnew file+88
@@ -0,0 +1,88 @@
1+(module
2+ (mold 1:1 maybe
3+ (union 2:3
4+ (const 2:3 ~)
5+ (cell 3:3
6+ (const 3:4 ~)
7+ (face 3:6 value (aura 3:12 @t)))))
8+ (value 4:1 need
9+ (gate 4:9
10+ (face 4:12 x (ref 4:14 maybe))
11+ (body 5:3
12+ (branch 5:3
13+ (wing 5:6 x)
14+ (arm 6:5 (lit 6:5 ~) (crash 6:7))
15+ (arm 7:5
16+ (cell 7:5 (lit 7:6 ~) (bind 7:8 value))
17+ (wing 7:15 value))))))
18+ (value 8:1 reverse
19+ (gate 8:12
20+ (face 8:15 x (ref 8:17 maybe))
21+ (body 9:3
22+ (branch 9:3
23+ (wing 9:6 x)
24+ (arm 10:5
25+ (cell 10:5 (lit 10:6 ~) (bind 10:8 value))
26+ (wing 10:15 value))
27+ (arm 11:5 (lit 11:5 ~) (crash 11:7))))))
28+ (value 12:1 shadow
29+ (gate 12:11
30+ (face 12:14 x (ref 12:16 maybe))
31+ (body 13:3
32+ (branch 13:3
33+ (wing 13:6 x)
34+ (arm 14:5 (lit 14:5 ~) (crash 14:7))
35+ (arm 15:5
36+ (cell 15:5 (lit 15:6 ~) (bind 15:8 x))
37+ (wing 15:11 x))))))
38+ (value 16:1 unknown
39+ (gate 16:12
40+ (bind 16:15 x)
41+ (body 17:3
42+ (branch 17:3
43+ (wing 17:6 x)
44+ (arm 18:5 (lit 18:5 ~) (crash 18:7))
45+ (arm 19:5
46+ (cell 19:5 (lit 19:6 ~) (bind 19:8 value))
47+ (wing 19:15 value))))))
48+ (value 20:1 missing
49+ (gate 20:12
50+ (face 20:15 x (ref 20:17 maybe))
51+ (body 21:3
52+ (branch 21:3
53+ (wing 21:6 x)
54+ (arm 22:5 (lit 22:5 ~) (crash 22:7))))))
55+ (value 23:1 duplicate
56+ (gate 23:14
57+ (face 23:17 x (ref 23:19 maybe))
58+ (body 24:3
59+ (branch 24:3
60+ (wing 24:6 x)
61+ (arm 25:5 (lit 25:5 ~) (crash 25:7))
62+ (arm 26:5 (lit 26:5 ~) (crash 26:7))
63+ (arm 27:5
64+ (cell 27:5 (lit 27:6 ~) (bind 27:8 value))
65+ (wing 27:15 value))))))
66+ (value 28:1 pending
67+ (gate 28:12
68+ (face 28:15 x (ref 28:17 maybe))
69+ (body 29:3
70+ (branch 29:3
71+ (wing 29:6 x)
72+ (arm 30:5 (lit 30:5 ~) (lit 30:7 %none))
73+ (arm 31:5
74+ (cell 31:5 (lit 31:6 ~) (bind 31:8 value))
75+ (lit 31:15 %some))))))
76+ (value 32:1 unrelated
77+ (gate 32:14
78+ (face 32:17 x (ref 32:19 maybe))
79+ (body 33:3
80+ (trace 33:3
81+ (lit 33:6 %unknown)
82+ (body 34:5
83+ (branch 34:5
84+ (wing 34:8 x)
85+ (arm 35:7 (lit 35:7 ~) (crash 35:9))
86+ (arm 36:7
87+ (cell 36:7 (lit 36:8 ~) (bind 36:10 value))
88+ (wing 36:17 value)))))))))
tests/golden/types-unit-patterns/types.txtnew file+10
@@ -0,0 +1,10 @@
1+partial inference; module not checked
2++$ maybe: cases proven disjoint; mold not checked
3+++ need: $-(maybe @t)
4+++ reverse: $-(maybe @t)
5+++ shadow: pattern binder shadows a name
6+++ unknown: unit branch subject is not supported by partial inference
7+++ missing: branch pattern is not supported by partial inference
8+++ duplicate: branch is not supported by partial inference
9+++ pending: unit branch results need unsupported inference
10+++ unrelated: tag needs context in partial inference
tests/lib/jibb-bridge.hoon+14−1
@@ -31,6 +31,14 @@
31 [%fixture %nested (read %nested)]
31 [%fixture %nested (read %nested)]
32 [%fixture %constant (read %constant)]
32 [%fixture %constant (read %constant)]
33 [%fixture %choice (read %choice)]
33 [%fixture %choice (read %choice)]
34+ [%fixture %chain (read %chain)]
35+ [%second %chain (read-named:jibb-import %second %chain cor)]
36+ [%fixture %other (read %other)]
37+ [%fixture %alias (read %alias)]
38+ [%fixture %foreign-ref [%& [%cell [%atom %ud ~] [%ref %other %foreign-ref]]]]
39+ [%fixture %overlap-ref [%& [%fork ~[[%atom %n [~ 0]] [%atom %tas [~ 0]] [%cell [%atom %ud ~] [%ref %fixture %overlap-ref]]]]]]
40+ [%fixture %raw-ref [%& [%ref %fixture %raw-ref]]]
41+ [%fixture %raw-null [%& [%atom %n [~ 0]]]]
34 [%fixture %texts (read %texts)]
42 [%fixture %texts (read %texts)]
35 [%fixture %numbers (read %numbers)]
43 [%fixture %numbers (read %numbers)]
36 [%fixture %maybe (read %maybe)]
44 [%fixture %maybe (read %maybe)]
@@ -56,5 +64,10 @@
56 [%missing %title [%& [%atom %t ~]]]
64 [%missing %title [%& [%atom %t ~]]]
57 ==
65 ==
58 =/ report=@t (golden-types-imports:jibb /test/jibb src external)
66 =/ report=@t (golden-types-imports:jibb /test/jibb src external)
67+ =/ dep=@t (crip "/- fixture\0a++ chain-id |= x=chain.fixture x\0a")
68+ =/ root=@t (crip "/= helper /ui/helper\0a++ use chain-id.helper\0a")
69+ =/ prelude=@t (crip "++ prelude-id |= x x\0a")
70+ =/ modules=(list [at=path source=@t]) ~[[~[%ui %helper %jibb] dep]]
71+ =/ hidden=@t (golden-types-with-prelude:jibb /ui/main/jibb root external ~ modules prelude)
59− (cat 3 report (crip "\0areader-collapsed: {(show-result:jibb-import (read %collapsed))}"))
72+ (cat 3 report (cat 3 (crip "\0areader-collapsed: {(show-result:jibb-import (read %collapsed))}\0a") hidden))
60--
73--
tests/lib/jibb.hoon+6
@@ -1722,6 +1722,10 @@
1722/* tok-493 %txt /tests/golden/types-union-unresolved-flow/tokens/txt
1722/* tok-493 %txt /tests/golden/types-union-unresolved-flow/tokens/txt
1723/* tre-493 %txt /tests/golden/types-union-unresolved-flow/tree/txt
1723/* tre-493 %txt /tests/golden/types-union-unresolved-flow/tree/txt
1724/* typ-493 %txt /tests/golden/types-union-unresolved-flow/types/txt
1724/* typ-493 %txt /tests/golden/types-union-unresolved-flow/types/txt
1725+/* src-494 %jibb /tests/golden/types-unit-patterns/source/jibb
1726+/* tok-494 %txt /tests/golden/types-unit-patterns/tokens/txt
1727+/* tre-494 %txt /tests/golden/types-unit-patterns/tree/txt
1728+/* typ-494 %txt /tests/golden/types-unit-patterns/types/txt
1725|%
1729|%
1726:: one arm per case
1730:: one arm per case
1727::
1731::
@@ -2711,6 +2715,8 @@
2711 (weld (check ~.types-union-unresolved src-492 `tok-492 `tre-492) (check-types ~.types-union-unresolved ~[%tests %golden ~.types-union-unresolved %source %jibb] src-492 `typ-492))
2715 (weld (check ~.types-union-unresolved src-492 `tok-492 `tre-492) (check-types ~.types-union-unresolved ~[%tests %golden ~.types-union-unresolved %source %jibb] src-492 `typ-492))
2712++ test-types-union-unresolved-flow
2716++ test-types-union-unresolved-flow
2713 (weld (check ~.types-union-unresolved-flow src-493 `tok-493 `tre-493) (check-types ~.types-union-unresolved-flow ~[%tests %golden ~.types-union-unresolved-flow %source %jibb] src-493 `typ-493))
2717 (weld (check ~.types-union-unresolved-flow src-493 `tok-493 `tre-493) (check-types ~.types-union-unresolved-flow ~[%tests %golden ~.types-union-unresolved-flow %source %jibb] src-493 `typ-493))
2718+++ test-types-unit-patterns
2719+ (weld (check ~.types-unit-patterns src-494 `tok-494 `tre-494) (check-types ~.types-unit-patterns ~[%tests %golden ~.types-unit-patterns %source %jibb] src-494 `typ-494))
2714::
2720::
2715:: +check: run golden:jibb on a case and compare both outputs
2721:: +check: run golden:jibb on a case and compare both outputs
2716::
2722::
tests/type-import/about.txt+14−11
@@ -2,14 +2,17 @@ The checker accepts unchanged, successful results from the source-aware Hoon
2reader as a trusted boundary. It independently rejects malformed graphs and
2reader as a trusted boundary. It independently rejects malformed graphs and
3forks whose supplied cases overlap, but a graph alone cannot reveal source
3forks whose supplied cases overlap, but a graph alone cannot reveal source
4cases Hoon already collapsed. This fixture compiles a small /sur against zuse,
4cases Hoon already collapsed. This fixture compiles a small /sur against zuse,
5−selects its molds, and passes the actual reader results to the inference
5+selects its molds, and passes the actual reader results to inference. It checks
6−fragment. It checks fixed atoms, valid term singletons including Hoon `%$`,
6+fixed atoms, term singletons including Hoon `%$`, nested faced records,
7−nested faced records, unnamed unions, and opaque standard collections.
7+unnamed unions and opaque standard collections. It covers matching and
8−Matching and mismatched collections, nested union arguments, and malformed
8+mismatched collections, nested union arguments, and malformed collection names
9−collection names and arities are covered; Jibb-written collection molds and
9+and arities. Jibb-written collection molds remain unsupported.
10−recursive references remain unsupported. Forged
10+
11−unsupported, malformed, singleton and overlapping forks are rejected; the
11+A self-recursive /sur mold is an opaque module/name identity. Distinct names
12+or modules conflict; aliases, foreign references, unfolding and out-of-scope
13+names remain unsupported. The fixture checks these limits as well as malformed
14+references and overlapping recursive graphs. The reader itself rejects an
12−reader itself rejects an overlapping source union before Hoon can collapse it.
15+overlapping source union before Hoon can collapse it. A missing import and a
13−A missing import and a duplicate supplied mold fail closed. The types output
16+duplicate supplied mold fail closed. The types output is partial and does not
14−is partial and does not claim that the module was checked. The same fixture
17+claim that the module was checked. The same fixture runs under urbit eval and
15−runs under urbit eval and through -test on the fake ship.
18+through -test on the fake ship.
tests/type-import/expected.txt+36−8
@@ -11,19 +11,19 @@ partial inference; module not checked
11++ undeclared: sample mold is not supported by partial inference
11++ undeclared: sample mold is not supported by partial inference
12++ yes: %no %yes
12++ yes: %no %yes
13++ absent: cast mold and expression type do not unify
13++ absent: cast mold and expression type do not unify
14−/test.jibb:13:14: cast mold
14+/test.jibb:14:14: cast mold
15++ absent ^- choice.fixture %other
15++ absent ^- choice.fixture %other
16 ^
16 ^
17−/test.jibb:13:29: expression
17+/test.jibb:14:29: expression
18++ absent ^- choice.fixture %other
18++ absent ^- choice.fixture %other
19 ^
19 ^
20++ status: $-([status=(%deny %approve) text=@t] (%deny %approve))
20++ status: $-([status=(%deny %approve) text=@t] (%deny %approve))
21++ nested-yes: [status=(%deny %approve) text=@t]
21++ nested-yes: [status=(%deny %approve) text=@t]
22++ nested-absent: cast mold and expression type do not unify
22++ nested-absent: cast mold and expression type do not unify
23−/test.jibb:16:21: cast mold
23+/test.jibb:17:21: cast mold
24++ nested-absent ^- nested-choice.fixture [%other "hi"]
24++ nested-absent ^- nested-choice.fixture [%other "hi"]
25 ^
25 ^
26−/test.jibb:16:43: expression
26+/test.jibb:17:43: expression
27++ nested-absent ^- nested-choice.fixture [%other "hi"]
27++ nested-absent ^- nested-choice.fixture [%other "hi"]
28 ^
28 ^
29++ overlap: sample mold is not supported by partial inference
29++ overlap: sample mold is not supported by partial inference
@@ -40,17 +40,17 @@ partial inference; module not checked
40++ get-items: $-([items=(list @t) slots=(unit @ud)] (list @t))
40++ get-items: $-([items=(list @t) slots=(unit @ud)] (list @t))
41++ same-list: $-((list @t) (list @t))
41++ same-list: $-((list @t) (list @t))
42++ wrong-item: call's sample and argument types do not unify
42++ wrong-item: call's sample and argument types do not unify
43−/test.jibb:30:37: called gate has type $-((list @t) (list @t))
43+/test.jibb:31:37: called gate has type $-((list @t) (list @t))
44++ wrong-item |= x=numbers.fixture (text-list x)
44++ wrong-item |= x=numbers.fixture (text-list x)
45 ^
45 ^
46−/test.jibb:30:47: argument has type (list @ud)
46+/test.jibb:31:47: argument has type (list @ud)
47++ wrong-item |= x=numbers.fixture (text-list x)
47++ wrong-item |= x=numbers.fixture (text-list x)
48 ^
48 ^
49++ wrong-kind: call's sample and argument types do not unify
49++ wrong-kind: call's sample and argument types do not unify
50−/test.jibb:31:35: called gate has type $-((list @t) (list @t))
50+/test.jibb:32:35: called gate has type $-((list @t) (list @t))
51++ wrong-kind |= x=maybe.fixture (text-list x)
51++ wrong-kind |= x=maybe.fixture (text-list x)
52 ^
52 ^
53−/test.jibb:31:45: argument has type (unit @t)
53+/test.jibb:32:45: argument has type (unit @t)
54++ wrong-kind |= x=maybe.fixture (text-list x)
54++ wrong-kind |= x=maybe.fixture (text-list x)
55 ^
55 ^
56++ bad-kind: sample mold is not supported by partial inference
56++ bad-kind: sample mold is not supported by partial inference
@@ -59,4 +59,32 @@ partial inference; module not checked
59++ bucket-id: $-((map @ud @t) (map @ud @t))
59++ bucket-id: $-((map @ud @t) (map @ud @t))
60++ raw-rec: sample mold is not supported by partial inference
60++ raw-rec: sample mold is not supported by partial inference
61++ direct-list: cast mold is not supported by partial inference
61++ direct-list: cast mold is not supported by partial inference
62+++ chain-id: $-(chain.fixture chain.fixture)
63+++ chain-again: $-(chain.fixture chain.fixture)
64+++ other-id: $-(other.fixture other.fixture)
65+++ chain-mismatch: call's sample and argument types do not unify
66+/test.jibb:42:39: called gate has type $-(chain.fixture chain.fixture)
67+++ chain-mismatch |= x=other.fixture (chain-id x)
68+ ^
69+/test.jibb:42:48: argument has type other.fixture
70+++ chain-mismatch |= x=other.fixture (chain-id x)
71+ ^
72+++ recursive-field: field is not available to partial inference
73+++ chain-null: cast constraint is not supported by partial inference
74+++ chain-alias: sample mold is not supported by partial inference
75+++ foreign-ref: sample mold is not supported by partial inference
76+++ overlap-ref: sample mold is not supported by partial inference
77+++ raw-ref: sample mold is not supported by partial inference
78+++ raw-null: sample mold is not supported by partial inference
79+++ cross-module: call's sample and argument types do not unify
80+/test.jibb:50:36: called gate has type $-(chain.fixture chain.fixture)
81+++ cross-module |= x=chain.second (chain-id x)
82+ ^
83+/test.jibb:50:45: argument has type chain.second
84+++ cross-module |= x=chain.second (chain-id x)
85+ ^
86+++ chain-pattern: recursive pattern is not supported by partial inference
87+++ chain-branch: recursive branch matching is not supported by partial inference
62reader-collapsed: error: a Hoon union has overlapping cases
88reader-collapsed: error: a Hoon union has overlapping cases
89+partial inference; module not checked
90+++ use: recursive mold name is not available in this scope
tests/type-import/source.txt+20
@@ -1,4 +1,5 @@
1/- fixture
1/- fixture
2+/- second
2++ f |= x=title.fixture x
3++ f |= x=title.fixture x
3++ g |= x=pair.fixture x
4++ g |= x=pair.fixture x
4++ face |= x=faced.fixture x
5++ face |= x=faced.fixture x
@@ -35,3 +36,22 @@
35++ bucket-id |= x=buckets.fixture x
36++ bucket-id |= x=buckets.fixture x
36++ raw-rec |= x=raw-rec.fixture x
37++ raw-rec |= x=raw-rec.fixture x
37++ direct-list |= x=texts.fixture ^- (list @t) x
38++ direct-list |= x=texts.fixture ^- (list @t) x
39+++ chain-id |= x=chain.fixture x
40+++ chain-again |= x=chain.fixture (chain-id x)
41+++ other-id |= x=other.fixture x
42+++ chain-mismatch |= x=other.fixture (chain-id x)
43+++ recursive-field |= x=chain.fixture next.x
44+++ chain-null ^- chain.fixture ~
45+++ chain-alias |= x=alias.fixture x
46+++ foreign-ref |= x=foreign-ref.fixture x
47+++ overlap-ref |= x=overlap-ref.fixture x
48+++ raw-ref |= x=raw-ref.fixture x
49+++ raw-null |= x=raw-null.fixture x
50+++ cross-module |= x=chain.second (chain-id x)
51+++ chain-pattern |= x=chain.fixture
52+ ?- x
53+ [a b] a
54+++ chain-branch |= [flag=? left=chain.fixture right=@t]
55+ ?- flag
56+ %.y left
57+ %.n right
tests/type-import/sur.txt+3
@@ -15,4 +15,7 @@
15+$ tagged-list (list $?([%x @ud] [%y @t]))
15+$ tagged-list (list $?([%x @ud] [%y @t]))
16+$ buckets (map @ud @t)
16+$ buckets (map @ud @t)
17+$ collection-rec [items=(list @t) slots=(unit @ud)]
17+$ collection-rec [items=(list @t) slots=(unit @ud)]
18++$ chain $@(~ [value=@ud next=chain])
19++$ other $@(~ [value=@ud next=other])
20++$ alias chain
18--
21--