on master help sign in
Check source-proven recursive tree patterns
commit30b0a9e1c49a7a3e4ee39ae0cc9c773958fa7b14 authoriko <mail@iko.soy> dateSat 31 Oct 2026, 14:44 (9 hours ago) parentd9d51e6
21 files changed+504−11
README.md+1−1 docs/compiler.md+1−1 docs/development.md+2 docs/status.md+5−3 jibb-dev/lib/jibb.hoon+55−1 tests/lib/jibb-generic-pattern.hoon+132 tests/lib/jibb-generic-recursion.hoon+1−1 tests/type-generic-pattern/about.txt+8 tests/type-generic-pattern/client.txt+114 tests/type-generic-pattern/expected.txt+55 tests/type-generic-pattern/no-event.txt+17 tests/type-generic-pattern/owner-bad.txt+22 tests/type-generic-pattern/owner.txt+18 tests/type-generic-pattern/poly-values.txt+2 tests/type-generic-pattern/values.txt+10 tests/type-generic-recursion/about.txt+1−1 tests/type-generic-recursion/bad-match.txt+1−1 tests/type-generic-recursion/expected.txt+1−1 tools/testdata/ship-selftest+5 tools/type-generic-pattern-test+52 tools/type-generic-recursion-test+1−1
README.md+1−1
@@ -42,7 +42,7 @@ tools/hoon '(golden /a/b/jibb (crip "++ main view"))'
4242tools/hoon --raw --cord src=tests/golden/foo/source.jibb 'tree:(golden /a/b/jibb src)'
4343```
4444
45−`tools/testdata/selftest` tests both tools against a stub library, and `tools/doc-blocks` against Markdown it writes. `tools/importer-test` compares the partial Hoon importer's fixture projections against their golden file. `tools/type-import-test`, `tools/type-import-overlap-test`, `tools/type-import-structural-test`, `tools/type-generic-recursion-test`, `tools/type-generic-construction-test`, `tools/type-tlon-test`, `tools/type-mark-test`, `tools/type-poke-test` and `tools/type-api-paths-test` check importer/checker, prelude and partial API path analysis boundaries under eval; the ship suite runs the same fixtures.
45+`tools/testdata/selftest` tests both tools against a stub library, and `tools/doc-blocks` against Markdown it writes. `tools/importer-test` compares the partial Hoon importer's fixture projections against their golden file. `tools/type-import-test`, `tools/type-import-overlap-test`, `tools/type-import-structural-test`, `tools/type-generic-recursion-test`, `tools/type-generic-construction-test`, `tools/type-generic-pattern-test`, `tools/type-tlon-test`, `tools/type-mark-test`, `tools/type-poke-test` and `tools/type-api-paths-test` check importer/checker, prelude and partial API path analysis boundaries under eval; the ship suite runs the same fixtures.
4646
4747### The doc's examples
4848
docs/compiler.md+1−1
@@ -94,7 +94,7 @@ A known closed union can also provide context to each arm of a complete loobean
9494
9595The 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 recognizes a direct ordered-map call with two simple named mold operands, as in Tlon `notes.log`, after validating provenance and operand source; revisiting the same map hold fails closed. 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. For a mark whose sample is exactly a named arm of its single Ford-imported `/sur` core, it can recover a source-proved nonrecursive union, including source-location wrappers around fields; recursive or unverified fallback results remain unsupported. The reader checks explicit Hoon `$%`, `$?` and `$^` unions against the Grammar’s disjointness rule, as @iko confirmed on 2026-10-30. For an explicit `$%` with distinct literal head tags, it can also prove restricted local named payload dependencies, including bare `~` fields, re-evaluate and validate each source branch, and recover the union before Hoon collapses it. The real Tlon `notes.u-note` exercises its `[%deleted ~]` case. A bounded fallback also recovers one level of local named tagged unions inside such a union, including Tlon `notes.u-notebook` and `notes.u-notes`. Source-proved tagged unions can also be wrapped in Hoon `$~` defaults, including a downstream union using Tlon `epic.saga`; the selected source and projected branches still undergo the same provenance and disjointness checks. The tagged-source helper peels up to four consecutive default wrappers, while default wrappers encountered inside dependency proofs consume the existing semantic-node budget. A source-proved nested self-recursive union retains its named owner, including Tlon `notes.import-node`; deeper, mutually recursive or unproven cases stay unsupported. The nested source proof has a 256-visit semantic-node budget, and source-location wrapper peeling is limited to four per node; Hoon evaluation and source scans are not metered. It rejects recursive or computed dependencies, inherited same-name collisions, unsupported nested unions and proofs exceeding a shared 4,096-node budget. 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, Hoon's `%n` zero singleton as Jibb `~`, 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. The validated standard prelude `list` mold shares the imported list representation; other Jibb-written collection molds and most prelude collection operations remain unsupported. 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/type-tlon-test` and its ship counterpart exercise four real Tlon presence unions and a field read through the source-aware reader and partial checker. `tools/clay-fixtures` also passes Clay-built `/sur` molds and mark samples through the Clay-built importer and partial checker.
9696
97−An import-free Jibb library can export an opaque single-parameter recursive identity when its source has exactly two distinct term-tagged cases: a text leaf and a node containing `(list (name M))`, optionally with one `$-(@t M)` event field. A module-local index records the exact parsed source, defining path and prepared prelude interface; it is shared across that module's inference passes, and its canonical `list` must be source-backed. The normal resolver publishes an interface only after all ordinary mold and value checks succeed. Same-origin aliases retain the defining identity and independently instantiate type arguments; distinct origins remain unsupported. The opaque identity is conservatively treated as gate-bearing for numeric defaulting, and gate-freedom remains unknown. Explicit literal-headed construction uses a source-proven one-layer view: the event result receives the existing type argument and recursive children keep the defining identity. It consumes the existing shared 256-step recursive inference budget, and a per-view 4,096-visit guard bounds the new face and substitution scan; longer scans decline. These bounds do not cover parsing, existing inference, atom sizes or repeated attempts. One startup-inclusive, shared-load comparison of 100 valid and 100 invalid source definitions took 29.67 s wall / 25.14 s user CPU before this slice and 33.47 s / 26.99 s with it; the former declined all 200 while the latter inferred the valid definitions. These single runs are not a production benchmark. General structural fits, pattern matching, recursive source with imports and local recursive molds remain unsupported; the test tree does not prescribe the eventual `element` representation. Caller-supplied detached interfaces and caches retain the existing trusted-input contract.
97+An import-free Jibb library can export an opaque single-parameter recursive identity when its source has exactly two distinct term-tagged cases: a text leaf and a node containing `(list (name M))`, optionally with one `$-(@t M)` event field. A module-local index records the exact parsed source, defining path and prepared prelude interface; it is shared across that module's inference passes, and its canonical `list` must be source-backed. The normal resolver publishes an interface only after all ordinary mold and value checks succeed. Same-origin aliases retain the defining identity and independently instantiate type arguments; distinct origins remain unsupported. The opaque identity is conservatively treated as gate-bearing for numeric defaulting, and gate-freedom remains unknown. Explicit literal-headed construction uses a source-proven one-layer view: the event result receives the existing type argument and recursive children keep the defining identity. It consumes the existing shared 256-step recursive inference budget, and a per-view 4,096-visit guard bounds the new face and substitution scan; longer scans decline. These bounds do not cover parsing, existing inference, atom sizes or repeated attempts. One startup-inclusive, shared-load comparison of 100 valid and 100 invalid source definitions took 29.67 s wall / 25.14 s user CPU before this slice and 33.47 s / 26.99 s with it; the former declined all 200 while the latter inferred the valid definitions. These single runs are not a production benchmark. Two-arm matches over explicit source tags and flat binder or wildcard fields use that same one-layer view and existing binder, coverage and branch checks; a single-arm match on a direct in-scope wing can report its missing case. Recursive children retain their nominal type and arguments. Subject recognition and view validation can each scan substitutions, and the scan cap applies separately to each attempt. In one separate startup-inclusive comparison of 100 valid and 100 invalid source matches, the construction-only checker took 30.18 s wall / 29.31 s user CPU and declined all 200, while the pattern slice took 28.28 s / 27.69 s and inferred or diagnosed them. These single shared-load figures are not production benchmarks. General structural fits, catch-all and nested patterns, recursive source with imports and local recursive molds remain unsupported; the test tree does not prescribe the eventual `element` representation. Caller-supplied detached interfaces and caches retain the existing trusted-input contract.
9898
9999Self-recursive `/sur` molds can cross the bridge as opaque module-and-name identities when their supported source-aware graph passes validation, including one-argument lists containing same-origin recursive references. The real Tlon `notes.import-node` reaches this bridge; it does not unfold or construct the recursive type. Aliases, foreign references, recursive marks, unfolding, construction and field access remain unsupported; the partial printer declines recursive names that are not in scope.
100100
docs/development.md+2
@@ -74,6 +74,7 @@ git clone https://github.com/urbit/tools /root/src/tools
7474| `tools/type-list-alias-test` | Runs the shared prelude list-alias and provenance guards under eval and on the ship |
7575| `tools/type-generic-recursion-test` | Checks source-backed generic recursive identity and provenance under eval and on the ship |
7676| `tools/type-generic-construction-test` | Checks source-backed one-layer recursive construction, provenance and fuel under eval and on the ship |
77+| `tools/type-generic-pattern-test` | Checks source-backed recursive patterns, coverage and fuel under eval and on the ship |
7778| `tools/type-generic-alias-test` | Runs public/private generic union alias and provenance guards under eval and on the ship |
7879| `tools/type-positional-face-guards-test` | Runs positional-call and nested-face guards under eval and on the ship |
7980| `tools/type-qath-guards-test` | Runs source-provenance, recursive unfolding and face guards under eval and on the ship |
@@ -126,6 +127,7 @@ tools/type-todo-api-test # real todo API partial inference and reader
126127tools/type-direct-gate-test # direct gate source and provenance guards pass
127128tools/type-generic-recursion-test # source-backed recursive identities and provenance
128129tools/type-generic-construction-test # source-backed recursive construction and bounds
130+tools/type-generic-pattern-test # source-backed recursive patterns and bounds
129131tools/type-closed-gate-guards-test # closed gate fit guards pass
130132tools/type-constructor-fit-guards-test # constructor fit guards pass
131133tools/type-nested-union-guards-test # nested union selector guards pass
docs/status.md+5−3
@@ -25,7 +25,7 @@ On 2026-10-31, @iko chose explicit, result-directed `(proj record)` to construct
2525
2626The internal prelude also defines `qath` as a transparent recursive knot sequence, re-exported by the public prelude. The partial checker recognizes its source identity, supports selected null/cell construction and matching, and types static application-path literals. Construction and matching for other recursive shapes, broader patterns and dynamic application-path segments remain unsupported. A 256-step limit applies per inference attempt, not to parsing or retries; checking routes and static files remains milestone 4.
2727
28−The checker also recognizes a limited source-proven generic recursive tree in an import-free defining module. Its nominal identity retains the defining path, mold name and type arguments, including across supported imports and aliases. With a known expected type it can construct supported tagged cases by checking one source-proven layer at a time. Pattern matching, general structural fits and gate-free proof remain unsupported; this is not yet the prelude's `element` implementation or island checking. The source index uses fixed-shape checks during mold collection. Each construction attempt has 256 unfolding steps and at most 4,096 face and substitution visits per view; parsing, other inference and retries remain outside those limits. A single startup-inclusive comparison of 1,000 ordinary gates produced identical reports and used 29.12 seconds of user CPU before the shared-resolver refactor and 28.55 seconds after it, with other work running; it is not a general performance bound.
28+The checker also recognizes a limited source-proven generic recursive tree in an import-free defining module. Its nominal identity retains the defining path, mold name and type arguments, including across supported imports and aliases. With a known expected type it can construct supported tagged cases by checking one source-proven layer at a time. Flat matches with two explicit tags also work for these source-proven trees, as does a missing-case diagnostic for a supported single-arm match. Broader pattern matching, general structural fits and gate-free proof remain unsupported; this is not yet the prelude's `element` implementation or island checking. The source index uses fixed-shape checks during mold collection. Each construction or supported pattern attempt has 256 unfolding steps and at most 4,096 face and substitution visits per scan; pattern subject recognition and view validation can each scan separately. Parsing, other inference and retries remain outside those limits. A single startup-inclusive comparison of 1,000 ordinary gates produced identical reports and used 29.12 seconds of user CPU before the shared-resolver refactor and 28.55 seconds after it, with other work running; it is not a general performance bound.
2929
3030The internal prelude defines a transparent generic `ask` with pure, agent and store cases, re-exported alongside the Jibb-written `pure` gate. The partial checker infers supported constructors and exhaustive matches. Agent and store receivers operate on raw nouns; their fields do not prove schema validity or watchability. The public prelude now defines the documented source-backed `sub` union; supported `%watch` payloads retain the public `(ask M)` type. Constructing a `sub` does not prove that its request is watchable. Request lowering, checked decoding, runtime validation and island watch checking remain unimplemented.
3131
@@ -76,7 +76,7 @@ What the plan asks for, and where it is:
7676Besides the plan's list:
7777
7878- 653 golden cases: 433 that parse and 220 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.
79−- The 653-case corpus passed under `urbit eval` in 105.9 s while other checks were running. The fake ship passed all 689 tests, including the source-overlap, structural-overlap, generic-recursion and generic-construction fixtures, importer, importer/checker and mark/checker bridges, module scope, unification, hole, qath, ask, default-wrapper importer, projection, arithmetic, generic-alias, positional-face, API-evidence, request, closed-gate, todo API, constructor, sub, nested-union, gate-free and direct-gate guards and the unchanged todo report. Desk sync took 98.2 s while other checks were running. The importer golden compares 221 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.
79+- The 653-case corpus passed under `urbit eval` in 94.1 s while other checks were running. The fake ship passed all 690 tests, including the source-overlap, structural-overlap, generic-recursion, generic-construction and generic-pattern fixtures, importer, importer/checker and mark/checker bridges, module scope, unification, hole, qath, ask, default-wrapper importer, projection, arithmetic, generic-alias, positional-face, API-evidence, request, closed-gate, todo API, constructor, sub, nested-union, gate-free and direct-gate guards and the unchanged todo report. Desk sync took 80.9 s while other checks were running. The importer golden compares 221 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.
8080- `tools/testdata/selftest` (22 checks), `tools/testdata/ship-selftest` and `tools/testdata/clay-fixtures-selftest` test the tools themselves; the last checks temporary Clay fixture cleanup and signal handling without a ship.
8181- 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.
8282- 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).
@@ -181,7 +181,8 @@ Besides the plan's list:
181181| 142f2e2 | 2026-10-31 | Prove structural overlaps across Hoon union cases |
182182| c96597d | 2026-10-31 | Track source-proven generic recursive identities |
183183| 190a9d5 | 2026-10-31 | Add source-backed list flattening |
184−| This change | 2026-10-31 | Check source-proven recursive tree construction |
184+| d9d51e6 | 2026-10-31 | Check source-proven recursive tree construction |
185+| This change | 2026-10-31 | Match source-proven recursive tree cases |
185186
186187## Checking it
187188
@@ -204,6 +205,7 @@ tools/type-list-alias-test # prelude list-alias guards
204205tools/type-generic-alias-test # generic union alias guards
205206tools/type-generic-recursion-test # source-proven generic recursion guards
206207tools/type-generic-construction-test # source-proven recursive construction guards
208+tools/type-generic-pattern-test # source-proven recursive pattern guards
207209tools/type-positional-face-guards-test # positional sample face guards
208210tools/type-qath-guards-test # qath source and unfolding guards
209211tools/type-ask-guards-test # ask source and bootstrap guards
jibb-dev/lib/jibb.hoon+55−1
@@ -7877,9 +7877,57 @@
78777877 =/ cell=itype [%cell [%face head.typ [%atom aura.typ]] [%face tail.typ typ]]
78787878 [%& [%union ~[nil cell] ~ ~ ~] state.p.subject(jrec-fuel (dec jrec-fuel.state.p.subject))]
78797879::
7880+:: Experimental bounded, source-proven tree match. Only two explicit
7881+:: flat tag/binder patterns may consume the view: a whole-case binder
7882+:: must not leak the temporary structural representation.
7883+++ grec-simple-pattern
7884+ |= [pat=pattern view=itype]
7885+ ^- ?
7886+ ?. ?=(%cell -.pat) |
7887+ ?~ items.pat |
7888+ =/ first=pattern i.items.pat
7889+ ?. ?=(%lit -.first) |
7890+ ?. =(%term form.lit.first) |
7891+ =/ chosen=(unit itype) (contextual-tagged-case view val.lit.first ~)
7892+ ?~ chosen |
7893+ ?> ?=(%cell -.u.chosen)
7894+ =/ rest=(list pattern) t.items.pat
7895+ ?. (levy rest |=(p=pattern |(?=(%bind -.p) ?=(%any -.p)))) |
7896+ ?: ?=(%cell -.tal.u.chosen) ?=([* [* ~]] rest)
7897+ ?=([* ~] rest)
7898+++ grec-match-view
7899+ |= [e=expr subject=iresult]
7900+ ^- iresult
7901+ ?: ?=(%| -.subject) subject
7902+ ?. ?=(%branch -.e) subject
7903+ ?. (grec-direct-context typ.p.subject sub.state.p.subject) subject
7904+ =/ typ=itype (itype-normalize typ.p.subject sub.state.p.subject)
7905+ ?. ?=(%grec -.typ) subject
7906+ ?~ arms.e subject
7907+ ?. |(?=(~ t.arms.e) ?=([* ~] t.arms.e)) subject
7908+ =/ view=(unit [view=itype left=@ud]) (grec-unfold-view typ jrec-fuel.state.p.subject)
7909+ ?~ view [%| ["recursive mold unfolding needs unsupported inference" ~]]
7910+ ?. (grec-simple-pattern pat.i.arms.e view.u.view) subject
7911+ =/ rest-ok=? ?~(t.arms.e & (grec-simple-pattern pat.i.t.arms.e view.u.view))
7912+ ?. rest-ok subject
7913+ [%& view.u.view state.p.subject(jrec-fuel left.u.view)]
7914+::
7915+:: A single-arm diagnostic only probes a direct, source-typed in-scope wing;
7916+:: other subjects retain the prior single-pattern inference path.
7917+++ grec-single-wing-subject
7918+ |= [e=expr env=ienv state=istate]
7919+ ^- (unit iresult)
7920+ ?. ?=(%wing -.e) ~
7921+ ?. ?=([@ ~] names.e) ~
7922+ =/ scheme=(unit ischeme) (ienv-lookup i.names.e env)
7923+ ?~ scheme ~
7924+ ?. (grec-direct-context typ.u.scheme sub.state) ~
7925+ `(infer-fragment e env state)
7926+::
78807927++ infer-known-union-branch-subject
78817928 |= [e=expr env=ienv subject=iresult]
78827929 ^- iresult
7930+ =. subject (grec-match-view e subject)
78837931 =. subject (jrec-match-view e subject)
78847932 =/ whole=(unit iresult) (infer-known-union-whole e env subject)
78857933 ?~ whole (infer-known-union-finite e env subject ~)
@@ -7894,6 +7942,7 @@
78947942++ infer-known-union-context-subject
78957943 |= [e=expr expected=itype env=ienv subject=iresult context=span]
78967944 ^- iresult
7945+ =. subject (grec-match-view e subject)
78977946 =. subject (jrec-match-view e subject)
78987947 =/ whole=(unit iresult) (infer-known-union-context-whole e expected env subject context)
78997948 ?~ whole (infer-known-union-finite e env subject `[expected context])
@@ -7946,7 +7995,7 @@
79467995 =/ subject=iresult (infer-fragment e env state)
79477996 ?: ?=(%| -.subject) ?:((union-subject-call e) `subject ~)
79487997 =/ typ=itype (itype-normalize typ.p.subject sub.state.p.subject)
7949− ?. |(?=(%union -.typ) ?=(%jrec -.typ)) ~
7998+ ?. |(?=(%union -.typ) ?=(%jrec -.typ) &(?=(%grec -.typ) (grec-direct-context typ.p.subject sub.state.p.subject))) ~
79507999 `subject
79518000::
79528001::
@@ -8218,6 +8267,8 @@
82188267 %branch
82198268 ?~ arms.e [%| ["branch is not supported by partial inference" ~]]
82208269 ?~ t.arms.e
8270+ =/ single=(unit iresult) (grec-single-wing-subject subject.e env state)
8271+ ?^ single (infer-known-union-branch-subject e env u.single)
82218272 ?: (single-structured-pattern e) (infer-single-case-branch e env state ~)
82228273 (infer-cell-branch e env state ~)
82238274 ?^ t.t.arms.e (infer-known-union-branch e env state)
@@ -8928,6 +8979,9 @@
89288979 [%& target state.p.value(sub sub.fit, next next.fit)]
89298980 ?: ?=(%call -.e) (infer-contextual-call e target env state)
89308981 ?: (single-structured-pattern e)
8982+ ?> ?=(%branch -.e)
8983+ =/ single=(unit iresult) (grec-single-wing-subject subject.e env state)
8984+ ?^ single (infer-known-union-context-subject e target env u.single context)
89318985 (infer-single-case-branch e env state `[target context])
89328986 ?: (cell-binder-pattern e)
89338987 (infer-cell-branch e env state `[target context])
tests/lib/jibb-generic-pattern.hoonnew file+132
@@ -0,0 +1,132 @@
1+:: Source-proven generic recursive patterns; no backend/runtime claim.
2+/+ jibb
3+/* real-prelude %jibb /lib/jibb/prelude/jibb
4+/* real-internal %jibb /lib/jibb/internal/jibb
5+/* tree %txt /tests/type-generic-construction/tree/txt
6+/* alias %txt /tests/type-generic-construction/alias/txt
7+/* tree-no-event %txt /tests/type-generic-construction/tree-no-event/txt
8+/* client %txt /tests/type-generic-pattern/client/txt
9+/* no-event %txt /tests/type-generic-pattern/no-event/txt
10+/* values %txt /tests/type-generic-pattern/values/txt
11+/* poly-values %txt /tests/type-generic-pattern/poly-values/txt
12+/* owner %txt /tests/type-generic-pattern/owner/txt
13+/* owner-bad %txt /tests/type-generic-pattern/owner-bad/txt
14+/* expected %txt /tests/type-generic-pattern/expected/txt
15+|%
16+++ text
17+ |= lines=wain
18+ ^- @t
19+ (rap 3 (turn lines |=(line=@t (cat 3 line '\0a'))))
20+++ line
21+ |= [label=@t ok=?]
22+ ^- @t
23+ (crip "{(trip label)}: {?:(ok "ok" "BAD")}")
24+++ pattern-output
25+ |= [pre=@t internal=@t tree=@t alias=@t tree-no-event=@t client=@t no-event=@t values=@t poly-values=@t owner=@t owner-bad=@t]
26+ ^- @t
27+ =/ prep=(unit iprelude:jibb) (prepare-prelude-with-internal:jibb pre internal)
28+ ?~ prep 'BAD prelude preparation'
29+ =/ checked=jfile-result:jibb
30+ (infer-jibb-file:jibb /ui/tree/jibb owner ~ ~ ~ ~[/ui/tree/jibb] values.u.prep molds.u.prep ~ `iface.u.prep)
31+ ?: ?=(%| -.checked) 'BAD valid owner'
32+ =/ bad-parse=(each module:jibb error:jibb) (parse:jibb /ui/tree/jibb owner-bad)
33+ =/ bad=jfile-result:jibb
34+ (infer-jibb-file:jibb /ui/tree/jibb owner-bad ~ ~ ~ ~[/ui/tree/jibb] values.u.prep molds.u.prep ~ `iface.u.prep)
35+ =/ mold=(unit ischeme:jibb) (find-jibb-mold:jibb %element iface.p.checked)
36+ ?~ mold 'BAD owner mold'
37+ ?. ?=(%grec -.typ.u.mold) 'BAD nominal owner'
38+ =/ target=itype:jibb typ.u.mold(args ~[[%atom %t]])
39+ =/ generic=itype:jibb typ.u.mold(args ~[[%var 42]])
40+ ?> ?=(%grec -.target)
41+ ?> ?=(%grec -.generic)
42+ =/ desc=(unit ischeme:jibb) (ienv-lookup:jibb %describe values.iface.p.checked)
43+ =/ children=(unit ischeme:jibb) (ienv-lookup:jibb %children values.iface.p.checked)
44+ =/ rebuild=(unit ischeme:jibb) (ienv-lookup:jibb %rebuild values.iface.p.checked)
45+ ?~ desc 'BAD owner describe missing'
46+ ?~ children 'BAD owner children missing'
47+ ?~ rebuild 'BAD owner rebuild missing'
48+ ?. &(?=(%fn -.typ.u.desc) ?=(%fn -.typ.u.children) ?=(%fn -.typ.u.rebuild)) 'BAD owner gate types'
49+ ?. ?=([@ ~] vars.u.children) 'BAD owner children quantification'
50+ =/ desc-sample=itype:jibb sample.typ.u.desc
51+ =/ children-sample=itype:jibb sample.typ.u.children
52+ =/ rebuild-sample=itype:jibb sample.typ.u.rebuild
53+ =? desc-sample ?=(%face -.desc-sample) body.desc-sample
54+ =? children-sample ?=(%face -.children-sample) body.children-sample
55+ =? rebuild-sample ?=(%face -.rebuild-sample) body.rebuild-sample
56+ =/ quantified=itype:jibb typ.u.mold(args ~[[%var i.vars.u.children]])
57+ =/ parsed=(each module:jibb error:jibb) (parse:jibb /tests/tree/values/jibb values)
58+ ?: ?=(%| -.parsed) 'BAD values parse'
59+ =/ ds=(list def:jibb) defs.p.parsed
60+ ?. ?=([* [* ~]] ds) 'BAD value count'
61+ =/ a=def:jibb i.ds
62+ =/ b=def:jibb i.t.ds
63+ ?. &(?=(%value -.a) ?=(%value -.b)) 'BAD value declarations'
64+ =/ parsed-poly=(each module:jibb error:jibb) (parse:jibb /tests/tree/poly/jibb poly-values)
65+ ?: ?=(%| -.parsed-poly) 'BAD polymorphic values parse'
66+ =/ ps=(list def:jibb) defs.p.parsed-poly
67+ ?. ?=([* ~] ps) 'BAD polymorphic value count'
68+ =/ p=def:jibb i.ps
69+ ?. ?=(%value -.p) 'BAD polymorphic value'
70+ ?. ?=(%branch -.val.p) 'BAD polymorphic branch'
71+ =/ env=ienv:jibb ~[[%x [target ~]]]
72+ =/ poly=ienv:jibb ~[[%x [generic ~[42]]]]
73+ =/ tag=ipending:jibb [100 [%term %tas %unrelated '%unrelated']]
74+ =/ projection=iwprojection:jibb *iwprojection:jibb
75+ =/ st=istate:jibb *istate:jibb
76+ =. st st(next 201, prelude `iface.u.prep, tags ~[tag], holes ~[[99 (span-of:jibb val.a)]], projections ~[projection])
77+ =/ zero=iresult:jibb (infer-fragment:jibb val.a env st(jrec-fuel 0))
78+ =/ one=iresult:jibb (infer-fragment:jibb val.a env st(jrec-fuel 1))
79+ =/ short=iresult:jibb (infer-fragment:jibb val.b env st(jrec-fuel 1))
80+ =/ nested=iresult:jibb (infer-fragment:jibb val.b env st(jrec-fuel 2))
81+ =/ retry=iresult:jibb (infer-fragment:jibb val.a env st(jrec-fuel 1))
82+ =/ hidden=ienv:jibb ~[[%x [[%var 200] ~]]]
83+ =/ hidden-result=iresult:jibb (infer-fragment:jibb val.a hidden st(sub ~[[200 target]], jrec-fuel 2))
84+ =/ first=(unit iresult:jibb) (grec-single-wing-subject:jibb subject.val.p poly st(jrec-fuel 4))
85+ =/ second=(unit iresult:jibb) (grec-single-wing-subject:jibb subject.val.p poly st(next 202, jrec-fuel 4))
86+ ?~ first 'BAD first scheme lookup'
87+ ?~ second 'BAD second scheme lookup'
88+ ?. &(?=(%& -.u.first) ?=(%& -.u.second)) 'BAD scheme inference'
89+ =/ ft=itype:jibb typ.p.u.first
90+ =/ stype=itype:jibb typ.p.u.second
91+ ?. &(?=(%grec -.ft) ?=(%grec -.stype)) 'BAD scheme nominal identity'
92+ ?. &(?=([* ~] args.ft) ?=([* ~] args.stype)) 'BAD scheme argument count'
93+ =/ pmissing=iresult:jibb (infer-fragment:jibb val.p poly st(jrec-fuel 4))
94+ =/ checks=(list [label=@t ok=?])
95+ :~ ['owner describe has nominal sample and text result' &(=(target desc-sample) =([%atom %t] product.typ.u.desc))]
96+ ['owner rebuild preserves nominal identity' &(=(target rebuild-sample) =(target product.typ.u.rebuild))]
97+ ['owner children preserves quantified recursive identity' &(=(quantified children-sample) =([%collection %list ~[quantified]] product.typ.u.children))]
98+ ['invalid owner parses and is rejected' &(?=(%& -.bad-parse) ?=(%| -.bad))]
99+ ['zero fuel declined' ?=(%| -.zero)]
100+ ['one-step match' ?:(?=(%& -.one) &(=([%atom %t] typ.p.one) =(0 jrec-fuel.state.p.one)) |)]
101+ ['nested match requires two steps' &(?=(%| -.short) ?:(?=(%& -.nested) =(0 jrec-fuel.state.p.nested) |))]
102+ ['same-state retry agrees' =(one retry)]
103+ ['pending tag retained' ?:(?=(%& -.one) (lien tags.state.p.one |=(entry=ipending:jibb =(entry tag))) |)]
104+ ['pending hole retained' ?:(?=(%& -.one) =(holes.st holes.state.p.one) |)]
105+ ['projection evidence retained' ?:(?=(%& -.one) =(projections.st projections.state.p.one) |)]
106+ ['raw bound variable declined' ?=(%| -.hidden-result)]
107+ ['polymorphic scheme gets fresh identities' &(=([%var 201] i.args.ft) =([%var 202] i.args.stype) =(at.ft at.stype) =(name.ft name.stype) =(proof.ft proof.stype))]
108+ ['polymorphic missing arm declined' ?=(%| -.pmissing)]
109+ ==
110+ =/ modules=jbundle:jibb ~[[/ui/tree/jibb tree] [/ui/tree-two/jibb tree] [/ui/tree-alias/jibb alias]]
111+ =/ small=jbundle:jibb ~[[/ui/tree/jibb tree-no-event]]
112+ =/ report=@t (golden-types-with-prepared-prelude:jibb /tests/tree/client/jibb client ~ ~ modules prep)
113+ =/ noreport=@t (golden-types-with-prepared-prelude:jibb /tests/tree/no-event/jibb no-event ~ ~ small prep)
114+ (cat 3 (of-wain:format (turn checks line)) (cat 3 '\0a' (cat 3 report (cat 3 '\0a' noreport))))
115+++ test-generic-pattern
116+ ^- tang
117+ ?~ tree ~[leaf+"tree source missing"]
118+ ?~ alias ~[leaf+"alias source missing"]
119+ ?~ tree-no-event ~[leaf+"tree without event missing"]
120+ ?~ client ~[leaf+"client source missing"]
121+ ?~ no-event ~[leaf+"no-event client missing"]
122+ ?~ values ~[leaf+"values source missing"]
123+ ?~ poly-values ~[leaf+"polymorphic values source missing"]
124+ ?~ owner ~[leaf+"owner source missing"]
125+ ?~ owner-bad ~[leaf+"invalid owner source missing"]
126+ ?~ expected ~[leaf+"expected output missing"]
127+ =/ actual=@t
128+ (pattern-output real-prelude real-internal (text tree) (text alias) (text tree-no-event) (text client) (text no-event) (text values) (text poly-values) (text owner) (text owner-bad))
129+ ?. =((to-wain:format (cat 3 actual '\0a')) expected)
130+ ~[leaf+"generic pattern output differs from expected"]
131+ ~
132+--
tests/lib/jibb-generic-recursion.hoon+1−1
@@ -157,7 +157,7 @@
157157 =/ invalid=(list [label=@t src=@t])
158158 :~ ['unused mold hole declined' (text bad-mold)]
159159 ['invalid constructor declined' (text bad-constructor)]
160− ['matching declined' (text bad-match)]
160+ ['invalid matching declined' (text bad-match)]
161161 ['nonuniform recursion declined' (text bad-parameter)]
162162 ['identical local shadow declined' (text local-shadow)]
163163 ['different local shadow declined' (text local-distinct)]
tests/type-generic-pattern/about.txtnew file+8
@@ -0,0 +1,8 @@
1+source: docs/spec/grammar.md:57,135 — “Molds may be recursive”; “recursive molds go by name and module”.
2+
3+This shared eval/ship fixture checks a narrow source-proven recursive tree
4+pattern slice, including explicit tags, binder scopes, payload arguments,
5+recursive children, aliases, coverage failures and shared fuel. The source client reports are explicitly partial. A separate owner check requires
6+all ordinary definitions to succeed before interface export. Whole-case
7+binders, nested patterns and unresolved recursive origins remain unsupported.
8+No runtime lowering, watchability, island acceptance or backend claim is made.
tests/type-generic-pattern/client.txtnew file+114
@@ -0,0 +1,114 @@
1+/= first /ui/tree
2+/= second /ui/tree-two
3+/= via /ui/tree-alias
4+++ describe |= x=(element.first @t)
5+ ?- x
6+ [%text value] value
7+ [%node event children] (event "click")
8+++ reverse |= x=(element.first @p)
9+ ?- x
10+ [%node event children] (event "click")
11+ [%text value] ~zod
12+++ generic-children |= x=(element.first A)
13+ ^- (list (element.first A)) ?- x
14+ [%text value] ~
15+ [%node event kids] kids
16+++ alias-children |= x=(alias.via @p)
17+ ^- (list (element.first @p)) ?- x
18+ [%text value] ~
19+ [%node event kids] kids
20+++ rebuild |= x=(element.first @t)
21+ ^- (element.first @t) ?- x
22+ [%text value] [%text value=value]
23+ [%node event children] [%node event=event children=children]
24+++ wrong-return |= x=(element.first @t)
25+ ?- x
26+ [%text value] value
27+ [%node event kids] ~zod
28+++ duplicate |= x=(element.first @t)
29+ ?- x
30+ [%text a] a
31+ [%text b] b
32+++ missing |= x=(element.first @t)
33+ ?- x
34+ [%text a] a
35+++ shadow |= x=(element.first @t)
36+ ?- x
37+ [%text x] x
38+ [%node event kids] "node"
39+++ bad-tag |= x=(element.first @t)
40+ ?- x
41+ [%text value] value
42+ [%other event kids] "node"
43+++ whole-tail |= x=(element.first @t)
44+ ?- x
45+ [%text value] value
46+ [%node payload] payload
47+++ unknown |= x
48+ ?- x
49+ [%text value] value
50+ [%node event kids] "node"
51+++ pending |= x=(element.first @t)
52+ ?- x
53+ [%text value] [%unrelated value]
54+ [%node event kids] [%unrelated "node"]
55+++ dead |= x=(element.first @t)
56+ ?- x
57+ [%text a] a
58+ [%node event kids] (event "ok")
59+ [%text b] b
60+++ wild |= x=(element.first @t)
61+ ?- x
62+ [%text *] "text"
63+ [%node * *] "node"
64+++ whole-wild |= x=(element.first @t)
65+ ?- x
66+ [%text value] value
67+ * "other"
68+++ foreign-children |= x=(element.first @t)
69+ ^- (list (element.second @t)) ?- x
70+ [%text value] ~
71+ [%node event kids] kids
72+++ wrong-argument |= x=(element.first @t)
73+ ^- (list (element.first @p)) ?- x
74+ [%text value] ~
75+ [%node event kids] kids
76+++ nested-child |= x=(element.first @t)
77+ ?- x
78+ [%text value] value
79+ [%node event kids] ?- kids
80+ ~ "empty"
81+ [item remainder] ?- item
82+ [%text value] value
83+ [%node action rest] (action "click")
84+++ duplicate-binder |= x=(element.first @t)
85+ ?- x
86+ [%text value] value
87+ [%node event event] "node"
88+++ target-shadow |= x=(element.first @t)
89+ ?- x
90+ [%text value] value
91+ [%node event x] "node"
92+++ grouped |= x=(element.first @t)
93+ ?- (x)
94+ [%text value] value
95+ [%node event kids] (event "click")
96+++ called |= x=(element.first @t)
97+ ?- (generic-id.first x)
98+ [%text value] value
99+ [%node event kids] (event "click")
100+++ missing-context |= x=(element.first @t)
101+ ^- @t ?- x
102+ [%text item] item
103+++ missing-node |= x=(element.first @t)
104+ ?- x
105+ [%node action kids] (action "click")
106+++ single-wild |= x=(element.first @t)
107+ ?- x
108+ * "all"
109+++ single-bind |= x=(element.first @t)
110+ ?- x
111+ all all
112+++ polymorphic-source ^- (element.first A) [%text value="source"]
113+++ polymorphic-match ?- polymorphic-source
114+ [%text text] text
tests/type-generic-pattern/expected.txtnew file+55
@@ -0,0 +1,55 @@
1+owner describe has nominal sample and text result: ok
2+owner rebuild preserves nominal identity: ok
3+owner children preserves quantified recursive identity: ok
4+invalid owner parses and is rejected: ok
5+zero fuel declined: ok
6+one-step match: ok
7+nested match requires two steps: ok
8+same-state retry agrees: ok
9+pending tag retained: ok
10+pending hole retained: ok
11+projection evidence retained: ok
12+raw bound variable declined: ok
13+polymorphic scheme gets fresh identities: ok
14+polymorphic missing arm declined: ok
15+partial inference; module not checked
16+++ describe: $-((element.first @t) @t)
17+++ reverse: $-((element.first @p) @p)
18+++ generic-children: $-((element.first A) (list (element.first A)))
19+++ alias-children: $-((alias.via @p) (list (element.first @p)))
20+++ rebuild: $-((element.first @t) (element.first @t))
21+++ wrong-return: union branch result matching needs unsupported inference
22+++ duplicate: branch arm is covered by earlier arms
23+/tests/tree/client.jibb:31:5: covered arm
24+ [%text b] b
25+ ^
26+++ missing: missing union case [%node [event=$-(@t @t) children=(list (element.first @t))]]
27+++ shadow: pattern binder shadows a name
28+++ bad-tag: branch is not supported by partial inference
29+++ whole-tail: branch is not supported by partial inference
30+++ unknown: branch is not supported by partial inference
31+++ pending: union branch result matching needs unsupported inference
32+++ dead: branch is not supported by partial inference
33+++ wild: $-((element.first @t) @t)
34+++ whole-wild: branch is not supported by partial inference
35+++ foreign-children: union branch result constraint is not supported by partial inference
36+++ wrong-argument: union branch result constraint is not supported by partial inference
37+++ nested-child: $-((element.first @t) @t)
38+++ duplicate-binder: pattern binder shadows a name
39+++ target-shadow: pattern binder shadows a name
40+++ grouped: $-((element.first @t) @t)
41+++ called: $-((element.first @t) @t)
42+++ missing-context: missing union case [%node [event=$-(@t @t) children=(list (element.first @t))]]
43+++ missing-node: missing union case [%text value=@t]
44+++ single-wild: branch is not supported by partial inference
45+++ single-bind: branch is not supported by partial inference
46+++ polymorphic-source: (element.first A)
47+++ polymorphic-match: missing union case [%node [event=$-(@t A) children=(list (element.first A))]]
48+partial inference; module not checked
49+++ words: $-((element.first @t) @t)
50+++ children: $-((element.first A) (list (element.first A)))
51+++ duplicate: branch arm is covered by earlier arms
52+/tests/tree/no-event.jibb:13:5: covered arm
53+ [%text b] b
54+ ^
55+++ nested-pattern: branch is not supported by partial inference
tests/type-generic-pattern/no-event.txtnew file+17
@@ -0,0 +1,17 @@
1+/= first /ui/tree
2+++ words |= x=(element.first @t)
3+ ?- x
4+ [%text value] value
5+ [%node kids] "node"
6+++ children |= x=(element.first A)
7+ ^- (list (element.first A)) ?- x
8+ [%text value] ~
9+ [%node kids] kids
10+++ duplicate |= x=(element.first @t)
11+ ?- x
12+ [%text a] a
13+ [%text b] b
14+++ nested-pattern |= x=(element.first @t)
15+ ?- x
16+ [%text value] value
17+ [%node [first rest]] "node"
tests/type-generic-pattern/owner-bad.txtnew file+22
@@ -0,0 +1,22 @@
1++$ element |$ [M]
2+ [%text value=@t]
3+ [%node event=$-(@t M) children=(list (element M))]
4+++ text-id |= x=(element @t) x
5+++ ship-id |= x=(element @p) x
6+++ generic-id |= x=(element A) x
7+++ describe |= x=(element @t)
8+ ?- x
9+ [%text value] value
10+ [%node event kids] (event "click")
11+++ children |= x=(element A)
12+ ^- (list (element A)) ?- x
13+ [%text value] ~
14+ [%node event kids] kids
15+++ rebuild |= x=(element @t)
16+ ^- (element @t) ?- x
17+ [%text value] [%text value=value]
18+ [%node event kids] [%node event=event children=kids]
19+++ wrong |= x=(element @t)
20+ ?- x
21+ [%text value] value
22+ [%node event event] "bad"
tests/type-generic-pattern/owner.txtnew file+18
@@ -0,0 +1,18 @@
1++$ element |$ [M]
2+ [%text value=@t]
3+ [%node event=$-(@t M) children=(list (element M))]
4+++ text-id |= x=(element @t) x
5+++ ship-id |= x=(element @p) x
6+++ generic-id |= x=(element A) x
7+++ describe |= x=(element @t)
8+ ?- x
9+ [%text value] value
10+ [%node event kids] (event "click")
11+++ children |= x=(element A)
12+ ^- (list (element A)) ?- x
13+ [%text value] ~
14+ [%node event kids] kids
15+++ rebuild |= x=(element @t)
16+ ^- (element @t) ?- x
17+ [%text value] [%text value=value]
18+ [%node event kids] [%node event=event children=kids]
tests/type-generic-pattern/poly-values.txtnew file+2
@@ -0,0 +1,2 @@
1+++ missing ?- x
2+ [%text value] value
tests/type-generic-pattern/values.txtnew file+10
@@ -0,0 +1,10 @@
1+++ simple ?- x
2+ [%text value] value
3+ [%node event kids] (event "click")
4+++ nested ?- x
5+ [%text value] value
6+ [%node event kids] ?- kids
7+ ~ "empty"
8+ [item remainder] ?- item
9+ [%text value] value
10+ [%node action rest] (action "click")
tests/type-generic-recursion/about.txt+1−1
@@ -5,7 +5,7 @@ recursive identity: two distinct tagged cases, a text leaf and a node with a
55message-producing gate and a list of recursive children. The test-only tree is
66not a chosen element representation. A complete import-free owner module must
77check before its interface is published. Same-origin aliases retain identity,
8−while different origins, unproved casts and matching remain unsupported.
8+while different origins, unproved casts and broader matching remain unsupported.
99A wrong-aura constructor fails the complete owner check. An opaque recursive type prevents premature numeric defaulting;
1010its gate-freedom remains unknown. Source/cache/prelude changes, cycles,
1111nonuniform recursion, invalid unused molds and local shadows fail closed.
tests/type-generic-recursion/bad-match.txt+1−1
@@ -7,4 +7,4 @@
77++ match |= x=(element @t)
88 ?- x
99 [%text value] value
10− [%node event children] "node"
10+ [%node event event] "node"
tests/type-generic-recursion/expected.txt+1−1
@@ -12,7 +12,7 @@ gate freedom unknown: ok
1212gate defaulting conservative: ok
1313unused mold hole declined: ok
1414invalid constructor declined: ok
15−matching declined: ok
15+invalid matching declined: ok
1616nonuniform recursion declined: ok
1717identical local shadow declined: ok
1818different local shadow declined: ok
tools/testdata/ship-selftest+5
@@ -312,6 +312,7 @@ def generator_checks(tmp):
312312 'tests/lib/jibb-generic-alias.hoon',
313313 'tests/lib/jibb-generic-recursion.hoon',
314314 'tests/lib/jibb-generic-construction.hoon',
315+ 'tests/lib/jibb-generic-pattern.hoon',
315316 'tests/lib/jibb-api-evidence.hoon',
316317 'tests/lib/jibb-ask-guards.hoon',
317318 'tests/lib/jibb-request-guards.hoon',
@@ -336,6 +337,10 @@ def generator_checks(tmp):
336337 'tree', 'alias', 'client', 'alias-client', 'tree-no-event',
337338 'no-event-client', 'owner-positive', 'owner-negative',
338339 'sibling-values', 'expected')} <= set(normal.files))
340+ check('generic pattern sources and golden are assembled',
341+ {f'tests/type-generic-pattern/{name}.txt' for name in (
342+ 'client', 'no-event', 'values', 'poly-values', 'owner',
343+ 'owner-bad', 'expected')} <= set(normal.files))
339344 check('direct gate source and golden are assembled',
340345 {'tests/type-direct-gate/source.txt', 'tests/type-direct-gate/expected.txt'}
341346 <= set(normal.files))
tools/type-generic-pattern-testnew file+52
@@ -0,0 +1,52 @@
1+#!/usr/bin/env python3
2+"""Check source-backed generic recursive pattern inference under eval."""
3+from pathlib import Path
4+import argparse
5+import difflib
6+import subprocess
7+import sys
8+import tempfile
9+
10+root = Path(__file__).resolve().parent.parent
11+parser = argparse.ArgumentParser(description=__doc__)
12+parser.add_argument('--lib', default=str(root / 'jibb-dev/lib/jibb.hoon'))
13+parser.add_argument('--update', action='store_true')
14+args = parser.parse_args()
15+fixture = root / 'tests/type-generic-pattern'
16+shared = root / 'tests/type-generic-construction'
17+source = (root / 'tests/lib/jibb-generic-pattern.hoon').read_text()
18+source = source[source.index('\n|%\n') + 1:]
19+reused = ['tree', 'alias', 'tree-no-event']
20+names = ['client', 'no-event', 'values', 'poly-values', 'owner', 'owner-bad']
21+cords = [item for name in reused
22+ for item in ('--cord', f'{name}-text={shared / (name + ".txt")}')]
23+cords += [item for name in names
24+ for item in ('--cord', f'{name}-text={fixture / (name + ".txt")}')]
25+cords += ['--cord', f'prelude={root / "jibb-dev/lib/jibb/prelude.jibb"}',
26+ '--cord', f'internal={root / "jibb-dev/lib/jibb/internal.jibb"}']
27+bindings = ''.join(f'=/ {name}=wain (to-wain:format {name}-text)\n'
28+ for name in reused + names)
29+program = ("=/ jibb .\n=/ real-prelude=@t prelude\n"
30+ "=/ real-internal=@t internal\n" + bindings +
31+ "=/ expected=wain ~\n=/ suite\n" + source +
32+ "\n^- @t\n(pattern-output:suite prelude internal " +
33+ ' '.join(name + '-text' for name in reused + names) + ")\n")
34+with tempfile.TemporaryDirectory(prefix='jibb-generic-pattern-') as tmp:
35+ path = Path(tmp) / 'test.hoon'
36+ path.write_text(program)
37+ result = subprocess.run([str(root / 'tools/hoon'), '--lib', args.lib, '--raw',
38+ '-f', str(path), *cords], stdout=subprocess.PIPE)
39+if result.returncode:
40+ sys.exit(result.returncode)
41+expected_path = fixture / 'expected.txt'
42+if args.update:
43+ expected_path.write_bytes(result.stdout)
44+else:
45+ expected = expected_path.read_bytes()
46+ if result.stdout != expected:
47+ sys.stderr.writelines(difflib.unified_diff(
48+ expected.decode().splitlines(keepends=True),
49+ result.stdout.decode().splitlines(keepends=True),
50+ fromfile='expected.txt', tofile='actual'))
51+ sys.exit(1)
52+print('source-backed generic pattern guards passed')
tools/type-generic-recursion-test+1−1
@@ -33,7 +33,7 @@ program = ("=/ jibb .\n=/ real-prelude=@t prelude\n"
3333 "\n^- @t\n=/ invalid=(list [label=@t src=@t])\n"
3434 " :~ ['unused mold hole declined' bad-mold-text]\n"
3535 " ['invalid constructor declined' bad-constructor-text]\n"
36− " ['matching declined' bad-match-text]\n"
36+ " ['invalid matching declined' bad-match-text]\n"
3737 " ['nonuniform recursion declined' bad-parameter-text]\n"
3838 " ['identical local shadow declined' local-shadow-text]\n"
3939 " ['different local shadow declined' local-distinct-text]\n"