on master help sign in
Report alias overlaps and retain deferred branch context
commit2826c93a9ae2a69d370b035269f4e8b1346c1873 authoriko <mail@iko.soy> dateSat 31 Oct 2026, 16:49 (5 hours ago) parentb93a94b
33 files changed+804−36
README.md+1−1 docs/compiler.md+4−2 docs/development.md+5−1 docs/status.md+8−5 jibb-dev/lib/jibb.hoon+225−24 tests/golden/types-union-source-alias/about.txt+1−1 tests/golden/types-union-source-alias/types.txt+14−2 tests/lib/jibb-deferred-context.hoon+83 tests/lib/jibb-union-alias-overlap.hoon+107 tests/type-deferred-context/about.txt+3 tests/type-deferred-context/collision-early.txt+27 tests/type-deferred-context/collision-late.txt+28 tests/type-deferred-context/dangling.txt+16 tests/type-deferred-context/duplicate.txt+14 tests/type-deferred-context/missing.txt+14 tests/type-deferred-context/plain.txt+11 tests/type-deferred-context/report.txt+15 tests/type-deferred-context/rich.txt+27 tests/type-deferred-context/simple.txt+10 tests/type-deferred-context/wrong.txt+14 tests/type-union-alias-overlap/duplicate.txt+5 tests/type-union-alias-overlap/expected.txt+25 tests/type-union-alias-overlap/flag-source.txt+7 tests/type-union-alias-overlap/multi.txt+5 tests/type-union-alias-overlap/negative.txt+16 tests/type-union-alias-overlap/nonzero.txt+4 tests/type-union-alias-overlap/opaque.txt+4 tests/type-union-alias-overlap/positive.txt+8 tests/type-union-alias-overlap/qualified.txt+5 tests/type-union-alias-overlap/wrong.txt+5 tools/testdata/ship-selftest+10 tools/type-deferred-context-test+31 tools/type-union-alias-overlap-test+52
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-union-source-test`, `tools/type-generic-recursion-test`, `tools/type-generic-construction-test`, `tools/type-generic-pattern-test`, `tools/type-deferred-test`, `tools/type-deferred-resolver-test`, `tools/type-empty-test`, `tools/type-cell-branch-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-union-source-test`, `tools/type-union-alias-overlap-test`, `tools/type-generic-recursion-test`, `tools/type-generic-construction-test`, `tools/type-generic-pattern-test`, `tools/type-deferred-test`, `tools/type-deferred-resolver-test`, `tools/type-deferred-context-test`, `tools/type-empty-test`, `tools/type-cell-branch-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+4−2
@@ -66,7 +66,7 @@ Before the empty-capital optimization below, single sequential eval runs includi
6666
6767Restricted-number promotion only applies to written capital variables. If none are present, it returns immediately instead of traversing the environment. In one startup-inclusive pair of runs on 2,000 independent gates, output was byte-identical and user CPU fell from 71.67 to 38.26 seconds; wall time was 72.80 versus 51.66 seconds while other checks were running. This does not establish a bound for dependent modules or a complete-checker benchmark.
6868
69−A bounded source proof can also establish distinct literal heads when a union case is a one-hop application of a uniquely resolved local generic mold. It compares literal runtime nouns and checks the original mold normally before publishing an interface. The same source proof improves the partial report, which still says the mold is not checked. The new proof declines nested or qualified applications, opaque references, recursive-index states, holes and deferred obligations; other valid unions can remain unsupported. A 256-node source budget and a separate 256-entry inherited-state budget apply per attempt. They do not bound the existing pairwise union pass, parsing, atom or name sizes, or downstream checking. Prepared interfaces and inference state remain trusted inputs. `tools/type-union-source-test` and the shared ship guard exercise source resolution, reporting, rollback and these bounds.
69+A bounded source proof can also establish distinct literal heads when a union case is a one-hop application of a uniquely resolved local generic mold. It compares literal runtime nouns and checks the original mold normally before publishing an interface. The same source proof improves the partial report, which still says the mold is not checked. The new proof declines nested or qualified applications, opaque references, recursive-index states, holes and deferred obligations; other valid unions can remain unsupported. A 256-node source budget and a separate 256-entry inherited-state budget apply per attempt. They do not bound the existing pairwise union pass, parsing, atom or name sizes, or downstream checking. Prepared interfaces and inference state remain trusted inputs. `tools/type-union-source-test` and the shared ship guard exercise source resolution, reporting, rollback and these bounds. A separate positive witness can identify overlap in two-field, literal-headed cases when a unique one-hop local, one-parameter generic alias is applied to a concrete primitive payload. It compares the literal noun values and proves the payloads have a shared noun, so an empty or unknown payload is not a witness. The partial report identifies both original case sites; full-module inference still rejects these cases with its generic unsupported-mold error. This witness uses a shared 256-node source/pair budget per attempt; an unsupported earlier case may hide a later overlap. Existing pairwise union analysis, parsing, atom and name sizes, and downstream checking remain outside this budget. `tools/type-union-alias-overlap-test` checks the source proof and its two-site report.
7070
7171A single-arm cell pattern can bind two or more positions, with its last binder capturing the remaining tail. Duplicate and shadowed names are rejected. A definite non-cell spine fails; an intermediate union or collection that might contain a cell remains unsupported. For a known non-union cell, a separate one-arm rule also accepts structured patterns, including nested literal and wildcard positions, when the existing coverage proof establishes the whole pattern. It binds payloads with their original types and checks result context; unknown, partially covered, union or recursive spines stay unsupported.
7272
@@ -190,12 +190,14 @@ Note 4 has examples of each.
190190
191191Starting `urbit eval` and building the library takes about 8 s. A 2,000-line module of 400 definitions then parses in about a second. Loops over definitions, arms, lines and items are tail calls, so a long module grows no stack; nesting costs a few stack frames a level, and 100,000 nested brackets still parse (note 3).
192192
193−The experimental deferred constructor path can close a small import-free library transaction when ordinary resolution stalls. Deferred definitions are restricted to simple one-component wings, literals, static strings, gates, transparent groups and empty bodies, calls and casts. Each successful call or cast fit must have exactly equal normalized operands so later substitutions cannot invalidate a face check. A multi-argument call also needs closed argument, sample and product types because positional face checks depend on slot boundaries and whether the result can be a cell. Other fits fail closed. A 512-node screen bounds visited expression nodes and a separate 512-node scan bounds each stability check; neither bounds mold shapes, string-piece checks, parsing, normalization, lookup, or total repeated inference. It accepts only a direct constructor callback with a closed input, records its pending fit under the source-owned inference state, and generalizes each definition against the live environment and pending constraints. Before publication it discharges and replays every edge, rejects unresolved tags or changed replay state, and checks that all molds and values are complete. The reporter reads the committed schemes for definitions added by the transaction. API and store paths, `main`, prepared preludes, imports, recursive groups, unresolved holes or written capitals, and new projection actions remain outside this path. `%grec` in the constraint or freshness scan is conservatively unsupported. This is not an island-kind, runtime, or watchability check.
193+The experimental deferred constructor path can close a small import-free library transaction when ordinary resolution stalls. It retries definitions whose earlier attempts failed from their original states, retaining only successful attempts. A transaction with at most 64 definitions makes at most 4,096 attempts; it stops after a pass with no progress. This bounds the new attempt count, not each attempt's parsing, type walks or ordinary inference. Deferred definitions are restricted to simple one-component wings, literals, static strings, gates, transparent groups and empty bodies, calls, casts and a restricted boolean branch. Each successful call or cast fit must have exactly equal normalized operands so later substitutions cannot invalidate a face check. A multi-argument call also needs closed argument, sample and product types because positional face checks depend on slot boundaries and whether the result can be a cell. Other fits fail closed. A 512-node screen bounds visited expression nodes and a separate 512-node scan bounds each stability check; neither bounds mold shapes, string-piece checks, parsing, normalization, lookup, or total repeated inference. It accepts only a direct constructor callback with a closed input, records its pending fit under the source-owned inference state, and generalizes each definition against the live environment and pending constraints. Before publication it discharges and replays every edge, rejects unresolved tags or changed replay state, and checks that all molds and values are complete. The reporter reads the committed schemes for definitions added by the transaction. API and store paths, `main`, prepared preludes, imports, recursive groups, unresolved holes or written capitals, and new projection actions remain outside this path. `%grec` in the constraint or freshness scan is conservatively unsupported. This is not an island-kind, runtime, or watchability check.
194194
195195An expected cell type can supply context to both arms of a loobean branch. This slice first retains any ordinary inference that already fits. Otherwise it retries from the original state only when a syntax screen excludes nested branches, casts, gates and local bodies, and a substitution screen excludes structural bindings. Both arms must fit, their normalized results and the expected type must be closed, their result types must match, and both fits must replay without changing the final substitution or fresh-ID counter. A successful retry retains the original arm type and its written faces; a failed retry returns the original inference result. The source and substitution screens each visit at most 512 entries. This permits at most one extra attempt for an eligible branch; the screens do not bound preexisting inference, type walks, list construction, or work across separate analyses.
196196
197197In an isolated diagnostic with the real todo API and reader inputs, this cell context allowed the original update body to infer when its result type was written explicitly and experimental source-defined text helpers were supplied. The unchanged island still needs those text helpers, an actual source-backed `program` and UI interface, context propagation across definitions, island-kind checking and watchability proof. This branch change itself provides no runtime implementation or watchability evidence.
198198
199+A deferred boolean branch must use both distinct literal loobean patterns. Its subject is unified with `?`; the arms are inferred in source order without contextual retry. Equal normalized arm types are retained directly. When both written arms are direct literals that each created their own unbound pending tag, the checker can instead retain two source-owned fit obligations to a shared, monomorphic result variable. A later definition may close that variable; replay uses the ordinary literal fit, requires a closed atomic target or a union of direct constant/cell cases, and rejects faced targets and faced union cases. It replays all edges under the final substitution without adding bindings or fresh IDs. This does not support deferred list literals, union-pattern reachability, arbitrary branch expressions or prepared-prelude/imported modules. It does not establish request watchability.
200+
199201Before a fallback, the checker screens the already parsed source for a direct single-constructor call beneath groups, gates, casts or bodies without local definitions. The screen visits at most 64 AST nodes per definition and rejects sources with more than 64 definitions or 65,536 bytes. A transaction is attempted at most once per resolver or report invocation and holds at most 64 pending edges. These limits do not bound the initial parsing, ordinary resolution, normalization, or total work across separate analyses. On the pre-recursive-index snapshot, startup-inclusive synthetic 1,000-gate comparisons had baseline/candidate user times of 25.97/29.30 seconds and 26.30/27.46 seconds in reverse order. These are limited shared-load measurements, not production benchmarks. The trimmed source-backed and direct-guard fixture passed in 28.13 seconds wall and 27.53 seconds user on one run after screening; its cases include parse-success checks in the same transaction source pass. The outer resolver and reporter each parse independently, and a fallback currently parses the source again.
200202
201203A single sequential, startup-inclusive comparison against checkpoint `c96597d` measured user CPU for an unrelated stalled library at 25.04 seconds baseline and 26.32 seconds with the screened fallback; an incompatible constructor took 25.72 and 27.16 seconds; a direct constructor that the baseline declined and the candidate resolved took 24.46 and 26.79 seconds. These are small shared-load measurements, not a production benchmark. The screen was separately checked with a trapping fallback: the unrelated source skipped it, while the eligible conflicting source reached it.
docs/development.md+5−1
@@ -14,7 +14,7 @@ From the plan's Setup and ground rules, and from @iko:
1414- **Push straight to `master`.** @iko reviews work in the project's chat, not in pull requests, so there are none. Run the checks below first.
1515- **Keep docs/ current**: [status.md](status.md) when a milestone moves, [open-questions.md](open-questions.md) when a question is settled or found.
1616
17−The source-backed generic-union guard can be run under eval with `tools/type-union-source-test`; its `test-union-source` arm is also assembled for the fake ship. It checks a bounded partial inference slice, not general union completeness.
17+The source-backed generic-union guard can be run under eval with `tools/type-union-source-test`; its `test-union-source` arm is also assembled for the fake ship. It checks a bounded partial inference slice, not general union completeness. `tools/type-union-alias-overlap-test` exercises the separate positive overlap witness and its shared ship test arm.
1818
1919## Setting up a machine
2020
@@ -67,6 +67,7 @@ git clone https://github.com/urbit/tools /root/src/tools
6767| `tools/type-import-overlap-test` | Checks source-aware Hoon union overlap, provenance and proof-budget controls under eval; the same fixture runs on the ship |
6868| `tools/type-import-structural-test` | Checks multi-case and nested Hoon union overlap witnesses under eval and on the ship |
6969| `tools/type-union-source-test` | Checks bounded local generic-alias disjointness and state guards under eval and on the ship |
70+| `tools/type-union-alias-overlap-test` | Checks source-backed generic-alias overlap witnesses under eval and on the ship |
7071| `tools/type-tlon-test` | Runs the real Tlon presence source through the importer and partial checker, comparing `tests/type-tlon/expected.txt` under eval |
7172| `tools/type-import-diagnostic-test` | Compares the structured reader diagnostic bridge report under eval |
7273| `tools/type-mark-test` | Checks the partial checker’s mark import boundary against `tests/type-mark/expected.txt` under eval |
@@ -80,6 +81,7 @@ git clone https://github.com/urbit/tools /root/src/tools
8081| `tools/type-generic-pattern-test` | Checks source-backed recursive patterns, catch-all coverage, state and per-attempt fuel/arm limits under eval and on the ship |
8182| `tools/type-deferred-test` | Checks the bounded source-owned deferred inference transaction under eval and on the ship |
8283| `tools/type-deferred-resolver-test` | Checks deferred resolution, report consistency and atomic failure under eval and on the ship |
84+| `tools/type-deferred-context-test` | Checks deferred source order and literal branch contexts under eval and on the ship |
8385| `tools/type-cell-branch-test` | Checks closed-cell branch context, rollback and bounded retry screens under eval and on the ship |
8486| `tools/type-generic-alias-test` | Runs public/private generic union alias and provenance guards under eval and on the ship |
8587| `tools/type-positional-face-guards-test` | Runs positional-call and nested-face guards under eval and on the ship |
@@ -119,6 +121,7 @@ tools/type-import-test # importer/checker bridge golden report match
119121tools/type-import-overlap-test # source-aware Hoon overlap and provenance controls
120122tools/type-import-structural-test # nested and multi-case Hoon overlap controls
121123tools/type-union-source-test # local generic-alias union guards
124+tools/type-union-alias-overlap-test # local generic-alias overlap diagnostics
122125tools/type-tlon-test # real Tlon importer/checker report matches
123126tools/type-import-diagnostic-test # direct structured reader diagnostics match
124127tools/type-mark-test # mark/checker bridge golden report matches
@@ -138,6 +141,7 @@ tools/type-generic-construction-test # source-backed recursive construction and
138141tools/type-generic-pattern-test # source-backed recursive patterns and bounds
139142tools/type-deferred-test # source-owned deferred inference guards
140143tools/type-deferred-resolver-test # resolver consistency and failure guards
144+tools/type-deferred-context-test # deferred source-order and branch guards
141145tools/type-cell-branch-test # contextual branch rollback and bounds
142146tools/type-closed-gate-guards-test # closed gate fit guards pass
143147tools/type-constructor-fit-guards-test # constructor fit guards pass
docs/status.md+8−5
@@ -27,7 +27,7 @@ The internal prelude also defines `qath` as a transparent recursive knot sequenc
2727
2828The 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 explicit tags or nonbinding catch-alls also work for these source-proven trees, as does a missing-case diagnostic for a supported single-arm match. At most 128 arms are screened per attempt; whole-case binders and nested patterns remain unsupported. 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
30−A bounded local source proof recognizes a narrow class of generic-alias union cases with different literal heads, then applies the ordinary mold checks. Nested or qualified aliases and several open states remain unsupported; the partial report states that the mold itself has not been checked. `tools/type-union-source-test` exercises the source and state bounds and failure paths under eval and on the ship.
30+A bounded local source proof recognizes a narrow class of generic-alias union cases with different literal heads, then applies the ordinary mold checks. Nested or qualified aliases and several open states remain unsupported, including a valid alias used during the deferred transaction; the partial report states that the mold itself has not been checked. `tools/type-union-source-test` exercises the source and state bounds and failure paths under eval and on the ship. A separate bounded witness now reports proven collisions between supported literal-headed aliases, with both original case sites; unknown payload intersections still decline. Full module failure remains generic, and the ship replay for this witness is pending.
3131
3232The checker can experimentally discharge a narrowly scoped direct-constructor constraint while resolving an import-free library without a prepared prelude. It preserves independent generalized schemes and publishes only after all source molds, values, and recorded constraints close. API/store paths, `main`, imports, prepared preludes, recursive groups, open constraints, projection actions, and `%grec` in the constraint graph remain unsupported by this route. The fallback also declines source shapes outside its bounded direct-call screen. It does not yet typecheck the unchanged todo island or establish watchability.
3333
@@ -61,9 +61,9 @@ The prelude has Jibb-written curried `lead` and `late` pair constructors, inferr
6161
6262The prelude also has a Jibb-written `zing` that concatenates homogeneous lists via `weld`. Its focused golden infers empty, nested text, ship, generic and gate-element lists and reports a non-list error; the same golden passes on the ship. A Hoon translation agrees with Hoon `zing` on selected finite inputs, not on Jibb execution. Hoon’s raw helper additionally accepts heterogeneous noun lists, which the Jibb signature does not express. Backend lowering and jet recognition remain unverified.
6363
64−The experimental deferred constructor transaction conservatively declines tuples, branches, field reads, nonidentical successful call/cast fits, and multi-argument calls with open argument, sample or result types. This prevents a later substitution from invalidating an earlier face-propagation check; cases requiring deferred structural fits remain unsupported. Focused regression checks exercise both source orders and independent polymorphism.
64+The experimental deferred constructor transaction conservatively declines tuples, most branches, field reads, nonidentical successful call/cast fits, and multi-argument calls with open argument, sample or result types. This prevents a later substitution from invalidating an earlier face-propagation check; cases requiring deferred structural fits remain unsupported. It can retry source definitions after later dependencies become available, and a two-arm boolean branch can retain separately owned literal-result obligations until a later closed context resolves them. Faced targets or union cases, list literals and general union-pattern branches remain unsupported.
6565
66−The checked inference slice now represents `!!`, proves empty cell positions and filters proven-empty union cases. List and unit payload arms whose elements are empty are rejected as unreachable; fixed opaque cases fail closed when reachability is unknown. The new shared guard covers checked interfaces, partial diagnostics and proof budgets. Its evaluator is run separately; ship verification remains pending.
66+The checked inference slice now represents `!!`, proves empty cell positions and filters proven-empty union cases. List and unit payload arms whose elements are empty are rejected as unreachable; fixed opaque cases fail closed when reachability is unknown. The new shared guard covers checked interfaces, partial diagnostics and proof budgets. Its shared guard runs under eval and on the ship.
6767
6868An eval and ship fixture now reports partial inference for the unchanged todo island with its real API and Hoon readers. It verifies that the source fixtures match `my-app` and exercises missing or duplicate imports; the report does not claim the module or island has been checked. The prelude also has a Jibb-written recursive `lent` whose supported generic calls infer `@ud`. Execution and shape-based jet recognition remain future backend work. Nested union tests check equal runtime nouns, different nested tags, empty fields and unknown inhabitation.
6969
@@ -90,7 +90,7 @@ What the plan asks for, and where it is:
9090Besides the plan's list:
9191
9292- 658 golden cases: 438 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.
93−- The 658-case corpus passed under `urbit eval` in 115.2 s while other checks were running. The fake ship passed all 700 tests, including the source-overlap, structural-overlap, generic-union source, generic-recursion, generic-construction, generic-pattern, deferred-inference, empty-mold and cell-branch 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 108.8 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.
93+- The 658-case corpus passed under `urbit eval` in 106.8 s while other checks were running. The fake ship passed all 702 tests, including the source-overlap, structural-overlap, generic-union source and alias-overlap, deferred-context, generic-recursion, generic-construction, generic-pattern, deferred-inference, empty-mold and cell-branch 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 107.6 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.
9494- `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.
9595- 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.
9696- 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).
@@ -200,7 +200,8 @@ Besides the plan's list:
200200| 72d85ab | 2026-10-31 | Add bounded deferred inference and expand runtime equality coverage |
201201| 22f3788 | 2026-10-31 | Check empty molds and propagate closed cell context into branches |
202202| 341bcb4 | 2026-10-31 | Keep deferred fits stable under later substitutions |
203−| This change | 2026-10-31 | Prove local generic-alias union tags and add successor |
203+| b93a94b | 2026-10-31 | Prove local generic-alias union tags and add successor |
204+| This change | 2026-10-31 | Report generic-alias overlaps and retain deferred branch contexts |
204205
205206## Checking it
206207
@@ -214,6 +215,7 @@ tools/type-import-test # the importer/checker bridge fixture
214215tools/type-import-overlap-test # source-aware Hoon overlap and provenance fixture
215216tools/type-import-structural-test # structural overlap and multi-case source fixture
216217tools/type-union-source-test # local generic-alias union guards
218+tools/type-union-alias-overlap-test # local generic-alias overlap diagnostics
217219tools/type-tlon-test # the real Tlon reader/checker fixture
218220tools/type-mark-test # the mark/checker bridge fixture
219221tools/type-poke-test # the source-backed poke/API fixture
@@ -227,6 +229,7 @@ tools/type-generic-construction-test # source-proven recursive construction guar
227229tools/type-generic-pattern-test # source-proven recursive pattern guards
228230tools/type-deferred-test # source-owned deferred inference guards
229231tools/type-deferred-resolver-test # resolver consistency and failure guards
232+tools/type-deferred-context-test # deferred source-order and branch guards
230233tools/type-empty-test # empty molds and coverage guards
231234tools/type-cell-branch-test # branch context and rollback guards
232235tools/type-positional-face-guards-test # positional sample face guards
jibb-dev/lib/jibb.hoon+225−24
@@ -8458,6 +8458,7 @@
84588458 ?: ?=(%| -.result) result
84598459 [%& typ.p.result state.p.result(globals globals.state, molds molds.state)]
84608460 %branch
8461+ ?^ defer.state (dg-boolean-branch e env state)
84618462 ?~ arms.e [%| ["branch is not supported by partial inference" ~]]
84628463 ?~ t.arms.e
84638464 =/ empty-list=(unit iresult) (empty-list-null-branch e env state ~)
@@ -9239,6 +9240,7 @@
92399240++ infer-call-context-arg
92409241 |= [e=expr expected=itype env=ienv state=istate context=span]
92419242 ^- iresult
9243+ ?: &(?=(^ defer.state) ?=(%branch -.e)) (infer-fragment e env state)
92429244 ?: ?=(%pair -.e) $(e (pair-as-cell e))
92439245 ?: ?=(%group -.e) $(e inner.e)
92449246 ?: &(?=(%body -.e) ?=(~ defs.e)) $(e final.e)
@@ -10694,6 +10696,92 @@
1069410696 ?~ active |
1069510697 ?=(^ (union-state-tags-budget tags.state u.active))
1069610698::
10699+:: Positive overlap witnesses from parsed, one-hop local source only.
10700+:: Unknown or exhausted work returns no witness, never disjointness.
10701++$ uow-case [at=span head=@ payload=upat]
10702+++ union-overlap-primitive
10703+ |= s=shape
10704+ ^- (unit upat)
10705+ ?- -.s
10706+ %aura `[%atom ~]
10707+ %noun `[%all ~]
10708+ %void `[%empty ~]
10709+ %flag `[%flag ~]
10710+ %const
10711+ ?. |(=(%term form.lit.s) =(%null form.lit.s) =(%loobean form.lit.s)) ~
10712+ ?: &(=(%loobean form.lit.s) !|(=(0 val.lit.s) =(1 val.lit.s))) ~
10713+ `[%atom `val.lit.s]
10714+ * ~
10715+ ==
10716+++ union-overlap-case
10717+ |= [s=shape molds=imolds fuel=@ud]
10718+ ^- (unit [item=uow-case left=@ud])
10719+ ?: =(0 fuel) ~
10720+ =. fuel (dec fuel)
10721+ :: Each body has exactly two fields; an application has exactly one
10722+ :: parameter and one concrete primitive argument. The full mold table
10723+ :: is scanned to establish unique ownership.
10724+ =/ expanded=(unit [body=shape param=(unit [name=@t pat=upat]) left=@ud])
10725+ ?: ?=(%cell -.s) `[s ~ fuel]
10726+ ?. ?=(%apply -.s) ~
10727+ ?. ?=([@ ~] ref.s) ~
10728+ ?. ?=([* ~] args.s) ~
10729+ ?: =(0 fuel) ~
10730+ =/ found=(unit [body=shape left=@ud]) (union-source-find i.ref.s molds (dec fuel))
10731+ ?~ found ~
10732+ ?. ?=(%generic -.body.u.found) ~
10733+ ?. ?=([@ ~] vars.body.u.found) ~
10734+ ?: =(0 left.u.found) ~
10735+ =/ argument=(unit upat) (union-overlap-primitive i.args.s)
10736+ ?~ argument ~
10737+ `[body.body.u.found `[i.vars.body.u.found u.argument] (dec left.u.found)]
10738+ ?~ expanded ~
10739+ =/ body=shape body.u.expanded
10740+ ?. ?=(%cell -.body) ~
10741+ ?. ?=([* * ~] fields.body) ~
10742+ :: Charge the cell, two fields, and the head and payload nodes.
10743+ ?: (lth left.u.expanded 5) ~
10744+ =/ head=(unit @) (union-source-cell-tag body)
10745+ ?~ head ~
10746+ =/ field=field i.t.fields.body
10747+ =/ payload=shape
10748+ ?- -.field
10749+ %bare shape.field
10750+ %face shape.field
10751+ %pun [%ref at.field ~[name.field]]
10752+ ==
10753+ =/ pattern=(unit upat)
10754+ ?: ?=(%tvar -.payload)
10755+ ?~ param.u.expanded ~
10756+ ?. =(name.payload name.u.param.u.expanded) ~
10757+ `pat.u.param.u.expanded
10758+ (union-overlap-primitive payload)
10759+ ?~ pattern ~
10760+ `[[at.s u.head u.pattern] (sub left.u.expanded 5)]
10761+++ union-overlap-against
10762+ |= [first=uow-case rest=(list shape) molds=imolds fuel=@ud]
10763+ ^- (unit [witness=(unit upair) left=@ud])
10764+ ?~ rest `[~ fuel]
10765+ ?: =(0 fuel) ~
10766+ =/ other=(unit [item=uow-case left=@ud]) (union-overlap-case i.rest molds (dec fuel))
10767+ ?~ other ~
10768+ ?: &(=(head.first head.item.u.other) =(%overlap (union-relate payload.first payload.item.u.other)))
10769+ `[`[at.first at.item.u.other %overlap] left.u.other]
10770+ $(rest t.rest, fuel left.u.other)
10771+++ union-source-overlap
10772+ |= [cases=(list shape) molds=imolds fuel=@ud]
10773+ ^- (unit upair)
10774+ ?~ cases ~
10775+ ?~ t.cases ~
10776+ ?: =(0 fuel) ~
10777+ =/ first=(unit [item=uow-case left=@ud]) (union-overlap-case i.cases molds (dec fuel))
10778+ ?~ first ~
10779+ =/ found=(unit [witness=(unit upair) left=@ud])
10780+ (union-overlap-against item.u.first t.cases molds left.u.first)
10781+ ?~ found ~
10782+ ?^ witness.u.found witness.u.found
10783+ $(cases t.cases, fuel left.u.found)
10784+::
1069710785++ union-pairs
1069810786 |= [cases=(list shape) molds=imolds]
1069910787 ^- (list upair)
@@ -10847,6 +10935,11 @@
1084710935 ?. ?=(%union -.sh) (crip "+$ {(trip name.d)}: not checked")
1084810936 =/ pairs=(list upair) (union-pairs cases.sh molds)
1084910937 =/ overlapping=(list upair) (skim pairs |=(p=upair =(%overlap relation.p)))
10938+ =. overlapping
10939+ ?^ overlapping overlapping
10940+ ?. (lien pairs |=(p=upair =(%unknown relation.p))) ~
10941+ =/ witness=(unit upair) (union-source-overlap cases.sh molds 256)
10942+ ?~(witness ~ ~[u.witness])
1085010943 ?^ overlapping
1085110944 =/ p=upair i.overlapping
1085210945 =/ summary=@t (crip "+$ {(trip name.d)}: overlapping cases; mold not checked")
@@ -12736,6 +12829,14 @@
1273612829 `[[normalized quantified]]
1273712830::
1273812831+$ dgcommit [env=ienv state=istate]
12832+:: A branch literal must not acquire a value face through replay.
12833+++ dg-literal-target-safe
12834+ |= target=itype
12835+ ^- ?
12836+ ?: ?=(%union -.target)
12837+ (levy cases.target |=(c=itype ?=(?(%const %cell) -.c)))
12838+ ?=(?(%const %atom %any-atom %flag %noun) -.target)
12839+::
1273912840++ dg-replay-edges
1274012841 |= [edges=(list dgo) state=istate fuel=@ud]
1274112842 ^- (unit istate)
@@ -12743,7 +12844,16 @@
1274312844 ?: =(0 fuel) ~
1274412845 =/ trial=ifit (itype-fit left.i.edges right.i.edges sub.state next.state)
1274512846 ?. ?=(%ok -.trial) ~
12746− ?. =((itype-walk left.i.edges sub.trial) (itype-walk right.i.edges sub.trial)) ~
12847+ =/ valid=?
12848+ ?: ?=(%tag -.left.i.edges)
12849+ ?. constructor.left.i.edges
12850+ =/ closed=(unit dgscan) (dg-scan ~[right.i.edges] sub.trial 512 ~)
12851+ ?~ closed |
12852+ ?^ ids.u.closed |
12853+ (dg-literal-target-safe (itype-normalize right.i.edges sub.trial))
12854+ =((itype-walk left.i.edges sub.trial) (itype-walk right.i.edges sub.trial))
12855+ =((itype-walk left.i.edges sub.trial) (itype-walk right.i.edges sub.trial))
12856+ ?. valid ~
1274712857 $(edges t.edges, state state(sub sub.trial, next next.trial), fuel (dec fuel))
1274812858::
1274912859++ dg-close-env
@@ -12764,6 +12874,68 @@
1276412874 ?~ rest ~
1276512875 `[[name.i.env [normalized vars.s]] u.rest]
1276612876::
12877+:: A deferred boolean branch has exactly the two literal flag patterns.
12878+:: Arms are inferred in order. Equal types are retained; two direct pending
12879+:: literals may instead retain source-owned obligations for later context.
12880+++ dg-boolean-patterns
12881+ |= e=expr
12882+ ^- ?
12883+ ?. ?=(%branch -.e) |
12884+ ?~ arms.e |
12885+ ?~ t.arms.e |
12886+ ?^ t.t.arms.e |
12887+ =/ x=arm i.arms.e
12888+ =/ y=arm i.t.arms.e
12889+ ?. &(?=(%lit -.pat.x) ?=(%lit -.pat.y)) |
12890+ ?. &(=(%loobean form.lit.pat.x) =(%loobean form.lit.pat.y)) |
12891+ !=(val.lit.pat.x val.lit.pat.y)
12892+:: Only a direct source literal which created its own unbound pending tag
12893+:: may create a branch edge. The caller must retain the exact source and state.
12894+++ dg-owned-arm-literal
12895+ |= [e=expr t=itype tags=(list ipending) sub=isub initial=@ud]
12896+ ^- ?
12897+ ?. ?=(%lit -.e) |
12898+ ?. ?=(%tag -.t) |
12899+ ?: constructor.t |
12900+ ?. =(lit.e literal.t) |
12901+ ?: (lth id.t initial) |
12902+ ?^ (itype-lookup id.t sub) |
12903+ (lien tags |=(x=ipending &(=(id.x id.t) =(literal.x literal.t))))
12904+++ dg-boolean-branch
12905+ |= [e=expr env=ienv state=istate]
12906+ ^- iresult
12907+ ?. (dg-boolean-patterns e) [%| ["deferred branch patterns need unsupported inference" ~]]
12908+ ?> ?=(%branch -.e)
12909+ ?> ?=(^ arms.e)
12910+ ?> ?=(^ t.arms.e)
12911+ =/ x=arm i.arms.e
12912+ =/ y=arm i.t.arms.e
12913+ =/ subject=iresult (infer-fragment subject.e env state)
12914+ ?: ?=(%| -.subject) subject
12915+ =/ su=(unit isub) (itype-unify typ.p.subject [%flag ~] sub.state.p.subject)
12916+ ?~ su [%| ["deferred branch subject does not unify with ?" ~]]
12917+ =/ left=iresult (infer-fragment val.x env state.p.subject(sub u.su))
12918+ ?: ?=(%| -.left) left
12919+ =/ right=iresult (infer-fragment val.y env state.p.left)
12920+ ?: ?=(%| -.right) right
12921+ ?. (dg-stable-fit typ.p.left typ.p.right sub.state.p.right)
12922+ =/ st=istate state.p.right
12923+ ?. (dg-owned-arm-literal val.x typ.p.left tags.st sub.st next.state)
12924+ [%| ["deferred branch arm context needs unsupported inference" ~]]
12925+ ?. (dg-owned-arm-literal val.y typ.p.right tags.st sub.st next.state)
12926+ [%| ["deferred branch arm context needs unsupported inference" ~]]
12927+ ?> ?=(%tag -.typ.p.left)
12928+ ?> ?=(%tag -.typ.p.right)
12929+ ?: =(id.typ.p.left id.typ.p.right)
12930+ [%| ["deferred branch arm context needs unsupported inference" ~]]
12931+ ?: (gth (lent obligations.st) 62)
12932+ [%| ["deferred branch arm context needs unsupported inference" ~]]
12933+ =/ result=itype [%var next.st]
12934+ =/ ex=dgo [typ.p.left result (span-of val.x) (span-of e)]
12935+ =/ ey=dgo [typ.p.right result (span-of val.y) (span-of e)]
12936+ [%& result st(next +(next.st), obligations [ex ey obligations.st])]
12937+ [%& typ.p.left state.p.right]
12938+::
1276712939:: Deferred inference uses only source forms whose successful fit sites are
1276812940:: guarded below; a shared source-node budget bounds this screen.
1276912941++ dg-safe-expressions
@@ -12790,6 +12962,13 @@
1279012962 $(e final.e, fuel fuel)
1279112963 %gate $(e body.e, fuel fuel)
1279212964 %gate-wide $(e body.e, fuel fuel)
12965+ %branch
12966+ ?. (dg-boolean-patterns e) ~
12967+ ?> ?=(^ arms.e)
12968+ ?> ?=(^ t.arms.e)
12969+ =/ subject=(unit @ud) $(e subject.e, fuel fuel)
12970+ ?~ subject ~
12971+ (dg-safe-expressions ~[val.i.arms.e val.i.t.arms.e] u.subject)
1279312972 %cast $(e val.e, fuel fuel)
1279412973 %call
1279512974 =/ head=(unit @ud) $(e head.e, fuel fuel)
@@ -12814,28 +12993,12 @@
1281412993 ?: &(positional ?=(^ ids.u.checked)) |
1281512994 =((itype-normalize actual sub) (itype-normalize expected sub))
1281612995::
12817−++ dg-stage
12818− |= [ds=(list def) base=ienv env=ienv state=istate pending=(list dgo) fuel=@ud]
12819− ^- (unit dgcommit)
12820− ?~ ds
12821− =/ replayed=(unit istate) (dg-replay-edges pending state 64)
12822− ?~ replayed ~
12823− ?: (pending-tags tags.u.replayed sub.u.replayed) ~
12824− =/ closed-edges=(unit dgscan) (dg-obligations pending sub.u.replayed 32.768 ~)
12825− ?~ closed-edges ~
12826− ?^ ids.u.closed-edges ~
12827− =/ rechecked=(unit istate) (dg-replay-edges pending u.replayed 64)
12828− ?~ rechecked ~
12829− ?. =(sub.u.rechecked sub.u.replayed) ~
12830− ?. =(next.u.rechecked next.u.replayed) ~
12831− =/ closed=(unit ienv) (dg-close-env env sub.u.replayed 32.768)
12832− ?~ closed ~
12833− `[[u.closed u.replayed(defer ~, obligations ~)]]
12834− ?: =(0 fuel) ~
12835− =/ d=def i.ds
12836− ?: ?=(%mold -.d) $(ds t.ds, fuel (dec fuel))
12837− ?^ (ienv-lookup name.d base) $(ds t.ds, fuel (dec fuel))
12838− ?^ (ienv-lookup name.d env) ~
12996+:: One definition attempt retains state only when inference succeeds.
12997++$ dg-step [env=ienv state=istate pending=(list dgo)]
12998+++ dg-stage-one
12999+ |= [d=def env=ienv state=istate]
13000+ ^- (unit dg-step)
13001+ ?> ?=(%value -.d)
1283913002 ?~ (dg-safe-expression val.d 512) ~
1284013003 =/ local=istate (reserve-def-caps val.d state)
1284113004 :: This first slice declines all written capitals and holes/projections.
@@ -12871,7 +13034,45 @@
1287113034 ?: (itype-has-nvar (itype-normalize typ sub.after)) ~
1287213035 =/ scheme=(unit ischeme) (dg-generalize typ env edges tags.after ~ after 32.768)
1287313036 ?~ scheme ~
12874− $(ds t.ds, env [[name.d u.scheme] env], state after, pending edges, fuel (dec fuel))
13037+ `[[[name.d u.scheme] env] after edges]
13038+:: Retry only definitions that did not infer. Successful definitions are
13039+:: committed into the local transaction, never published until all close.
13040+++ dg-stage
13041+ |= [ds=(list def) base=ienv env=ienv state=istate pending=(list dgo) fuel=@ud]
13042+ ^- (unit dgcommit)
13043+ ?: (gth fuel 64) ~
13044+ ?: (gth (lent ds) fuel) ~
13045+ =/ remaining=(list def) ds
13046+ =/ waiting=(list def) ~
13047+ =/ progress=? |
13048+ =/ attempts=@ud (mul fuel fuel)
13049+ |-
13050+ ?~ remaining
13051+ ?^ waiting
13052+ ?. progress ~
13053+ $(remaining (flop waiting), waiting ~, progress |)
13054+ =/ replayed=(unit istate) (dg-replay-edges pending state 64)
13055+ ?~ replayed ~
13056+ ?: (pending-tags tags.u.replayed sub.u.replayed) ~
13057+ =/ closed-edges=(unit dgscan) (dg-obligations pending sub.u.replayed 32.768 ~)
13058+ ?~ closed-edges ~
13059+ ?^ ids.u.closed-edges ~
13060+ =/ rechecked=(unit istate) (dg-replay-edges pending u.replayed 64)
13061+ ?~ rechecked ~
13062+ ?. =(sub.u.rechecked sub.u.replayed) ~
13063+ ?. =(next.u.rechecked next.u.replayed) ~
13064+ =/ closed=(unit ienv) (dg-close-env env sub.u.replayed 32.768)
13065+ ?~ closed ~
13066+ `[[u.closed u.replayed(defer ~, obligations ~)]]
13067+ ?: =(0 attempts) ~
13068+ =. attempts (dec attempts)
13069+ =/ d=def i.remaining
13070+ ?: ?=(%mold -.d) $(remaining t.remaining)
13071+ ?^ (ienv-lookup name.d base) $(remaining t.remaining)
13072+ ?^ (ienv-lookup name.d env) ~
13073+ =/ done=(unit dg-step) (dg-stage-one d env state)
13074+ ?~ done $(remaining t.remaining, waiting [d waiting])
13075+ $(remaining t.remaining, env env.u.done, state state.u.done, pending pending.u.done, progress &)
1287513076::
1287613077:: Cheap bounded eligibility check on the already parsed source. This
1287713078:: deliberately covers only direct constructor calls beneath transparent
tests/golden/types-union-source-alias/about.txt+1−1
@@ -1,4 +1,4 @@
11source: Grammar, Unions: "Cases must differ as nouns"; Grammar, Generic molds.
2−tests: A one-hop local generic alias with a literal head is proven disjoint from a different head, including with outer generic parameters. Same-head and equal-noun zero spellings remain unsupported or rejected; this partial report does not claim the full module is checked.
2+tests: A one-hop local generic alias with a literal head is proven disjoint from a different head, including with outer generic parameters. Same-head and equal-noun zero spellings with overlapping payloads report both case sites; this partial report does not claim the full module is checked.
33expect: parses
44expect-types: a partial inference report
tests/golden/types-union-source-alias/types.txt+14−2
@@ -4,8 +4,20 @@ partial inference; module not checked
44++ id: $-(choice choice)
55+$ poly: cases proven disjoint; mold not checked
66++ typed: $-((poly @t @p) (poly @t @p))
7−+$ same: case disjointness unresolved; mold not checked
7++$ same: overlapping cases; mold not checked
8+/tests/golden/types-union-source-alias/source.jibb:11:3: first union case
9+ (tagged @t)
10+ ^
11+/tests/golden/types-union-source-alias/source.jibb:12:3: overlapping case
12+ [%wrapped @p]
13+ ^
814++ collide: sample mold is not supported by partial inference
915+$ zero: not checked
10−+$ same-zero: case disjointness unresolved; mold not checked
16++$ same-zero: overlapping cases; mold not checked
17+/tests/golden/types-union-source-alias/source.jibb:16:3: first union case
18+ (zero @t)
19+ ^
20+/tests/golden/types-union-source-alias/source.jibb:17:3: overlapping case
21+ [%$ @p]
22+ ^
1123++ collide-zero: sample mold is not supported by partial inference
tests/lib/jibb-deferred-context.hoonnew file+83
@@ -0,0 +1,83 @@
1+/+ jibb
2+/* rich %txt /tests/type-deferred-context/rich/txt
3+/* plain %txt /tests/type-deferred-context/plain/txt
4+/* simple %txt /tests/type-deferred-context/simple/txt
5+/* missing %txt /tests/type-deferred-context/missing/txt
6+/* wrong %txt /tests/type-deferred-context/wrong/txt
7+/* duplicate %txt /tests/type-deferred-context/duplicate/txt
8+/* dangling %txt /tests/type-deferred-context/dangling/txt
9+/* collision-early %txt /tests/type-deferred-context/collision-early/txt
10+/* collision-late %txt /tests/type-deferred-context/collision-late/txt
11+/* expected %txt /tests/type-deferred-context/report/txt
12+|%
13+++ text
14+ |= lines=wain
15+ ^- @t
16+ (rap 3 (turn lines |=(line=@t (cat 3 line '\0a'))))
17+++ test-context
18+ ^- tang
19+ =/ at=path /ui/context/jibb
20+ =/ good=(list wain) ~[rich plain simple]
21+ =/ bad=(list wain) ~[missing wrong duplicate dangling collision-early collision-late]
22+ ?. (levy (weld good bad) |=(src=wain ?=(^ src))) ~[leaf+"source missing"]
23+ =/ source=@t (text rich)
24+ =/ parsed (parse:jibb at source)
25+ ?: ?=(%| -.parsed) ~[leaf+"positive source does not parse"]
26+ =/ result=jfile-result:jibb (infer-jibb-file:jibb at source ~ ~ ~ ~[at] ~ ~ ~ ~)
27+ ?: ?=(%| -.result) ~[leaf+"positive library did not publish"]
28+ =/ values=ienv:jibb values.iface.p.result
29+ =/ built=(unit ischeme:jibb) (ienv-lookup:jibb %built values)
30+ ?~ built ~[leaf+"built missing"]
31+ ?~ vars.u.built ~[leaf+"principal polymorphism lost"]
32+ ?^ t.vars.u.built ~[leaf+"unexpected additional quantifiers"]
33+ =/ built-type=itype:jibb typ.u.built
34+ ?. ?=(%fn -.built-type) ~[leaf+"built is not a gate"]
35+ ?. ?=(%var -.sample.built-type) ~[leaf+"built sample is not generic"]
36+ ?. =(sample.built-type product.built-type) ~[leaf+"built input and result differ"]
37+ ?. =(id.sample.built-type i.vars.u.built) ~[leaf+"built quantified identity differs"]
38+ =/ reveal=(unit ischeme:jibb) (ienv-lookup:jibb %reveal values)
39+ =/ reveal-ship=(unit ischeme:jibb) (ienv-lookup:jibb %reveal-ship values)
40+ ?~ reveal ~[leaf+"text result missing"]
41+ ?~ reveal-ship ~[leaf+"ship result missing"]
42+ ?. ?=([%atom %t] typ.u.reveal) ~[leaf+"text result changed"]
43+ ?. ?=([%atom %p] typ.u.reveal-ship) ~[leaf+"ship result changed"]
44+ =/ report=@t (golden-types-with-marks:jibb at source ~ ~)
45+ ?. =((to-wain:format (cat 3 report '\0a')) expected) ~[leaf+"report differs from committed types"]
46+ =/ rest-good=(list wain) ~[plain simple]
47+ =/ checked=?
48+ %+ levy rest-good
49+ |= src=wain
50+ =/ source=@t (text src)
51+ =/ parsed (parse:jibb at source)
52+ ?: ?=(%| -.parsed) |
53+ =/ result=jfile-result:jibb (infer-jibb-file:jibb at source ~ ~ ~ ~[at] ~ ~ ~ ~)
54+ ?=(%& -.result)
55+ ?. checked ~[leaf+"simple positive declined or did not parse"]
56+ =. checked
57+ %+ levy bad
58+ |= src=wain
59+ =/ source=@t (text src)
60+ =/ parsed (parse:jibb at source)
61+ ?: ?=(%| -.parsed) |
62+ =/ result=jfile-result:jibb (infer-jibb-file:jibb at source ~ ~ ~ ~[at] ~ ~ ~ ~)
63+ ?=(%| -.result)
64+ ?. checked ~[leaf+"negative source published or did not parse"]
65+ =/ literal=lit:jibb [%term %tas %none '%none']
66+ =/ actual=itype:jibb [%tag 10 literal |]
67+ =/ st=istate:jibb *istate:jibb
68+ =. st st(next 20)
69+ ?^ (dg-stage:jibb ~ ~ ~ st ~ 65) ~[leaf+"oversized retry fuel accepted"]
70+ ?^ (dg-stage:jibb defs.p.parsed ~ ~ st ~ 0) ~[leaf+"source beyond retry fuel accepted"]
71+ =/ atom=itype:jibb [%const literal]
72+ =/ faced=itype:jibb [%face %named atom]
73+ =/ union=itype:jibb [%union ~[atom [%cell [%const [%term %tas %some '%some']] [%face %value [%atom %t]]]] ~ ~ ~]
74+ =/ faced-case=itype:jibb [%union ~[faced] ~ ~ ~]
75+ =/ fit
76+ |= target=itype:jibb
77+ ^- ?
78+ =/ edge=dgo:jibb [actual target *span:jibb *span:jibb]
79+ ?=(^ (dg-replay-edges:jibb ~[edge] st 64))
80+ ?. &((fit atom) (fit union)) ~[leaf+"plain literal replay declined"]
81+ ?: |((fit faced) (fit faced-case)) ~[leaf+"faced literal replay accepted"]
82+ ~
83+--
tests/lib/jibb-union-alias-overlap.hoonnew file+107
@@ -0,0 +1,107 @@
1+:: Source-backed alias overlap diagnostics, without a full module claim.
2+/+ jibb
3+/* positive %txt /tests/type-union-alias-overlap/positive/txt
4+/* negative %txt /tests/type-union-alias-overlap/negative/txt
5+/* nonzero %txt /tests/type-union-alias-overlap/nonzero/txt
6+/* multi %txt /tests/type-union-alias-overlap/multi/txt
7+/* opaque %txt /tests/type-union-alias-overlap/opaque/txt
8+/* wrong %txt /tests/type-union-alias-overlap/wrong/txt
9+/* duplicate %txt /tests/type-union-alias-overlap/duplicate/txt
10+/* qualified %txt /tests/type-union-alias-overlap/qualified/txt
11+/* flag-source %txt /tests/type-union-alias-overlap/flag-source/txt
12+/* expected %txt /tests/type-union-alias-overlap/expected/txt
13+|%
14+++ checks
15+ |= [positive=@t negative=@t nonzero=@t multi=@t opaque=@t wrong=@t duplicate=@t qualified=@t flag-source=@t]
16+ ^- (list [@t ?])
17+ =/ project
18+ |= [source=@t name=term]
19+ ^- (unit [cases=(list shape:jibb) molds=imolds:jibb])
20+ =/ parsed (parse:jibb /tests/overlap/jibb source)
21+ ?: ?=(%| -.parsed) ~
22+ =/ molds=imolds:jibb (collect-simple-molds:jibb defs.p.parsed)
23+ =/ found=(unit shape:jibb) (find-simple-mold:jibb name molds)
24+ ?~ found ~
25+ =/ sh=shape:jibb u.found
26+ =? sh ?=(%generic -.sh) body.sh
27+ ?. ?=(%union -.sh) ~
28+ `[cases.sh molds]
29+ =/ a (project positive %same)
30+ =/ z (project positive %same-zero)
31+ =/ d (project negative %distinct-head)
32+ =/ c (project negative %distinct-constant)
33+ =/ e (project negative %empty-payload)
34+ =/ g (project negative %generic)
35+ =/ x (project negative %unknown-name)
36+ =/ nz (project nonzero %same)
37+ =/ m (project multi %choice)
38+ =/ o (project opaque %choice)
39+ =/ w (project wrong %bad)
40+ =/ dup (project duplicate %choice)
41+ =/ q (project qualified %choice)
42+ =/ fz (project flag-source %flag-zero)
43+ =/ fn (project flag-source %flag-nonzero)
44+ ?~ a ~[['BAD same parse' |]]
45+ ?~ z ~[['BAD zero parse' |]]
46+ ?~ d ~[['BAD distinct parse' |]]
47+ ?~ c ~[['BAD constants parse' |]]
48+ ?~ e ~[['BAD empty parse' |]]
49+ ?~ g ~[['BAD generic parse' |]]
50+ ?~ x ~[['BAD reference parse' |]]
51+ ?~ nz ~[['BAD nonzero parse' |]]
52+ ?~ m ~[['BAD multi parse' |]]
53+ ?~ o ~[['BAD opaque parse' |]]
54+ ?~ w ~[['BAD wrong arity parse' |]]
55+ ?~ dup ~[['BAD duplicate parse' |]]
56+ ?~ q ~[['BAD qualified parse' |]]
57+ ?~ fz ~[['BAD flag-zero parse' |]]
58+ ?~ fn ~[['BAD flag-nonzero parse' |]]
59+ ?~ cases.u.a ~[['BAD same cases' |]]
60+ ?~ t.cases.u.a ~[['BAD second same case' |]]
61+ =/ same=(unit upair:jibb) (union-source-overlap:jibb cases.u.a molds.u.a 256)
62+ =/ zero=(unit upair:jibb) (union-source-overlap:jibb cases.u.z molds.u.z 256)
63+ =/ report=wain (to-wain:format (golden-types-in-context:jibb /tests/overlap/jibb positive ~ ~ ~ ~ ~ ~))
64+ =/ failed=jfile-result:jibb (infer-jibb-file:jibb /tests/overlap/jibb positive ~ ~ ~ ~[/tests/overlap/jibb] ~ ~ ~ ~)
65+ =/ nonzero-report=wain (to-wain:format (golden-types-in-context:jibb /tests/overlap/jibb nonzero ~ ~ ~ ~ ~ ~))
66+ :~ ['same head and inhabited payload overlap' ?=(^ same)]
67+ ['zero spelling overlap' ?=(^ zero)]
68+ ['original same-case spans retained' ?~(same | &(=(first.u.same at.i.cases.u.a) =(second.u.same at.i.t.cases.u.a)))]
69+ ['partial report displays same overlap' (lien report |=(line=@t =(line '+$ same: overlapping cases; mold not checked')))]
70+ ['partial report displays zero overlap' (lien report |=(line=@t =(line '+$ same-zero: overlapping cases; mold not checked')))]
71+ ['distinct heads do not overlap' ?=(~ (union-source-overlap:jibb cases.u.d molds.u.d 256))]
72+ ['distinct singleton payloads do not overlap' ?=(~ (union-source-overlap:jibb cases.u.c molds.u.c 256))]
73+ ['empty payload has no witness' ?=(~ (union-source-overlap:jibb cases.u.e molds.u.e 256))]
74+ ['outer variable has no witness' ?=(~ (union-source-overlap:jibb cases.u.g molds.u.g 256))]
75+ ['unresolved reference has no witness' ?=(~ (union-source-overlap:jibb cases.u.x molds.u.x 256))]
76+ ['equal nonzero singleton payload overlaps' ?=(^ (union-source-overlap:jibb cases.u.nz molds.u.nz 256))]
77+ ['nonzero overlap reported' (lien nonzero-report |=(line=@t =(line '+$ same: overlapping cases; mold not checked')))]
78+ ['late pair in three cases overlaps' ?=(^ (union-source-overlap:jibb cases.u.m molds.u.m 256))]
79+ ['opaque gate payload declines' ?=(~ (union-source-overlap:jibb cases.u.o molds.u.o 256))]
80+ ['wrong alias arity declines' ?=(~ (union-source-overlap:jibb cases.u.w molds.u.w 256))]
81+ ['full module remains rejected' ?=(%| -.failed)]
82+ ['first source excerpt retained' (lien report |=(line=@t =(line '/tests/overlap.jibb:3:3: first union case')))]
83+ ['second source excerpt retained' (lien report |=(line=@t =(line '/tests/overlap.jibb:4:3: overlapping case')))]
84+ ['flag and zero payload overlap' ?=(^ (union-source-overlap:jibb cases.u.fz molds.u.fz 256))]
85+ ['flag and nonzero payload decline' ?=(~ (union-source-overlap:jibb cases.u.fn molds.u.fn 256))]
86+ ['duplicate owner declines' ?=(~ (union-source-overlap:jibb cases.u.dup molds.u.dup 256))]
87+ ['qualified owner declines' ?=(~ (union-source-overlap:jibb cases.u.q molds.u.q 256))]
88+ ['20 proof fuel succeeds' ?=(^ (union-source-overlap:jibb cases.u.a molds.u.a 20))]
89+ ['19 proof fuel declines' ?=(~ (union-source-overlap:jibb cases.u.a molds.u.a 19))]
90+ ['zero proof fuel declines' ?=(~ (union-source-overlap:jibb cases.u.a molds.u.a 0))]
91+ ==
92+++ overlap-output
93+ |= [positive=@t negative=@t nonzero=@t multi=@t opaque=@t wrong=@t duplicate=@t qualified=@t flag-source=@t]
94+ ^- @t
95+ =/ result=(list [@t ?]) (checks positive negative nonzero multi opaque wrong duplicate qualified flag-source)
96+ (of-wain:format (turn result |=(entry=[@t ?] (crip "{(trip -.entry)}: {?:(+.entry "ok" "BAD")}"))))
97+++ text
98+ |= lines=wain
99+ ^- @t
100+ (rap 3 (turn lines |=(line=@t (cat 3 line '\0a'))))
101+++ test-union-alias-overlap
102+ ^- tang
103+ ?~ expected ~[leaf+"expected output missing"]
104+ =/ actual=@t (overlap-output (text positive) (text negative) (text nonzero) (text multi) (text opaque) (text wrong) (text duplicate) (text qualified) (text flag-source))
105+ ?. =((to-wain:format (cat 3 actual '\0a')) expected) ~[leaf+"generic union overlap output differs"]
106+ ~
107+--
tests/type-deferred-context/about.txtnew file+3
@@ -0,0 +1,3 @@
1+source: docs/spec/grammar.md, Tag resolution: “A variable constrained by tag resolution, and any it mentions, doesn't generalize; it stays open until something in the module fixes it.”
2+
3+Tests bounded deferred source-order retries and source-owned literal branch obligations in import-free libraries. Each negative source must parse before it is rejected. This does not establish island kind, watchability or standard-prelude list semantics.
tests/type-deferred-context/collision-early.txtnew file+27
@@ -0,0 +1,27 @@
1+++ incompatible ^- $-(? other) choose
2++$ message
3+ [%got value=@t]
4+ [%other value=@p]
5++$ maybe
6+ %none
7+ [%some value=@t]
8++$ other
9+ %none
10+ [%ship value=@p]
11+++ final ^- $-(? maybe) choose
12+++ final-ship ^- $-(? other) choose-ship
13+++ choose |= flag=?
14+ ?- flag
15+ %.y %none
16+ %.n %none
17+++ choose-ship |= flag=?
18+ ?- flag
19+ %.y %none
20+ %.n %none
21+++ send |= f=$-(@t A) f
22+++ first (send |=%got)
23+++ fixed ^- $-(@t message) first
24+++ make |= ignored=A |= x=B x
25+++ built (make choose)
26+++ reveal (built "hello")
27+++ reveal-ship (built ~zod)
tests/type-deferred-context/collision-late.txtnew file+28
@@ -0,0 +1,28 @@
1++$ message
2+ [%got value=@t]
3+ [%other value=@p]
4++$ maybe
5+ %none
6+ [%some value=@t]
7++$ other
8+ %none
9+ [%ship value=@p]
10+++ final ^- $-(? maybe) choose
11+++ final-ship ^- $-(? other) choose-ship
12+++ choose |= flag=?
13+ ?- flag
14+ %.y %none
15+ %.n %none
16+++ choose-ship |= flag=?
17+ ?- flag
18+ %.y %none
19+ %.n %none
20+++ send |= f=$-(@t A) f
21+++ first (send |=%got)
22+++ fixed ^- $-(@t message) first
23+++ make |= ignored=A |= x=B x
24+++ built (make choose)
25+++ reveal (built "hello")
26+++ reveal-ship (built ~zod)
27+
28+++ incompatible ^- $-(? other) choose
tests/type-deferred-context/dangling.txtnew file+16
@@ -0,0 +1,16 @@
1++$ message
2+ [%got value=@t]
3+ [%other value=@p]
4++$ maybe
5+ %none
6+ [%some value=@t]
7+++ final ^- $-(? maybe) choose
8+++ choose |= flag=?
9+ ?- flag
10+ %.y %none
11+ %.n %none
12+++ send |= f=$-(@t A) f
13+++ first (send |=%got)
14+++ fixed ^- $-(@t message) first
15+
16+++ unrelated |= x=@t %dangling
tests/type-deferred-context/duplicate.txtnew file+14
@@ -0,0 +1,14 @@
1++$ message
2+ [%got value=@t]
3+ [%other value=@p]
4++$ maybe
5+ %none
6+ [%some value=@t]
7+++ final ^- $-(? maybe) choose
8+++ choose |= flag=?
9+ ?- flag
10+ %.y %none
11+ %.y %none
12+++ send |= f=$-(@t A) f
13+++ first (send |=%got)
14+++ fixed ^- $-(@t message) first
tests/type-deferred-context/missing.txtnew file+14
@@ -0,0 +1,14 @@
1++$ message
2+ [%got value=@t]
3+ [%other value=@p]
4++$ maybe
5+ %none
6+ [%some value=@t]
7+++ final ^- $-(? maybe) choose
8+++ choose |= flag=?
9+ ?- flag
10+ %.y %none
11+ %.n %absent
12+++ send |= f=$-(@t A) f
13+++ first (send |=%got)
14+++ fixed ^- $-(@t message) first
tests/type-deferred-context/plain.txtnew file+11
@@ -0,0 +1,11 @@
1+++ final ^- $-(@t message) second
2++$ message
3+ [%got value=@t]
4+ [%other value=@p]
5+++ send |= f=$-(@t A) f
6+++ make |= ignored=A |= x=B x
7+++ first (send |=%got)
8+++ second first
9+++ built (make first)
10+++ reveal (built "hello")
11+++ reveal-ship (built ~zod)
tests/type-deferred-context/report.txtnew file+15
@@ -0,0 +1,15 @@
1+partial inference; module not checked
2++$ message: cases proven disjoint; mold not checked
3++$ maybe: cases proven disjoint; mold not checked
4++$ other: cases proven disjoint; mold not checked
5+++ final: $-(? maybe)
6+++ final-ship: $-(? other)
7+++ choose: $-(? maybe)
8+++ choose-ship: $-(? other)
9+++ send: $-($-(@t A) $-(@t A))
10+++ first: $-(@t message)
11+++ fixed: $-(@t message)
12+++ make: $-(A $-(B B))
13+++ built: $-(A A)
14+++ reveal: @t
15+++ reveal-ship: @p
tests/type-deferred-context/rich.txtnew file+27
@@ -0,0 +1,27 @@
1++$ message
2+ [%got value=@t]
3+ [%other value=@p]
4++$ maybe
5+ %none
6+ %stop
7+ [%some value=@t]
8++$ other
9+ %none
10+ [%ship value=@p]
11+++ final ^- $-(? maybe) choose
12+++ final-ship ^- $-(? other) choose-ship
13+++ choose |= flag=?
14+ ?- flag
15+ %.y %none
16+ %.n %stop
17+++ choose-ship |= flag=?
18+ ?- flag
19+ %.y %none
20+ %.n %none
21+++ send |= f=$-(@t A) f
22+++ first (send |=%got)
23+++ fixed ^- $-(@t message) first
24+++ make |= ignored=A |= x=B x
25+++ built (make choose)
26+++ reveal (built "hello")
27+++ reveal-ship (built ~zod)
tests/type-deferred-context/simple.txtnew file+10
@@ -0,0 +1,10 @@
1++$ message
2+ [%got value=@t]
3+ [%other value=@p]
4+++ final ^- $-(@t message) (pick %.y)
5+++ pick |= flag=?
6+ ?- flag
7+ %.y first
8+ %.n first
9+++ send |= f=$-(@t A) f
10+++ first (send |=%got)
tests/type-deferred-context/wrong.txtnew file+14
@@ -0,0 +1,14 @@
1++$ message
2+ [%got value=@t]
3+ [%other value=@p]
4++$ maybe
5+ %none
6+ [%some value=@t]
7+++ final ^- $-(? @t) choose
8+++ choose |= flag=?
9+ ?- flag
10+ %.y %none
11+ %.n %none
12+++ send |= f=$-(@t A) f
13+++ first (send |=%got)
14+++ fixed ^- $-(@t message) first
tests/type-union-alias-overlap/duplicate.txtnew file+5
@@ -0,0 +1,5 @@
1++$ tagged |$ [A] [%wrapped A]
2++$ tagged |$ [A] [%other A]
3++$ choice
4+ (tagged @t)
5+ [%wrapped @p]
tests/type-union-alias-overlap/expected.txtnew file+25
@@ -0,0 +1,25 @@
1+same head and inhabited payload overlap: ok
2+zero spelling overlap: ok
3+original same-case spans retained: ok
4+partial report displays same overlap: ok
5+partial report displays zero overlap: ok
6+distinct heads do not overlap: ok
7+distinct singleton payloads do not overlap: ok
8+empty payload has no witness: ok
9+outer variable has no witness: ok
10+unresolved reference has no witness: ok
11+equal nonzero singleton payload overlaps: ok
12+nonzero overlap reported: ok
13+late pair in three cases overlaps: ok
14+opaque gate payload declines: ok
15+wrong alias arity declines: ok
16+full module remains rejected: ok
17+first source excerpt retained: ok
18+second source excerpt retained: ok
19+flag and zero payload overlap: ok
20+flag and nonzero payload decline: ok
21+duplicate owner declines: ok
22+qualified owner declines: ok
23+20 proof fuel succeeds: ok
24+19 proof fuel declines: ok
25+zero proof fuel declines: ok
tests/type-union-alias-overlap/flag-source.txtnew file+7
@@ -0,0 +1,7 @@
1++$ tagged |$ [A] [%wrapped A]
2++$ flag-zero
3+ (tagged ?)
4+ [%wrapped %$]
5++$ flag-nonzero
6+ (tagged ?)
7+ [%wrapped %hello]
tests/type-union-alias-overlap/multi.txtnew file+5
@@ -0,0 +1,5 @@
1++$ tagged |$ [A] [%wrapped A]
2++$ choice
3+ [%first @t]
4+ (tagged @p)
5+ [%wrapped @t]
tests/type-union-alias-overlap/negative.txtnew file+16
@@ -0,0 +1,16 @@
1++$ tagged |$ [A] [%wrapped A]
2++$ distinct-head
3+ (tagged @t)
4+ [%other @p]
5++$ distinct-constant
6+ (tagged %a)
7+ [%wrapped %b]
8++$ empty-payload
9+ (tagged !!)
10+ [%wrapped @t]
11++$ generic |$ [A]
12+ (tagged A)
13+ [%wrapped @t]
14++$ unknown-name
15+ (tagged missing)
16+ [%wrapped @t]
tests/type-union-alias-overlap/nonzero.txtnew file+4
@@ -0,0 +1,4 @@
1++$ tagged |$ [A] [%wrapped A]
2++$ same
3+ (tagged %hello)
4+ [%wrapped %hello]
tests/type-union-alias-overlap/opaque.txtnew file+4
@@ -0,0 +1,4 @@
1++$ tagged |$ [A] [%wrapped A]
2++$ choice
3+ (tagged $-(@t @t))
4+ [%wrapped $-(@t @t)]
tests/type-union-alias-overlap/positive.txtnew file+8
@@ -0,0 +1,8 @@
1++$ tagged |$ [A] [%wrapped A]
2++$ same
3+ (tagged @t)
4+ [%wrapped @p]
5++$ zero |$ [A] [%.y A]
6++$ same-zero
7+ (zero @t)
8+ [%$ @p]
tests/type-union-alias-overlap/qualified.txtnew file+5
@@ -0,0 +1,5 @@
1+/= lib /ui/lib
2++$ tagged |$ [A] [%wrapped A]
3++$ choice
4+ (tagged.lib @t)
5+ [%wrapped @p]
tests/type-union-alias-overlap/wrong.txtnew file+5
@@ -0,0 +1,5 @@
1++$ tagged |$ [A] [%wrapped A]
2++$ bad
3+ (tagged @t @p)
4+ [%wrapped @p]
5+++ id |= x=bad x
tools/testdata/ship-selftest+10
@@ -315,6 +315,7 @@ def generator_checks(tmp):
315315 'tests/lib/jibb-generic-pattern.hoon',
316316 'tests/lib/jibb-empty.hoon',
317317 'tests/lib/jibb-union-source.hoon',
318+ 'tests/lib/jibb-union-alias-overlap.hoon',
318319 'tests/lib/jibb-api-evidence.hoon',
319320 'tests/lib/jibb-ask-guards.hoon',
320321 'tests/lib/jibb-request-guards.hoon',
@@ -330,6 +331,7 @@ def generator_checks(tmp):
330331 'tests/lib/jibb-gate-free-source.hoon',
331332 'tests/lib/jibb-deferred.hoon',
332333 'tests/lib/jibb-deferred-resolver.hoon',
334+ 'tests/lib/jibb-deferred-context.hoon',
333335 'tests/lib/jibb-cell-branch.hoon'}
334336 <= normal_tests, sorted(normal_tests))
335337 check('generic recursion source and golden are assembled',
@@ -357,6 +359,10 @@ def generator_checks(tmp):
357359 'duplicate-params', 'hidden-ref', 'qualified', 'direct-source',
358360 'wide-source', 'free-source', 'late-source', 'shadow-source',
359361 'mixed-source', 'expected')} <= set(normal.files))
362+ check('generic union alias overlap fixtures are assembled',
363+ {f'tests/type-union-alias-overlap/{name}.txt' for name in (
364+ 'positive', 'negative', 'nonzero', 'multi', 'opaque', 'wrong',
365+ 'duplicate', 'qualified', 'flag-source', 'expected')} <= set(normal.files))
360366 check('deferred transaction and resolver sources are assembled',
361367 {'tests/type-deferred/nested-src.txt', 'tests/type-deferred/principal-src.txt',
362368 'tests/type-deferred/expected.txt', 'tests/type-deferred-resolver/good.txt',
@@ -366,6 +372,10 @@ def generator_checks(tmp):
366372 'tests/type-deferred-resolver/stability-output-early.txt', 'tests/type-deferred-resolver/stability-output-late.txt',
367373 'tests/type-deferred-resolver/stability-slot-early.txt', 'tests/type-deferred-resolver/stability-slot-late.txt'}
368374 <= set(normal.files))
375+ check('deferred context sources and report are assembled',
376+ {f'tests/type-deferred-context/{name}.txt' for name in (
377+ 'rich', 'plain', 'simple', 'missing', 'wrong', 'duplicate',
378+ 'dangling', 'collision-early', 'collision-late', 'report')} <= set(normal.files))
369379 check('cell branch sources are assembled',
370380 {'tests/type-cell-branch/good.txt', 'tests/type-cell-branch/bad.txt'}
371381 <= set(normal.files))
tools/type-deferred-context-testnew file+31
@@ -0,0 +1,31 @@
1+#!/usr/bin/env python3
2+"""Evaluate source-driven deferred context and replay guards."""
3+from pathlib import Path
4+import argparse, subprocess, sys, tempfile
5+root = Path(__file__).resolve().parent.parent
6+p = argparse.ArgumentParser(description=__doc__)
7+p.add_argument('--lib', default=str(root/'jibb-dev/lib/jibb.hoon'))
8+p.add_argument('--hoon', default=str(root/'tools/hoon'))
9+a = p.parse_args()
10+core = (root / 'tests/lib/jibb-deferred-context.hoon').read_text()
11+core = core[core.index('\n|%\n') + 1:]
12+names = ['rich', 'plain', 'simple', 'missing', 'wrong', 'duplicate', 'dangling', 'collision-early', 'collision-late', 'report']
13+bindings = ''.join('=/ '+('expected' if n == 'report' else n)+'=wain (to-wain:format '+n+'-text)\n' for n in names)
14+program = ('=/ jibb .\n' + bindings + '=/ suite\n' + core +
15+ "\n^- @t\n=/ checked=tang test-context:suite\n?~ checked 'deferred context guards passed'\n(crip \"{<checked>}\")\n")
16+with tempfile.TemporaryDirectory(prefix='jibb-deferred-context-') as d:
17+ f = Path(d) / 'report.hoon'
18+ f.write_text(program)
19+ cmd = [a.hoon, '--lib', a.lib, '--raw', '-f', str(f)]
20+ for n in names:
21+ fixture = root/'tests/type-deferred-context'/(n+'.txt')
22+ if not fixture.is_file() or fixture.stat().st_size == 0:
23+ sys.exit('missing or empty fixture: '+str(fixture))
24+ cmd.extend(['--cord', n+'-text='+str(fixture)])
25+ got = subprocess.run(cmd, stdout=subprocess.PIPE)
26+if got.returncode:
27+ sys.exit(got.returncode)
28+if got.stdout != b'deferred context guards passed\n':
29+ sys.stderr.write(got.stdout.decode(errors='replace'))
30+ sys.exit(1)
31+print('deferred context guards passed')
tools/type-union-alias-overlap-testnew file+52
@@ -0,0 +1,52 @@
1+#!/usr/bin/env python3
2+"""Check source-backed generic-union overlap witnesses 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-union-alias-overlap'
16+source = (root / 'tests/lib/jibb-union-alias-overlap.hoon').read_text()
17+source = source[source.index('\n|%\n') + 1:]
18+names = ['positive', 'negative', 'nonzero', 'multi', 'opaque', 'wrong', 'duplicate', 'qualified', 'flag-source']
19+cords = [item for name in names for item in ('--cord', f'{name}-text={fixture / (name + ".txt")}')]
20+if not args.update:
21+ cords += ['--cord', f'expected-text={fixture / "expected.txt"}']
22+bindings = ''.join(f'=/ {name}=wain (to-wain:format {name}-text)\n' for name in names)
23+program = ('=/ jibb .\n' + bindings +
24+ ('=/ expected=wain ~\n' if args.update else '=/ expected=wain (to-wain:format expected-text)\n') +
25+ '=/ suite\n' + source +
26+ '\n=/ output=@t (overlap-output:suite ' +
27+ ' '.join(name + '-text' for name in names) + ')\n' +
28+ ('output\n' if args.update else
29+ "=/ actual-test=tang test-union-alias-overlap:suite\n?~ actual-test output\n'BAD ship test arm'\n"))
30+with tempfile.TemporaryDirectory(prefix='jibb-union-overlap-') as tmp:
31+ path = Path(tmp) / 'test.hoon'
32+ path.write_text(program)
33+ result = subprocess.run([str(root / 'tools/hoon'), '--lib', args.lib, '--raw',
34+ '-f', str(path), *cords], stdout=subprocess.PIPE)
35+if result.returncode:
36+ sys.exit(result.returncode)
37+if any(line.startswith(b'BAD ') or line.endswith(b': BAD') for line in result.stdout.splitlines()):
38+ sys.stderr.write('generic-union guard assertion failed:\n')
39+ sys.stderr.buffer.write(result.stdout)
40+ sys.exit(1)
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-union overlap guards passed')