diff --git a/docs/build-system.md b/docs/build-system.md index 74b1100b..9401eb95 100644 --- a/docs/build-system.md +++ b/docs/build-system.md @@ -4159,7 +4159,7 @@ Hex encoding keeps arbitrary legal filesystem bytes inside one comment. The metadata makes ordinary-to-vendor changes and vendor symlink retargeting invalidate the importer even when its source bytes and both already-warm export bytes happen to match. It contains no dependency body and does not alter source -positions. The current workdir formats are build 14 and test 13. An equivalent +positions. The current workdir formats are build 15 and test 14. An equivalent warm request remains a package-production no-op; an export change propagates only through ordinary direct-export comparison. @@ -4509,15 +4509,16 @@ source-file binding is named `wire`. The path leaf `codec` is not installed as another qualifier, and a sibling source file receives no `wire` binding unless that file has its own import. -The implemented representation keeps five facts distinct: +The implemented representation keeps six facts distinct: 1. source import spelling, including its source file, line, and column; 2. contextually expanded canonical import identity; 3. canonical physical directory; 4. the one declared package name read from eligible source clauses and `.wwi` - package markers; and -5. the source-file-local default qualifier binding from that declaration to - the canonical target. + package markers; +5. the optional explicit alias written at that import occurrence; and +6. the effective source-file-local qualifier, selected from the explicit alias + when present and otherwise from the imported declaration. Both drivers retain one dynamically allocated `sepbind` occurrence for every real import site. The occurrence stores the source spelling and position plus a @@ -4541,14 +4542,15 @@ declared name are no longer collapsed into one token. `w6c` and `w6c_ww` validate every direct export's leading canonical owner against its paired `--import` path. After all direct exports are parsed, they build canonical-path-to-declared-name metadata from those interfaces, apply any -vendor `--import-map` only to canonical identity, and bind the imported -declaration as the default qualifier of each primary source import. The -checkers and code generators select bindings by source-section ID and canonical -owner. Qualified and retained bare-import lookups mark only that file's binding -used. Two files may consequently bind the same name to different canonical +vendor `--import-map` only to canonical identity, and bind each primary source +import through its explicit alias or, when absent, the imported declaration. +The checkers and code generators select bindings by source-section ID and +canonical owner. Only a qualified lookup through that effective binding marks +the owning file's occurrence used; imported declarations are never a bare-name +fallback. Two files may consequently bind the same name to different canonical packages, while the graph still contains one edge/action for each target. -Within one file, two imports that produce the same default qualifier are a +Within one file, two imports that produce the same effective qualifier are a redeclared binding; the later unused binding is also reported. An unused import is reported at its own import position even if a sibling file uses the same qualifier or canonical dependency. A package-scope declaration collides with @@ -4590,30 +4592,32 @@ identities and archive closures. A generated dispatcher privately binds its tested target through `--test-target-package ` so a command variant declared `main` does not collide with the dispatcher's own synthesized `main`; this is -compiler-generated wiring, not source alias syntax. Zero-test dispatchers mark -their compiler-owned target/support metadata imports consumed. That private -binding is installed or marked consumed only in the generated dispatcher's -source section; an import from an earlier test-file section neither supplies -nor satisfies it. +compiler-generated wiring distinct from ordinary source aliases. Zero-test +dispatchers mark their compiler-owned target/support metadata imports consumed. +That private binding is installed or marked consumed only in the generated +dispatcher's source section; an import from an earlier test-file section +neither supplies nor satisfies it. Vendor expansion changes only canonical identity and physical selection. A source spelling such as `lib.codec` can resolve to `domain.app.vendor.lib.codec`, while the vendored package's declaration, for -example `package wire`, supplies the file-local qualifier. The driver emits one -sorted semantic `--import-map lib.codec domain.app.vendor.lib.codec` and one -direct export input despite repeated import occurrences in separate files. -The expanded identity continues to own symbols, `.wwi`, archive, voucher, and -link inputs. +example `package wire`, supplies the default file-local qualifier. An explicit +alias overrides only that qualifier. The driver emits one sorted semantic +`--import-map lib.codec domain.app.vendor.lib.codec` and one direct export input +despite repeated import occurrences in separate files. The expanded identity +continues to own symbols, `.wwi`, archive, voucher, and link inputs. Canonical action interning remains the directory/path/variant model of sections 11.7 and 11.14. Independent file bindings never clone an action, and a declared -name never enters an artifact basename or storage locator. The name is semantic -content in the owner unit and export. Changing only a dependency's declaration -therefore keeps the same action identity but changes its export bytes, causes -each direct importer to be reconsidered, and stops propagation after an -importer's regenerated export is unchanged. An identical warm request remains -a producer no-op. Build workdir format 14 and test format 13 prevent reuse of -older vouchers that lack these semantics. +name or source alias never enters an artifact basename or storage locator. +Changing a dependency's declaration keeps the same action identity and causes +each direct importer to be reconsidered. A default-bound importer may then fail +because its old qualifier disappeared; an explicitly aliased importer keeps its +binding, regenerates a canonical semantic export, and stops reverse propagation +when those bytes are unchanged. An alias-only source edit rebuilds its owner but +likewise leaves canonical export identity unchanged. An identical warm request +remains a producer no-op. Build workdir format 15 and test format 14 prevent +reuse of older vouchers that lack these semantics. Compiler argv still contains exactly the sorted, deduplicated `.wwi` exports of direct canonical dependencies; no transitive `.wwi` and no qualifier-derived @@ -4634,10 +4638,11 @@ Responsibility is intentionally split as follows: publication. Their storage, diagnostics, call positions, and allocation failures are isomorphic. - `cmd/w6c`/`cmd/wcc` and `selfhost/cmd/w6c`/`selfhost/cmd/wcc` own export - owner/name reading and writing, file-local binding installation, collision - and unused-import diagnostics, name/type lookup, canonical symbol ownership, - and generated-dispatcher private qualification. Cstage and WWstage emit - byte-identical applicable interfaces, assembly, archives, and binaries. + owner/name reading and writing, ordinary/aliased parse facts, file-local + binding installation, collision and unused-import diagnostics, name/type + lookup, canonical symbol ownership, and generated-dispatcher private + qualification. Cstage and WWstage emit byte-identical applicable interfaces, + assembly, archives, and binaries. - `cmd/wwtest` remains only the test-command dispatcher. The behavior follows pinned official Go 1.26.5 source at commit @@ -4684,11 +4689,9 @@ The behavior follows pinned official Go 1.26.5 source at commit [lines 628–659](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L659), [`cmd/go/internal/test/test.go`, lines 1133–1226](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1133-L1226)). -The deliberately retained source grammar is `import dotted.path;`. Quoted Go -imports, explicit aliases, dot imports, and blank imports are not implemented. -Because bindings already retain source ownership and position separately from -canonical identity and declared name, adding explicit aliases later will not -require another package-wide namespace redesign. +Section 11.19 completes ordinary explicit aliases while retaining dotted, +unquoted paths. Grouped, quoted, dot, and blank imports remain deliberately +unimplemented. The focused native observer `declared_name_identity_and_file_import_scope` generates every tree @@ -4700,6 +4703,204 @@ exports and archive-only links, warm no-op behavior, declared-name invalidation, rejection-state preservation, normalized argv, artifact/binary identity, allocation-bearing runtime behavior, and request/product-order independence. +### 11.19 Implemented ordinary and explicitly aliased file-scoped imports + +WW now implements the two ordinary binding modes for its local dotted import +model: + +```ww +import acme.codec; // effective qualifier is the declared package name +import stable acme.codec; // effective qualifier is exactly stable +``` + +If canonical package `acme.codec` declares `package wire`, the first form +exposes only `wire.Name`; the second exposes only `stable.Name`. Neither form +also exposes `codec.Name`, the unused alternative qualifier, or bare `Name`. +WW has no dot-import form, so an ordinary import never inserts the dependency's +exported declarations into unqualified lookup. Builtins, lexical declarations, +and same-package declarations retain ordinary bare lookup. + +This is the dotted-path counterpart of Go's independent local name and quoted +path, without adopting quoted paths. The syntax AST stores the optional source +alias independently from the original dotted spelling, canonical expanded +identity, imported declared name, effective qualifier, owning source section, +and source position. `Sym.use_alias` remains the older checker coexistence bit +for a declaration that shares a leaf with a package qualifier; it is not the +source-language alias fact. + +#### Pinned Go evidence + +The reference is official Go 1.26.5 source at commit +`c19862e5f8415b4f24b189d065ed739517c548ba`: + +- `go/ast.ImportSpec` stores `Name` and `Path` independently, and `Pos` selects + the alias position when one exists + ([`go/ast/ast.go`, lines 908–915 and 939–955](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/ast/ast.go#L908-L955)). + `parseImportSpec` parses the optional local name separately from the path + ([`go/parser/parser.go`, lines 2509–2546](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/parser/parser.go#L2509-L2546)). +- `go/types` creates one child scope per source file, chooses an explicit alias + when present and the imported package's declared name otherwise, inserts one + ordinary package-name object, and inserts bare exports only for an explicit + dot import + ([`go/types/resolver.go`, lines 237–350](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L237-L350)). + Package/file collisions and unused occurrences are handled at + [`resolver.go`, lines 463–480](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L463-L480) + and + [`resolver.go`, lines 701–735](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L701-L735). + The production compiler mirrors those rules at + [`cmd/compile/internal/types2/resolver.go`, lines 223–335](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L223-L335), + [lines 472–489](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L472-L489), + and + [lines 706–740](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L706-L740). +- Qualified selection marks the exact file-local package-name object used; + bare imported declarations are associated only with the dot-import table + ([`go/types/call.go`, lines 682–693](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/call.go#L682-L693), + [`go/types/typexpr.go`, lines 20–31 and 79–86](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/typexpr.go#L20-L86)). + Compiler diagnostics are stably sorted by source position before printing + ([`cmd/compile/internal/base/print.go`, lines 70–92](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/base/print.go#L70-L92)). +- Official testdata permits one path under distinct names, including default + plus aliases, while keeping every occurrence independently subject to unused + checking + ([`internal/types/testdata/check/importdecl0/importdecl0a.go`, lines 29–52](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/importdecl0/importdecl0a.go#L29-L52), + [`test/import.go`, lines 7–23](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/test/import.go#L7-L23), + [`test/import1.go`, lines 7–18](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/test/import1.go#L7-L18)). +- Canonical package path and declared name are independent in + [`go/types/package.go`, lines 26–40](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/package.go#L26-L40). + Unified export writes and restores canonical path and name independently + ([`cmd/compile/internal/noder/writer.go`, lines 430–465](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/writer.go#L430-L465), + [`cmd/compile/internal/importer/ureader.go`, lines 157–196](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/importer/ureader.go#L157-L196), + [`cmd/compile/internal/noder/reader.go`, lines 342–376](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/reader.go#L342-L376)). +- `go/build` keeps production, internal-test, and external-test files/imports + separate and constructs their source import sets + ([`go/build/build.go`, lines 436–505](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L436-L505), + [lines 939–1040](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L939-L1040), + [lines 1061–1063](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L1061-L1063)). + `cmd/go` interns by canonical import path, performs internal/vendor checks at + every real site, rejects imported `main`, and builds distinct test variants + ([`cmd/go/internal/load/pkg.go`, lines 633–636](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L633-L636), + [lines 757–806](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L757-L806), + [lines 2024–2047](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L2024-L2047), + [`cmd/go/internal/load/test.go`, lines 175–293](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L175-L293), + [lines 421–484](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L421-L484)). +- Build actions are keyed by operation and canonical package object, consume + canonical direct dependencies, and link the reachable canonical closure + ([`cmd/go/internal/work/action.go`, lines 202–206](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L202-L206), + [lines 437–447](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L437-L447), + [lines 628–708](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L708), + [lines 919–968](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L919-L968), + [lines 1034–1068](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L1034-L1068)). + Root construction and test execution retain those canonical production and + generated-test objects + ([`cmd/go/internal/work/build.go`, lines 495–558](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L495-L558), + [`cmd/go/internal/test/test.go`, lines 1133–1226 and 1297–1366](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1133-L1366)). + +Go's dot-import branch is negative evidence only: it demonstrates that bare +foreign declarations require a distinct explicit mode. WW does not implement +that mode. + +#### Scope, duplicate, collision, and usage rules + +Every import occurrence owns its spelling, optional alias, position, source +section, and used bit. The effective qualifier is installed only in that +section. A qualified type, value, function, def, const, or variable lookup maps +the effective qualifier to canonical identity and marks that exact occurrence +used. A failed bare lookup marks nothing. A sibling file cannot use or satisfy +the occurrence, while two files may independently reuse one alias for different +canonical packages. + +In one source file, equal effective qualifiers are duplicate bindings. The +later occurrence remains independently unused; at one source position the +duplicate diagnostic precedes its unused diagnostic. A later bare undefined +name is printed after the earlier unused-import diagnostic. Equal canonical +paths are otherwise not a conflict: distinct aliases, or default plus explicit +alias, are accepted when their effective names differ and each occurrence is +used. A package-scope declaration colliding with a file import is rejected in +the existing deterministic reconciliation pass. The unused wording follows +Go's leaf comparison: a binding equal to the path leaf says `imported and not +used`; any other binding, including an unusual default declared name, says +`imported as and not used`. + +`_` is rejected at parse time as `blank import alias _ is not implemented`. +Grouped imports, quoted paths, dot imports, and blank imports are deliberately +deferred rather than partially implemented. + +#### Graph, export, artifact, and persistence identity + +The imports-only parser and full parser share one import-spec routine and retain +alias and dotted path separately. Both drivers sort and resolve occurrences by +the dotted spelling, perform contextual internal and nearest-first vendor checks +at every real site, and intern the expanded canonical action. `sepbind` and +`--import-map` continue to mean source dotted spelling to expanded vendor +identity; neither contains the alias. Repeated occurrences remain separate +scope facts but form one sorted canonical edge/action. + +The compiler independently reads the direct dependency's declared name. It +installs the explicit alias when present or that declared name otherwise, while +keeping the canonical owner on declarations, symbols, and code generation. A +source alias cannot bypass imported-`main` rejection. Production, internal-test, +external-test, support, and generated-main identities remain isolated, and an +import found only in a test file reaches only its corresponding test variant. +The coordinator remains responsible only for package/test classification and +submitting those roots; it does not parse or rewrite imports. + +`.wwi` data never exports a local alias as package identity. Qualified exported +type and constant references are normalized to a deterministic compiler-private +qualifier `__wwi_` followed by the lowercase hexadecimal bytes of the canonical +path. Matching import records still name the canonical path, and transitive fact +sections carry the same canonical private spelling. The reader restores the +real declared name from the direct owner's metadata while treating those +private names as semantic placeholders. Thus two source aliases for the same +canonical type produce the same interface bytes, even if two dependencies have +the same declared name. + +The source/import and interface protocol change advances persistent build +workdirs to format 15 and test workdirs to format 14. Older unit vouchers are +invalidated before reuse, so an interface written with historical implicit-dot +or declared-name spelling cannot preserve a stale qualifier under the new +checker. + +Compiler argv remains exactly one sorted `--import ` +pair per direct dependency plus the exact required vendor import maps. No +transitive `.wwi` is passed. Symbols, objects, archives, vouchers, stamps, +persistent directories, and product basenames remain canonical-action owned. +Linker argv remains root plus reachable archives and native/runtime inputs only; +it contains neither `.wwi` files nor alias-derived archive names. + +A dependency declared-name change invalidates its semantic export and +reconsiders every direct importer. Default-bound unchanged source loses the old +qualifier and is rejected cleanly. Explicitly aliased source stays valid; after +its canonical interface regenerates unchanged, invalidation stops before +unaffected reverse dependencies. An alias-only source edit rebuilds the edited +owner but likewise cannot rename symbols or alter canonical export identity, so +unchanged semantic bytes stop reverse rebuilding. + +Parser, identity, alias, declaration, binding, and scope failures occur before +publication. Compiler-owned failures may start the compiler, but staged unit, +interface, assembly, object, archive, voucher, stamp, status, and product state +is discarded or invalidated under section 11.18's partial-commit protocol. + +#### Stage and observer ownership + +The C and WW parsers retain identical alias/path/position facts. The Cstage and +WWstage drivers resolve only the dotted path; the compiler mains restore the +declared name and choose the effective binding; the checkers own duplicate, +collision, usage, and file-scope lookup; the interface writers own canonical +normalization; and code generators consume checker-stamped canonical types and +module ownership. WWstage resolves all top-level function signatures in their +declaring package before checking bodies, matching Cstage and preventing a +consumer from reinterpreting a later declaration's bare same-package types. + +The native `explicit_import_alias_binding_modes` observer owns the focused +ordinary/alias/bare-negative, duplicate, repeated-path, file-scope, canonical +artifact, `.wwi`, and rejection-state matrix. The existing +`declared_name_identity_and_file_import_scope` observer owns default-versus- +stable-alias invalidation and propagation. Existing directory, command-test, +vendor, recursive, exact-argv, link-closure, persistent-workdir, and +partial-commit observers retain their broader variant and action ownership. +Every applicable proof runs Cstage and WWstage from independent cold roots and +compares diagnostics, normalized tool arguments, artifacts, binaries, and +runtime output. + ## 12. Candidate architectures and hard-gate decision Five candidates were developed as coherent systems, not as feature bins. diff --git a/docs/spec.md b/docs/spec.md index f47194f3..564bf9fd 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -246,7 +246,8 @@ does not return (e.g. a call to `abort`). ``` SourceFile = PackageClause { ImportDecl } { TopDecl } . PackageClause = "package" ident ";" . -ImportDecl = "import" ident ";" . +ImportDecl = "import" ( ImportPath | ident ImportPath ) ";" . +ImportPath = ident { "." ident } . ``` - Every source file begins with a package clause. A directory of eligible @@ -255,8 +256,13 @@ ImportDecl = "import" ident ";" . canonical import identity. - `import acme.codec;` loads the canonical package `acme.codec`. If that package declares `package wire;`, the importing file sees its exported names - as `wire.Name`; `codec.Name` is not an additional binding. The import binding - is scoped to that source file. A sibling file must declare its own import. + as `wire.Name`; `codec.Name` is not an additional binding. An explicit alias + replaces only that visible qualifier: `import stable acme.codec;` exposes + `stable.Name`, not `wire.Name` or `codec.Name`. Both kinds of binding are + scoped to that source file. A sibling file must declare its own import. + Neither form exposes an imported declaration as a bare `Name`; ordinary + unqualified lookup remains limited to lexical, builtin, and same-package + declarations. The package dependency graph is the sorted, deduplicated union of the real imports in all eligible files. Self-import is rejected. - An executable package is one declared `package main` and containing a @@ -265,8 +271,9 @@ ImportDecl = "import" ident ";" . colocated external-test wiring. - Only names marked `export` (§5) are visible across module boundaries. -The implemented grammar remains dotted and unaliased. Quoted import paths, -explicit aliases, dot imports, and blank imports are not currently accepted. +Import paths remain unquoted and dotted. Grouped imports, quoted import paths, +dot imports, and blank imports are not implemented. In particular, `_` is +rejected as an alias rather than being treated as a blank import. --- diff --git a/docs/test-system-v2.md b/docs/test-system-v2.md index efa12da8..b5bf8e06 100644 --- a/docs/test-system-v2.md +++ b/docs/test-system-v2.md @@ -228,17 +228,26 @@ Separate compilation is the only driver build path; no compatibility mode switch remains. `test/package/package_test.ww` also owns the dual-stage -`declared_name_identity_and_file_import_scope` observer. It generates temporary +`declared_name_identity_and_file_import_scope` and +`explicit_import_alias_binding_modes` observers. They generate temporary directory trees proving that canonical import identity, physical directory, -declared package name, and source-file default qualifier remain distinct; -imports are file-scoped while dependency edges are the package-wide sorted -union; command and production/internal/external/generated-main variants retain -canonical action ownership; vendor expansion changes identity but not the -declared qualifier; compiler argv contains only direct `.wwi` inputs; and its -named rejected actions leave neither committed nor staged action artifacts or a -published binary. It also forces a staged multi-artifact commit to fail after -the interface rename, then proves that both stages invalidate old unit vouchers -and reconsider the importer rather than accepting a mixed warm generation. A +declared package name, optional source alias, and effective source-file +qualifier remain distinct. The alias matrix covers default and explicit-only +qualification, bare function/type/def/const/variable rejection with exact +unused-before-undefined diagnostics, same-file duplicate bindings, accepted +same-path imports under distinct bindings, alias reuse across files, sibling +scope isolation, package-declaration collisions, unused aliases, and explicit +blank-alias rejection. It also pins canonical `.wwi` spelling, archive/action +ownership, stage-equal diagnostics and binaries, and clean rejection state. +The declared-name observer proves imports are file-scoped while dependency +edges are the package-wide sorted union; command and +production/internal/external/generated-main variants retain canonical action +ownership; vendor expansion changes identity but not the effective qualifier; +compiler argv contains only direct `.wwi` inputs; and its named rejected +actions leave neither committed nor staged action artifacts or a published +binary. It also forces a staged multi-artifact commit to fail after the +interface rename, then proves that both stages invalidate old unit vouchers and +reconsider the importer rather than accepting a mixed warm generation. A separate injected stamp-removal failure proves the pre-commit gate leaves all previously committed artifacts byte-identical. Together with the existing directory, recursive, vendor, diff --git a/lib/path/buffer_test.ww b/lib/path/buffer_test.ww index ad4e2e70..59ce9c60 100644 --- a/lib/path/buffer_test.ww +++ b/lib/path/buffer_test.ww @@ -17,19 +17,19 @@ import path; let i: i32 = 0; for (i < 4) { let got: i32 = 0; - if (abs(ins[i])) { got = 1; }; + if (path.abs(ins[i])) { got = 1; }; assert(!(got != want[i])); i += 1; }; // buffer-arm: &buf ptr-ident-widens into the union. - let buf = buffer { ... }; - assert(!(abs(&buf))); // empty → false - assert(!(push(&buf, "/foo")! != "/foo")); - assert(!(!abs(&buf))); // "/foo" → true + let buf: path.buffer; + assert(!(path.abs(&buf))); // empty → false + assert(!(path.push(&buf, "/foo")! != "/foo")); + assert(!(!path.abs(&buf))); // "/foo" → true buf.end = 0; - assert(!(push(&buf, "foo")! != "foo")); - assert(!(abs(&buf))); // "foo" → false + assert(!(path.push(&buf, "foo")! != "foo")); + assert(!(path.abs(&buf))); // "foo" → false }; // ref/hare/path/buffer.ha:44-51. Both arms: str (== sepstr) and *buffer @@ -37,24 +37,24 @@ import path; // inline; str-arm rows table-driven. @test fn isroot_cases() void = { - let buf = buffer { ... }; + let buf: path.buffer; // empty buffer (end 0) - assert(!(isroot(&buf))); + assert(!(path.isroot(&buf))); // "/" — root. local() is c3, so seed the SEP byte directly. - buf.buf[0] = SEP; buf.end = 1; - assert(!(!isroot(&buf))); - assert(!(string(&buf) != "/")); + buf.buf[0] = path.SEP; buf.end = 1; + assert(!(!path.isroot(&buf))); + assert(!(path.string(&buf) != "/")); // "foo" — relative, not root. buf.end = 0; - assert(!(push(&buf, "foo")! != "foo")); - assert(!(isroot(&buf))); + assert(!(path.push(&buf, "foo")! != "foo")); + assert(!(path.isroot(&buf))); // "/foo" — absolute but not root. - buf.buf[0] = SEP; buf.end = 1; - assert(!(push(&buf, "foo")! != "/foo")); - assert(!(isroot(&buf))); + buf.buf[0] = path.SEP; buf.end = 1; + assert(!(path.push(&buf, "foo")! != "/foo")); + assert(!(path.isroot(&buf))); // str-arm (buffer.ha:47): only the bare separator is root. let ins: [4]str; @@ -66,7 +66,7 @@ import path; let i: i32 = 0; for (i < 4) { let got: i32 = 0; - if (isroot(ins[i])) { got = 1; }; + if (path.isroot(ins[i])) { got = 1; }; assert(!(got != want[i])); i += 1; }; @@ -76,10 +76,10 @@ import path; // buffer views the byte prefix. @test fn string_cases() void = { - let buf = buffer { ... }; - assert(!(string(&buf) != ".")); - assert(!(push(&buf, "foo")! != "foo")); - assert(!(string(&buf) != "foo")); + let buf: path.buffer; + assert(!(path.string(&buf) != ".")); + assert(!(path.push(&buf, "foo")! != "foo")); + assert(!(path.string(&buf) != "foo")); }; // ref/hare/path/buffer.ha:55-77. Rewrites '/'→SEP into a static buffer; @@ -95,7 +95,7 @@ import path; ins[3]=""; want[3]=""; let i: i32 = 0; for (i < 4) { - assert(!(local(ins[i])! != want[i])); + assert(!(path.local(ins[i])! != want[i])); i += 1; }; }; diff --git a/lib/path/posix_test.ww b/lib/path/posix_test.ww index e0b13b9a..6188a891 100644 --- a/lib/path/posix_test.ww +++ b/lib/path/posix_test.ww @@ -29,8 +29,8 @@ import path; inputs[9]="/home//dwc//test"; wantdir[9]="/home//dwc"; wantbase[9]="test"; let i: i32 = 0; for (i < 10) { - assert(!(dirname(inputs[i]) != wantdir[i])); - assert(!(basename(inputs[i]) != wantbase[i])); + assert(!(path.dirname(inputs[i]) != wantdir[i])); + assert(!(path.basename(inputs[i]) != wantbase[i])); i += 1; }; }; diff --git a/lib/path/stack_test.ww b/lib/path/stack_test.ww index 01305e90..d9922722 100644 --- a/lib/path/stack_test.ww +++ b/lib/path/stack_test.ww @@ -19,8 +19,8 @@ import path; // appendnorm @test is needed. @test fn push_cases() void = { - let buf = buffer { ... }; - assert(!(string(&buf) != ".")); + let buf: path.buffer; + assert(!(path.string(&buf) != ".")); // current-dir + parent-dir invariants (stack.ha:82-88). Single // segment per row → table-driven sequential apply. @@ -33,13 +33,13 @@ import path; segs[4]="."; wants[4]=".."; let i: i32 = 0; for (i < 5) { - assert(!(push(&buf, segs[i])! != wants[i])); + assert(!(path.push(&buf, segs[i])! != wants[i])); i += 1; }; // set(&buf) resets to "." (stack.ha:91). set forwards an EMPTY // variadic into push (KEN-oracle flag 1: zero-arg gather+forward). - assert(!(set(&buf)! != ".")); + assert(!(path.set(&buf)! != ".")); // regular path + parent (stack.ha:101-104, minus the local() row). let segs2: [3]str; @@ -49,25 +49,25 @@ import path; segs2[2]=".."; wants2[2]="."; let k: i32 = 0; for (k < 3) { - assert(!(push(&buf, segs2[k])! != wants2[k])); + assert(!(path.push(&buf, segs2[k])! != wants2[k])); k += 1; }; // multi-segment (stack.ha:107-111). Variadic arity varies (1 or 2) // → inline. - assert(!(push(&buf, "a", "b")! != "a/b")); - assert(!(push(&buf, "..", "c")! != "a/c")); - assert(!(push(&buf, "..")! != "a")); + assert(!(path.push(&buf, "a", "b")! != "a/b")); + assert(!(path.push(&buf, "..", "c")! != "a/c")); + assert(!(path.push(&buf, "..")! != "a")); // stack.ha:110; local()→literal, SEP='/' on Linux, proper local() rides c3 - assert(!(push(&buf, "/d")! != "a/d")); - assert(!(push(&buf, "..", "..")! != ".")); // stack.ha:111 + assert(!(path.push(&buf, "/d")! != "a/d")); + assert(!(path.push(&buf, "..", "..")! != ".")); // stack.ha:111 // leading-SEP segment into an EMPTY buffer exercises push's // `j==0 && buf.end==0` root-seed arm (stack.ha:18-20), unreached // by the relative rows above. Hare covers it via local("/")-seeded // rows that defer to c3; literal "/foo" stands in (SEP='/' on Linux). - assert(!(set(&buf)! != ".")); - assert(!(push(&buf, "/foo")! != "/foo")); + assert(!(path.set(&buf)! != ".")); + assert(!(path.push(&buf, "/foo")! != "/foo")); }; // ref/hare/path/stack.ha:172-180. Parent dir; root → root; empty → "..". @@ -76,7 +76,7 @@ import path; // not "") can share the table. @test fn parent_cases() void = { - let buf = buffer { ... }; + let buf: path.buffer; let ins: [5]str; let want: [5]str; ins[0]="/usr/lib"; want[0]="/usr"; @@ -87,8 +87,8 @@ import path; let i: i32 = 0; for (i < 5) { buf.end = 0; - push(&buf, ins[i])!; - assert(!(parent(&buf)! != want[i])); + path.push(&buf, ins[i])!; + assert(!(path.parent(&buf)! != want[i])); i += 1; }; }; @@ -99,30 +99,30 @@ import path; // #48 respelling didn't smuggle in pop's buf.end mutation. @test fn popsplit_cases() void = { - let buf = buffer { ... }; + let buf: path.buffer; // empty - assert(!(!(pop(&buf) is void))); - assert(!(string(&buf) != ".")); + assert(!(!(path.pop(&buf) is void))); + assert(!(path.string(&buf) != ".")); // root dir buf.end = 0; - assert(!(push(&buf, "/")! != "/")); - assert(!(!(pop(&buf) is void))); - assert(!(string(&buf) != "/")); + assert(!(path.push(&buf, "/")! != "/")); + assert(!(!(path.pop(&buf) is void))); + assert(!(path.string(&buf) != "/")); // relative file — peek is NON-mutating buf.end = 0; - assert(!(push(&buf, "foo")! != "foo")); - assert(!(peek(&buf) as str != "foo")); - assert(!(string(&buf) != "foo")); - assert(!(pop(&buf) as str != "foo")); - assert(!(string(&buf) != ".")); + assert(!(path.push(&buf, "foo")! != "foo")); + assert(!(path.peek(&buf) as str != "foo")); + assert(!(path.string(&buf) != "foo")); + assert(!(path.pop(&buf) as str != "foo")); + assert(!(path.string(&buf) != ".")); // absolute file buf.end = 0; - assert(!(push(&buf, "/foo")! != "/foo")); - assert(!(peek(&buf) as str != "foo")); - assert(!(pop(&buf) as str != "foo")); - assert(!(string(&buf) != "/")); + assert(!(path.push(&buf, "/foo")! != "/foo")); + assert(!(path.peek(&buf) as str != "foo")); + assert(!(path.pop(&buf) as str != "foo")); + assert(!(path.string(&buf) != "/")); }; diff --git a/lib/regex/regex_test.ww b/lib/regex/regex_test.ww index 243e4d20..ae3faa23 100644 --- a/lib/regex/regex_test.ww +++ b/lib/regex/regex_test.ww @@ -21,12 +21,10 @@ // the regex/capture struct shapes, and finish(). A failing row aborts // via the assert/abort builtin (task #5 @test conversion). // -// Struct literals below name the type UNQUALIFIED (`inst_charset { … }`, -// not `regex.inst_charset { … }`): the parser rejects a module-qualified -// name in struct-literal position (#29), and the imported EXPORTED type -// is in scope unqualified. These reach only exported types — the #29 -// axis, distinct from the white-box privacy boundary handled by the -// whitebox_test.ww split above. +// The parser rejects a qualified name in struct-literal position (#29), so +// the few struct payload rows below initialize a qualified typed local before +// injecting it into the exported union. This stays distinct from the +// white-box privacy boundary handled by the whitebox_test.ww split above. package regex_test; import regex; @@ -107,7 +105,10 @@ import strings; // inst_charset carries a struct payload; its fields survive the union // round-trip. @test fn charset_payload() void = { - let c: regex.inst = (inst_charset { idx = 3, is_positive = true }); + let value: regex.inst_charset; + value.idx = 3; + value.is_positive = true; + let c: regex.inst = value; match (c) { case let cs: regex.inst_charset => { assert(!(cs.idx != (3: size))); @@ -123,9 +124,12 @@ import strings; // diverges cs≠ww (#26 — the wwstage frames it wider), so // asserting the bounds here would seed a rule-10-divergent fixture. @test fn repeat_payload() void = { - let r: regex.inst = (inst_repeat { - id = 1, origin = 4, min = (2: size), max = void, - }); + let value: regex.inst_repeat; + value.id = 1; + value.origin = 4; + value.min = (2: size); + value.max = void; + let r: regex.inst = value; match (r) { case let rp: regex.inst_repeat => { assert(!(rp.id != (1: size))); @@ -138,13 +142,12 @@ import strings; // The regex/capture structs hold their fields; finish() is a no-op // (no-free runtime) and must accept a built regex. @test fn struct_shapes_and_finish() void = { - let cap: regex.capture = capture { - content = "abc", - start = 0, - start_bytesize = 0, - end = 3, - end_bytesize = 3, - }; + let cap: regex.capture; + cap.content = "abc"; + cap.start = 0; + cap.start_bytesize = 0; + cap.end = 3; + cap.end_bytesize = 3; assert(!(cap.content.len != 3)); assert(!(cap.end != (3: size))); @@ -256,15 +259,18 @@ import strings; // the documented no-op (no-free runtime), so the header must stay // readable after — a future real free changes this row consciously. // The local is spelled []regex.capture, not the regex.result alias: -// wwstage falsely loud-bails appending a struct literal onto an -// alias-typed dst (#20); the alias + signature stay exercised by the -// result_free call itself. Reverts to `regex.result` when #20 lands. +// wwstage falsely loud-bails appending a capture value onto an alias-typed +// dst (#20); the alias + signature stay exercised by result_free itself. +// Reverts to `regex.result` when #20 lands. @test fn result_free_noop() void = { let res: []regex.capture; - append(res, capture { - content = "x", start = 0, start_bytesize = 0, - end = 1, end_bytesize = 1, - }); + let value: regex.capture; + value.content = "x"; + value.start = 0; + value.start_bytesize = 0; + value.end = 1; + value.end_bytesize = 1; + append(res, value); regex.result_free(res); assert(!(len(res) != 1)); assert(!(res[0].end != (1: size))); @@ -872,16 +878,16 @@ type cerow = struct { // class items as -2 (presence sentinel — fn-ptr address not stable). // The element binds structurally, not via the `charset` alias — an // alias-typed slice local's index read mis-scales in wwstage (#68). -fn cssig(cs: [](charset_lit_item | charset_range_item | - charset_class_item), k: size) i64 = { - let cur: (charset_lit_item | charset_range_item | - charset_class_item) = cs[k]; +fn cssig(cs: [](regex.charset_lit_item | regex.charset_range_item | + regex.charset_class_item), k: size) i64 = { + let cur: (regex.charset_lit_item | regex.charset_range_item | + regex.charset_class_item) = cs[k]; match (cur) { - case let l: charset_lit_item => + case let l: regex.charset_lit_item => return 1000000 + ((l: rune): i64); - case let range: charset_range_item => + case let range: regex.charset_range_item => return (range.0: i64) * 10000 + (range.1: i64); - case charset_class_item => return -2; // fn-ptr address not stable + case regex.charset_class_item => return -2; // fn-ptr address not stable case => return -1; }; }; @@ -928,8 +934,8 @@ type cscase = struct { match (c) { case let re: regex.regex => { assert(!(re.charsets.len != 1)); - let cs0: [](charset_lit_item | charset_range_item | - charset_class_item) = re.charsets[0]; + let cs0: [](regex.charset_lit_item | regex.charset_range_item | + regex.charset_class_item) = re.charsets[0]; assert(!((len(cs0): i32) != rows[i].ecnt)); let k: i32 = 0; for (k < rows[i].ecnt) { @@ -1715,8 +1721,8 @@ type smcase = struct { match (c) { case let re: regex.regex => { assert(!(re.charsets.len != 1)); - let cs0: [](charset_lit_item | charset_range_item | - charset_class_item) = re.charsets[0]; + let cs0: [](regex.charset_lit_item | regex.charset_range_item | + regex.charset_class_item) = re.charsets[0]; assert(!((len(cs0): i32) != 1)); assert(!(cssig(cs0, 0) != -2)); regex.finish(&re); diff --git a/lib/strconv/ftos_test.ww b/lib/strconv/ftos_test.ww index b43e68d8..a2144490 100644 --- a/lib/strconv/ftos_test.ww +++ b/lib/strconv/ftos_test.ww @@ -28,25 +28,25 @@ fn fstreq(a: str, b: str) bool = { }; fn chk(n: f64, want: str) bool = { - return fstreq(f64tos(n), want); + return fstreq(strconv.f64tos(n), want); }; // rt64 — bit-exact round-trip: render then parse back must reproduce the // exact bits. The parse-back re-runs strconv's decimal slow path. fn rt64(x: f64) bool = { - match (stof64(f64tos(x), base.DEC)) { + match (strconv.stof64(strconv.f64tos(x), strconv.base.DEC)) { case let v: f64 => { return math.f64bits(v) == math.f64bits(x); }; - case let e: invalid => { return false; }; - case let e: overflow => { return false; }; + case let e: strconv.invalid => { return false; }; + case let e: strconv.overflow => { return false; }; }; return false; }; fn rt32(x: f32) bool = { - match (stof32(f32tos(x), base.DEC)) { + match (strconv.stof32(strconv.f32tos(x), strconv.base.DEC)) { case let v: f32 => { return math.f32bits(v) == math.f32bits(x); }; - case let e: invalid => { return false; }; - case let e: overflow => { return false; }; + case let e: strconv.invalid => { return false; }; + case let e: strconv.overflow => { return false; }; }; return false; }; @@ -96,7 +96,7 @@ fn rt32(x: f32) bool = { }; fn chkf32(n: f32, want: str) bool = { - return fstreq(f32tos(n), want); + return fstreq(strconv.f32tos(n), want); }; // ftos_test.ha tcs G/void rows (the "pass for both f32 and f64" set) — diff --git a/lib/strconv/int_test.ww b/lib/strconv/int_test.ww index 4ef9000c..a050cbdb 100644 --- a/lib/strconv/int_test.ww +++ b/lib/strconv/int_test.ww @@ -14,177 +14,177 @@ package strconv_test; import strconv; -fn cki(id: i32, s: str, b: base, want: i64) void = { - match (stoi64(s, b)) { +fn cki(id: i32, s: str, b: strconv.base, want: i64) void = { + match (strconv.stoi64(s, b)) { case let v: i64 => assert(!(v != want)); - case let e: invalid => abort(); - case let e: overflow => abort(); + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => abort(); }; }; -fn cki_inv(id: i32, s: str, b: base, idx: i32) void = { - match (stoi64(s, b)) { +fn cki_inv(id: i32, s: str, b: strconv.base, idx: i32) void = { + match (strconv.stoi64(s, b)) { case let v: i64 => abort(); - case let e: invalid => assert(!(e: i32 != idx)); - case let e: overflow => abort(); + case let e: strconv.invalid => assert(!(e: i32 != idx)); + case let e: strconv.overflow => abort(); }; }; -fn cki_ovf(id: i32, s: str, b: base) void = { - match (stoi64(s, b)) { +fn cki_ovf(id: i32, s: str, b: strconv.base) void = { + match (strconv.stoi64(s, b)) { case let v: i64 => abort(); - case let e: invalid => abort(); - case let e: overflow => { }; + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => { }; }; }; -fn cku(id: i32, s: str, b: base, want: u64) void = { - match (stou64(s, b)) { +fn cku(id: i32, s: str, b: strconv.base, want: u64) void = { + match (strconv.stou64(s, b)) { case let v: u64 => assert(!(v != want)); - case let e: invalid => abort(); - case let e: overflow => abort(); + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => abort(); }; }; -fn cku_inv(id: i32, s: str, b: base, idx: i32) void = { - match (stou64(s, b)) { +fn cku_inv(id: i32, s: str, b: strconv.base, idx: i32) void = { + match (strconv.stou64(s, b)) { case let v: u64 => abort(); - case let e: invalid => assert(!(e: i32 != idx)); - case let e: overflow => abort(); + case let e: strconv.invalid => assert(!(e: i32 != idx)); + case let e: strconv.overflow => abort(); }; }; -fn cku_ovf(id: i32, s: str, b: base) void = { - match (stou64(s, b)) { +fn cku_ovf(id: i32, s: str, b: strconv.base) void = { + match (strconv.stou64(s, b)) { case let v: u64 => abort(); - case let e: invalid => abort(); - case let e: overflow => { }; + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => { }; }; }; -fn cki32_ovf(id: i32, s: str, b: base) void = { - match (stoi32(s, b)) { +fn cki32_ovf(id: i32, s: str, b: strconv.base) void = { + match (strconv.stoi32(s, b)) { case let v: i32 => abort(); - case let e: invalid => abort(); - case let e: overflow => { }; + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => { }; }; }; -fn cki32(id: i32, s: str, b: base, want: i32) void = { - match (stoi32(s, b)) { +fn cki32(id: i32, s: str, b: strconv.base, want: i32) void = { + match (strconv.stoi32(s, b)) { case let v: i32 => assert(!(v != want)); - case let e: invalid => abort(); - case let e: overflow => abort(); + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => abort(); }; }; -fn ck_int(id: i32, s: str, b: base, want: int) void = { - match (stoi(s, b)) { +fn ck_int(id: i32, s: str, b: strconv.base, want: int) void = { + match (strconv.stoi(s, b)) { case let v: int => assert(!(v != want)); - case let e: invalid => abort(); - case let e: overflow => abort(); + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => abort(); }; }; -fn ck_uint(id: i32, s: str, b: base, want: uint) void = { - match (stou(s, b)) { +fn ck_uint(id: i32, s: str, b: strconv.base, want: uint) void = { + match (strconv.stou(s, b)) { case let v: uint => assert(!(v != want)); - case let e: invalid => abort(); - case let e: overflow => abort(); + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => abort(); }; }; -fn ck_size(id: i32, s: str, b: base, want: size) void = { - match (stoz(s, b)) { +fn ck_size(id: i32, s: str, b: strconv.base, want: size) void = { + match (strconv.stoz(s, b)) { case let v: size => assert(!(v != want)); - case let e: invalid => abort(); - case let e: overflow => abort(); + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => abort(); }; }; -fn ck_int_ovf(id: i32, s: str, b: base) void = { - match (stoi(s, b)) { +fn ck_int_ovf(id: i32, s: str, b: strconv.base) void = { + match (strconv.stoi(s, b)) { case let v: int => abort(); - case let e: invalid => abort(); - case let e: overflow => { }; + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => { }; }; }; -fn ck_uint_ovf(id: i32, s: str, b: base) void = { - match (stou(s, b)) { +fn ck_uint_ovf(id: i32, s: str, b: strconv.base) void = { + match (strconv.stou(s, b)) { case let v: uint => abort(); - case let e: invalid => abort(); - case let e: overflow => { }; + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => { }; }; }; // ref/hare/strconv/stoi.ha:56-79. @test fn test_stoi64() void = { - cki_inv(1, "", base.DEC, 0); - cki_inv(2, "abc", base.DEC, 0); - cki_inv(3, "1a", base.DEC, 1); - cki_inv(4, "+", base.DEC, 1); - cki_inv(5, "-+", base.DEC, 1); - cki_inv(6, "-z", base.DEC, 1); + cki_inv(1, "", strconv.base.DEC, 0); + cki_inv(2, "abc", strconv.base.DEC, 0); + cki_inv(3, "1a", strconv.base.DEC, 1); + cki_inv(4, "+", strconv.base.DEC, 1); + cki_inv(5, "-+", strconv.base.DEC, 1); + cki_inv(6, "-z", strconv.base.DEC, 1); - cki_ovf(7, "9223372036854775808", base.DEC); - cki_ovf(8, "-9223372036854775809", base.DEC); + cki_ovf(7, "9223372036854775808", strconv.base.DEC); + cki_ovf(8, "-9223372036854775809", strconv.base.DEC); // The last multiply wraps to a value larger than its prefix, so a // post-arithmetic `n < old` check misses this overflow. - cki_ovf(19, "21000000000000000000", base.DEC); + cki_ovf(19, "21000000000000000000", strconv.base.DEC); - cki(9, "0", base.DEC, 0); - cki(10, "1", base.DEC, 1); - cki(11, "+1", base.DEC, 1); - cki(12, "-1", base.DEC, -1); - cki(13, "9223372036854775807", base.DEC, 9223372036854775807i64); + cki(9, "0", strconv.base.DEC, 0); + cki(10, "1", strconv.base.DEC, 1); + cki(11, "+1", strconv.base.DEC, 1); + cki(12, "-1", strconv.base.DEC, -1); + cki(13, "9223372036854775807", strconv.base.DEC, 9223372036854775807i64); // I64_MIN. Spelled -I64_MAX-1 (Hare's own two's-complement identity, // stoi64 comment in stoi.ha:11) because the wwstage mis-lexes the // direct literal -9223372036854775808 (and types.I64_MIN) to 0 — // proj #245. The INPUT string is unaffected; stoi64 parses it to the // correct value on both stages. This isolates the parse test from #245. - cki(14, "-9223372036854775808", base.DEC, -9223372036854775807i64 - 1i64); + cki(14, "-9223372036854775808", strconv.base.DEC, -9223372036854775807i64 - 1i64); // width wrapper boundaries (ref/hare/strconv/stoi.ha:74-78). - cki32_ovf(15, "2147483648", base.DEC); - cki32_ovf(16, "-2147483649", base.DEC); - cki32(17, "2147483647", base.DEC, 2147483647i32); - cki32(18, "-2147483648", base.DEC, -2147483648i32); + cki32_ovf(15, "2147483648", strconv.base.DEC); + cki32_ovf(16, "-2147483649", strconv.base.DEC); + cki32(17, "2147483647", strconv.base.DEC, 2147483647i32); + cki32(18, "-2147483648", strconv.base.DEC, -2147483648i32); }; // ref/hare/strconv/stoi.ha:81-86. @test fn test_stoi64_bases() void = { - cki(20, "-7f", base.HEX, -127i64); // -0x7f - cki(21, "7F", base.HEX, 127i64); // 0x7f - cki(22, "37", base.OCT, 31i64); // 0o37 - cki(23, "-110101", base.BIN, -53i64); // -0b110101 + cki(20, "-7f", strconv.base.HEX, -127i64); // -0x7f + cki(21, "7F", strconv.base.HEX, 127i64); // 0x7f + cki(22, "37", strconv.base.OCT, 31i64); // 0o37 + cki(23, "-110101", strconv.base.BIN, -53i64); // -0b110101 }; // ref/hare/strconv/stou.ha:116-130. @test fn test_stou64() void = { - cku_inv(30, "", base.DEC, 0); - cku_inv(31, "+", base.DEC, 1); - cku_inv(32, "+a", base.DEC, 1); - cku_inv(33, "abc", base.DEC, 0); - cku_inv(34, "1a", base.DEC, 1); + cku_inv(30, "", strconv.base.DEC, 0); + cku_inv(31, "+", strconv.base.DEC, 1); + cku_inv(32, "+a", strconv.base.DEC, 1); + cku_inv(33, "abc", strconv.base.DEC, 0); + cku_inv(34, "1a", strconv.base.DEC, 1); - cku_ovf(35, "18446744073709551616", base.DEC); - cku_ovf(36, "184467440737095516150", base.DEC); - cku_ovf(37, "-1", base.DEC); - cku_ovf(46, "21000000000000000000", base.DEC); + cku_ovf(35, "18446744073709551616", strconv.base.DEC); + cku_ovf(36, "184467440737095516150", strconv.base.DEC); + cku_ovf(37, "-1", strconv.base.DEC); + cku_ovf(46, "21000000000000000000", strconv.base.DEC); - cku(38, "0", base.DEC, 0u64); - cku(39, "1", base.DEC, 1u64); - cku(40, "18446744073709551615", base.DEC, 18446744073709551615u64); + cku(38, "0", strconv.base.DEC, 0u64); + cku(39, "1", strconv.base.DEC, 1u64); + cku(40, "18446744073709551615", strconv.base.DEC, 18446744073709551615u64); }; // ref/hare/strconv/stou.ha:132-138. @test fn test_stou64_bases() void = { - cku(41, "f", base.HEX_LOWER, 15u64); // 0xf - cku(42, "7f", base.HEX, 127u64); // 0x7f - cku(43, "7F", base.HEX, 127u64); // 0x7f - cku(44, "37", base.OCT, 31u64); // 0o37 - cku(45, "110101", base.BIN, 53u64); // 0b110101 + cku(41, "f", strconv.base.HEX_LOWER, 15u64); // 0xf + cku(42, "7f", strconv.base.HEX, 127u64); // 0x7f + cku(43, "7F", strconv.base.HEX, 127u64); // 0x7f + cku(44, "37", strconv.base.OCT, 31u64); // 0o37 + cku(45, "110101", strconv.base.BIN, 53u64); // 0b110101 }; // stoi / stou / stoz — int/uint/size machine-word wrappers @@ -192,102 +192,102 @@ fn ck_uint_ovf(id: i32, s: str, b: base) void = { // 8B, so the iN/uN clamp is a no-op: the full i64/u64 range parses with // no spurious overflow. That no-clamp fidelity is what these check. @test fn test_stoi_stou_stoz() void = { - ck_int(50, "0", base.DEC, 0); - ck_int(51, "-1", base.DEC, -1); - ck_int(52, "9223372036854775807", base.DEC, 9223372036854775807i64: int); // I64_MAX fits int - ck_int_ovf(53, "9223372036854775808", base.DEC); - ck_int_ovf(54, "-9223372036854775809", base.DEC); + ck_int(50, "0", strconv.base.DEC, 0); + ck_int(51, "-1", strconv.base.DEC, -1); + ck_int(52, "9223372036854775807", strconv.base.DEC, 9223372036854775807i64: int); // I64_MAX fits int + ck_int_ovf(53, "9223372036854775808", strconv.base.DEC); + ck_int_ovf(54, "-9223372036854775809", strconv.base.DEC); - ck_uint(55, "0", base.DEC, 0u64: uint); - ck_uint(56, "18446744073709551615", base.DEC, 18446744073709551615u64: uint); // U64_MAX fits uint - ck_uint_ovf(57, "18446744073709551616", base.DEC); - ck_uint_ovf(58, "-1", base.DEC); + ck_uint(55, "0", strconv.base.DEC, 0u64: uint); + ck_uint(56, "18446744073709551615", strconv.base.DEC, 18446744073709551615u64: uint); // U64_MAX fits uint + ck_uint_ovf(57, "18446744073709551616", strconv.base.DEC); + ck_uint_ovf(58, "-1", strconv.base.DEC); - ck_size(59, "0", base.DEC, 0u64: size); - ck_size(60, "18446744073709551615", base.DEC, 18446744073709551615u64: size); // U64_MAX fits size + ck_size(59, "0", strconv.base.DEC, 0u64: size); + ck_size(60, "18446744073709551615", strconv.base.DEC, 18446744073709551615u64: size); // U64_MAX fits size // bases route through the same parseint core. - ck_int(61, "-7f", base.HEX, -127i64: int); // -0x7f - ck_uint(62, "110101", base.BIN, 53u64: uint); // 0b110101 + ck_int(61, "-7f", strconv.base.HEX, -127i64: int); // -0x7f + ck_uint(62, "110101", strconv.base.BIN, 53u64: uint); // 0b110101 }; @test fn test_narrow_parse_boundaries() void = { - match (stoi16("32767", base.DEC)) { + match (strconv.stoi16("32767", strconv.base.DEC)) { case let v: i16 => assert(v == 32767i16); - case let e: invalid => abort(); - case let e: overflow => abort(); + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => abort(); }; - match (stoi16("-32768", base.DEC)) { + match (strconv.stoi16("-32768", strconv.base.DEC)) { case let v: i16 => assert(v == -32768i16); - case let e: invalid => abort(); - case let e: overflow => abort(); + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => abort(); }; - match (stoi16("32768", base.DEC)) { + match (strconv.stoi16("32768", strconv.base.DEC)) { case let v: i16 => abort(); - case let e: invalid => abort(); - case let e: overflow => { }; + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => { }; }; - match (stoi16("-32769", base.DEC)) { + match (strconv.stoi16("-32769", strconv.base.DEC)) { case let v: i16 => abort(); - case let e: invalid => abort(); - case let e: overflow => { }; + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => { }; }; - match (stoi8("127", base.DEC)) { + match (strconv.stoi8("127", strconv.base.DEC)) { case let v: i8 => assert(v == 127i8); - case let e: invalid => abort(); - case let e: overflow => abort(); + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => abort(); }; - match (stoi8("-128", base.DEC)) { + match (strconv.stoi8("-128", strconv.base.DEC)) { case let v: i8 => assert(v == -128i8); - case let e: invalid => abort(); - case let e: overflow => abort(); + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => abort(); }; - match (stoi8("128", base.DEC)) { + match (strconv.stoi8("128", strconv.base.DEC)) { case let v: i8 => abort(); - case let e: invalid => abort(); - case let e: overflow => { }; + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => { }; }; - match (stoi8("-129", base.DEC)) { + match (strconv.stoi8("-129", strconv.base.DEC)) { case let v: i8 => abort(); - case let e: invalid => abort(); - case let e: overflow => { }; + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => { }; }; - match (stou16("65535", base.DEC)) { + match (strconv.stou16("65535", strconv.base.DEC)) { case let v: u16 => assert(v == 65535u16); - case let e: invalid => abort(); - case let e: overflow => abort(); + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => abort(); }; - match (stou16("65536", base.DEC)) { + match (strconv.stou16("65536", strconv.base.DEC)) { case let v: u16 => abort(); - case let e: invalid => abort(); - case let e: overflow => { }; + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => { }; }; - match (stou8("255", base.DEC)) { + match (strconv.stou8("255", strconv.base.DEC)) { case let v: u8 => assert(v == 255u8); - case let e: invalid => abort(); - case let e: overflow => abort(); + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => abort(); }; - match (stou8("256", base.DEC)) { + match (strconv.stou8("256", strconv.base.DEC)) { case let v: u8 => abort(); - case let e: invalid => abort(); - case let e: overflow => { }; + case let e: strconv.invalid => abort(); + case let e: strconv.overflow => { }; }; }; @test fn test_strerror_static() void = { - let inv: invalid = 0: invalid; - let ie: error = inv; - let ia: str = strerror(ie); - let ib: str = strerror(ie); + let inv: strconv.invalid = 0: strconv.invalid; + let ie: strconv.error = inv; + let ia: str = strconv.strerror(ie); + let ib: str = strconv.strerror(ie); assert(ia.ptr == ib.ptr); assert(streq(ia, "input is not a valid number")); - let ov: overflow; - let oe: error = ov; - let oa: str = strerror(oe); - let ob: str = strerror(oe); + let ov: strconv.overflow; + let oe: strconv.error = ov; + let oa: str = strconv.strerror(oe); + let ob: str = strconv.strerror(oe); assert(oa.ptr == ob.ptr); assert(streq(oa, "input number doesn't fit target type")); }; @@ -314,48 +314,48 @@ fn cks(id: i32, got: str, want: str) void = { // ref/hare/strconv/utos.ha:74-83. @test fn test_u64tos_bases() void = { - cks(70, u64tos(26u64, base.BIN), "11010"); // 0b11010 - cks(71, u64tos(342391u64, base.OCT), "1234567"); // 0o1234567 - cks(72, u64tos(123456789u64, base.DEC), "123456789"); - cks(73, u64tos(81985529216486895u64, base.HEX), "123456789ABCDEF"); // 0x123456789ABCDEF - cks(74, u64tos(81985529216486895u64, base.HEX_UPPER), "123456789ABCDEF"); - cks(75, u64tos(81985529216486895u64, base.HEX_LOWER), "123456789abcdef"); - cks(76, u64tos(18446744073709551615u64, base.BIN), // U64_MAX + cks(70, strconv.u64tos(26u64, strconv.base.BIN), "11010"); // 0b11010 + cks(71, strconv.u64tos(342391u64, strconv.base.OCT), "1234567"); // 0o1234567 + cks(72, strconv.u64tos(123456789u64, strconv.base.DEC), "123456789"); + cks(73, strconv.u64tos(81985529216486895u64, strconv.base.HEX), "123456789ABCDEF"); // 0x123456789ABCDEF + cks(74, strconv.u64tos(81985529216486895u64, strconv.base.HEX_UPPER), "123456789ABCDEF"); + cks(75, strconv.u64tos(81985529216486895u64, strconv.base.HEX_LOWER), "123456789abcdef"); + cks(76, strconv.u64tos(18446744073709551615u64, strconv.base.BIN), // U64_MAX "1111111111111111111111111111111111111111111111111111111111111111"); }; // ref/hare/strconv/utos.ha:85-103. @test fn test_u64tos() void = { - cks(77, u64tos(1234u64, base.DEC), "1234"); - cks(78, u64tos(4321u64, base.DEC), "4321"); - cks(79, u64tos(0u64, base.DEC), "0"); // U64_MIN - cks(80, u64tos(18446744073709551615u64, base.DEC), "18446744073709551615"); // U64_MAX + cks(77, strconv.u64tos(1234u64, strconv.base.DEC), "1234"); + cks(78, strconv.u64tos(4321u64, strconv.base.DEC), "4321"); + cks(79, strconv.u64tos(0u64, strconv.base.DEC), "0"); // U64_MIN + cks(80, strconv.u64tos(18446744073709551615u64, strconv.base.DEC), "18446744073709551615"); // U64_MAX }; // ref/hare/strconv/itos.ha:54-63. @test fn test_i64tos_bases() void = { - cks(81, i64tos(26i64, base.BIN), "11010"); - cks(82, i64tos(342391i64, base.OCT), "1234567"); - cks(83, i64tos(123456789i64, base.DEC), "123456789"); - cks(84, i64tos(81985529216486895i64, base.HEX), "123456789ABCDEF"); - cks(85, i64tos(81985529216486895i64, base.HEX_UPPER), "123456789ABCDEF"); - cks(86, i64tos(81985529216486895i64, base.HEX_LOWER), "123456789abcdef"); + cks(81, strconv.i64tos(26i64, strconv.base.BIN), "11010"); + cks(82, strconv.i64tos(342391i64, strconv.base.OCT), "1234567"); + cks(83, strconv.i64tos(123456789i64, strconv.base.DEC), "123456789"); + cks(84, strconv.i64tos(81985529216486895i64, strconv.base.HEX), "123456789ABCDEF"); + cks(85, strconv.i64tos(81985529216486895i64, strconv.base.HEX_UPPER), "123456789ABCDEF"); + cks(86, strconv.i64tos(81985529216486895i64, strconv.base.HEX_LOWER), "123456789abcdef"); // I64_MIN binary = '1' + 63 zeros, prefixed '-'. Spelled -I64_MAX-1 // (proj #245: wwstage mis-lexes the direct 2^63 / types.I64_MIN literal). - cks(87, i64tos(-9223372036854775807i64 - 1i64, base.BIN), + cks(87, strconv.i64tos(-9223372036854775807i64 - 1i64, strconv.base.BIN), "-1000000000000000000000000000000000000000000000000000000000000000"); }; // ref/hare/strconv/itos.ha:65-87. @test fn test_i64tos() void = { - cks(88, i64tos(1234i64, base.DEC), "1234"); - cks(89, i64tos(4321i64, base.DEC), "4321"); - cks(90, i64tos(-1337i64, base.DEC), "-1337"); - cks(91, i64tos(0i64, base.DEC), "0"); - cks(92, i64tos(9223372036854775807i64, base.DEC), "9223372036854775807"); // I64_MAX + cks(88, strconv.i64tos(1234i64, strconv.base.DEC), "1234"); + cks(89, strconv.i64tos(4321i64, strconv.base.DEC), "4321"); + cks(90, strconv.i64tos(-1337i64, strconv.base.DEC), "-1337"); + cks(91, strconv.i64tos(0i64, strconv.base.DEC), "0"); + cks(92, strconv.i64tos(9223372036854775807i64, strconv.base.DEC), "9223372036854775807"); // I64_MAX // drew probe-1: NEG-of-I64_MIN + i64→u64 reinterpret preserves the // two's-complement bit pattern. I64_MIN spelled -I64_MAX-1 (proj #245). - cks(93, i64tos(-9223372036854775807i64 - 1i64, base.DEC), + cks(93, strconv.i64tos(-9223372036854775807i64 - 1i64, strconv.base.DEC), "-9223372036854775808"); }; @@ -363,11 +363,11 @@ fn cks(id: i32, got: str, want: str) void = { // all 8B, so the full i64/u64 range renders untruncated. ww-authored // (Hare's wrappers are trivial aliases): itos.ha:37-52, utos.ha:44-72. @test fn test_word_wrappers() void = { - cks(94, itos(-1337, base.DEC), "-1337"); - cks(95, itos(9223372036854775807i64: int, base.DEC), "9223372036854775807"); - cks(96, utos(18446744073709551615u64: uint, base.DEC), "18446744073709551615"); - cks(97, ztos(255u64: size, base.HEX), "FF"); - cks(98, uptrtos(4096u64: uintptr, base.DEC), "4096"); - cks(99, i32tos(-2147483648i32, base.DEC), "-2147483648"); - cks(100, u8tos(255u8, base.HEX_LOWER), "ff"); + cks(94, strconv.itos(-1337, strconv.base.DEC), "-1337"); + cks(95, strconv.itos(9223372036854775807i64: int, strconv.base.DEC), "9223372036854775807"); + cks(96, strconv.utos(18446744073709551615u64: uint, strconv.base.DEC), "18446744073709551615"); + cks(97, strconv.ztos(255u64: size, strconv.base.HEX), "FF"); + cks(98, strconv.uptrtos(4096u64: uintptr, strconv.base.DEC), "4096"); + cks(99, strconv.i32tos(-2147483648i32, strconv.base.DEC), "-2147483648"); + cks(100, strconv.u8tos(255u8, strconv.base.HEX_LOWER), "ff"); }; diff --git a/lib/strconv/stof_test.ww b/lib/strconv/stof_test.ww index 6fcdc326..85d1264e 100644 --- a/lib/strconv/stof_test.ww +++ b/lib/strconv/stof_test.ww @@ -12,75 +12,75 @@ import strconv; import math; -fn chk64(s: str, b: base, want: f64) bool = { - match (stof64(s, b)) { +fn chk64(s: str, b: strconv.base, want: f64) bool = { + match (strconv.stof64(s, b)) { case let v: f64 => { return math.f64bits(v) == math.f64bits(want); }; - case let e: invalid => { return false; }; - case let e: overflow => { return false; }; + case let e: strconv.invalid => { return false; }; + case let e: strconv.overflow => { return false; }; }; return false; }; -fn chk32(s: str, b: base, want: f32) bool = { - match (stof32(s, b)) { +fn chk32(s: str, b: strconv.base, want: f32) bool = { + match (strconv.stof32(s, b)) { case let v: f32 => { return math.f32bits(v) == math.f32bits(want); }; - case let e: invalid => { return false; }; - case let e: overflow => { return false; }; + case let e: strconv.invalid => { return false; }; + case let e: strconv.overflow => { return false; }; }; return false; }; -fn ovf64(s: str, b: base) bool = { - match (stof64(s, b)) { +fn ovf64(s: str, b: strconv.base) bool = { + match (strconv.stof64(s, b)) { case let v: f64 => { return false; }; - case let e: invalid => { return false; }; - case let e: overflow => { return true; }; + case let e: strconv.invalid => { return false; }; + case let e: strconv.overflow => { return true; }; }; return false; }; -fn ovf32(s: str, b: base) bool = { - match (stof32(s, b)) { +fn ovf32(s: str, b: strconv.base) bool = { + match (strconv.stof32(s, b)) { case let v: f32 => { return false; }; - case let e: invalid => { return false; }; - case let e: overflow => { return true; }; + case let e: strconv.invalid => { return false; }; + case let e: strconv.overflow => { return true; }; }; return false; }; // inv64 — the invalid byte-index, or -1 if not invalid. fn inv64(s: str) i32 = { - match (stof64(s, base.DEC)) { + match (strconv.stof64(s, strconv.base.DEC)) { case let v: f64 => { return -1; }; - case let e: invalid => { return (e: i32); }; - case let e: overflow => { return -1; }; + case let e: strconv.invalid => { return (e: i32); }; + case let e: strconv.overflow => { return -1; }; }; return -1; }; fn inv32(s: str) i32 = { - match (stof32(s, base.DEC)) { + match (strconv.stof32(s, strconv.base.DEC)) { case let v: f32 => { return -1; }; - case let e: invalid => { return (e: i32); }; - case let e: overflow => { return -1; }; + case let e: strconv.invalid => { return (e: i32); }; + case let e: strconv.overflow => { return -1; }; }; return -1; }; fn nan64(s: str) bool = { - match (stof64(s, base.DEC)) { + match (strconv.stof64(s, strconv.base.DEC)) { case let v: f64 => { return math.isnan(v); }; - case let e: invalid => { return false; }; - case let e: overflow => { return false; }; + case let e: strconv.invalid => { return false; }; + case let e: strconv.overflow => { return false; }; }; return false; }; fn nan32(s: str) bool = { - match (stof32(s, base.DEC)) { + match (strconv.stof32(s, strconv.base.DEC)) { case let v: f32 => { return math.isnan((v: f64)); }; - case let e: invalid => { return false; }; - case let e: overflow => { return false; }; + case let e: strconv.invalid => { return false; }; + case let e: strconv.overflow => { return false; }; }; return false; }; @@ -93,20 +93,20 @@ fn nan32(s: str) bool = { let nzero: f64 = math.f64frombits(1u64 << 63u64); // -0.0 (1<<63 dodges the I64_MIN-literal emit bug #144) let subn: f64 = math.f64frombits(0x0000000000000001u64); // 5e-324 - assert(!(!chk64("0", base.DEC, 0.0))); - assert(!(!chk64("200", base.DEC, 200.0))); - assert(!(!chk64("12345", base.DEC, 12345.0))); - assert(!(!chk64("+112233445566778899", base.DEC, 1.122334455667789e17))); - assert(!(!chk64("3.14", base.DEC, 3.14))); - assert(!(!chk64("2.99792458E+8", base.DEC, 299792458.0))); - assert(!(!chk64("6.022e23", base.DEC, 6.022e23))); - assert(!(!ovf64("1e310", base.DEC))); - assert(!(!chk64("9007199254740991", base.DEC, 9007199254740991.0))); - assert(!(!chk64("90071992547409915", base.DEC, 90071992547409920.0))); - assert(!(!chk64("90071992547409925", base.DEC, 90071992547409920.0))); - assert(!(!chk64("2.2250738585072014e-308", base.DEC, 2.2250738585072014e-308))); - assert(!(!chk64("-1e-324", base.DEC, nzero))); - assert(!(!chk64("5e-324", base.DEC, subn))); + assert(!(!chk64("0", strconv.base.DEC, 0.0))); + assert(!(!chk64("200", strconv.base.DEC, 200.0))); + assert(!(!chk64("12345", strconv.base.DEC, 12345.0))); + assert(!(!chk64("+112233445566778899", strconv.base.DEC, 1.122334455667789e17))); + assert(!(!chk64("3.14", strconv.base.DEC, 3.14))); + assert(!(!chk64("2.99792458E+8", strconv.base.DEC, 299792458.0))); + assert(!(!chk64("6.022e23", strconv.base.DEC, 6.022e23))); + assert(!(!ovf64("1e310", strconv.base.DEC))); + assert(!(!chk64("9007199254740991", strconv.base.DEC, 9007199254740991.0))); + assert(!(!chk64("90071992547409915", strconv.base.DEC, 90071992547409920.0))); + assert(!(!chk64("90071992547409925", strconv.base.DEC, 90071992547409920.0))); + assert(!(!chk64("2.2250738585072014e-308", strconv.base.DEC, 2.2250738585072014e-308))); + assert(!(!chk64("-1e-324", strconv.base.DEC, nzero))); + assert(!(!chk64("5e-324", strconv.base.DEC, subn))); // Decimal-engine edge probes migrated here when the white-box // decimaltest.ww retired (#16): the external-test idiom can't reach @@ -117,20 +117,20 @@ fn nan32(s: str) bool = { // Micro-gap (rule-7, honest): decimal_shift's k=0 early-return no-op // is a trivial guard, not reached transitively by any slow-path input // — consciously un-migrated, not silently dropped. - assert(!(!chk64("9999999999999999", base.DEC, 1.0e16))); - assert(!(!chk64("100000000000000000001", base.DEC, 1.0e20))); + assert(!(!chk64("9999999999999999", strconv.base.DEC, 1.0e16))); + assert(!(!chk64("100000000000000000001", strconv.base.DEC, 1.0e20))); assert(!(inv64("") != 0)); assert(!(inv64("0ZO") != 1)); assert(!(inv64("1.23ezz") != 5)); - assert(!(!chk64("Infinity", base.DEC, inf))); - assert(!(!chk64("+Infinity", base.DEC, inf))); - assert(!(!chk64("-Infinity", base.DEC, ninf))); - assert(!(!chk64("infinity", base.DEC, inf))); - assert(!(!chk64("inFinIty", base.DEC, inf))); - assert(!(!chk64("-infinity", base.DEC, ninf))); - assert(!(!chk64("-infiNity", base.DEC, ninf))); + assert(!(!chk64("Infinity", strconv.base.DEC, inf))); + assert(!(!chk64("+Infinity", strconv.base.DEC, inf))); + assert(!(!chk64("-Infinity", strconv.base.DEC, ninf))); + assert(!(!chk64("infinity", strconv.base.DEC, inf))); + assert(!(!chk64("inFinIty", strconv.base.DEC, inf))); + assert(!(!chk64("-infinity", strconv.base.DEC, ninf))); + assert(!(!chk64("-infiNity", strconv.base.DEC, ninf))); assert(!(!nan64("NaN"))); assert(!(!nan64("nan"))); assert(!(!nan64("naN"))); @@ -144,34 +144,34 @@ fn nan32(s: str) bool = { let nzero: f32 = math.f32frombits(0x80000000u32); // -0.0 let subn: f32 = math.f32frombits(0x00000001u32); // 1e-45 - assert(!(!chk32("0", base.DEC, 0.0f32))); - assert(!(!chk32("1e10", base.DEC, 1.0e10f32))); - assert(!(!chk32("299792458", base.DEC, 299792458.0f32))); - assert(!(!chk32("6.022e23", base.DEC, 6.022e23f32))); - assert(!(!ovf32("1e40", base.DEC))); - assert(!(!chk32("16777215", base.DEC, 16777215.0f32))); - assert(!(!chk32("167772155", base.DEC, 167772160.0f32))); - assert(!(!chk32("167772145", base.DEC, 167772140.0f32))); - assert(!(!chk32("6.62607015e-34", base.DEC, 6.62607015e-34f32))); - assert(!(!chk32("1.1754944e-38", base.DEC, 1.1754944e-38f32))); - assert(!(!chk32("-1e-50", base.DEC, nzero))); - assert(!(!chk32("1e-45", base.DEC, subn))); + assert(!(!chk32("0", strconv.base.DEC, 0.0f32))); + assert(!(!chk32("1e10", strconv.base.DEC, 1.0e10f32))); + assert(!(!chk32("299792458", strconv.base.DEC, 299792458.0f32))); + assert(!(!chk32("6.022e23", strconv.base.DEC, 6.022e23f32))); + assert(!(!ovf32("1e40", strconv.base.DEC))); + assert(!(!chk32("16777215", strconv.base.DEC, 16777215.0f32))); + assert(!(!chk32("167772155", strconv.base.DEC, 167772160.0f32))); + assert(!(!chk32("167772145", strconv.base.DEC, 167772140.0f32))); + assert(!(!chk32("6.62607015e-34", strconv.base.DEC, 6.62607015e-34f32))); + assert(!(!chk32("1.1754944e-38", strconv.base.DEC, 1.1754944e-38f32))); + assert(!(!chk32("-1e-50", strconv.base.DEC, nzero))); + assert(!(!chk32("1e-45", strconv.base.DEC, subn))); assert(!(inv32("") != 0)); assert(!(inv32("0ZO") != 1)); assert(!(inv32("1.23e-zz") != 6)); - assert(!(!chk32("Infinity", base.DEC, inf))); - assert(!(!chk32("+Infinity", base.DEC, inf))); - assert(!(!chk32("-Infinity", base.DEC, ninf))); - assert(!(!chk32("infinity", base.DEC, inf))); - assert(!(!chk32("inFinIty", base.DEC, inf))); - assert(!(!chk32("-infinity", base.DEC, ninf))); - assert(!(!chk32("-infiniTy", base.DEC, ninf))); + assert(!(!chk32("Infinity", strconv.base.DEC, inf))); + assert(!(!chk32("+Infinity", strconv.base.DEC, inf))); + assert(!(!chk32("-Infinity", strconv.base.DEC, ninf))); + assert(!(!chk32("infinity", strconv.base.DEC, inf))); + assert(!(!chk32("inFinIty", strconv.base.DEC, inf))); + assert(!(!chk32("-infinity", strconv.base.DEC, ninf))); + assert(!(!chk32("-infiniTy", strconv.base.DEC, ninf))); assert(!(!nan32("NaN"))); assert(!(!nan32("nan"))); assert(!(!nan32("naN"))); - assert(!(!chk32("9.19100241453305036800e+20", base.DEC, 9.19100241453305036800e+20f32))); + assert(!(!chk32("9.19100241453305036800e+20", strconv.base.DEC, 9.19100241453305036800e+20f32))); }; // ref/hare/strconv/stof.ha:590. Hex-float surface-form literals (0x1.fp-2, @@ -179,36 +179,36 @@ fn nan32(s: str) bool = { // has no hex-float literal lexer / no F*_MAX_NORMAL math consts. @test fn stof64_hex() void = { - assert(!(!chk64("0p0", base.HEX, 0.0))); - assert(!(!chk64("1p0", base.HEX, 1.0))); - assert(!(!chk64("-1p0", base.HEX_LOWER, -1.0))); + assert(!(!chk64("0p0", strconv.base.HEX, 0.0))); + assert(!(!chk64("1p0", strconv.base.HEX, 1.0))); + assert(!(!chk64("-1p0", strconv.base.HEX_LOWER, -1.0))); // 0x1.fp-2 = 1.9375 * 2^-2 = 0.484375 (exact). - assert(!(!chk64("1.fp-2", base.HEX, 0.484375))); + assert(!(!chk64("1.fp-2", strconv.base.HEX, 0.484375))); // F64_MAX_NORMAL. - if (!chk64("1.fffffffffffffp+1023", base.HEX, + if (!chk64("1.fffffffffffffp+1023", strconv.base.HEX, math.f64frombits(0x7FEFFFFFFFFFFFFFu64))) { abort(); }; // F64_MIN_NORMAL. - if (!chk64("1.0000000000000p-1022", base.HEX, + if (!chk64("1.0000000000000p-1022", strconv.base.HEX, math.f64frombits(0x0010000000000000u64))) { abort(); }; // F64_MIN_SUBNORMAL. - if (!chk64("0.0000000000001p-1022", base.HEX, + if (!chk64("0.0000000000001p-1022", strconv.base.HEX, math.f64frombits(0x0000000000000001u64))) { abort(); }; - assert(!(!ovf64("1p+1024", base.HEX))); - assert(!(!chk64("0.00000000000001p-1022", base.HEX, 0.0))); + assert(!(!ovf64("1p+1024", strconv.base.HEX))); + assert(!(!chk64("0.00000000000001p-1022", strconv.base.HEX, 0.0))); }; @test fn stof32_hex() void = { - assert(!(!chk32("0p0", base.HEX, 0.0f32))); - assert(!(!chk32("1p0", base.HEX, 1.0f32))); - assert(!(!chk32("-1p0", base.HEX, -1.0f32))); - assert(!(!chk32("1.fp-2", base.HEX, 0.484375f32))); + assert(!(!chk32("0p0", strconv.base.HEX, 0.0f32))); + assert(!(!chk32("1p0", strconv.base.HEX, 1.0f32))); + assert(!(!chk32("-1p0", strconv.base.HEX, -1.0f32))); + assert(!(!chk32("1.fp-2", strconv.base.HEX, 0.484375f32))); // F32_MAX_NORMAL. - if (!chk32("1.fffffd586b834p+127", base.HEX, + if (!chk32("1.fffffd586b834p+127", strconv.base.HEX, math.f32frombits(0x7F7FFFFFu32))) { abort(); }; // F32_MIN_NORMAL. - assert(!(!chk32("1.0p-126", base.HEX, math.f32frombits(0x00800000u32)))); + assert(!(!chk32("1.0p-126", strconv.base.HEX, math.f32frombits(0x00800000u32)))); // F32_MIN_SUBNORMAL. - assert(!(!chk32("1.6p-150", base.HEX, math.f32frombits(0x00000001u32)))); - assert(!(!ovf32("1.0p+128", base.HEX))); - assert(!(!chk32("1.0p-151", base.HEX, 0.0f32))); + assert(!(!chk32("1.6p-150", strconv.base.HEX, math.f32frombits(0x00000001u32)))); + assert(!(!ovf32("1.0p+128", strconv.base.HEX))); + assert(!(!chk32("1.0p-151", strconv.base.HEX, 0.0f32))); }; diff --git a/lib/ww/syntax/ast_test.ww b/lib/ww/syntax/ast_test.ww index 7d6ba51a..2e711874 100644 --- a/lib/ww/syntax/ast_test.ww +++ b/lib/ww/syntax/ast_test.ww @@ -10,90 +10,90 @@ package syntax_test; import ww.syntax; -fn checknkname(k: nkind, want: str) void = { - assert(!(nkname(k) != want)); +fn checknkname(k: syntax.nkind, want: str) void = { + assert(!(syntax.nkname(k) != want)); }; // One row per nkind value — same string the if-ladder returned. The // final row pins the unknown-kind fallback ("?"). @test fn nkname_cases() void = { - checknkname(nkind.N_NONE, "none"); - checknkname(nkind.N_INTLIT, "int"); - checknkname(nkind.N_FLOATLIT, "float"); - checknkname(nkind.N_STRLIT, "str"); - checknkname(nkind.N_RUNELIT, "rune"); - checknkname(nkind.N_TRUE, "true"); - checknkname(nkind.N_FALSE, "false"); - checknkname(nkind.N_NIL, "nil"); - checknkname(nkind.N_IDENT, "id"); + checknkname(syntax.nkind.N_NONE, "none"); + checknkname(syntax.nkind.N_INTLIT, "int"); + checknkname(syntax.nkind.N_FLOATLIT, "float"); + checknkname(syntax.nkind.N_STRLIT, "str"); + checknkname(syntax.nkind.N_RUNELIT, "rune"); + checknkname(syntax.nkind.N_TRUE, "true"); + checknkname(syntax.nkind.N_FALSE, "false"); + checknkname(syntax.nkind.N_NIL, "nil"); + checknkname(syntax.nkind.N_IDENT, "id"); - checknkname(nkind.N_BIN, "bin"); - checknkname(nkind.N_UN, "un"); - checknkname(nkind.N_CALL, "call"); - checknkname(nkind.N_INDEX, "index"); - checknkname(nkind.N_DOT, "dot"); - checknkname(nkind.N_CAST, "cast"); - checknkname(nkind.N_STRUCTLIT, "structlit"); - checknkname(nkind.N_ARRLIT, "arrlit"); - checknkname(nkind.N_FIELD, "field"); - checknkname(nkind.N_ASSIGN, "assign"); - checknkname(nkind.N_ALLOC, "alloc"); - checknkname(nkind.N_FREE, "free"); - checknkname(nkind.N_RECV, "recv"); - checknkname(nkind.N_SLICE, "slice"); - checknkname(nkind.N_SPREAD, "spread"); + checknkname(syntax.nkind.N_BIN, "bin"); + checknkname(syntax.nkind.N_UN, "un"); + checknkname(syntax.nkind.N_CALL, "call"); + checknkname(syntax.nkind.N_INDEX, "index"); + checknkname(syntax.nkind.N_DOT, "dot"); + checknkname(syntax.nkind.N_CAST, "cast"); + checknkname(syntax.nkind.N_STRUCTLIT, "structlit"); + checknkname(syntax.nkind.N_ARRLIT, "arrlit"); + checknkname(syntax.nkind.N_FIELD, "field"); + checknkname(syntax.nkind.N_ASSIGN, "assign"); + checknkname(syntax.nkind.N_ALLOC, "alloc"); + checknkname(syntax.nkind.N_FREE, "free"); + checknkname(syntax.nkind.N_RECV, "recv"); + checknkname(syntax.nkind.N_SLICE, "slice"); + checknkname(syntax.nkind.N_SPREAD, "spread"); - checknkname(nkind.N_BLOCK, "block"); - checknkname(nkind.N_EXPRSTMT, "exprstmt"); - checknkname(nkind.N_LET, "let"); - checknkname(nkind.N_RETURN, "return"); - checknkname(nkind.N_IF, "if"); - checknkname(nkind.N_FOR, "for"); - checknkname(nkind.N_FORRANGE, "forrange"); - checknkname(nkind.N_DEFER, "defer"); - checknkname(nkind.N_BREAK, "break"); - checknkname(nkind.N_CONTINUE, "continue"); - checknkname(nkind.N_SWITCH, "switch"); - checknkname(nkind.N_CASE, "case"); + checknkname(syntax.nkind.N_BLOCK, "block"); + checknkname(syntax.nkind.N_EXPRSTMT, "exprstmt"); + checknkname(syntax.nkind.N_LET, "let"); + checknkname(syntax.nkind.N_RETURN, "return"); + checknkname(syntax.nkind.N_IF, "if"); + checknkname(syntax.nkind.N_FOR, "for"); + checknkname(syntax.nkind.N_FORRANGE, "forrange"); + checknkname(syntax.nkind.N_DEFER, "defer"); + checknkname(syntax.nkind.N_BREAK, "break"); + checknkname(syntax.nkind.N_CONTINUE, "continue"); + checknkname(syntax.nkind.N_SWITCH, "switch"); + checknkname(syntax.nkind.N_CASE, "case"); - checknkname(nkind.N_FILE, "file"); - checknkname(nkind.N_USE, "use"); - checknkname(nkind.N_DEF, "def"); - checknkname(nkind.N_TYPEDECL, "typedecl"); - checknkname(nkind.N_FNDECL, "fn"); - checknkname(nkind.N_PARAM, "param"); + checknkname(syntax.nkind.N_FILE, "file"); + checknkname(syntax.nkind.N_USE, "use"); + checknkname(syntax.nkind.N_DEF, "def"); + checknkname(syntax.nkind.N_TYPEDECL, "typedecl"); + checknkname(syntax.nkind.N_FNDECL, "fn"); + checknkname(syntax.nkind.N_PARAM, "param"); - checknkname(nkind.N_TNAME, "tname"); - checknkname(nkind.N_TPTR, "tptr"); - checknkname(nkind.N_TSLICE, "tslice"); - checknkname(nkind.N_TARRAY, "tarray"); - checknkname(nkind.N_TFN, "tfn"); - checknkname(nkind.N_TSTRUCT, "tstruct"); - checknkname(nkind.N_TFIELD, "tfield"); - checknkname(nkind.N_TCHAN, "tchan"); + checknkname(syntax.nkind.N_TNAME, "tname"); + checknkname(syntax.nkind.N_TPTR, "tptr"); + checknkname(syntax.nkind.N_TSLICE, "tslice"); + checknkname(syntax.nkind.N_TARRAY, "tarray"); + checknkname(syntax.nkind.N_TFN, "tfn"); + checknkname(syntax.nkind.N_TSTRUCT, "tstruct"); + checknkname(syntax.nkind.N_TFIELD, "tfield"); + checknkname(syntax.nkind.N_TCHAN, "tchan"); - checknkname(nkind.N_ATTR, "attr"); - checknkname(nkind.N_TTUPLE, "ttuple"); - checknkname(nkind.N_TTAGGED, "ttagged"); - checknkname(nkind.N_TUPLE, "tuple"); - checknkname(nkind.N_MATCH, "match"); - checknkname(nkind.N_MCASE, "mcase"); - checknkname(nkind.N_TRYPROP, "tryprop"); - checknkname(nkind.N_TRYUNW, "tryunw"); - checknkname(nkind.N_MLET, "mlet"); - checknkname(nkind.N_MASSIGN, "massign"); + checknkname(syntax.nkind.N_ATTR, "attr"); + checknkname(syntax.nkind.N_TTUPLE, "ttuple"); + checknkname(syntax.nkind.N_TTAGGED, "ttagged"); + checknkname(syntax.nkind.N_TUPLE, "tuple"); + checknkname(syntax.nkind.N_MATCH, "match"); + checknkname(syntax.nkind.N_MCASE, "mcase"); + checknkname(syntax.nkind.N_TRYPROP, "tryprop"); + checknkname(syntax.nkind.N_TRYUNW, "tryunw"); + checknkname(syntax.nkind.N_MLET, "mlet"); + checknkname(syntax.nkind.N_MASSIGN, "massign"); - checknkname(nkind.N_TYPETEST, "typetest"); - checknkname(nkind.N_TYPEASSERT, "typeassert"); - checknkname(nkind.N_VOIDLIT, "voidlit"); - checknkname(nkind.N_TBANG, "tbang"); - checknkname(nkind.N_YIELD, "yield"); - checknkname(nkind.N_TENUM, "tenum"); - checknkname(nkind.N_TENUMMEMBER, "tenummember"); - checknkname(nkind.N_TPARAM, "tparam"); - checknkname(nkind.N_LAST, "last"); + checknkname(syntax.nkind.N_TYPETEST, "typetest"); + checknkname(syntax.nkind.N_TYPEASSERT, "typeassert"); + checknkname(syntax.nkind.N_VOIDLIT, "voidlit"); + checknkname(syntax.nkind.N_TBANG, "tbang"); + checknkname(syntax.nkind.N_YIELD, "yield"); + checknkname(syntax.nkind.N_TENUM, "tenum"); + checknkname(syntax.nkind.N_TENUMMEMBER, "tenummember"); + checknkname(syntax.nkind.N_TPARAM, "tparam"); + checknkname(syntax.nkind.N_LAST, "last"); // Unknown kind → the post-switch fallback. N_LAST is the highest // named value (68); 69 is out of band, exercising the "?" tail. - checknkname(69: nkind, "?"); + checknkname(69: syntax.nkind, "?"); }; diff --git a/lib/ww/syntax/sym_test.ww b/lib/ww/syntax/sym_test.ww index b247d0cf..7385890d 100644 --- a/lib/ww/syntax/sym_test.ww +++ b/lib/ww/syntax/sym_test.ww @@ -12,34 +12,34 @@ package syntax_test; import ww.syntax; @test fn scope_define_lookup() void = { - let s: *scope = newscope(nil); + let s: *syntax.scope = syntax.newscope(nil); assert(!(s == nil)); - let r1: *sym = scopedefine(s, "foo", skind.SK_VAR, nil, nil); + let r1: *syntax.sym = syntax.scopedefine(s, "foo", syntax.skind.SK_VAR, nil, nil); assert(!(r1 == nil)); - let r2: *sym = scopedefine(s, "bar", skind.SK_TYPE, nil, nil); + let r2: *syntax.sym = syntax.scopedefine(s, "bar", syntax.skind.SK_TYPE, nil, nil); assert(!(r2 == nil)); - let l1: *sym = scopelookup(s, "foo"); + let l1: *syntax.sym = syntax.scopelookup(s, "foo"); assert(!(l1 == nil)); - assert(!(l1.skind != skind.SK_VAR)); - let l2: *sym = scopelookup(s, "bar"); + assert(!(l1.skind != syntax.skind.SK_VAR)); + let l2: *syntax.sym = syntax.scopelookup(s, "bar"); assert(!(l2 == nil)); - assert(!(l2.skind != skind.SK_TYPE)); + assert(!(l2.skind != syntax.skind.SK_TYPE)); }; @test fn scope_duplicate_reject() void = { - let s: *scope = newscope(nil); + let s: *syntax.scope = syntax.newscope(nil); assert(!(s == nil)); - let r1: *sym = scopedefine(s, "foo", skind.SK_VAR, nil, nil); + let r1: *syntax.sym = syntax.scopedefine(s, "foo", syntax.skind.SK_VAR, nil, nil); assert(!(r1 == nil)); - let r3: *sym = scopedefine(s, "foo", skind.SK_VAR, nil, nil); + let r3: *syntax.sym = syntax.scopedefine(s, "foo", syntax.skind.SK_VAR, nil, nil); assert(!(r3 != nil)); }; @test fn scope_notfound_nil() void = { - let s: *scope = newscope(nil); + let s: *syntax.scope = syntax.newscope(nil); assert(!(s == nil)); - let l3: *sym = scopelookup(s, "baz"); + let l3: *syntax.sym = syntax.scopelookup(s, "baz"); assert(!(l3 != nil)); }; diff --git a/lib/ww/syntax/tok_test.ww b/lib/ww/syntax/tok_test.ww index 41555563..585cf0ec 100644 --- a/lib/ww/syntax/tok_test.ww +++ b/lib/ww/syntax/tok_test.ww @@ -23,120 +23,120 @@ import ww.syntax; import temp; -fn checktokname(k: tkind, want: str) void = { - assert(!(tokname(k) != want)); +fn checktokname(k: syntax.tkind, want: str) void = { + assert(!(syntax.tokname(k) != want)); }; // One row per tkind value — same string the if-ladder returned. The // final row pins the unknown-kind fallback (""). @test fn tokname_cases() void = { - checktokname(tkind.TK_NONE, ""); - checktokname(tkind.TK_EOF, "EOF"); - checktokname(tkind.TK_ERR, "ERR"); - checktokname(tkind.TK_IDENT, "IDENT"); - checktokname(tkind.TK_INT, "INT"); - checktokname(tkind.TK_FLOAT, "FLOAT"); - checktokname(tkind.TK_RUNE, "RUNE"); - checktokname(tkind.TK_STR, "STR"); + checktokname(syntax.tkind.TK_NONE, ""); + checktokname(syntax.tkind.TK_EOF, "EOF"); + checktokname(syntax.tkind.TK_ERR, "ERR"); + checktokname(syntax.tkind.TK_IDENT, "IDENT"); + checktokname(syntax.tkind.TK_INT, "INT"); + checktokname(syntax.tkind.TK_FLOAT, "FLOAT"); + checktokname(syntax.tkind.TK_RUNE, "RUNE"); + checktokname(syntax.tkind.TK_STR, "STR"); - checktokname(tkind.TK_FN, "fn"); - checktokname(tkind.TK_LET, "let"); - checktokname(tkind.TK_DEF, "def"); - checktokname(tkind.TK_IF, "if"); - checktokname(tkind.TK_ELSE, "else"); - checktokname(tkind.TK_FOR, "for"); - checktokname(tkind.TK_SWITCH, "switch"); - checktokname(tkind.TK_CASE, "case"); - checktokname(tkind.TK_RETURN, "return"); - checktokname(tkind.TK_USE, "import"); - checktokname(tkind.TK_TYPE, "type"); - checktokname(tkind.TK_STRUCT, "struct"); - checktokname(tkind.TK_DEFER, "defer"); - checktokname(tkind.TK_BREAK, "break"); - checktokname(tkind.TK_CONTINUE, "continue"); - checktokname(tkind.TK_EXPORT, "export"); - checktokname(tkind.TK_PROC, "proc"); - checktokname(tkind.TK_CHAN, "chan"); - checktokname(tkind.TK_NIL, "nil"); - checktokname(tkind.TK_TRUE, "true"); - checktokname(tkind.TK_FALSE, "false"); - checktokname(tkind.TK_AS, "as"); - checktokname(tkind.TK_IS, "is"); - checktokname(tkind.TK_VOID, "void"); - checktokname(tkind.TK_YIELD, "yield"); - checktokname(tkind.TK_STATIC, "static"); - checktokname(tkind.TK_MATCH, "match"); - checktokname(tkind.TK_CONST, "const"); - checktokname(tkind.TK_UNDER, "_"); - checktokname(tkind.TK_ENUM, "enum"); - checktokname(tkind.TK_MODULE, "package"); + checktokname(syntax.tkind.TK_FN, "fn"); + checktokname(syntax.tkind.TK_LET, "let"); + checktokname(syntax.tkind.TK_DEF, "def"); + checktokname(syntax.tkind.TK_IF, "if"); + checktokname(syntax.tkind.TK_ELSE, "else"); + checktokname(syntax.tkind.TK_FOR, "for"); + checktokname(syntax.tkind.TK_SWITCH, "switch"); + checktokname(syntax.tkind.TK_CASE, "case"); + checktokname(syntax.tkind.TK_RETURN, "return"); + checktokname(syntax.tkind.TK_USE, "import"); + checktokname(syntax.tkind.TK_TYPE, "type"); + checktokname(syntax.tkind.TK_STRUCT, "struct"); + checktokname(syntax.tkind.TK_DEFER, "defer"); + checktokname(syntax.tkind.TK_BREAK, "break"); + checktokname(syntax.tkind.TK_CONTINUE, "continue"); + checktokname(syntax.tkind.TK_EXPORT, "export"); + checktokname(syntax.tkind.TK_PROC, "proc"); + checktokname(syntax.tkind.TK_CHAN, "chan"); + checktokname(syntax.tkind.TK_NIL, "nil"); + checktokname(syntax.tkind.TK_TRUE, "true"); + checktokname(syntax.tkind.TK_FALSE, "false"); + checktokname(syntax.tkind.TK_AS, "as"); + checktokname(syntax.tkind.TK_IS, "is"); + checktokname(syntax.tkind.TK_VOID, "void"); + checktokname(syntax.tkind.TK_YIELD, "yield"); + checktokname(syntax.tkind.TK_STATIC, "static"); + checktokname(syntax.tkind.TK_MATCH, "match"); + checktokname(syntax.tkind.TK_CONST, "const"); + checktokname(syntax.tkind.TK_UNDER, "_"); + checktokname(syntax.tkind.TK_ENUM, "enum"); + checktokname(syntax.tkind.TK_MODULE, "package"); - checktokname(tkind.TK_LPAREN, "("); - checktokname(tkind.TK_RPAREN, ")"); - checktokname(tkind.TK_LBRACE, "{"); - checktokname(tkind.TK_RBRACE, "}"); - checktokname(tkind.TK_LBRACK, "["); - checktokname(tkind.TK_RBRACK, "]"); - checktokname(tkind.TK_COMMA, ","); - checktokname(tkind.TK_SEMI, ";"); - checktokname(tkind.TK_COLON, ":"); - checktokname(tkind.TK_DOT, "."); - checktokname(tkind.TK_ELLIPSIS, "..."); - checktokname(tkind.TK_DOTDOT, ".."); - checktokname(tkind.TK_AT, "@"); - checktokname(tkind.TK_QUESTION, "?"); + checktokname(syntax.tkind.TK_LPAREN, "("); + checktokname(syntax.tkind.TK_RPAREN, ")"); + checktokname(syntax.tkind.TK_LBRACE, "{"); + checktokname(syntax.tkind.TK_RBRACE, "}"); + checktokname(syntax.tkind.TK_LBRACK, "["); + checktokname(syntax.tkind.TK_RBRACK, "]"); + checktokname(syntax.tkind.TK_COMMA, ","); + checktokname(syntax.tkind.TK_SEMI, ";"); + checktokname(syntax.tkind.TK_COLON, ":"); + checktokname(syntax.tkind.TK_DOT, "."); + checktokname(syntax.tkind.TK_ELLIPSIS, "..."); + checktokname(syntax.tkind.TK_DOTDOT, ".."); + checktokname(syntax.tkind.TK_AT, "@"); + checktokname(syntax.tkind.TK_QUESTION, "?"); - checktokname(tkind.TK_ASSIGN, "="); - checktokname(tkind.TK_PLUSEQ, "+="); - checktokname(tkind.TK_MINUSEQ, "-="); - checktokname(tkind.TK_STAREQ, "*="); - checktokname(tkind.TK_SLASHEQ, "/="); - checktokname(tkind.TK_PERCENTEQ, "%="); - checktokname(tkind.TK_AMPEQ, "&="); - checktokname(tkind.TK_PIPEEQ, "|="); - checktokname(tkind.TK_CARETEQ, "^="); - checktokname(tkind.TK_LSHIFTEQ, "<<="); - checktokname(tkind.TK_RSHIFTEQ, ">>="); + checktokname(syntax.tkind.TK_ASSIGN, "="); + checktokname(syntax.tkind.TK_PLUSEQ, "+="); + checktokname(syntax.tkind.TK_MINUSEQ, "-="); + checktokname(syntax.tkind.TK_STAREQ, "*="); + checktokname(syntax.tkind.TK_SLASHEQ, "/="); + checktokname(syntax.tkind.TK_PERCENTEQ, "%="); + checktokname(syntax.tkind.TK_AMPEQ, "&="); + checktokname(syntax.tkind.TK_PIPEEQ, "|="); + checktokname(syntax.tkind.TK_CARETEQ, "^="); + checktokname(syntax.tkind.TK_LSHIFTEQ, "<<="); + checktokname(syntax.tkind.TK_RSHIFTEQ, ">>="); - checktokname(tkind.TK_PLUS, "+"); - checktokname(tkind.TK_MINUS, "-"); - checktokname(tkind.TK_STAR, "*"); - checktokname(tkind.TK_SLASH, "/"); - checktokname(tkind.TK_PERCENT, "%"); - checktokname(tkind.TK_AMP, "&"); - checktokname(tkind.TK_PIPE, "|"); - checktokname(tkind.TK_CARET, "^"); - checktokname(tkind.TK_TILDE, "~"); - checktokname(tkind.TK_LSHIFT, "<<"); - checktokname(tkind.TK_RSHIFT, ">>"); + checktokname(syntax.tkind.TK_PLUS, "+"); + checktokname(syntax.tkind.TK_MINUS, "-"); + checktokname(syntax.tkind.TK_STAR, "*"); + checktokname(syntax.tkind.TK_SLASH, "/"); + checktokname(syntax.tkind.TK_PERCENT, "%"); + checktokname(syntax.tkind.TK_AMP, "&"); + checktokname(syntax.tkind.TK_PIPE, "|"); + checktokname(syntax.tkind.TK_CARET, "^"); + checktokname(syntax.tkind.TK_TILDE, "~"); + checktokname(syntax.tkind.TK_LSHIFT, "<<"); + checktokname(syntax.tkind.TK_RSHIFT, ">>"); - checktokname(tkind.TK_EQ, "=="); - checktokname(tkind.TK_NEQ, "!="); - checktokname(tkind.TK_LT, "<"); - checktokname(tkind.TK_LE, "<="); - checktokname(tkind.TK_GT, ">"); - checktokname(tkind.TK_GE, ">="); + checktokname(syntax.tkind.TK_EQ, "=="); + checktokname(syntax.tkind.TK_NEQ, "!="); + checktokname(syntax.tkind.TK_LT, "<"); + checktokname(syntax.tkind.TK_LE, "<="); + checktokname(syntax.tkind.TK_GT, ">"); + checktokname(syntax.tkind.TK_GE, ">="); - checktokname(tkind.TK_AND, "&&"); - checktokname(tkind.TK_OR, "||"); - checktokname(tkind.TK_NOT, "!"); + checktokname(syntax.tkind.TK_AND, "&&"); + checktokname(syntax.tkind.TK_OR, "||"); + checktokname(syntax.tkind.TK_NOT, "!"); - checktokname(tkind.TK_LARROW, "<-"); - checktokname(tkind.TK_ARROW, "->"); - checktokname(tkind.TK_FATARROW, "=>"); + checktokname(syntax.tkind.TK_LARROW, "<-"); + checktokname(syntax.tkind.TK_ARROW, "->"); + checktokname(syntax.tkind.TK_FATARROW, "=>"); - checktokname(tkind.TK_MODRESET, "//ww:module-reset"); - checktokname(tkind.TK_MODPATH, "//ww:module"); - checktokname(tkind.TK_LAST, ""); + checktokname(syntax.tkind.TK_MODRESET, "//ww:module-reset"); + checktokname(syntax.tkind.TK_MODPATH, "//ww:module"); + checktokname(syntax.tkind.TK_LAST, ""); // Unknown kind → the post-switch fallback. TK_LAST is the highest // named value (89, after TK_MODPATH=88 landed); 90 is out of band, // exercising the "" tail. - checktokname(90: tkind, ""); + checktokname(90: syntax.tkind, ""); }; -fn checkkw(s: str, want: tkind) void = { - assert(!(kwlookup(s.ptr, s.len) != want)); +fn checkkw(s: str, want: syntax.tkind) void = { + assert(!(syntax.kwlookup(s.ptr, s.len) != want)); }; // Table-driven (parallel-array idiom; tuple rows blocked by #111). The @@ -153,15 +153,15 @@ fn checkkw(s: str, want: tkind) void = { "import", "let", "match", "nil", "package", "proc", "return", "static", "struct", "switch", "true", "type", "void", "yield", ]; - let kwexp: [30]tkind = [ - tkind.TK_AS, tkind.TK_BREAK, tkind.TK_CASE, tkind.TK_CHAN, - tkind.TK_CONST, tkind.TK_CONTINUE, tkind.TK_DEF, tkind.TK_DEFER, - tkind.TK_ELSE, tkind.TK_ENUM, tkind.TK_EXPORT, tkind.TK_FALSE, - tkind.TK_FN, tkind.TK_FOR, tkind.TK_IF, tkind.TK_IS, - tkind.TK_USE, tkind.TK_LET, tkind.TK_MATCH, tkind.TK_NIL, - tkind.TK_MODULE, tkind.TK_PROC, tkind.TK_RETURN, tkind.TK_STATIC, - tkind.TK_STRUCT, tkind.TK_SWITCH, tkind.TK_TRUE, tkind.TK_TYPE, - tkind.TK_VOID, tkind.TK_YIELD, + let kwexp: [30]syntax.tkind = [ + syntax.tkind.TK_AS, syntax.tkind.TK_BREAK, syntax.tkind.TK_CASE, syntax.tkind.TK_CHAN, + syntax.tkind.TK_CONST, syntax.tkind.TK_CONTINUE, syntax.tkind.TK_DEF, syntax.tkind.TK_DEFER, + syntax.tkind.TK_ELSE, syntax.tkind.TK_ENUM, syntax.tkind.TK_EXPORT, syntax.tkind.TK_FALSE, + syntax.tkind.TK_FN, syntax.tkind.TK_FOR, syntax.tkind.TK_IF, syntax.tkind.TK_IS, + syntax.tkind.TK_USE, syntax.tkind.TK_LET, syntax.tkind.TK_MATCH, syntax.tkind.TK_NIL, + syntax.tkind.TK_MODULE, syntax.tkind.TK_PROC, syntax.tkind.TK_RETURN, syntax.tkind.TK_STATIC, + syntax.tkind.TK_STRUCT, syntax.tkind.TK_SWITCH, syntax.tkind.TK_TRUE, syntax.tkind.TK_TYPE, + syntax.tkind.TK_VOID, syntax.tkind.TK_YIELD, ]; let i: i32 = 0; for (i < len(kwin)) { @@ -197,7 +197,7 @@ fn checkkw(s: str, want: tkind) void = { ]; let j: i32 = 0; for (j < len(nk)) { - checkkw(nk[j], tkind.TK_NONE); + checkkw(nk[j], syntax.tkind.TK_NONE); j += 1; }; }; @@ -205,9 +205,9 @@ fn checkkw(s: str, want: tkind) void = { // The fd is RDWR; we lseek to 0 // before each write so earlier (possibly longer) content past want.len // is irrelevant — only want.len bytes from offset 0 are compared. -fn checkprint(fd: i32, t: *tok, want: str) void = { +fn checkprint(fd: i32, t: *syntax.tok, want: str) void = { assert(!(os.lseek(fd, 0i64, os.whence.SET) != 0i64)); - tokprint(fd, t); + syntax.tokprint(fd, t); assert(!(os.lseek(fd, 0i64, os.whence.SET) != 0i64)); let rbuf: [256]u8; let z: i32 = 0; @@ -233,33 +233,33 @@ fn checkprint(fd: i32, t: *tok, want: str) void = { }; assert(!(fd < 0)); - let t: tok; + let t: syntax.tok; t.file = "t"; t.line = 1; t.col = 1; - t.kind = tkind.TK_STR; + t.kind = syntax.tkind.TK_STR; t.text = "\\\"\n\t\r\x01\x7fA"; checkprint(fd, &t, "t:1:1 STR \"\\\\\\\"\\n\\t\\r\\x01\\x7fA\"\n"); - t.kind = tkind.TK_IDENT; + t.kind = syntax.tkind.TK_IDENT; t.text = "name"; checkprint(fd, &t, "t:1:1 IDENT \"name\"\n"); - t.kind = tkind.TK_ERR; + t.kind = syntax.tkind.TK_ERR; t.text = "oops"; checkprint(fd, &t, "t:1:1 ERR \"oops\"\n"); - t.kind = tkind.TK_INT; + t.kind = syntax.tkind.TK_INT; t.uval = 42u64; checkprint(fd, &t, "t:1:1 INT 42\n"); - t.kind = tkind.TK_RUNE; + t.kind = syntax.tkind.TK_RUNE; t.uval = 65u64; checkprint(fd, &t, "t:1:1 RUNE 65\n"); // Default arm: a kind outside the switch appends no value. - t.kind = tkind.TK_NONE; + t.kind = syntax.tkind.TK_NONE; checkprint(fd, &t, "t:1:1 \n"); assert(!(os.close(fd) != 0)); @@ -267,11 +267,11 @@ fn checkprint(fd: i32, t: *tok, want: str) void = { }; fn checkfloat(src: str, want: u64) void = { - let l: lex; - lexinit(&l, "t", src.ptr, src.len: u64); - let t: tok; - lexnext(&l, &t); - assert(!(t.kind != tkind.TK_FLOAT)); + let l: syntax.lex; + syntax.lexinit(&l, "t", src.ptr, src.len: u64); + let t: syntax.tok; + syntax.lexnext(&l, &t); + assert(!(t.kind != syntax.tkind.TK_FLOAT)); assert(!(t.uval != want)); }; @@ -312,29 +312,29 @@ fn checkfloat(src: str, want: u64) void = { @test fn intfinalband() void = { let src: str = "18446744073709551616"; - let l: lex; - lexinit(&l, "t", src.ptr, src.len: u64); - let t: tok; - lexnext(&l, &t); - assert(!(t.kind != tkind.TK_ERR)); + let l: syntax.lex; + syntax.lexinit(&l, "t", src.ptr, src.len: u64); + let t: syntax.tok; + syntax.lexnext(&l, &t); + assert(!(t.kind != syntax.tkind.TK_ERR)); assert(!(l.errs != 1)); }; @test fn suffixrewindcols() void = { let src: str = "1foo bar"; - let l: lex; - lexinit(&l, "t", src.ptr, src.len: u64); - let t: tok; - lexnext(&l, &t); - assert(!(t.kind != tkind.TK_INT)); + let l: syntax.lex; + syntax.lexinit(&l, "t", src.ptr, src.len: u64); + let t: syntax.tok; + syntax.lexnext(&l, &t); + assert(!(t.kind != syntax.tkind.TK_INT)); assert(!(t.col != 1)); assert(!(t.uval != 1u64)); - lexnext(&l, &t); - assert(!(t.kind != tkind.TK_IDENT)); + syntax.lexnext(&l, &t); + assert(!(t.kind != syntax.tkind.TK_IDENT)); assert(!(t.col != 2)); assert(!(t.text != "foo")); - lexnext(&l, &t); - assert(!(t.kind != tkind.TK_IDENT)); + syntax.lexnext(&l, &t); + assert(!(t.kind != syntax.tkind.TK_IDENT)); assert(!(t.col != 6)); assert(!(t.text != "bar")); }; diff --git a/test/misc/qualast_test.ww b/test/misc/qualast_test.ww index 31b2edc1..bd8e13f9 100644 --- a/test/misc/qualast_test.ww +++ b/test/misc/qualast_test.ww @@ -75,6 +75,31 @@ fn astrow(label: str, src: str, needles: []str) void = { testenv.clean(td); }; +fn rejectrow(label: str, src: str, needle: str) void = { + let td: str = testenv.fresh(); + testenv.writefile(strings.concat(td, "/a.ww"), src); + let tools: []str = ["wwdump", "wwdump_ww"]; + let stages: []str = ["cstage", "wwstage"]; + let errors: []str = ["", ""]; + let i: i32 = 0; + for (i < tools.len) { + let av: []str = [testenv.driver(tools[i]), "-a", + strings.concat(td, "/a.ww")]; + let co: testenv.commandout; + testenv.runcommand(td, td, stages[i], av, tmo(), &co); + if (co.termination != exec.termination.EXIT || co.code == 0 + || !testenv.has(co.stderr, needle)) { + fail(label, strings.concat(stages[i], " accepted rejected import")); + }; + errors[i] = strings.dup(co.stderr); + i += 1; + }; + if (!testenv.same(errors[0], errors[1])) { + fail(label, "cs/ww parse diagnostics DIFFER"); + }; + testenv.clean(td); +}; + @test fn astproof() void = { let trailing: []str = ["(dot \"x\"", "(structlit", "(tname \"pkg.point\""]; @@ -92,4 +117,19 @@ fn astrow(label: str, src: str, needles: []str) void = { "\tlet v: i64 = a.b.C { x = 1i64 }.x;\n", "\treturn v;\n", "};\n"), multilevel); + let ordinary: []str = ["(use \"codec\""]; + astrow("ordinary-import", strings.concat( + "package main;\n", + "import acme.codec;\n", + "fn main() i32 = { return 0; };\n"), ordinary); + let aliased: []str = ["(use \"stable\""]; + astrow("aliased-import", strings.concat( + "package main;\n", + "import stable acme.codec;\n", + "fn main() i32 = { return 0; };\n"), aliased); + rejectrow("blank-alias", strings.concat( + "package main;\n", + "import _ acme.codec;\n", + "fn main() i32 = { return 0; };\n"), + "blank import alias _ is not implemented"); }; diff --git a/test/package/package_test.ww b/test/package/package_test.ww index 7ae4e6d0..1611c44f 100644 --- a/test/package/package_test.ww +++ b/test/package/package_test.ww @@ -7906,9 +7906,11 @@ fn runtimepath(relative: str) str = { let wirebody: str = strings.concat( "package wire;\n", + "export type Thing = i32;\n", "export fn value() i32 = { return 42; };\n"); let cablebody: str = strings.concat( "package cable;\n", + "export type Thing = i32;\n", "export fn value() i32 = { return 42; };\n"); let codecfile: str = strings.concat(codec, "/codec.ww"); writefile(codecfile, wirebody); @@ -7918,12 +7920,16 @@ fn runtimepath(relative: str) str = { writefile(commitappfile, strings.concat( "package main;\nimport commit.codec;\n", "fn main() i32 = { return wire.value(); };\n")); - writefile(strings.concat(bridge, "/bridge.ww"), strings.concat( - "package bridge;\nimport acme.codec;\n", - // WW retains its direct-import bare-declaration convenience. It is - // useful here because changing only the dependency declaration leaves - // this source unchanged while still forcing its action to reconsider. - "export fn bridged() i32 = { return value(); };\n")); + let bridgefile: str = strings.concat(bridge, "/bridge.ww"); + let stablebridgebody: str = strings.concat( + "package bridge;\nimport stable acme.codec;\n", + "export type BridgedThing = stable.Thing;\n", + "export fn bridged() i32 = { return stable.value(); };\n"); + let steadybridgebody: str = strings.concat( + "package bridge;\nimport steady acme.codec;\n", + "export type BridgedThing = steady.Thing;\n", + "export fn bridged() i32 = { return steady.value(); };\n"); + writefile(bridgefile, stablebridgebody); writefile(strings.concat(direct, "/main.ww"), strings.concat( "package main;\nimport acme.codec;\n", "fn main() i32 = { return wire.value(); };\n")); @@ -7936,17 +7942,17 @@ fn runtimepath(relative: str) str = { writefile(strings.concat(scopetwo, "/two.ww"), "package same;\nexport fn value() i32 = { return 22; };\n"); writefile(strings.concat(scoped, "/a.ww"), strings.concat( - "package main;\nimport scope.one;\n", - "fn left() i32 = { return same.value(); };\n")); + "package main;\nimport shared scope.one;\n", + "fn left() i32 = { return shared.value(); };\n")); writefile(strings.concat(scoped, "/b.ww"), strings.concat( - "package main;\nimport scope.two;\n", - "fn right() i32 = { return same.value(); };\n")); + "package main;\nimport shared scope.two;\n", + "fn right() i32 = { return shared.value(); };\n")); writefile(strings.concat(scoped, "/main.ww"), "package main;\nfn main() i32 = { return left() + right(); };\n"); writefile(strings.concat(repeated, "/a.ww"), strings.concat( - "package main;\nimport acme.codec;\n", - "fn first() i32 = { return wire.value(); };\n")); + "package main;\nimport stable acme.codec;\n", + "fn first() i32 = { return stable.value(); };\n")); writefile(strings.concat(repeated, "/b.ww"), strings.concat( "package main;\nimport acme.codec;\n", "fn second() i32 = { return wire.value(); };\n")); @@ -7990,26 +7996,26 @@ fn runtimepath(relative: str) str = { "package main;\nimport domain.main;\n", "fn main() i32 = { return utility.value(); };\n")); writefile(strings.concat(programuser, "/main.ww"), strings.concat( - "package main;\nimport domain.program;\n", - "fn main() i32 = { return main.value(); };\n")); + "package main;\nimport stable domain.program;\n", + "fn main() i32 = { return stable.value(); };\n")); writefile(strings.concat(testhelper, "/helper.ww"), "package helper;\nexport fn value() i32 = { return 1; };\n"); writefile(strings.concat(testcodec, "/codec.ww"), wirebody); writefile(strings.concat(testcodec, "/internal_test.ww"), strings.concat( - "package wire;\nimport testonly.helper;\n", + "package wire;\nimport assist testonly.helper;\n", "@test fn internal_name_uses_declaration() void = {\n", - " assert(value() + helper.value() == 43);\n};\n")); + " assert(value() + assist.value() == 43);\n};\n")); writefile(strings.concat(testcodec, "/external_test.ww"), strings.concat( - "package wire_test;\nimport testpkg.codec;\n", + "package wire_test;\nimport prod testpkg.codec;\n", "@test fn external_name_uses_declaration() void = {\n", - " assert(wire.value() == 42);\n};\n")); + " assert(prod.value() == 42);\n};\n")); writefile(strings.concat(vendored, "/codec.ww"), "package cable;\nexport fn value() i32 = { return 13; };\n"); writefile(strings.concat(vendorclient, "/a.ww"), strings.concat( - "package main;\nimport short.codec;\n", - "fn vendora() i32 = { return cable.value(); };\n")); + "package main;\nimport stable short.codec;\n", + "fn vendora() i32 = { return stable.value(); };\n")); writefile(strings.concat(vendorclient, "/b.ww"), strings.concat( "package main;\nimport short.codec;\n", "fn vendorb() i32 = { return cable.value(); };\n")); @@ -8080,6 +8086,7 @@ fn runtimepath(relative: str) str = { let scopedtraceref: str = ""; let repeatedtraceref: str = ""; let changedtraceref: str = ""; + let aliastraceref: str = ""; let commitfailref: str = ""; let commitpreflightref: str = ""; let commitbindingref: str = ""; @@ -8090,6 +8097,7 @@ fn runtimepath(relative: str) str = { let si: i32 = 0; for (si < stages.len) { rewritefile(codecfile, wirebody); + rewritefile(bridgefile, stablebridgebody); rewritefile(commitcodecfile, wirebody); rewritefile(commitappfile, strings.concat( "package main;\nimport commit.codec;\n", @@ -8313,7 +8321,8 @@ fn runtimepath(relative: str) str = { expectexit(&out, 0); let repeatedunit: str = readfile(strings.concat(works[si], "/cmd.repeated.unit.ww")); - assert(occurrences(repeatedunit, "import acme.codec;") == 2); + assert(occurrences(repeatedunit, "import stable acme.codec;") == 1); + assert(occurrences(repeatedunit, "import acme.codec;") == 1); let repeatedtrace: str = readfile(traces[si]); let repeatedline: str = linecontaining(repeatedtrace, "cmd.repeated.unit.new"); @@ -8427,7 +8436,7 @@ fn runtimepath(relative: str) str = { let externalunit: str = readfile(strings.concat(testwork, "/testpkg.codec_test-external-test.unit.ww")); assert(!has(productionunit, "testonly.helper")); - assert(has(internalunit, "import testonly.helper;")); + assert(has(internalunit, "import assist testonly.helper;")); assert(!has(externalunit, "testonly.helper")); assert(has(externalunit, "package wire_test;")); assert(os.exists(strings.concat(testwork, @@ -8475,7 +8484,8 @@ fn runtimepath(relative: str) str = { expectexit(&out, 0); let vendorunit: str = readfile(strings.concat(works[si], "/vend.client.unit.ww")); - assert(occurrences(vendorunit, "import short.codec;") == 2); + assert(occurrences(vendorunit, "import stable short.codec;") == 1); + assert(occurrences(vendorunit, "import short.codec;") == 1); assert(has(vendorunit, strings.concat( "//ww:import-map short.codec vend.vendor.short.codec ", hexbytes(vendored), "\n"))); @@ -8498,9 +8508,9 @@ fn runtimepath(relative: str) str = { vendorrun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 13); - // Build the bare-import bridge and its command, establish a no-op warm - // rebuild, then change only the dependency's declared name. The direct - // importer is reconsidered; its unchanged export stops propagation. + // A stable explicit alias survives a dependency declared-name change. + // Canonicalized exported type references keep the bridge interface + // unchanged, so reconsideration stops after the direct importer. rewritefile(traces[si], ""); let appav: []str = [driver(stages[si]), "build", "-w", works[si], "-I", source, "-o", appbins[si], app]; @@ -8511,6 +8521,13 @@ fn runtimepath(relative: str) str = { runcommand(root, strings.concat("name-app-run-", stages[si]), apprun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 42); + let stablebridgewwi: str = readfile(strings.concat(works[si], + "/acme.bridge.wwi")); + assert(has(stablebridgewwi, + "import __wwi_61636d652e636f646563 acme.codec;\n")); + assert(has(stablebridgewwi, + "export type BridgedThing = __wwi_61636d652e636f646563.Thing;\n")); + assert(!has(stablebridgewwi, "stable.Thing")); rewritefile(traces[si], ""); runcommandenv(root, strings.concat("name-app-warm-", stages[si]), appav, env, (120i64 * (time.second: i64)): time.duration, &out); @@ -8534,10 +8551,34 @@ fn runtimepath(relative: str) str = { "/acme.codec.wwi")); assert(has(renamedwwi, "//ww:module acme.codec\npackage cable;\n")); assert(!has(renamedwwi, "package wire;")); + assert(same(stablebridgewwi, readfile(strings.concat(works[si], + "/acme.bridge.wwi")))); runcommand(root, strings.concat("name-app-renamed-run-", stages[si]), apprun, (30i64 * (time.second: i64)): time.duration, &out); expectexit(&out, 42); + // Editing only the local alias rebuilds its owning source action. It + // cannot rename canonical exports or force the downstream command. + rewritefile(bridgefile, steadybridgebody); + rewritefile(traces[si], ""); + runcommandenv(root, strings.concat("name-app-alias-edit-", stages[si]), + appav, env, (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + let aliastrace: str = readfile(traces[si]); + assert(occurrences(aliastrace, "\n") == 1); + assert(occurrences(aliastrace, "/acme.bridge.unit.new") == 1); + assert(!has(aliastrace, "/acme.codec.unit.new")); + assert(!has(aliastrace, "/cmd.app.unit.new")); + assert(same(stablebridgewwi, readfile(strings.concat(works[si], + "/acme.bridge.wwi")))); + let nalias: str = normalizedtrace(aliastrace, + strings.concat(works[si], "/"), appbins[si]); + if (si == 0) { aliastraceref = strings.dup(nalias); } + else { assert(same(aliastraceref, nalias)); }; + runcommand(root, strings.concat("name-app-alias-edit-run-", stages[si]), + apprun, (30i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 42); + // New semantic artifacts and binaries are byte-identical across the // independently cold Cstage and WWstage work roots. ari = 0; @@ -8567,6 +8608,272 @@ fn runtimepath(relative: str) str = { clean(root); }; +@test fn explicit_import_alias_binding_modes() void = { + let root: str = fresh(); + let source: str = strings.concat(root, "/source"); + let one: str = strings.concat(source, "/pkg/one"); + let two: str = strings.concat(source, "/pkg/two"); + let positives: []str = ["default", "alias", "reuse", "repeatok", + "repeatedfiles"]; + let negatives: []str = ["barefn", "baretype", "baredef", "bareconst", + "barevar", "aliasbad", "aliasleak", "repeatdup", "aliasdup", + "defaultalias", "declcollision", "unusedalias", "blankalias"]; + let dirs: []str = [one, two]; + let i: i32 = 0; + for (i < positives.len) { + append(dirs, strings.concat(source, "/cmd/", positives[i])); + i += 1; + }; + i = 0; + for (i < negatives.len) { + append(dirs, strings.concat(source, "/cmd/", negatives[i])); + i += 1; + }; + i = 0; + for (i < dirs.len) { mkdirall(dirs[i]); i += 1; }; + + writefile(strings.concat(one, "/one.ww"), strings.concat( + "package wire;\n", + "export fn value() i32 = { return 11; };\n", + "export type Thing = i32;\n", + "export def DEFINED: i32 = 13;\n", + "export const CONSTANT: i32 = 17;\n", + "export let VARIABLE: i32 = 19;\n")); + writefile(strings.concat(two, "/two.ww"), + "package cable;\nexport fn value() i32 = { return 29; };\n"); + let defaultbody: str = strings.concat( + "package main;\nimport pkg.one;\n", + "fn typed(v: wire.Thing) i32 = { return v: i32; };\n", + "fn main() i32 = { return wire.value() + wire.DEFINED + ", + "wire.CONSTANT + wire.VARIABLE + typed(23); };\n"); + writefile(strings.concat(source, "/cmd/default/main.ww"), defaultbody); + writefile(strings.concat(source, "/cmd/alias/main.ww"), strings.concat( + "package main;\nimport stable pkg.one;\n", + "fn typed(v: stable.Thing) i32 = { return v: i32; };\n", + "fn main() i32 = { return stable.value() + stable.DEFINED + ", + "stable.CONSTANT + stable.VARIABLE + typed(23); };\n")); + writefile(strings.concat(source, "/cmd/reuse/a.ww"), strings.concat( + "package main;\nimport shared pkg.one;\n", + "fn left() i32 = { return shared.value(); };\n")); + writefile(strings.concat(source, "/cmd/reuse/b.ww"), strings.concat( + "package main;\nimport shared pkg.two;\n", + "fn right() i32 = { return shared.value(); };\n")); + writefile(strings.concat(source, "/cmd/reuse/main.ww"), + "package main;\nfn main() i32 = { return left() + right(); };\n"); + writefile(strings.concat(source, "/cmd/repeatok/main.ww"), strings.concat( + "package main;\nimport pkg.one;\nimport first pkg.one;\n", + "import second pkg.one;\n", + "fn main() i32 = { return wire.value() + first.value() + ", + "second.value(); };\n")); + writefile(strings.concat(source, "/cmd/repeatedfiles/a.ww"), strings.concat( + "package main;\nimport left pkg.one;\n", + "fn first() i32 = { return left.value(); };\n")); + writefile(strings.concat(source, "/cmd/repeatedfiles/b.ww"), strings.concat( + "package main;\nimport right pkg.one;\n", + "fn second() i32 = { return right.value(); };\n")); + writefile(strings.concat(source, "/cmd/repeatedfiles/main.ww"), + "package main;\nfn main() i32 = { return first() + second(); };\n"); + + let bareexprs: []str = ["value()", "take(7)", "DEFINED", "CONSTANT", + "VARIABLE"]; + let barenames: []str = ["barefn", "baretype", "baredef", "bareconst", + "barevar"]; + i = 0; + for (i < barenames.len) { + let pre: str = ""; + if (i == 1) { pre = "fn take(v: Thing) i32 = { return v: i32; };\n"; }; + writefile(strings.concat(source, "/cmd/", barenames[i], "/main.ww"), + strings.concat("package main;\nimport pkg.one;\n", pre, + "fn main() i32 = { return ", bareexprs[i], "; };\n")); + i += 1; + }; + writefile(strings.concat(source, "/cmd/aliasbad/main.ww"), strings.concat( + "package main;\nimport stable pkg.one;\n", + "fn bad(v: wire.Thing) i32 = { return v: i32; };\n", + "fn declared() i32 = { return wire.value(); };\n", + "fn leaf() i32 = { return one.value(); };\n", + "fn bare() i32 = { return value(); };\n", + "fn main() i32 = { return stable.value(); };\n")); + writefile(strings.concat(source, "/cmd/aliasleak/a.ww"), strings.concat( + "package main;\nimport stable pkg.one;\n", + "fn anchor() i32 = { return stable.value(); };\n")); + writefile(strings.concat(source, "/cmd/aliasleak/b.ww"), + "package main;\nfn leaked() i32 = { return stable.value(); };\n"); + writefile(strings.concat(source, "/cmd/aliasleak/main.ww"), + "package main;\nfn main() i32 = { return leaked(); };\n"); + writefile(strings.concat(source, "/cmd/repeatdup/main.ww"), strings.concat( + "package main;\nimport pkg.one;\nimport pkg.one;\n", + "fn main() i32 = { return wire.value(); };\n")); + writefile(strings.concat(source, "/cmd/aliasdup/main.ww"), strings.concat( + "package main;\nimport stable pkg.one;\nimport stable pkg.two;\n", + "fn main() i32 = { return stable.value(); };\n")); + writefile(strings.concat(source, "/cmd/defaultalias/main.ww"), strings.concat( + "package main;\nimport pkg.one;\nimport wire pkg.two;\n", + "fn main() i32 = { return wire.value(); };\n")); + writefile(strings.concat(source, "/cmd/declcollision/main.ww"), strings.concat( + "package main;\nimport stable pkg.one;\n", + "fn stable() i32 = { return 0; };\n", + "fn main() i32 = { return stable(); };\n")); + writefile(strings.concat(source, "/cmd/unusedalias/main.ww"), + "package main;\nimport stable pkg.one;\nfn main() i32 = { return 0; };\n"); + writefile(strings.concat(source, "/cmd/blankalias/main.ww"), + "package main;\nimport _ pkg.one;\nfn main() i32 = { return 0; };\n"); + + let stages: []str = ["ww", "ww_ww"]; + let expected: []i32 = [83, 83, 40, 33, 22]; + let binaryrefs: []str = ["", "", "", "", ""]; + let diagrefs: []str = ["", "", "", "", "", "", "", "", "", + "", "", "", ""]; + let needles: []str = ["undefined: value", "unknown type 'Thing'", + "undefined: DEFINED", "undefined: CONSTANT", "undefined: VARIABLE", + "unknown type 'wire.Thing'", "package 'stable' is not directly imported", + "wire redeclared in this block", "stable redeclared in this block", + "wire redeclared in this block", + "stable already declared through import of package stable", + "\"pkg.one\" imported as stable and not used", + "blank import alias _ is not implemented"]; + let exactdiags: []str = [ + strings.concat( + "$WORK/cmd.barefn.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n", + "$WORK/cmd.barefn.unit.new:4:26: error: undefined: value\n", + "ww: w6c failed for cmd.barefn\n"), + strings.concat( + "$WORK/cmd.baretype.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n", + "$WORK/cmd.baretype.unit.new:4:12: error: unknown type 'Thing'\n", + "ww: w6c failed for cmd.baretype\n"), + strings.concat( + "$WORK/cmd.baredef.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n", + "$WORK/cmd.baredef.unit.new:4:26: error: undefined: DEFINED\n", + "ww: w6c failed for cmd.baredef\n"), + strings.concat( + "$WORK/cmd.bareconst.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n", + "$WORK/cmd.bareconst.unit.new:4:26: error: undefined: CONSTANT\n", + "ww: w6c failed for cmd.bareconst\n"), + strings.concat( + "$WORK/cmd.barevar.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n", + "$WORK/cmd.barevar.unit.new:4:26: error: undefined: VARIABLE\n", + "ww: w6c failed for cmd.barevar\n"), + "", "", + strings.concat( + "$WORK/cmd.repeatdup.unit.new:4:1: error: wire redeclared in this block\n", + "\t$WORK/cmd.repeatdup.unit.new:3:1: other declaration of wire\n", + "$WORK/cmd.repeatdup.unit.new:4:1: error: \"pkg.one\" imported as wire and not used\n", + "ww: w6c failed for cmd.repeatdup\n"), + strings.concat( + "$WORK/cmd.aliasdup.unit.new:4:1: error: stable redeclared in this block\n", + "\t$WORK/cmd.aliasdup.unit.new:3:1: other declaration of stable\n", + "$WORK/cmd.aliasdup.unit.new:4:1: error: \"pkg.two\" imported as stable and not used\n", + "ww: w6c failed for cmd.aliasdup\n"), + strings.concat( + "$WORK/cmd.defaultalias.unit.new:4:1: error: wire redeclared in this block\n", + "\t$WORK/cmd.defaultalias.unit.new:3:1: other declaration of wire\n", + "$WORK/cmd.defaultalias.unit.new:4:1: error: \"pkg.two\" imported as wire and not used\n", + "ww: w6c failed for cmd.defaultalias\n"), + strings.concat( + "$WORK/cmd.declcollision.unit.new:3:1: error: \"pkg.one\" imported as stable and not used\n", + "$WORK/cmd.declcollision.unit.new:4:1: error: stable already declared through import of package stable (\"pkg.one\")\n", + "\t$WORK/cmd.declcollision.unit.new:3:1: other declaration of stable\n", + "ww: w6c failed for cmd.declcollision\n"), + strings.concat( + "$WORK/cmd.unusedalias.unit.new:3:1: error: \"pkg.one\" imported as stable and not used\n", + "ww: w6c failed for cmd.unusedalias\n"), + strings.concat(source, + "/cmd/blankalias/main.ww:2:8: error: blank import alias _ is not implemented\n"), + ]; + let si: i32 = 0; + for (si < stages.len) { + let work: str = strings.concat(root, "/work-", stages[si]); + let rejectwork: str = strings.concat(root, "/reject-", stages[si]); + mkdirall(work); mkdirall(rejectwork); + i = 0; + for (i < positives.len) { + let target: str = strings.concat(source, "/cmd/", positives[i]); + let binary: str = strings.concat(root, "/", stages[si], "-", + positives[i]); + let av: []str = [driver(stages[si]), "build", "-w", work, "-I", + source, "-o", binary, target]; + let out: commandout; + runcommand(root, strings.concat("alias-positive-", stages[si], "-", + positives[i]), av, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 0); + let runav: []str = [binary]; + runcommand(root, strings.concat("alias-positive-run-", stages[si], + "-", positives[i]), runav, + (30i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, expected[i]); + let bytes: str = readfile(binary); + if (si == 0) { binaryrefs[i] = strings.dup(bytes); } + else { assert(same(binaryrefs[i], bytes)); }; + i += 1; + }; + let aliasunit: str = readfile(strings.concat(work, "/cmd.alias.unit.ww")); + assert(has(aliasunit, "import stable pkg.one;")); + let aliaswwi: str = readfile(strings.concat(work, "/cmd.alias.wwi")); + assert(has(aliaswwi, "import __wwi_706b672e6f6e65 pkg.one;")); + assert(!has(aliaswwi, "import stable")); + assert(os.exists(strings.concat(work, "/pkg.one.a"))); + assert(!os.exists(strings.concat(work, "/stable.a"))); + let repeatunit: str = readfile(strings.concat(work, + "/cmd.repeatok.unit.ww")); + assert(occurrences(repeatunit, "pkg.one;") == 3); + let repeatedunit: str = readfile(strings.concat(work, + "/cmd.repeatedfiles.unit.ww")); + assert(occurrences(repeatedunit, "pkg.one;") == 2); + + i = 0; + for (i < negatives.len) { + let target: str = strings.concat(source, "/cmd/", negatives[i]); + let binary: str = strings.concat(root, "/reject-bin-", stages[si], + "-", negatives[i]); + let av: []str = [driver(stages[si]), "build", "-w", rejectwork, + "-I", source, "-o", binary, target]; + let out: commandout; + runcommand(root, strings.concat("alias-negative-", stages[si], "-", + negatives[i]), av, + (120i64 * (time.second: i64)): time.duration, &out); + expectexit(&out, 1); + assert(has(out.stderr, needles[i])); + if (i == 5) { + assert(has(out.stderr, "undefined: wire")); + assert(has(out.stderr, "undefined: one")); + assert(has(out.stderr, "undefined: value")); + }; + if (i < 5) { + let unusedpos: i32 = pos(out.stderr, + "\"pkg.one\" imported as wire and not used"); + let namepos: i32 = pos(out.stderr, needles[i]); + assert(unusedpos >= 0 && namepos > unusedpos); + }; + if (i == 7 || i == 8 || i == 9) { + let redeclared: i32 = pos(out.stderr, "redeclared in this block"); + let unused: i32 = pos(out.stderr, "imported as"); + assert(redeclared >= 0 && unused > redeclared); + }; + let normalized: str = normalizedtrace(out.stderr, + strings.concat(rejectwork, "/"), binary); + if (exactdiags[i].len > 0) { + assert(same(normalized, exactdiags[i])); + }; + if (si == 0) { diagrefs[i] = strings.dup(normalized); } + else { assert(same(diagrefs[i], normalized)); }; + assert(!os.exists(binary)); + let action: str = strings.concat("cmd.", negatives[i]); + let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a", + ".unit.new", ".wwi.new", ".s.new", ".o.new", ".a.new"]; + let j: i32 = 0; + for (j < suffixes.len) { + assert(!os.exists(strings.concat(rejectwork, "/", action, + suffixes[j]))); + j += 1; + }; + i += 1; + }; + si += 1; + }; + clean(root); +}; + @test fn compile_artifact_naming() void = { let root: str = fresh(); let pdir: str = strings.concat(root, "/pkg"); diff --git a/test/sep/localbuild_test.ww b/test/sep/localbuild_test.ww index a3ea3cf5..1161c040 100644 --- a/test/sep/localbuild_test.ww +++ b/test/sep/localbuild_test.ww @@ -206,8 +206,8 @@ fn rejectstable(dir: str, label: str, target: str, needle: str) void = { "import dimensions;\n", "def PRIVATE_WIDTH: i32 = 3;\n", "export type Buffer = [PRIVATE_WIDTH]u8;\n", - "export type PublicBuffer = [WIDTH]u8;\n", - "export type CastBuffer = [(WIDTH: u64)]u8;\n", + "export type PublicBuffer = [dimensions.WIDTH]u8;\n", + "export type CastBuffer = [(dimensions.WIDTH: u64)]u8;\n", "export type Detail = struct { code: i64, };\n", "export type Member = struct { detail: Detail, payload: i64, };\n", "export type Unused = struct { noise: i32, };\n", @@ -223,7 +223,7 @@ fn rejectstable(dir: str, label: str, target: str, needle: str) void = { "export type Member = struct { local: i32, };\n", "export fn produce(v: i32) implementation.Member = { return implementation.construct(v); };\n", "export fn score(m: implementation.Member) i32 = { return (m.detail.code + m.payload): i32; };\n", - "export fn width_of(b: Buffer) i32 = { return len(b): i32; };\n", + "export fn width_of(b: implementation.Buffer) i32 = { return len(b): i32; };\n", "export fn public_width_of(b: implementation.PublicBuffer) i32 = { return len(b): i32; };\n", "export fn cast_width_of(b: implementation.CastBuffer) i32 = { return len(b): i32; };\n"); let mainsrc: str = strings.concat( @@ -423,7 +423,8 @@ fn writediamond(td: str, reverse: bool) str = { "left.wwi")); let rightiface: str = testenv.readfile(strings.concat(scratch, "right.wwi")); - if (testenv.occurrences(leftiface, "import shared;") != 1) { + if (testenv.occurrences(leftiface, + "import __wwi_736861726564 shared;") != 1) { fail("diamond", "direct import is missing or duplicated in export data"); }; if (testenv.occurrences(leftiface, "export type token") != 1 @@ -907,7 +908,7 @@ fn writediamond(td: str, reverse: bool) str = { "/.wwtool.w6a")), testenv.readfile(assembler)) || !testenv.same(testenv.readfile(strings.concat(work, "/.wwtool.stamp")), - "ww workdir fmt 14 mode build asm 0\n")) { + "ww workdir fmt 15 mode build asm 0\n")) { fail("driver-identity", "persistent artifacts or identities are incomplete"); }; let coldwwi: str = testenv.readfile(strings.concat(work, "/dep.wwi")); @@ -1140,7 +1141,7 @@ fn writediamond(td: str, reverse: bool) str = { "package main;\nimport left;\n", "fn main() i32 = { return shared.base(); };\n")); rejectstable(td, "transitive", strings.concat(td, "/transitive.ww"), - "package 'shared' is not directly imported"); + "undefined: shared"); testenv.writefile(strings.concat(td, "/transitive-bare-value.ww"), strings.concat("package main;\nimport left;\n", "fn main() i32 = { return base(); };\n")); diff --git a/test/wcc/200_parse.c b/test/wcc/200_parse.c index 649b6f13..5fab0646 100644 --- a/test/wcc/200_parse.c +++ b/test/wcc/200_parse.c @@ -100,6 +100,37 @@ imports_to_str(const char *src, int *errs, const char **pkg) return buf; } +static int +must_import_shape(const char *src, int imports_only, const char *binding, + const char *path, const char *alias) +{ + Arena *a = newarena(); + Lex l; + Parser p; + char *wrapped = imports_only ? strdup(src) : wwtest_wrap(src); + lexinit(&l, a, imports_only ? "imports.ww" : "", wrapped, + strlen(wrapped)); + parserinit(&p, a, &l); + Node *file = imports_only ? parseimports(&p) : parsefile(&p); + Node *u = file ? file->list : NULL; + while (u != NULL && u->kind != N_USE) u = u->next; + int ok = p.errs == 0 && l.errs == 0 && u != NULL + && u->str != NULL && strcmp(u->str, binding) == 0 + && u->usesource != NULL && strcmp(u->usesource, path) == 0 + && u->usepath != NULL && strcmp(u->usepath, path) == 0 + && ((alias == NULL && u->usealias == NULL) + || (alias != NULL && u->usealias != NULL + && strcmp(u->usealias, alias) == 0)) + && u->usepkgname == NULL && u->pos.file != NULL + && u->pos.line > 0 && u->pos.col > 0; + if (!ok) + fprintf(stderr, "import shape mismatch (%s): %s\n", + imports_only ? "imports-only" : "full", src); + free(wrapped); + freearena(a); + return ok; +} + int main(void) { @@ -108,6 +139,7 @@ main(void) const char *parses[] = { "import io;", "import io.bufio;", + "import stable io.bufio;", "def MAX: i32 = 4096;", "export def MAX: i32 = 4096;", "type point = struct { x: i32, y: i32 };", @@ -193,6 +225,7 @@ main(void) } if (!must_contain("import io;", "(use \"io\"")) fail++; + if (!must_contain("import stable io.bufio;", "(use \"stable\"")) fail++; if (!must_contain("def N: i32 = 4;", "(def \"N\"")) fail++; if (!must_contain("def N: i32 = 4;", "(int 4")) fail++; if (!must_contain("export fn f() void = {};", "(fn \"f\" export")) fail++; @@ -201,6 +234,14 @@ main(void) if (!must_contain("fn f() void = { x = 1; };", "(assign =")) fail++; if (!must_contain("fn f() i32 = { return a + b; };", "(bin +")) fail++; if (!must_contain("@symbol(\"x\") fn f() void;", "(attr \"symbol\""))fail++; + if (!must_import_shape("import io.bufio;", 0, "bufio", "io.bufio", + NULL)) fail++; + if (!must_import_shape("import stable io.bufio;", 0, "stable", + "io.bufio", "stable")) fail++; + if (!must_import_shape("package main;\nimport io.bufio;\n", 1, + "bufio", "io.bufio", NULL)) fail++; + if (!must_import_shape("package main;\nimport stable io.bufio;\n", 1, + "stable", "io.bufio", "stable")) fail++; { const char *src = @@ -268,6 +309,6 @@ main(void) fprintf(stderr, "%d parse tests failed\n", fail); return 1; } - printf("parse: %d/%d ok + 9 shape ok\n", n, n); + printf("parse: %d/%d ok + 14 shape ok\n", n, n); return 0; } diff --git a/test/wcc/data/r957_assert_shadow_xmod/case.ww b/test/wcc/data/r957_assert_shadow_xmod/case.ww index 412c13fe..d138c482 100644 --- a/test/wcc/data/r957_assert_shadow_xmod/case.ww +++ b/test/wcc/data/r957_assert_shadow_xmod/case.ww @@ -5,6 +5,6 @@ export fn assert(b: bool, msg: str) void = { }; package main; import m; export fn main() i32 = { - assert(false, "routed to m.assert"); + m.assert(false, "routed to m.assert"); return 42; }; diff --git a/test/wcc/data/r989_trycallcollide_call/case.ww b/test/wcc/data/r989_trycallcollide_call/case.ww index 6cca5810..65922e1b 100644 --- a/test/wcc/data/r989_trycallcollide_call/case.ww +++ b/test/wcc/data/r989_trycallcollide_call/case.ww @@ -11,4 +11,4 @@ export type gerr2 = !u8; export fn op() (i32 | gerr1 | gerr2) = { return 0; }; package main; import beta; -fn main() int = { match (beta.user()) { case let n: i32 => return n: int; case berr => return 99; }; }; +fn main() int = { match (beta.user()) { case let n: i32 => return n: int; case beta.berr => return 99; }; }; diff --git a/test/wcc/data/r989_trycallcollide_control/case.ww b/test/wcc/data/r989_trycallcollide_control/case.ww index 022976d6..1e386411 100644 --- a/test/wcc/data/r989_trycallcollide_control/case.ww +++ b/test/wcc/data/r989_trycallcollide_control/case.ww @@ -10,4 +10,4 @@ export type gerr1 = !void; export fn other() (i32 | gerr1) = { return 0; }; package main; import beta; -fn main() int = { match (beta.user()) { case let n: i32 => return n: int; case berr => return 99; }; }; +fn main() int = { match (beta.user()) { case let n: i32 => return n: int; case beta.berr => return 99; }; };