test: prove ordinary import binding modes

This commit is contained in:
2026-08-14 10:56:54 +09:00
parent 792b6ecbe5
commit 10e02a00ee
20 changed files with 1284 additions and 672 deletions

View File

@@ -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 <canonical-path>` 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 628659](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L659),
[`cmd/go/internal/test/test.go`, lines 11331226](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 908915 and 939955](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 25092546](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 237350](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 463480](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L463-L480)
and
[`resolver.go`, lines 701735](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 223335](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L223-L335),
[lines 472489](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L472-L489),
and
[lines 706740](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 682693](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/call.go#L682-L693),
[`go/types/typexpr.go`, lines 2031 and 7986](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 7092](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 2952](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/importdecl0/importdecl0a.go#L29-L52),
[`test/import.go`, lines 723](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/test/import.go#L7-L23),
[`test/import1.go`, lines 718](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 2640](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 430465](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/writer.go#L430-L465),
[`cmd/compile/internal/importer/ureader.go`, lines 157196](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/importer/ureader.go#L157-L196),
[`cmd/compile/internal/noder/reader.go`, lines 342376](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 436505](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L436-L505),
[lines 9391040](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L939-L1040),
[lines 10611063](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 633636](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L633-L636),
[lines 757806](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L757-L806),
[lines 20242047](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L2024-L2047),
[`cmd/go/internal/load/test.go`, lines 175293](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L175-L293),
[lines 421484](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 202206](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L202-L206),
[lines 437447](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L437-L447),
[lines 628708](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L708),
[lines 919968](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L919-L968),
[lines 10341068](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 495558](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L495-L558),
[`cmd/go/internal/test/test.go`, lines 11331226 and 12971366](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 <name> 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 <canonical-path> <direct.wwi>`
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.

View File

@@ -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.
---

View File

@@ -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,

View File

@@ -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;
};
};

View File

@@ -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;
};
};

View File

@@ -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) != "/"));
};

View File

@@ -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);

View File

@@ -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) —

View File

@@ -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");
};

View File

@@ -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)));
};

View File

@@ -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, "?");
};

View File

@@ -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));
};

View File

@@ -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, "<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, "<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, "<last>");
checktokname(syntax.tkind.TK_MODRESET, "//ww:module-reset");
checktokname(syntax.tkind.TK_MODPATH, "//ww:module");
checktokname(syntax.tkind.TK_LAST, "<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 <none>\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"));
};

View File

@@ -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");
};

View File

@@ -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");

View File

@@ -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"));

View File

@@ -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" : "<test>", 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;
}

View File

@@ -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;
};

View File

@@ -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; }; };

View File

@@ -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; }; };