test: prove package initialization semantics

This commit is contained in:
2026-08-14 19:02:49 +09:00
parent c6bec0914d
commit 763bbc8f25
24 changed files with 2243 additions and 349 deletions

View File

@@ -349,7 +349,7 @@ $(PACKAGE_TEST_BIN): $(PACKAGE_TEST_SRC) $(WWTEST_BIN) \
test-package: $(PACKAGE_TEST_BIN) $(WWTEST_BIN) $(BIN)/ww $(BIN)/ww_ww \
$(BIN)/w6c $(BIN)/w6c_ww $(BIN)/w6a $(BIN)/w6a_ww \
$(BIN)/w6l $(BIN)/w6l_ww $(LIB)/libwwrt.a
@WW_PACKAGE_REPO=$(CURDIR) $(CURDIR)/$(PACKAGE_TEST_BIN)
@WW_PACKAGE_REPO=$(CURDIR) $(CURDIR)/$(PACKAGE_TEST_BIN) -timeout-ms=120000
# ---- directories -------------------------------------------------------
$(BIN) $(LIB) $(OBJ)/wcc $(OBJ)/ww $(OBJ)/wwdump $(OBJ)/w6c $(OBJ)/w6a $(OBJ)/w6l $(OBJ)/rt:
@@ -384,10 +384,17 @@ BYTEID_WW_TARGETS = $(BYTEID_WW_TESTS:%=wwtest/%)
# export arguments, link rejects), NOT byteid suites: they run under
# test-compiler beside the surviving residual carriers.
SEP_WW_TESTS = test/sep/sepbuild_test.ww test/sep/sepimport_test.ww \
test/sep/seplink_test.ww test/sep/sepscratch_test.ww \
test/sep/seplink_test.ww test/sep/sepinit_test.ww \
test/sep/sepscratch_test.ww \
test/sep/septest_test.ww test/sep/m3sep_test.ww \
test/sep/localbuild_test.ww test/sep/importdir_test.ww
SEP_WW_TARGETS = $(SEP_WW_TESTS:%=wwtest/%)
SEP_LIMITEXEC_BIN = $(BIN)/sep-limitexec
$(SEP_LIMITEXEC_BIN): test/sep/limitexec.c | $(BIN)
$(CC) $(CFLAGS) -o $@ $<
wwtest/test/sep/sepinit_test.ww: $(SEP_LIMITEXEC_BIN)
# Ww-native lib env/OS arranger observers: the env contracts of the
# lib/os and lib/dirs suites (getenv cohorts, XDG cohorts, the dirs
# too-long abort) need per-row env construction ww cannot self-arrange

View File

@@ -3078,11 +3078,13 @@ binding is identical before reusing its compile action. A different binding is
a deterministic package-resolution failure for the roots that reach it, rather
than a first-root-wins build.
A production action failure blocks exactly the roots that reach it. A
root-local compile or link failure does not suppress a successfully built
sibling product: the command records completion per product, and the
coordinator can run and report successful siblings while attributing each
missing product as a build failure. The single union build and completed test
A production action failure is attributed to exactly the roots that reach it,
but the complete command is one publication transaction. The driver may
continue enough of the already validated plan to retain deterministic action
and product diagnostics, but one failed producer, linker, status stage, or
commit suppresses every new action voucher, tool record, product, and status
from that request. The coordinator therefore runs no sibling test binary from
a rejected union build. After one successful union build, the completed test
products share the coordinator's existing `-j` process bound; captured output
is still emitted only in byte-sorted directory/package order.
@@ -3153,11 +3155,11 @@ compiler-owned dispatcher with empty output is reported as `[no tests]`.
Persistent workdirs keep a global driver/compiler/assembler/stamp identity and
per-action committed units. When that global identity is stale, the command
first removes every old `.unit.ww` voucher while leaving artifacts recoverable.
It can then commit the new identity even if one root fails: successful actions
have freshly committed units, whereas failed or no-longer-requested actions
cannot be reused. A retry therefore recompiles the failed action without
discarding unchanged canonical packages built for independent products.
forces every requested action cold while preserving the complete old generation
as rollback state. New tool records, artifacts, units, products, statuses, and
the new stamp become visible only in the request-wide commit after every product
has staged successfully. A rejected request leaves the old generation
byte-identical, and a retry cannot reuse any uncommitted work from the rejection.
Warm reuse compares the staged owner-only unit, committed artifacts, and the
actual bytes of each direct dependency export. A changed shared dependency is
@@ -4176,12 +4178,13 @@ succeeds; a rejected request therefore leaves neither directory behind.
Resolution, identity collision checks, contextual legality, dependency-failure
propagation, cycles, command kind, publication paths, and action closures all
finish before cold scratch acquisition, product status removal, stale-voucher
invalidation, tool identity records, producer execution, publication, or
runtime execution. A request whose every product is already invalid returns
without acquiring scratch; independent valid sibling products retain the
existing isolated-build behavior. A forbidden warm request leaves committed
vouchers, status files, tool records, and products byte-unchanged.
finish before cold scratch acquisition, tool identity staging, producer
execution, publication, or runtime execution. A request whose every product is
already invalid returns without acquiring scratch. If a later producer or
product fails, otherwise viable siblings may have been produced only into the
same transaction; none is committed or executed. A forbidden or otherwise
rejected warm request leaves committed vouchers, status files, tool records,
and products byte-unchanged.
This is only manifest-free local source-tree behavior. It does not implement
Go modules, module vendor mode, `go.mod`, `vendor/modules.txt`, importcfg, build
@@ -4557,20 +4560,15 @@ qualifier or canonical dependency. A package-scope declaration collides with
an equal import binding from any contributing file, matching Go's reconciliation
of package and file scopes. Conflicting production package clauses remain a
loader-owned deterministic error before producers. Compiler-owned scope or use
errors may invoke the compiler, but the driver removes that action's staged
`.new` unit/export/assembly/object/archive files instead of renaming them. No
completion/status marker is written, and no publication occurs after a failed
compile. If an artifact rename itself fails after an earlier rename has
published part of a new generation, the driver removes every committed unit
voucher as well as the remaining staged files. Before the first artifact
rename, it must successfully remove the workdir identity stamp; failure is a
pre-commit rejection that leaves the prior artifact generation untouched. The
stamp remains absent after any partial commit, and later actions in that
invocation cannot take the freshness shortcut. Each non-reusable action also
loses its old unit voucher before its producers run. The next request must
therefore rebuild the incomplete action set and reconsider direct importers; a
partly replaced `.wwi` or a failed importer can never make an old importer
voucher look fresh.
errors may invoke the compiler, but every action artifact remains staged under
an adjacent `.new` name. No completion/status marker is written and no product
is published after a failed compile. Once every action and product has staged,
the driver preserves each existing destination under a request-owned backup,
installs the complete new generation, and rolls all installed destinations back
if any installation fails. Unit vouchers and the global stamp are transaction
members rather than early invalidation markers. Thus a rejected or interrupted
request leaves the previous committed generation byte-identical and removes all
remaining stages; a mixed `.wwi`/object/archive generation is never reusable.
Package kind follows the declaration. `package main`, not a path component,
marks a command. A path ending in `main` remains importable when it declares a
@@ -4690,8 +4688,8 @@ The behavior follows pinned official Go 1.26.5 source at commit
[`cmd/go/internal/test/test.go`, lines 11331226](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1133-L1226)).
Section 11.19 completes ordinary explicit aliases while retaining dotted,
unquoted paths. Grouped, quoted, dot, and blank imports remain deliberately
unimplemented.
unquoted paths. Section 11.20 adds blank side-effect imports without adding a
name. Grouped, quoted, and dot imports remain deliberately unimplemented.
The focused native observer
`declared_name_identity_and_file_import_scope` generates every tree
@@ -4820,19 +4818,21 @@ 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.
`_` selects the no-binding side-effect mode completed in section 11.20. It is
never an ordinary effective qualifier and therefore neither collides with
another `_` occurrence nor receives an unused diagnostic. Grouped imports,
quoted paths, and dot imports remain deliberately deferred.
#### 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.
ordinary alias, blank mode, 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 or `_`.
Repeated occurrences remain separate file 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
@@ -4877,7 +4877,7 @@ 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.
is discarded under section 11.20's request transaction.
#### Stage and observer ownership
@@ -4896,11 +4896,300 @@ 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.
request-transaction 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.
### 11.20 Implemented blank side-effect imports and package initialization
WW now completes the third file-local import mode and the package
initialization path needed to give it meaning:
```ww
import acme.codec; // qualifier from the dependency declaration
import stable acme.codec; // explicit file-local qualifier stable
import _ acme.codec; // no qualifier; initialization side effect only
```
A blank occurrence retains its dotted source spelling, owning file, line,
column, source section, and resolved canonical action, but creates no source
binding and is exempt from unused-import checking. It exposes neither
`wire.Name`, `codec.Name`, nor bare `Name`. Resolution always uses
`acme.codec`, never `_`: missing-package, self-import, cycle, final-`internal`,
nearest-first vendor, vendor-spelling, and imported-`main` checks run at every
blank site exactly as they do for a named occurrence. A vendored blank import
therefore keeps a source-to-expanded-identity `--import-map`; `_` enters no
map, action key, task symbol, artifact, voucher, stamp, variant, or link input.
Repeated blank imports of one path, in one file or several files, are valid.
A blank occurrence may coexist with the default binding or any explicit alias
of the same path. Every named occurrence remains independently subject to its
ordinary duplicate-binding and usage rules. All occurrences survive in the
owner unit and per-site validation data, while their package-wide union still
forms one sorted canonical dependency edge, one package action, and one direct
compiler export input.
#### Pinned Go evidence
The reference is official Go 1.26.5 at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`:
- `ast.ImportSpec` keeps an optional local `Name`, including `_`, independent
from `Path`, and the parser reads that optional name before the path
([`go/ast/ast.go`, lines 908915 and 939955](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/ast/ast.go#L908-L955),
[`go/parser/parser.go`, lines 25092546](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/parser/parser.go#L2509-L2546)).
Function parsing treats `init` as an ordinary syntactic function name; its
special meaning is assigned later
([`go/parser/parser.go`, lines 27842842](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/parser/parser.go#L2784-L2842)).
- `go/types` creates an import object for each occurrence, inserts no binding
for `_`, exempts `_` from unused checking, and keeps a valid `init` function
out of package scope while checking its body and signature
([`go/types/decl.go`, lines 1633](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/decl.go#L16-L33),
[`go/types/resolver.go`, lines 103124](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L103-L124),
[lines 279350](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L279-L350),
[lines 400433](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L400-L433),
[lines 701716](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L701-L716)).
The production compiler equivalent is
[`cmd/compile/internal/types2/resolver.go`, lines 90111](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L90-L111),
[lines 264335](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L264-L335),
[lines 416444](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L416-L444)
and
[lines 706721](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L706-L721).
- Official import testdata accepts repeated blanks and blank plus default or
explicit named imports of the same path, while diagnosing only the unused
named occurrences
([`internal/types/testdata/check/importdecl0/importdecl0a.go`, lines 931 and 4352](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/importdecl0/importdecl0a.go#L9-L52)).
Multiple valid init declarations, invalid signatures, and direct
invisibility are pinned by
[`test/init.go`, lines 1218](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/test/init.go#L12-L18),
[`test/noinit.go`, lines 315326](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/test/noinit.go#L315-L326),
[`internal/types/testdata/check/decls0.go`, lines 4046](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/decls0.go#L40-L46),
and
[`decls1.go`, lines 141146](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/decls1.go#L141-L146).
- After constants, variable initialization chooses the declaration with the
fewest unresolved variable dependencies and uses source order as its tie;
references through functions are dependencies. Cycle reporting is
deterministic, and graph removal continues so later independent cycles are
also diagnosed
([`go/types/initorder.go`, lines 19185 and 226335](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/initorder.go#L19-L335),
[`cmd/compile/internal/types2/initorder.go`, lines 16182 and 223332](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/initorder.go#L16-L332)).
Source ties and calls are exercised by
[`go/types/api_test.go`, lines 14081619](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/api_test.go#L1408-L1619),
with cycle ordering in
[`internal/types/testdata/check/init0.go`, lines 2289](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/init0.go#L22-L89).
- The compiler first attempts static initialization and serializes remaining
ordered assignments into a compiler-generated init function. It then emits
one package task whose dependency tasks are separate `R_INITORDER`
relocations and whose payload is an ordered function-pointer list
([`cmd/compile/internal/staticinit/sched.go`, lines 34145](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/staticinit/sched.go#L34-L145),
[`cmd/compile/internal/noder/writer.go`, lines 27172773](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/writer.go#L2717-L2773),
[`cmd/compile/internal/noder/reader.go`, lines 32883345](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/reader.go#L3288-L3345),
[lines 33893416](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/reader.go#L3389-L3416),
[`cmd/compile/internal/pkginit/init.go`, lines 20145](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/pkginit/init.go#L20-L145)).
The requested historical `cmd/compile/internal/walk/init.go` path does not
exist at this tag; the noder, `staticinit`, and `pkginit` files above are the
active implementation.
- The linker schedules ready tasks by canonical task symbol and emits each
exactly once, and the runtime executes those tasks before user main
([`cmd/link/internal/ld/inittask.go`, lines 1939 and 104180](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/inittask.go#L19-L180),
[`cmd/link/internal/ld/heap.go`, lines 5699](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/heap.go#L56-L99),
[`runtime/proc.go`, lines 203290](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/runtime/proc.go#L203-L290),
[lines 80498124](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/runtime/proc.go#L8049-L8124)).
The `issue31636` packages exercise imports written out of order, while the
linker queue above—not that fixture's stale comment—pins lexical ready-task
order
([`test/fixedbugs/issue31636.dir/main.go`, lines 717](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/test/fixedbugs/issue31636.dir/main.go#L7-L17)).
- `go/build` specifies sorted directory presentation; its default path obtains
byte-sorted names and then classifies production, internal-test, and
external-test files/imports separately
([`go/build/build.go`, lines 108111 and 193207](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L108-L207),
[`os/dir.go`, lines 109125](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/os/dir.go#L109-L125),
[`go/build/build.go`, lines 9481040](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L948-L1040),
[lines 10611063](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L1061-L1063),
[lines 15121518](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L1512-L1518)).
The test loader constructs and rewrites canonical internal, external, and
generated-main variants before work actions. In the internal variant it
presents the already-sorted production category first and the already-sorted
internal-test category second, rather than globally sorting the merge; its
effective-test-cycle rule is pinned by
[`cmd/go/internal/load/test.go`, lines 85101, 175293, and 421550](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L85-L550)
and
[`cmd/go/testdata/script/list_test_cycle.txt`, lines 120](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/list_test_cycle.txt#L1-L20).
Compile/link action ownership remains canonical in
[`cmd/go/internal/work/action.go`, lines 437455, 628708, and 9191068](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L437-L1068),
while `go test` rejects a bad test graph before creating those actions
([`cmd/go/internal/test/test.go`, lines 11851226](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1185-L1226)).
- Unified export data separates public objects from private bodies/init data;
a blank import declaration serializes no declaration, and import readers
consume the semantic package export independently of that local spelling
([`cmd/compile/internal/noder/unified.go`, lines 314353](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/unified.go#L314-L353),
[lines 463570](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/unified.go#L463-L570),
[`cmd/compile/internal/noder/writer.go`, lines 27422749](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/writer.go#L2742-L2749),
[`cmd/compile/internal/importer/ureader.go`, lines 4162](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/importer/ureader.go#L41-L62)).
Together with the resolver's omission from package scope, that public/private
split is why another package cannot select `pkg.init`.
- Loader import checks, build-root action ownership, direct compiler import
inputs, and transitive linker inputs remain per canonical package rather
than per local import name
([`cmd/go/internal/load/pkg.go`, lines 787805](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L787-L805),
[`cmd/go/internal/work/build.go`, lines 519558](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L519-L558),
[`cmd/go/internal/work/exec.go`, lines 864884](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/exec.go#L864-L884),
[lines 15921653](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/exec.go#L1592-L1653),
[`cmd/go/internal/work/gc.go`, lines 136177](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/gc.go#L136-L177),
[lines 590672](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/gc.go#L590-L672)).
#### Declaration and package-variable semantics
`fn init() void = { ... };` is a special initializer declaration. It must have
a body, no parameters, no result, no `export`, and no attribute. Multiple valid
declarations are accepted in one file and across files. No declaration of
another kind may claim `init`; a rejected non-function form is not inserted
into scope, so later references still fail lookup. A valid init retains its
file, source section, and position, but is never installed as a callable
declaration: `init()` and `pkg.init` fail lookup, it is absent from `.wwi`, and
it cannot affect canonical package identity.
Mutable package-level `let` is WW's Go-variable analogue. An initializer that
the existing static-data emitter can represent remains static. Every other
otherwise valid expression—including calls, allocation, and supported nested
array, struct, tuple, and slice values—is evaluated once by a hidden
package-owned helper and assigned to zero-backed package storage. Runtime slice
literals use canonical writable backing storage rather than escaping a helper
stack. `def` and `const` retain their existing compile-time/static rules and are
not broadened by this implementation.
The checker orders all initialized mutable lets by their checked declaration
dependencies. References through package functions are transparent edges.
Among remaining declarations, the one with the fewest unresolved dependencies
wins and original declaration order breaks ties. Files arrive byte-sorted
inside each loader category; a combined internal-test variant presents its
production category before its internal-test category, and declarations retain
source order inside each file. A cycle is
reported on the same deterministic walk as the pinned type checkers; removal
continues to expose later independent cycles, but any cycle suppresses all init
lowering and publication. Runtime variable assignments execute in that order,
then every special init function executes in owner-file/source order.
Each semantic package action owns one hidden task symbol:
```text
__ww..pkg.p.<canonical-path>.v<variant>.r<role>.init
```
The reversible empty-owner form is likewise variant/role qualified. Neither a
declared package name, default qualifier, explicit alias, blank spelling, path
leaf, physical directory, request ordinal, nor output name contributes to this
symbol. Compiler argv supplies it with
`--package-init-symbol <symbol>`. Only an executable command root or generated
test main additionally receives `--init-dispatch-symbol __ww..dispatch`, and
its compiler-generated entry calls that dispatcher before source `main` or the
generated test main body.
#### Product graph, variants, artifacts, and persistence
Before any producer, the driver forms the effective reachable graph for each
product, including internal-test replacement, and rejects a cycle introduced by
that replacement. It repeatedly chooses the byte-lexically smallest ready
canonical path, then variant and role, while blocking every importer on its
dependencies. The resulting root-owned dispatcher calls each effective package
task exactly once. Thus dependencies precede importers, a shared diamond task
runs once per product, independent ties ignore source import order and linker
argv order, and the root task completes before user main or tests.
An ordinary library object/archive contains its hidden task but building the
library does not execute it. An executable or generated-main root archive has
two deterministic members, `pkg.o/` followed by `init.o/`; the second member is
the root-owned dispatcher. Dependency archives remain ordinary `pkg.o/`
archives. Both driver stages stream member bytes through the same bounded
transfer buffer instead of retaining archive-sized allocations. Existing
linker archive fixpoint extraction pulls the dispatcher and
all referenced package tasks without a new linker format or free-floating
artifact. Logical linker argv is still the canonical root archive followed by
the reachable archive closure and runtime/native inputs; no `.wwi`, alias,
blank spelling, or dispatcher sidecar appears.
Production, production-plus-internal-test, external `_test`, test support, and
generated main retain separate action identities. An internal product replaces
the colocated production task with its combined variant and includes test-only
blank edges/init declarations exactly once. An external product initializes
ordinary production and its production dependencies before the external task.
Support is an ordinary dependency task; generated main owns only the final
dispatcher call and cannot duplicate a tested task. Test-file-only imports,
runtime lets, and init functions never enter production. A blank import cannot
bypass imported-`main` rejection, including through vendor expansion.
`.wwi` contains neither blank-only spelling, init declarations/bodies, hidden
variable helpers, slice backing symbols, package tasks, nor dispatcher facts.
It continues to encode only semantic exported declarations and their canonical
reachable type/constant facts. Consequently an init-body-only edit rebuilds the
owning object/archive and relinks affected products, while unchanged `.wwi`
bytes prevent importer recompilation. Adding or removing a blank edge rebuilds
the owning source action and changes exactly the affected reachable dispatcher;
a dispatcher-only change rebuilds the root `init.o/` member/archive without
recompiling an unchanged root source object. Reverse propagation stops at the
first regenerated byte-identical semantic export.
The owner source voucher remains `<action>.unit.ww`; a linked product root also
owns `<root>.init.unit.ww` for its dispatcher unit, `.init.s`, `.init.o`, and
two-member archive. Current persistent formats are build 17 and test 16. Warm
consumers select a dependency's staged `.wwi.new` or `.a.new` when that exact
action changed in the same request. All action artifacts, init artifacts, tool
identity copies, stamp, library/executable publications, and test statuses are
then one rollback-capable request transaction. No destination changes unless
every product stages successfully; a compiler, checker, init-order,
dispatcher, assembler, archiver, linker, allocation, status, or installation
failure removes remaining stages and restores the complete prior generation.
Before scratch acquisition or producer execution, `lstat`-style no-follow
checks reserve every action, tool, product, interface, status, and rollback
name; a dangling staging or backup symlink is an occupied structural conflict
and is never followed or removed. A committed dispatcher voucher must itself
be a regular file before it can authorize reuse. Compiler assembly and
interface bytes are first generated through anonymous files with checked full
writes and then published as their own rollback group. A non-regular compiler
destination is rejected before preservation, and installation—not cleanup of
a recoverable old backup—is the commit point. Cold rejection removes the exact
request-owned scratch tree. Stale init code, stale closure metadata, and mixed
committed generations therefore cannot be reused.
#### Stage and observer ownership
The shared syntax AST, C parser, and WW parser own blank/init facts and source
positions. Both imports-only driver scans resolve the dotted path and retain
per-site legality; neither treats `_` as an alias. The C and WW checkers own
special-init validation, invisibility, mutable-let dependency ordering, cycle
diagnostics, and runtime lowering. The interface writers omit initialization
implementation; the code generators emit static storage, runtime helpers,
package tasks, canonical slice backings, and the entry dispatcher call. The two
drivers own task identity, effective test graphs, global dispatcher ordering,
exact direct compiler inputs, archive membership, link closure, persistence,
and the request transaction. The assemblers consume the dynamically sized
canonical symbols. Both linkers are unchanged and use their existing iterative
archive extraction. `internal/wwpackage` and `cmd/wwtest` retain package/test
classification and execution coordination; they do not parse imports, invent
tasks, or call init manually.
The focused native `test/sep/sepinit_test.ww` observer generates every source
tree temporarily and proves runtime let/init order, dependency chains,
diamonds, independent lexical ties, aggregate and allocation initialization,
multiple-cycle diagnostics, special-init rejection, test-variant isolation,
canonical task/dispatcher/archive bytes, init-only invalidation, and warm
invalid-init rollback across independent Cstage and WWstage roots. Direct and
recursive test legs compare variant dispatcher/archive bytes and observable
production-dependency order; exact task counts include support and generated
main. Rejection rows compare complete normalized diagnostics and prove cold
scratch absence. The observer also uses a repository-built `setrlimit` launcher
to find one shared bounded-memory ceiling at which both drivers fail before a
producer, and proves no-follow atomic rejection of dangling driver staging and
compiler rollback names, non-regular compiler destinations, and compiler
output-write failures. The extended
`explicit_import_alias_binding_modes` observer owns repeated blank/default/
alias/file-scope combinations and no-binding/unused behavior. Existing
internal, vendor, imported-command, recursive, exact-argv, link-closure,
persistent-workdir, rejection-state, byte-identity, and bootstrap observers own
their unchanged broader boundaries. Grouped imports, quoted imports, and dot
imports remain deliberately unimplemented.
## 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" ( ImportPath | ident ImportPath ) ";" .
ImportDecl = "import" ( ImportPath | ImportName ImportPath ) ";" .
ImportName = ident .
ImportPath = ident { "." ident } .
```
@@ -263,8 +264,18 @@ ImportPath = ident { "." ident } .
Neither form exposes an imported declaration as a bare `Name`; ordinary
unqualified lookup remains limited to lexical, builtin, and same-package
declarations.
- `import _ acme.codec;` is a blank side-effect import. The lone `_` creates no
qualifier, exposes no bare declaration, and is never diagnosed as unused.
It is nevertheless a real import occurrence: resolution and all missing,
self-import, cycle, `internal`, vendor, and imported-command checks use the
dotted path, and the dependency participates in executable and test package
initialization. Repeated blank occurrences and blank plus default/explicit
named occurrences of one path are valid; every named occurrence remains
independently subject to duplicate-binding and unused checks.
The package dependency graph is the sorted, deduplicated union of the real
imports in all eligible files. Self-import is rejected.
imports in all eligible files. Occurrences retain their owning file and
position, but equal canonical targets create one graph edge/package action.
Self-import is rejected.
- An executable package is one declared `package main` and containing a
`fn main`; path and directory spelling do not classify commands. An ordinary
import of a package declared `main` is rejected, except for the toolchain's
@@ -272,8 +283,8 @@ ImportPath = ident { "." ident } .
- Only names marked `export` (§5) are visible across module boundaries.
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.
and dot imports are not implemented. `_` is reserved here for the blank form;
it is not an ordinary alias.
---
@@ -298,23 +309,42 @@ The `=` between signature and body is required. A bodiless fn (`;`
terminator) declares an external symbol (C FFI); the bare `...` C-style
variadic is legal only on a bodiless declaration (§6.3).
At package scope, exactly `fn init() void = Block;` declares a special package
initializer. It must have a body, no parameters, no result, no `export`, and no
attribute. Multiple init declarations are permitted and retain owner-file and
source order. `init` is not inserted into ordinary package scope: it cannot be
called as `init()`, selected as `pkg.init`, exported, or used by another kind
of declaration. Package-variable initialization completes before these
functions run.
### 5.2 `let`
```
LetDecl = "let" ident ":" Type [ "=" Expr ] ";" .
```
A mutable binding. Without an initialiser the storage is uninitialised.
A mutable binding. Without an initialiser, package-level storage is
zero-backed; an uninitialized function-local binding retains the existing
uninitialized-local rule.
A `static` qualifier (`static let …`) gives function-local storage
static lifetime.
For a package-level `let` with an initializer, values representable by the
static-data emitter are installed statically. Every other otherwise valid
initializer is evaluated exactly once at runtime. Checked references through
package functions contribute variable-dependency edges; dependencies precede
dependents, and source declaration order breaks ready ties. Initialization
cycles are errors. Runtime package lets execute after imported package tasks and
before the package's init functions. `const` and `def` are not broadened by
this runtime path.
### 5.3 `const`
`const` introduces an immutable binding of the same shape as `let`.
TODO(spec): pin the exact `const` vs `def` boundary against `cmd/wcc`
(both keywords exist; this text takes `def` = compile-time constant
[Hare's `def`] and `const` = immutable runtime binding — confirm).
`const` introduces an immutable storage binding of the same declaration shape
as `let`; assignment to it is rejected. At package scope its initializer stays
within the existing static-data forms. A `const` is not entered into the
runtime package-variable schedule described above; use mutable `let` when an
otherwise valid initializer requires runtime evaluation.
### 5.4 `def`
@@ -561,6 +591,15 @@ the program. Two placements, following the Go `foo` / `foo_test` model:
};
```
Production, production-plus-white-box-test, external black-box-test, test
support, and generated test main are distinct package actions. Before a test
function runs, the generated test product initializes its exact reachable
variant graph dependency-first and once per canonical action. A white-box
variant replaces the colocated production task rather than initializing both;
an external variant depends on ordinary production. Imports, runtime lets, and
init declarations found only in `*_test.ww` never enter an ordinary production
build.
---
## 11. Concurrency (reserved)

View File

@@ -24,7 +24,7 @@ categories out of the ordinary developer target.
| --- | --- |
| Arena, lexer, parser, checker, module-decl parse, narrow codegen | Six in-process C unit binaries |
| Compile success/rejection, stage-routed diagnostics, and runtime exit | `test/wcc/data/*/case.ww`, executed by `wwfixture` |
| Package semantics | `test/package` and the native package-test coordinator |
| Package semantics | `test/package`, `test/sep/sepinit_test.ww`, and the native package-test coordinator |
| Language behavior | `test/lang/*_test.ww` through the language `@test` runtime |
| Library behavior | The package coordinator's `lib/...` tree walk (`ww test -j N lib/...`) |
| Standalone library-source compilation | Three import-free real source paths named by `LIBRARY_STANDALONE_SOURCES`, compiled directly by both frontends |
@@ -236,25 +236,41 @@ 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.
scope isolation, package-declaration collisions, unused aliases, blank
side-effect imports, repeated blank/default/explicit combinations, and blank
no-binding/no-unused behavior. It also pins canonical `.wwi` spelling,
archive/action ownership, one canonical direct edge for repeated occurrences,
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.
binary. It also forces a request transaction to reject after dependency work
has staged, then proves that both stages restore every prior artifact, unit,
tool record, stamp, and product rather than accepting a mixed warm generation.
Together with the existing directory, recursive, vendor,
exact-argv, command, and persistent-workdir observers, the package suite proves
archive-only link argv and exact warm/rejection-state behavior without
duplicating those broader mechanisms in this observer.
`test/sep/sepinit_test.ww` is the single focused package-initialization owner.
It generates all source trees temporarily and runs independent cold/persistent
Cstage and WWstage legs. Its matrix covers blank-only reachability; dependency,
diamond, and independent ready-task order; runtime call/allocation and aggregate
package lets; multiple init declarations and invalid forms; direct/qualified
init invisibility; initialization cycles and diagnostic order; command,
internal-test, external-test, support, and generated-main ownership; canonical
task and dispatcher symbols; deterministic one- and two-member archives;
`.wwi` exclusion; byte-identical artifacts/binaries; init-only invalidation;
warm invalid-init rollback; direct/recursive variant equivalence; exact
production, support, and generated-main task ordering; complete normalized
rejection diagnostics; dangling staging/rollback no-follow rejection; and
checked compiler-output failure/non-regular-destination rollback; and
bounded-memory allocation-failure parity under one shared ceiling supplied by
the repository-built `sep-limitexec` helper.
`ww build`, an explicit single-file `ww test -o <stem>`, and each successful
directory-package `ww test -c` build publish `<stem>.sepwork` as a caller-owned
artifact directory. The driver acquires it with one fresh `mkdir` and refuses
@@ -279,13 +295,17 @@ no hashes, every decision reproducible with `cmp` against plain files. The
driver identity covers graph construction, owner-unit composition,
direct-export argument construction, archive creation, and commit algorithms
that neither unit bytes nor compiler identity can name.
Recompiled artifacts land at staged `.new` names and commit by rename with the
unit renamed last, so an interrupted build forces a recompile rather than a
false reuse; the link always reruns. One workdir serves one invocation at a
time and may hold the complete command-global action universe for all selected
roots and isolated variants; semantic identity, rather than request shape,
controls reuse. Both driver stages implement the identical contract. This is
build staleness in the Make/mk/Go sense, not a result cache:
Recompiled artifacts, dispatcher artifacts, products, statuses, tool copies,
and the stamp land at staged `.new` names. After every requested product stages
successfully, one rollback-capable transaction installs the whole generation;
any producer or installation failure preserves the prior committed bytes and
removes remaining stages. An interrupted or rejected request therefore cannot
create false reuse or a mixed generation; a successful executable link still
reruns when required. One workdir serves one invocation at a time and may hold
the complete command-global action universe for all selected roots and isolated
variants; semantic identity, rather than request shape, controls reuse. Both
driver stages implement the identical contract. This is build staleness in the
Make/mk/Go sense, not a result cache:
tests always run, and the byte-identity and bootstrap gates keep building on
fresh scratch. `make clean` reclaims every workdir under `out/`.

View File

@@ -73,7 +73,7 @@ fn checkpartialflush(zero: bool) void = {
sink.zero = zero;
let rb: [1]u8;
let wb: [4]u8;
let b: bufio.stream = bufio.init(&sink.vt, rb[0:1], wb[0:4]);
let b: bufio.stream = bufio.newstream(&sink.vt, rb[0:1], wb[0:4]);
let src: [4]u8;
let _: i32 = sputstr("ABCD", src[0:4], 0);
let wr: (size | io.error) = io.write(&b.vt, src[0:4]);
@@ -109,7 +109,7 @@ fn checkpartialflush(zero: bool) void = {
let rb: [8]u8;
let wb: [8]u8;
let b: bufio.stream = bufio.init(m, rb[0:8], wb[0:8]);
let b: bufio.stream = bufio.newstream(m, rb[0:8], wb[0:8]);
// Write under the buffer limit — nothing reaches src.
let buf: [5]u8;
@@ -139,7 +139,7 @@ fn checkpartialflush(zero: bool) void = {
let rb: [4]u8;
let wb: [4]u8;
let b: bufio.stream = bufio.init(m, rb[0:4], wb[0:4]);
let b: bufio.stream = bufio.newstream(m, rb[0:4], wb[0:4]);
// 10B payload through a 4B wbuf: bwrite must flush twice mid-write.
let buf: [10]u8;
@@ -170,7 +170,7 @@ fn checkpartialflush(zero: bool) void = {
let rb: [4]u8;
let wb: [16]u8;
let b: bufio.stream = bufio.init(m, rb[0:4], wb[0:16]);
let b: bufio.stream = bufio.newstream(m, rb[0:4], wb[0:16]);
let p: io.stream = &b.vt;
@@ -204,7 +204,7 @@ fn checkpartialflush(zero: bool) void = {
let rb: [4]u8;
let wb: [16]u8;
let b: bufio.stream = bufio.init(m, rb[0:4], wb[0:16]);
let b: bufio.stream = bufio.newstream(m, rb[0:4], wb[0:16]);
let nilbs: [1]u8;
bufio.setflush(&b, nilbs[0:0]);
@@ -245,7 +245,7 @@ fn checkpartialflush(zero: bool) void = {
let rb: [4]u8;
let wb: [4]u8;
let b: bufio.stream = bufio.init(m, rb[0:4], wb[0:4]);
let b: bufio.stream = bufio.newstream(m, rb[0:4], wb[0:4]);
assert(!(!bufio.isbuffered(&b.vt)));
assert(!(bufio.isbuffered(m)));
@@ -264,7 +264,7 @@ fn checkpartialflush(zero: bool) void = {
let rb: [8]u8;
let wb: [4]u8;
let b: bufio.stream = bufio.init(m, rb[0:8], wb[0:4]);
let b: bufio.stream = bufio.newstream(m, rb[0:8], wb[0:4]);
let p: io.stream = &b.vt;
@@ -303,7 +303,7 @@ fn checkpartialflush(zero: bool) void = {
let rb: [8]u8;
let wb: [4]u8;
let b: bufio.stream = bufio.init(m, rb[0:8], wb[0:4]);
let b: bufio.stream = bufio.newstream(m, rb[0:8], wb[0:4]);
let p: io.stream = &b.vt;
@@ -339,7 +339,7 @@ fn checkpartialflush(zero: bool) void = {
let rb: [8]u8;
let wb: [4]u8;
let b: bufio.stream = bufio.init(m, rb[0:8], wb[0:4]);
let b: bufio.stream = bufio.newstream(m, rb[0:8], wb[0:4]);
let p: io.stream = &b.vt;
@@ -385,7 +385,7 @@ fn checkpartialflush(zero: bool) void = {
let rb: [4]u8;
let _: i32 = sputstr("ABCD", rb[0:4], 0);
let wb: [1]u8;
let b: bufio.stream = bufio.init(&mem.vt, rb[0:4], wb[0:1]);
let b: bufio.stream = bufio.newstream(&mem.vt, rb[0:4], wb[0:1]);
bufio.unread(&b, rb[0:3]);
let out: [3]u8;
let r: (size | io.eof | io.error) = io.read(&b.vt, out[0:3]);
@@ -406,7 +406,7 @@ fn checkpartialflush(zero: bool) void = {
let rb: [16]u8;
let wb: [4]u8;
let b: bufio.stream = bufio.init(m, rb[0:16], wb[0:4]);
let b: bufio.stream = bufio.newstream(m, rb[0:16], wb[0:4]);
let push: [3]u8;
push[0] = 88u8; push[1] = 89u8; push[2] = 90u8; // "XYZ"
@@ -437,7 +437,7 @@ fn checkpartialflush(zero: bool) void = {
let rb: [4]u8;
let wb: [4]u8;
let b: bufio.stream = bufio.init(m, rb[0:4], wb[0:4]);
let b: bufio.stream = bufio.newstream(m, rb[0:4], wb[0:4]);
let r: (void | io.error) = bufio.flush(&b);
match (r) {
@@ -454,7 +454,7 @@ fn checkpartialflush(zero: bool) void = {
let rb: [4]u8;
let wb: [4]u8;
let b: bufio.stream = bufio.init(m, rb[0:4], wb[0:4]);
let b: bufio.stream = bufio.newstream(m, rb[0:4], wb[0:4]);
let nilbs: [1]u8;
bufio.setflush(&b, nilbs[0:0]); // suppress the default '\n' detector
@@ -490,7 +490,7 @@ fn checkpartialflush(zero: bool) void = {
let rb: [4]u8;
let wb: [4]u8;
let b: bufio.stream = bufio.init(src, rb[0:4], wb[0:4]);
let b: bufio.stream = bufio.newstream(src, rb[0:4], wb[0:4]);
let nilbs: [1]u8;
bufio.setflush(&b, nilbs[0:0]); // suppress the default '\n' detector
@@ -524,7 +524,7 @@ fn checkpartialflush(zero: bool) void = {
let rb: [4]u8;
let wb: [16]u8;
let b: bufio.stream = bufio.init(m, rb[0:4], wb[0:16]);
let b: bufio.stream = bufio.newstream(m, rb[0:4], wb[0:16]);
let bs: [1]u8;
bs[0] = 32u8; // ' '
bufio.setflush(&b, bs[0:1]);

View File

@@ -4,7 +4,7 @@ import math.random;
import test;
@test fn seq() void = {
let r: random.random = random.init(1234567u64);
let r: random.random = random.fromseed(1234567u64);
assert(!(random.next(&r) != 6457827717110365317u64));
assert(!(random.next(&r) != 3203168211198807973u64));
assert(!(random.next(&r) != 9817491932198370423u64));
@@ -13,8 +13,8 @@ import test;
};
@test fn deterministic() void = {
let a: random.random = random.init(42u64);
let b: random.random = random.init(42u64);
let a: random.random = random.fromseed(42u64);
let b: random.random = random.fromseed(42u64);
let i: i32 = 0;
for (i < 32) {
assert(!(random.next(&a) != random.next(&b)));
@@ -23,7 +23,7 @@ import test;
};
@test fn u32n_inrange() void = {
let r: random.random = random.init(7u64);
let r: random.random = random.fromseed(7u64);
let i: i32 = 0;
for (i < 200) {
let v: u32 = random.u32n(&r, 17u32);
@@ -35,7 +35,7 @@ import test;
};
@test fn u64n_pow2() void = {
let r: random.random = random.init(99u64);
let r: random.random = random.fromseed(99u64);
let i: i32 = 0;
for (i < 200) {
let v: u64 = random.u64n(&r, 16u64);
@@ -45,7 +45,7 @@ import test;
};
@test fn u64n_nonpow2() void = {
let r: random.random = random.init(123u64);
let r: random.random = random.fromseed(123u64);
let i: i32 = 0;
for (i < 200) {
let v: u64 = random.u64n(&r, 100u64);
@@ -60,12 +60,12 @@ import test;
@test fn u32n_zero_aborts() void = {
test.expectabort();
let r: random.random = random.init(1u64);
let r: random.random = random.fromseed(1u64);
random.u32n(&r, 0u32);
};
@test fn u64n_zero_aborts() void = {
test.expectabort();
let r: random.random = random.init(1u64);
let r: random.random = random.fromseed(1u64);
random.u64n(&r, 0u64);
};

View File

@@ -75,31 +75,6 @@ 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\""];
@@ -127,9 +102,11 @@ fn rejectrow(label: str, src: str, needle: str) void = {
"package main;\n",
"import stable acme.codec;\n",
"fn main() i32 = { return 0; };\n"), aliased);
rejectrow("blank-alias", strings.concat(
// A blank N_USE deliberately has no `.str` qualifier, so astprint's
// `(use` line has no quoted binding while remaining a real use node.
let blank: []str = ["(use\n"];
astrow("blank-import", strings.concat(
"package main;\n",
"import _ acme.codec;\n",
"fn main() i32 = { return 0; };\n"),
"blank import alias _ is not implemented");
"fn main() i32 = { return 0; };\n"), blank);
};

View File

@@ -1157,12 +1157,16 @@ fn hexbytes(value: str) str = {
assert(!has(externalproduction, "//ww:module "));
assert(!has(externalproduction, "SAME_TEST_SOURCE"));
assert(!has(externalproduction, "EXTERNAL_TEST_SOURCE"));
assert(same(samemainunit, strings.concat(
assert(strings.hasprefix(samemainunit, strings.concat(
"//ww:module-reset __wwtestmain.pkg.internal.main\n",
"package main;\nimport pkg;\nimport test;\n")));
assert(same(externalmainunit, strings.concat(
assert(strings.hasprefix(externalmainunit, strings.concat(
"//ww:module-reset __wwtestmain.pkg_test.external.main\n",
"package main;\nimport pkg_test;\nimport test;\n")));
assert(has(samemainunit, "//ww:direct-export pkg "));
assert(has(samemainunit, "//ww:direct-export test "));
assert(has(externalmainunit, "//ww:direct-export pkg_test "));
assert(has(externalmainunit, "//ww:direct-export test "));
assert(!has(samemainunit, "PACKAGE_PRODUCTION"));
assert(!has(samemainunit, "SAME_TEST_SOURCE"));
assert(!has(externalmainunit, "EXTERNAL_TEST_SOURCE"));
@@ -1221,13 +1225,15 @@ fn hexbytes(value: str) str = {
let productioncompile: str = linecontaining(ctrace,
"/pkg.unit.ww");
assert(same(samecompile, strings.concat(
"--test-package -c --import __same ",
"--test-package --package-init-symbol ",
"__ww..pkg.p.pkg.v1.r0.init -c --import __same ",
sharedwork, "__same.wwi --import api ", sharedwork,
"api.wwi -I ", sharedwork, "pkg-internal-test.wwi -o ",
sharedwork, "pkg-internal-test.s ", sharedwork,
"pkg-internal-test.unit.ww")));
assert(same(externalcompile, strings.concat(
"--test-package -c --import __external ",
"--test-package --package-init-symbol ",
"__ww..pkg.p.pkg_test.v2.r0.init -c --import __external ",
sharedwork, "__external.wwi --import pkg ", sharedwork,
"pkg.wwi -I ", sharedwork,
"pkg_test-external-test.wwi -o ", sharedwork,
@@ -1239,7 +1245,9 @@ fn hexbytes(value: str) str = {
"pkg_test-external-test-main.unit.ww");
assert(same(samemaincompile, strings.concat(
"-T --entry --test-support-module test ",
"--test-target-package pkg -c --import pkg ",
"--test-target-package pkg --package-init-symbol ",
"__ww..pkg.p.__wwtestmain.pkg.internal.main.v3.r2.init ",
"--init-dispatch-symbol __ww..dispatch -c --import pkg ",
sharedwork, "pkg-internal-test.wwi --import test ",
sharedwork, "test.wwi -I ", sharedwork,
"pkg-internal-test-main.wwi -o ", sharedwork,
@@ -1247,22 +1255,25 @@ fn hexbytes(value: str) str = {
"pkg-internal-test-main.unit.ww")));
assert(same(externalmaincompile, strings.concat(
"-T --entry --test-support-module test ",
"--test-target-package pkg_test -c --import pkg_test ",
"--test-target-package pkg_test --package-init-symbol ",
"__ww..pkg.p.__wwtestmain.pkg_test.external.main.v3.r2.init ",
"--init-dispatch-symbol __ww..dispatch -c --import pkg_test ",
sharedwork, "pkg_test-external-test.wwi --import test ",
sharedwork, "test.wwi -I ", sharedwork,
"pkg_test-external-test-main.wwi -o ", sharedwork,
"pkg_test-external-test-main.s ", sharedwork,
"pkg_test-external-test-main.unit.ww")));
assert(same(productioncompile, strings.concat(
"--package-init-symbol __ww..pkg.p.pkg.v0.r0.init ",
"-c --import api ", sharedwork, "api.wwi -I ", sharedwork,
"pkg.wwi -o ", sharedwork,
"pkg.s ", sharedwork, "pkg.unit.ww")));
let ltrace: str = readfile(linkertrace);
assert(occurrences(ltrace, "\n") == 2);
let samelink: str = linecontaining(ltrace,
strings.concat("-o ", samebin, " "));
strings.concat("-o ", samebin, ".new "));
let externallink: str = linecontaining(ltrace,
strings.concat("-o ", externalbin, " "));
strings.concat("-o ", externalbin, ".new "));
assert(has(samelink, strings.concat(sharedwork, "api.a")));
assert(has(samelink,
strings.concat(sharedwork, "implementation.a")));
@@ -1848,7 +1859,7 @@ fn hexbytes(value: str) str = {
ri = 0;
for (ri < bins.len) {
let link: str = linecontaining(ltrace,
strings.concat("-o ", bins[ri], " "));
strings.concat("-o ", bins[ri], ".new "));
assert(has(link, strings.concat(sharedwork, mainkeys[ri], ".a")));
assert(has(link, strings.concat(sharedwork, rootkeys[ri], ".a")));
assert(!has(link, ".wwi"));
@@ -2403,13 +2414,13 @@ fn hexbytes(value: str) str = {
let links: str = readfile(linktrace);
assert(occurrences(links, "\n") == 4);
let namedsame: str = linecontaining(links,
strings.concat("-o ", bins[0], " "));
strings.concat("-o ", bins[0], ".new "));
let namedexternal: str = linecontaining(links,
strings.concat("-o ", bins[1], " "));
strings.concat("-o ", bins[1], ".new "));
let consumersame: str = linecontaining(links,
strings.concat("-o ", bins[2], " "));
strings.concat("-o ", bins[2], ".new "));
let consumerexternal: str = linecontaining(links,
strings.concat("-o ", bins[3], " "));
strings.concat("-o ", bins[3], ".new "));
assert(has(namedsame, strings.concat(work, "__wwtest.a")));
assert(!has(namedsame, strings.concat(work, "test.a")));
assert(has(namedexternal, strings.concat(work, "test.a")));
@@ -2595,19 +2606,22 @@ fn hexbytes(value: str) str = {
let atrace: str = readfile(assemblertraces[si]);
let ltrace: str = readfile(linkertraces[si]);
assert(occurrences(ctrace, "\n") == packagecount + 1);
assert(occurrences(atrace, "\n") == packagecount + 1);
assert(occurrences(atrace, "\n") == packagecount + 2);
assert(occurrences(ltrace, "\n") == 1);
let rootline: str = linecontaining(ctrace,
strings.concat("/", rootaction, ".unit.new"));
assert(occurrences(rootline, "<--import>") == packagecount);
let expectedroot: str = "BEGIN<--entry><-c>";
let expectedroot: str = strings.concat(
"BEGIN<--entry><--package-init-symbol><__ww..pkg.p.",
rootaction, ".v0.r0.init><--init-dispatch-symbol>",
"<__ww..dispatch><-c>");
i = 0;
for (i < packagecount) {
let name: str = boundarypkgname(i);
expectedroot = strings.concat(expectedroot, "<--import><", name,
"><", works[si], "/", name, ".wwi>");
"><", works[si], "/", name, ".wwi.new>");
assert(occurrences(rootline, strings.concat("<", name, "><",
works[si], "/", name, ".wwi>")) == 1);
works[si], "/", name, ".wwi.new>")) == 1);
i += 1;
};
expectedroot = strings.concat(expectedroot, "<-I><", works[si], "/",
@@ -2617,12 +2631,14 @@ fn hexbytes(value: str) str = {
i = 0;
for (i < packagecount) {
let name: str = boundarypkgname(i);
let expectedcompile: str = "BEGIN<-c>";
let expectedcompile: str = strings.concat(
"BEGIN<--package-init-symbol><__ww..pkg.p.", name,
".v0.r0.init><-c>");
if (i + 1 < packagecount) {
let next: str = boundarypkgname(i + 1);
expectedcompile = strings.concat(expectedcompile,
"<--import><", next, "><", works[si], "/", next,
".wwi>");
".wwi.new>");
};
expectedcompile = strings.concat(expectedcompile, "<-I><",
works[si], "/", name, ".wwi.new><-o><", works[si], "/",
@@ -2634,7 +2650,7 @@ fn hexbytes(value: str) str = {
assert(same(linecontaining(atrace,
strings.concat("/", name, ".o.new")), expectedassemble));
assert(occurrences(ltrace, strings.concat("<", works[si], "/",
name, ".a>")) == 1);
name, ".a.new>")) == 1);
assert(os.exists(strings.concat(works[si], "/", name, ".wwi")));
assert(os.exists(strings.concat(works[si], "/", name, ".s")));
assert(os.exists(strings.concat(works[si], "/", name, ".o")));
@@ -2652,35 +2668,47 @@ fn hexbytes(value: str) str = {
};
let z: str = strings.concat("package ", name, ";\n// OWNER_Z_",
name, "\nfn private_value() i32 = { return value(); };\n");
assert(same(readfile(strings.concat(works[si], "/", name,
".unit.ww")), strings.concat("//ww:module-reset ", name,
"\n", a, "\n//ww:module-reset ", name, "\n", z, "\n")));
let unit: str = readfile(strings.concat(works[si], "/", name,
".unit.ww"));
assert(strings.hasprefix(unit, strings.concat("//ww:module-reset ",
name, "\n", a, "\n//ww:module-reset ", name, "\n", z, "\n")));
if (i + 1 < packagecount) {
assert(has(unit, strings.concat("//ww:direct-export ",
boundarypkgname(i + 1), " ")));
};
assert(strings.hasprefix(readfile(strings.concat(works[si], "/",
name, ".wwi")), strings.concat("//ww:module ", name, "\n")));
i += 1;
};
assert(occurrences(ltrace, strings.concat("<", works[si], "/",
rootaction, ".a>")) == 1);
rootaction, ".a.new>")) == 1);
assert(!has(ltrace, ".wwi>"));
let expectedlink: str = strings.concat("BEGIN<-o><", bins[si], "><",
works[si], "/", rootaction, ".a>");
let expectedlink: str = strings.concat("BEGIN<-o><", bins[si], ".new><",
works[si], "/", rootaction, ".a.new>");
i = 0;
for (i < packagecount) {
expectedlink = strings.concat(expectedlink, "<", works[si], "/",
boundarypkgname(i), ".a>");
boundarypkgname(i), ".a.new>");
i += 1;
};
expectedlink = strings.concat(expectedlink, "<", repo(),
"/out/bin/../lib/libwwrt.a>\n");
assert(same(ltrace, expectedlink));
assert(same(readfile(strings.concat(works[si], "/", rootaction,
".unit.ww")),
let rootunit: str = readfile(strings.concat(works[si], "/", rootaction,
".unit.ww"));
assert(strings.hasprefix(rootunit,
strings.concat("//ww:module-reset target\n", roota,
"\n//ww:module-reset target\n", rootz, "\n")));
assert(occurrences(rootunit, "//ww:direct-export p") == packagecount);
let rootassemble: str = strings.concat("BEGIN<-o><", works[si], "/",
rootaction, ".o.new><", works[si], "/", rootaction, ".s.new>");
assert(same(linecontaining(atrace,
strings.concat("/", rootaction, ".o.new")), rootassemble));
let dispatchassemble: str = strings.concat("BEGIN<-o><", works[si],
"/", rootaction, ".init.o.new><", works[si], "/", rootaction,
".init.s.new>");
assert(same(linecontaining(atrace,
strings.concat("/", rootaction, ".init.o.new")), dispatchassemble));
// Repeating the same semantic request in the persistent work root may
// relink the requested binary, but must not compile or assemble any action.
@@ -2711,16 +2739,21 @@ fn hexbytes(value: str) str = {
let adelta: str = strings.sub(chainatrace, chainabefore, chainatrace.len);
let ldelta: str = strings.sub(chainltrace, chainlbefore, chainltrace.len);
assert(occurrences(cdelta, "\n") == 1);
assert(occurrences(adelta, "\n") == 1);
assert(occurrences(adelta, "\n") == 2);
assert(occurrences(ldelta, "\n") == 1);
assert(same(cdelta, strings.concat("BEGIN<--entry><-c><--import><p000><",
assert(same(cdelta, strings.concat(
"BEGIN<--entry><--package-init-symbol><__ww..pkg.p.",
chainaction, ".v0.r0.init><--init-dispatch-symbol>",
"<__ww..dispatch><-c><--import><p000><",
works[si], "/p000.wwi><-I><", works[si], "/", chainaction,
".wwi.new><-o><", works[si], "/", chainaction, ".s.new><",
works[si], "/", chainaction, ".unit.new>\n")));
assert(same(adelta, strings.concat("BEGIN<-o><", works[si], "/",
chainaction, ".o.new><", works[si], "/", chainaction, ".s.new>\n")));
chainaction, ".o.new><", works[si], "/", chainaction, ".s.new>\n",
"BEGIN<-o><", works[si], "/", chainaction, ".init.o.new><",
works[si], "/", chainaction, ".init.s.new>\n")));
let expectedchainlink: str = strings.concat("BEGIN<-o><", chainbins[si],
"><", works[si], "/", chainaction, ".a>");
".new><", works[si], "/", chainaction, ".a.new>");
i = 0;
for (i < packagecount) {
expectedchainlink = strings.concat(expectedchainlink, "<", works[si],
@@ -2731,9 +2764,11 @@ fn hexbytes(value: str) str = {
"/out/bin/../lib/libwwrt.a>\n");
assert(same(ldelta, expectedchainlink));
assert(!has(ldelta, ".wwi>"));
assert(same(readfile(strings.concat(works[si], "/", chainaction,
".unit.ww")), strings.concat("//ww:module-reset chain\n",
chainsource, "\n")));
let chainunit: str = readfile(strings.concat(works[si], "/", chainaction,
".unit.ww"));
assert(strings.hasprefix(chainunit, strings.concat(
"//ww:module-reset chain\n", chainsource, "\n")));
assert(has(chainunit, "//ww:direct-export p000 "));
let chainrun: []str = [chainbins[si]];
runcommand(root, strings.concat("boundary-chain-run-", stages[si]),
chainrun, (60i64 * (time.second: i64)): time.duration, &out);
@@ -3308,26 +3343,20 @@ fn hexbytes(value: str) str = {
runcommand(root, strings.concat("multidir-failure-", stages[i]), av,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stdout, "alpha_same_survives ... ok\n"));
assert(has(out.stdout, "beta_same_survives ... ok\n"));
assert(has(out.stdout, "beta_external_survives ... ok\n"));
assert(has(out.stdout, strings.concat("ok ", alpha,
" [alpha, same-package]\n")));
assert(has(out.stdout, strings.concat("ok ", beta,
" [beta, same-package]\n")));
assert(has(out.stdout, strings.concat("ok ", beta,
" [beta_test, external]\n")));
assert(!has(out.stdout, "alpha_external_fails ..."));
assert(out.stdout.len == 0);
assert(has(out.stderr, "has no exported declaration 'hidden'"));
assert(has(out.stderr, strings.concat("FAIL ", alpha,
" [alpha] (build exit 1)\n")));
assert(has(out.stderr, strings.concat("FAIL ", alpha,
" [alpha_test] (build exit 1)\n")));
assert(!has(out.stderr, strings.concat("FAIL ", alpha,
" [alpha]")));
assert(!has(out.stderr, strings.concat("FAIL ", beta, " [")));
let alphapos: i32 = pos(out.stdout, strings.concat("ok ", alpha,
" [alpha, same-package]\n"));
let betapos: i32 = pos(out.stdout, strings.concat("ok ", beta,
" [beta, same-package]\n"));
assert(has(out.stderr, strings.concat("FAIL ", beta,
" [beta] (build exit 1)\n")));
assert(has(out.stderr, strings.concat("FAIL ", beta,
" [beta_test] (build exit 1)\n")));
let alphapos: i32 = pos(out.stderr, strings.concat("FAIL ", alpha,
" [alpha] (build exit 1)\n"));
let betapos: i32 = pos(out.stderr, strings.concat("FAIL ", beta,
" [beta] (build exit 1)\n"));
assert(alphapos >= 0 && alphapos < betapos);
diagnostics[i] = strings.dup(primarydiagnostic(out.stderr));
if (i == 0) { referenceout = strings.dup(out.stdout); }
@@ -3338,8 +3367,8 @@ fn hexbytes(value: str) str = {
clean(root);
};
// One failing root is a product failure, not permission for the shared build
// request to suppress an independently compilable sibling root.
// Roots remain independently diagnosed and may be staged in parallel, but one
// failing product rejects publication and execution of the entire request.
@test fn sibling_test_variant_failures_are_isolated() void = {
let root: str = fresh();
let badexternal: str = strings.concat(root, "/badexternal");
@@ -3380,15 +3409,15 @@ fn hexbytes(value: str) str = {
runcommand(root, strings.concat("isolated-external-", tags[i]),
externalav, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stdout, "valid_same_runs ... ok\n"));
assert(has(out.stdout, "[badexternal, same-package]\n"));
assert(out.stdout.len == 0);
assert(has(out.stderr,
"has no exported declaration 'hidden'"));
assert(has(out.stderr,
strings.concat("FAIL ", badexternal,
" [badexternal] (build exit 1)\n")));
assert(has(out.stderr,
strings.concat("FAIL ", badexternal,
" [badexternal_test] (build exit 1)\n")));
assert(!has(out.stderr, strings.concat("FAIL ", badexternal,
" [badexternal]")));
externaldiagnostics[i] = strings.dup(primarydiagnostic(out.stderr));
let sameav: []str = [driver(stages[i]), "test", "-I", root,
@@ -3396,23 +3425,20 @@ fn hexbytes(value: str) str = {
runcommand(root, strings.concat("isolated-same-", tags[i]), sameav,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stdout, "valid_external_runs ... ok\n"));
assert(has(out.stdout, "[badsame_test, external]\n"));
assert(out.stdout.len == 0);
assert(has(out.stderr, "undefined: missing"));
assert(has(out.stderr, strings.concat("FAIL ", badsame,
" [badsame] (build exit 1)\n")));
assert(!has(out.stderr, strings.concat("FAIL ", badsame,
" [badsame_test]")));
assert(has(out.stderr, strings.concat("FAIL ", badsame,
" [badsame_test] (build exit 1)\n")));
samediagnostics[i] = strings.dup(primarydiagnostic(out.stderr));
i += 1;
};
assert(same(externaldiagnostics[0], externaldiagnostics[1]));
assert(same(samediagnostics[0], samediagnostics[1]));
// A first persistent union pass may contain a root-local compiler failure.
// Its successfully committed actions still become reusable: only the
// uncommitted failing root is compiled again on the next request in both
// driver stages.
// A persistent request is one publication transaction. A rejection leaves
// no reusable generation, so every staged action is reconsidered next time.
let warmroot: str = strings.concat(root, "/warm-partial");
assert(os.mkdir(warmroot, 448i32) == 0);
let trace: str = strings.concat(root, "/warm-partial.trace");
@@ -3452,9 +3478,14 @@ fn hexbytes(value: str) str = {
expectexit(&out, 1);
let secondtrace: str = readfile(trace);
assert(occurrences(secondtrace,
"/badexternal-internal-test.unit.new") == 1);
"/badexternal-internal-test.unit.new") == 2);
assert(occurrences(secondtrace,
"/badexternal_test-external-test.unit.new") == 2);
assert(!os.exists(strings.concat(warmroot,
"/badexternal-internal-test.unit.ww")));
assert(!os.exists(strings.concat(warmroot,
"/badexternal_test-external-test.unit.ww")));
assert(!os.exists(strings.concat(warmroot, "/.wwtool.stamp")));
let wwtracebin: str = strings.concat(repo(), "/out/partial-trace-",
workescape(root));
@@ -3502,9 +3533,14 @@ fn hexbytes(value: str) str = {
expectexit(&out, 1);
let wwsecond: str = readfile(wwtrace);
assert(occurrences(wwsecond,
"/badexternal-internal-test.unit.new") == 1);
"/badexternal-internal-test.unit.new") == 2);
assert(occurrences(wwsecond,
"/badexternal_test-external-test.unit.new") == 2);
assert(!os.exists(strings.concat(wwwarmroot,
"/badexternal-internal-test.unit.ww")));
assert(!os.exists(strings.concat(wwwarmroot,
"/badexternal_test-external-test.unit.ww")));
assert(!os.exists(strings.concat(wwwarmroot, "/.wwtool.stamp")));
clean(wwtracebin);
clean(root);
};
@@ -3850,8 +3886,10 @@ fn hexbytes(value: str) str = {
let wantsecret: str = strings.concat(
"//ww:module-reset domain.internal.secret\n", secreta,
"\n//ww:module-reset domain.internal.secret\n", secretz, "\n");
assert(same(readfile(strings.concat(works[si],
"/domain.internal.secret.unit.ww")), wantsecret));
let secretunit: str = readfile(strings.concat(works[si],
"/domain.internal.secret.unit.ww"));
assert(strings.hasprefix(secretunit, wantsecret));
assert(has(secretunit, "//ww:direct-export domain.leaf "));
let ai: i32 = 0;
for (ai < artifacts.len) {
let xi: i32 = 0;
@@ -3869,8 +3907,13 @@ fn hexbytes(value: str) str = {
let atrace: str = readfile(assemblertraces[si]);
let ltrace: str = readfile(linkertraces[si]);
assert(occurrences(ctrace, "\n") == 3);
assert(occurrences(atrace, "\n") == 3);
assert(occurrences(atrace, "\n") == 4);
assert(occurrences(ltrace, "\n") == 1);
let dispatchline: str = strings.concat("BEGIN<-o><", works[si],
"/domain.client.init.o.new><", works[si],
"/domain.client.init.s.new>");
assert(same(linecontaining(atrace, "/domain.client.init.o.new"),
dispatchline));
let secretline: str = linecontaining(ctrace,
"domain.internal.secret.unit.new");
assert(occurrences(secretline, "domain.leaf.wwi") == 1);
@@ -4420,7 +4463,16 @@ fn hexbytes(value: str) str = {
for (ui < unitartifacts.len) {
let unit: str = readfile(strings.concat(work, unitartifacts[ui],
".unit.ww"));
assert(same(unit, wantunits[ui]));
assert(strings.hasprefix(unit, wantunits[ui]));
if (ui == 1 || ui == 2) {
assert(has(unit, strings.concat("//ww:direct-export ",
baseidentity, " ")));
} else { if (ui == 3) {
assert(has(unit, strings.concat("//ww:direct-export ",
leftidentity, " ")));
assert(has(unit, strings.concat("//ww:direct-export ",
rightidentity, " ")));
}; };
assert(!has(unit, "//ww:module "));
if (si == 0) { referenceunits[ui] = strings.dup(unit); }
else { assert(same(referenceunits[ui], unit)); };
@@ -4458,23 +4510,32 @@ fn hexbytes(value: str) str = {
let atrace: str = readfile(assemblertrace);
let ltrace: str = readfile(linkertrace);
assert(occurrences(ctrace, "\n") == 4);
let baseline: str = strings.concat("BEGIN<-c><-I><", work,
let baseline: str = strings.concat(
"BEGIN<--package-init-symbol><__ww..pkg.p.", baseidentity,
".v0.r0.init><-c><-I><", work,
artifacts[0], ".wwi><-o><", work,
artifacts[0], ".s><", work,
artifacts[0], ".unit.ww>");
let leftline: str = strings.concat("BEGIN<-c><--import>",
let leftline: str = strings.concat(
"BEGIN<--package-init-symbol><__ww..pkg.p.", leftidentity,
".v0.r0.init><-c><--import>",
"<", baseidentity, "><", work,
artifacts[0], ".wwi><-I><", work,
artifacts[1], ".wwi><-o><", work,
artifacts[1], ".s><", work,
artifacts[1], ".unit.ww>");
let rightline: str = strings.concat("BEGIN<-c><--import>",
let rightline: str = strings.concat(
"BEGIN<--package-init-symbol><__ww..pkg.p.", rightidentity,
".v0.r0.init><-c><--import>",
"<", baseidentity, "><", work,
artifacts[0], ".wwi><-I><", work,
artifacts[2], ".wwi><-o><", work,
artifacts[2], ".s><", work,
artifacts[2], ".unit.ww>");
let rootline: str = strings.concat("BEGIN<--entry><-c><--import>",
let rootline: str = strings.concat(
"BEGIN<--entry><--package-init-symbol><__ww..pkg.p.",
rootidentity, ".v0.r0.init><--init-dispatch-symbol>",
"<__ww..dispatch><-c><--import>",
"<", leftidentity, "><", work,
artifacts[1], ".wwi><--import>",
"<", rightidentity, "><", work,
@@ -4520,7 +4581,7 @@ fn hexbytes(value: str) str = {
expectexit(&out, 1);
assert(same(out.stderr, strings.concat("w6c: import ", leftidentity,
": export owner mismatch in ", work, artifacts[0], ".wwi\n")));
assert(occurrences(atrace, "\n") == 4);
assert(occurrences(atrace, "\n") == 5);
ai = 0;
for (ai < artifacts.len) {
let assemblerline: str = strings.concat("BEGIN<-o><", work,
@@ -4529,8 +4590,12 @@ fn hexbytes(value: str) str = {
artifacts[ai], ".o")), assemblerline));
ai += 1;
};
let dispatcherline: str = strings.concat("BEGIN<-o><", work,
artifacts[3], ".init.o><", work, artifacts[3], ".init.s>");
assert(same(linecontaining(atrace, strings.concat(work,
artifacts[3], ".init.o")), dispatcherline));
assert(occurrences(ltrace, "\n") == 1);
assert(has(ltrace, strings.concat("BEGIN<-o><", bin, "><", work,
assert(has(ltrace, strings.concat("BEGIN<-o><", bin, ".new><", work,
artifacts[3], ".a>")));
ai = 0;
for (ai < artifacts.len) {
@@ -4842,17 +4907,17 @@ fn hexbytes(value: str) str = {
let externalline: str = linecontaining(ctrace,
strings.concat(artifacts[2], ".unit.new"));
assert(has(externalline, strings.concat("<--import><", commandid,
"><", work, artifacts[0], ".wwi>")));
"><", work, artifacts[0], ".wwi.new>")));
let internalmainline: str = linecontaining(ctrace,
strings.concat(artifacts[3], ".unit.new"));
let externalmainline: str = linecontaining(ctrace,
strings.concat(artifacts[4], ".unit.new"));
assert(has(internalmainline, strings.concat("<--import><", internalid,
"><", work, artifacts[1], ".wwi>")));
"><", work, artifacts[1], ".wwi.new>")));
assert(has(internalmainline, strings.concat(
"<--test-target-package><", internalid, ">")));
assert(has(externalmainline, strings.concat("<--import><", externalid,
"><", work, artifacts[2], ".wwi>")));
"><", work, artifacts[2], ".wwi.new>")));
assert(has(externalmainline, strings.concat(
"<--test-target-package><", externalid, ">")));
assert(occurrences(ctrace, strings.concat("<", work, artifacts[0],
@@ -5470,27 +5535,37 @@ fn hexbytes(value: str) str = {
let commandatrace: str = readfile(commandassemblertraces[commandstage]);
let commandltrace: str = readfile(commandlinkertraces[commandstage]);
assert(occurrences(commandctrace, "\n") == 3);
assert(occurrences(commandatrace, "\n") == 3);
assert(occurrences(commandatrace, "\n") == 5);
assert(occurrences(commandltrace, "\n") == 2);
let commandalphacompile: str = linecontaining(commandctrace,
strings.concat(commandalphaid, ".unit.new"));
assert(occurrences(commandalphacompile, strings.concat(
"<--import><library><", commandwork, "library.wwi>")) == 1);
"<--import><library><", commandwork, "library.wwi.new>")) == 1);
assert(!has(commandalphacompile, "cmd-zeta.wwi"));
let alphaassembly: str = strings.concat("BEGIN<-o><", commandwork,
commandalphaid, ".init.o.new><", commandwork, commandalphaid,
".init.s.new>");
let zetaassembly: str = strings.concat("BEGIN<-o><", commandwork,
commandzetaid, ".init.o.new><", commandwork, commandzetaid,
".init.s.new>");
assert(same(linecontaining(commandatrace, strings.concat(commandalphaid,
".init.o.new")), alphaassembly));
assert(same(linecontaining(commandatrace, strings.concat(commandzetaid,
".init.o.new")), zetaassembly));
let commandalphalink: str = linecontaining(commandltrace,
strings.concat("<-o><", commandbins[commandstage], "/cmd-alpha>"));
strings.concat("<-o><", commandbins[commandstage], "/cmd-alpha.new>"));
let commandzetalink: str = linecontaining(commandltrace,
strings.concat("<-o><", commandbins[commandstage], "/cmd-zeta>"));
strings.concat("<-o><", commandbins[commandstage], "/cmd-zeta.new>"));
assert(occurrences(commandalphalink, strings.concat("<", commandwork,
commandalphaid, ".a>")) == 1);
commandalphaid, ".a.new>")) == 1);
assert(occurrences(commandalphalink, strings.concat("<", commandwork,
"library.a>")) == 1);
"library.a.new>")) == 1);
assert(!has(commandalphalink, strings.concat(commandzetaid, ".a")));
assert(!has(commandalphalink, ".wwi>"));
assert(has(commandalphalink, strings.concat("<-L><", repo(),
"/out/lib><-l><wcc>")));
assert(has(commandzetalink, strings.concat("<", commandwork,
commandzetaid, ".a>")));
commandzetaid, ".a.new>")));
assert(!has(commandzetalink, "library.a"));
assert(!has(commandzetalink, ".wwi>"));
let normalizedcommandcompiler: str = normalizedtrace(commandctrace,
@@ -7276,8 +7351,10 @@ fn runtimepath(relative: str) str = {
"//ww:module-reset ", expanded, "\n", neara, "\n",
"//ww:module-reset ", expanded, "\n", nearz, "\n",
"//ww:vendor-dir ", hexbytes(nearest), "\n");
assert(same(readfile(strings.concat(works[si], "/", expanded,
".unit.ww")), wantnear));
let nearunit: str = readfile(strings.concat(works[si], "/", expanded,
".unit.ww"));
assert(strings.hasprefix(nearunit, wantnear));
assert(has(nearunit, "//ww:direct-export lib.leaf "));
let clientunit: str = readfile(strings.concat(works[si],
"/domain.app.client.unit.ww"));
assert(has(clientunit, strings.concat("//ww:import-map lib.math ",
@@ -7299,8 +7376,14 @@ fn runtimepath(relative: str) str = {
let atrace: str = readfile(assemblertraces[si]);
let ltrace: str = readfile(linkertraces[si]);
assert(occurrences(ctrace, "\n") == 3);
assert(occurrences(atrace, "\n") == 3);
assert(occurrences(atrace, "\n") == 4);
assert(occurrences(ltrace, "\n") == 1);
assert(has(ltrace, strings.concat("BEGIN<-o><", bins[si], ".new>")));
let dispatchline: str = strings.concat("BEGIN<-o><", works[si],
"/domain.app.client.init.o.new><", works[si],
"/domain.app.client.init.s.new>");
assert(same(linecontaining(atrace,
"/domain.app.client.init.o.new"), dispatchline));
let nearline: str = linecontaining(ctrace,
strings.concat(expanded, ".unit.new"));
assert(occurrences(nearline, "<--import><lib.leaf>") == 1);
@@ -7308,16 +7391,16 @@ fn runtimepath(relative: str) str = {
let clientline: str = linecontaining(ctrace,
"domain.app.client.unit.new");
assert(occurrences(clientline, strings.concat("<--import><", expanded,
"><", works[si], "/", expanded, ".wwi>")) == 1);
"><", works[si], "/", expanded, ".wwi.new>")) == 1);
assert(occurrences(clientline, strings.concat(
"<--import-map><lib.math><", expanded, ">")) == 1);
assert(!has(clientline, "lib.leaf.wwi"));
assert(occurrences(ltrace, strings.concat("<", works[si],
"/domain.app.client.a>")) == 1);
"/domain.app.client.a.new>")) == 1);
assert(occurrences(ltrace, strings.concat("<", works[si], "/",
expanded, ".a>")) == 1);
expanded, ".a.new>")) == 1);
assert(occurrences(ltrace, strings.concat("<", works[si],
"/lib.leaf.a>")) == 1);
"/lib.leaf.a.new>")) == 1);
assert(!has(ltrace, ".wwi>"));
let nc: str = normalizedtrace(ctrace,
strings.concat(works[si], "/"), bins[si]);
@@ -7732,9 +7815,9 @@ fn runtimepath(relative: str) str = {
let externalline: str = linecontaining(testtrace,
"domain.app.testclient_test-external-test.unit.new");
assert(occurrences(internalline, strings.concat("<--import><", expanded,
"><", testwork, expanded, ".wwi>")) == 1);
"><", testwork, expanded, ".wwi.new>")) == 1);
assert(occurrences(externalline, strings.concat("<--import><", expanded,
"><", testwork, expanded, ".wwi>")) == 1);
"><", testwork, expanded, ".wwi.new>")) == 1);
assert(occurrences(internalline, strings.concat(
"<--import-map><lib.math><", expanded, ">")) == 1);
assert(occurrences(externalline, strings.concat(
@@ -8029,15 +8112,7 @@ fn runtimepath(relative: str) str = {
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_NAME_COMPILER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_NAME_COMPILER_TRACE\"\n",
"\"$WW_NAME_REAL_COMPILER\" \"$@\"\n",
"status=$?\n",
"if [ \"$status\" -eq 0 ] && ",
"[ -n \"$WW_COMMIT_BLOCK_STAMP\" ]; then\n",
" mv -- \"$WW_COMMIT_BLOCK_STAMP\" ",
"\"$WW_COMMIT_BLOCK_STAMP.saved\" || exit 97\n",
" mkdir -- \"$WW_COMMIT_BLOCK_STAMP\" || exit 98\n",
"fi\n",
"exit \"$status\"\n"));
"exec \"$WW_NAME_REAL_COMPILER\" \"$@\"\n"));
let stages: []str = ["ww", "ww_ww"];
let compilers: []str = ["w6c", "w6c_ww"];
@@ -8059,8 +8134,6 @@ fn runtimepath(relative: str) str = {
strings.concat(root, "/ww-commit-work")];
let commitcoldbins: []str = [strings.concat(root, "/c-commit-cold"),
strings.concat(root, "/ww-commit-cold")];
let commitblockbins: []str = [strings.concat(root, "/c-commit-block"),
strings.concat(root, "/ww-commit-block")];
let commitrejectbins: []str = [strings.concat(root, "/c-commit-reject"),
strings.concat(root, "/ww-commit-reject")];
let commitfixedbins: []str = [strings.concat(root, "/c-commit-fixed"),
@@ -8087,8 +8160,6 @@ fn runtimepath(relative: str) str = {
let repeatedtraceref: str = "";
let changedtraceref: str = "";
let aliastraceref: str = "";
let commitfailref: str = "";
let commitpreflightref: str = "";
let commitbindingref: str = "";
let commitbinref: str = "";
let initialwwiref: str = "";
@@ -8115,9 +8186,7 @@ fn runtimepath(relative: str) str = {
&& !strings.hasprefix(baseenv[ei],
"WW_NAME_COMPILER_TRACE=")
&& !strings.hasprefix(baseenv[ei],
"WW_NAME_REAL_COMPILER=")
&& !strings.hasprefix(baseenv[ei],
"WW_COMMIT_BLOCK_STAMP=")) {
"WW_NAME_REAL_COMPILER=")) {
append(env, baseenv[ei]);
};
ei += 1;
@@ -8168,11 +8237,10 @@ fn runtimepath(relative: str) str = {
(30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
// A failed multi-file artifact rename may have published a new
// interface before a later rename fails. It invalidates every old unit
// voucher and the workdir identity, so the next request must reconsider
// an importer even when the regenerated dependency export now compares
// equal to that partially published interface.
// The package and command form one publication transaction. A changed
// declared name is visible to the importer through the staged .wwi, but
// its resulting semantic rejection must preserve the complete prior
// generation and force the next request to reconsider both actions.
let commitcoldav: []str = [driver(stages[si]), "build", "-w",
commitworks[si], "-I", source, "-o", commitcoldbins[si], commitapp];
runcommandenv(root, strings.concat("name-commit-cold-", stages[si]),
@@ -8182,96 +8250,72 @@ fn runtimepath(relative: str) str = {
runcommand(root, strings.concat("name-commit-cold-run-", stages[si]),
commitrun, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
let commitpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c",
"/.wwtool.w6a", "/.wwtool.stamp", "/commit.codec.unit.ww",
"/commit.codec.wwi", "/commit.codec.s", "/commit.codec.o",
"/commit.codec.a", "/cmd.commit.unit.ww", "/cmd.commit.wwi",
"/cmd.commit.s", "/cmd.commit.o", "/cmd.commit.a",
"/cmd.commit.init.unit.ww", "/cmd.commit.init.s",
"/cmd.commit.init.o"];
let commitsnapshots: []str = alloc([], commitpaths.len: u64)!;
let cpi: i32 = 0;
for (cpi < commitpaths.len) {
append(commitsnapshots, readfile(strings.concat(commitworks[si],
commitpaths[cpi])));
cpi += 1;
};
let commitcoldbytes: str = readfile(commitcoldbins[si]);
rewritefile(commitcodecfile, cablebody);
let commitstamp: str = strings.concat(commitworks[si],
"/.wwtool.stamp");
let savedstamp: str = strings.concat(commitstamp, ".saved");
let oldcommitwwi: str = readfile(strings.concat(commitworks[si],
"/commit.codec.wwi"));
let oldcommits: str = readfile(strings.concat(commitworks[si],
"/commit.codec.s"));
let oldcommito: str = readfile(strings.concat(commitworks[si],
"/commit.codec.o"));
let oldcommita: str = readfile(strings.concat(commitworks[si],
"/commit.codec.a"));
let blockenv: []str = alloc([], (env.len + 1): u64)!;
let bei: i32 = 0;
for (bei < env.len) { append(blockenv, env[bei]); bei += 1; };
append(blockenv, strings.concat("WW_COMMIT_BLOCK_STAMP=", commitstamp));
let commitblockav: []str = [driver(stages[si]), "build", "-w",
commitworks[si], "-I", source, "-o", commitblockbins[si],
commitapp];
runcommandenv(root, strings.concat("name-commit-preflight-", stages[si]),
commitblockav, blockenv,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "ww: cannot invalidate package workdir\n"));
if (si == 0) { commitpreflightref = strings.dup(out.stderr); }
else { assert(same(commitpreflightref, out.stderr)); };
assert(same(oldcommitwwi, readfile(strings.concat(commitworks[si],
"/commit.codec.wwi"))));
assert(same(oldcommits, readfile(strings.concat(commitworks[si],
"/commit.codec.s"))));
assert(same(oldcommito, readfile(strings.concat(commitworks[si],
"/commit.codec.o"))));
assert(same(oldcommita, readfile(strings.concat(commitworks[si],
"/commit.codec.a"))));
assert(!os.exists(strings.concat(commitworks[si],
"/commit.codec.unit.ww")));
assert(!os.exists(commitblockbins[si]));
assert(os.rmdir(commitstamp) == 0);
assert(os.rename(savedstamp, commitstamp) == 0);
// With the stamp preflight restored, force a later artifact rename to
// fail after the changed interface has already been published.
let blockedasm: str = strings.concat(commitworks[si],
"/commit.codec.s");
assert(os.remove(blockedasm) == 0);
assert(os.mkdir(blockedasm, 448i32) == 0);
let commitrejectav: []str = [driver(stages[si]), "build", "-w",
commitworks[si], "-I", source, "-o", commitrejectbins[si],
commitapp];
runcommandenv(root, strings.concat("name-commit-partial-", stages[si]),
commitrejectav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "ww: cannot commit commit.codec\n"));
if (si == 0) { commitfailref = strings.dup(out.stderr); }
else { assert(same(commitfailref, out.stderr)); };
assert(has(readfile(strings.concat(commitworks[si],
"/commit.codec.wwi")),
"//ww:module commit.codec\npackage cable;\n"));
assert(!os.exists(strings.concat(commitworks[si],
"/commit.codec.unit.ww")));
assert(!os.exists(strings.concat(commitworks[si],
"/cmd.commit.unit.ww")));
assert(!os.exists(strings.concat(commitworks[si], "/.wwtool.stamp")));
let partialsuffixes: []str = [".unit.new", ".wwi.new", ".s.new",
".o.new", ".a.new"];
let psi: i32 = 0;
for (psi < partialsuffixes.len) {
assert(!os.exists(strings.concat(commitworks[si],
"/commit.codec", partialsuffixes[psi])));
psi += 1;
".o.new", ".a.new", ".init.unit.new", ".init.s.new",
".init.o.new"];
let rejectedactions: []str = ["commit.codec", "cmd.commit"];
let attemptlabels: []str = ["0", "1"];
let attempt: i32 = 0;
for (attempt < 2) {
rewritefile(traces[si], "");
runcommandenv(root, strings.concat("name-commit-reject-",
stages[si], "-", attemptlabels[attempt]),
commitrejectav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "imported as cable and not used"));
assert(has(out.stderr, "undefined: wire"));
assert(has(out.stderr, "ww: w6c failed for cmd.commit\n"));
let ncommitbinding: str = normalizedtrace(out.stderr,
strings.concat(commitworks[si], "/"), commitrejectbins[si]);
if (si == 0 && attempt == 0) {
commitbindingref = strings.dup(ncommitbinding);
} else { assert(same(commitbindingref, ncommitbinding)); };
let rejecttrace: str = readfile(traces[si]);
assert(occurrences(rejecttrace,
"/commit.codec.unit.new") == 1);
assert(occurrences(rejecttrace,
"/cmd.commit.unit.new") == 1);
cpi = 0;
for (cpi < commitpaths.len) {
assert(same(commitsnapshots[cpi], readfile(strings.concat(
commitworks[si], commitpaths[cpi]))));
cpi += 1;
};
assert(same(commitcoldbytes, readfile(commitcoldbins[si])));
let rai: i32 = 0;
for (rai < rejectedactions.len) {
let psi: i32 = 0;
for (psi < partialsuffixes.len) {
assert(!os.exists(strings.concat(commitworks[si], "/",
rejectedactions[rai], partialsuffixes[psi])));
psi += 1;
};
rai += 1;
};
assert(!os.exists(commitrejectbins[si]));
assert(!os.exists(strings.concat(commitrejectbins[si], ".new")));
attempt += 1;
};
assert(!os.exists(commitrejectbins[si]));
assert(os.rmdir(blockedasm) == 0);
rewritefile(traces[si], "");
runcommandenv(root, strings.concat("name-commit-reconsider-", stages[si]),
commitrejectav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "undefined: wire"));
let ncommitbinding: str = normalizedtrace(out.stderr,
strings.concat(commitworks[si], "/"), commitrejectbins[si]);
if (si == 0) { commitbindingref = strings.dup(ncommitbinding); }
else { assert(same(commitbindingref, ncommitbinding)); };
assert(has(readfile(traces[si]), "/cmd.commit.unit.new"));
assert(os.exists(strings.concat(commitworks[si],
"/commit.codec.unit.ww")));
assert(!os.exists(strings.concat(commitworks[si],
"/cmd.commit.unit.ww")));
assert(!os.exists(commitrejectbins[si]));
rewritefile(commitappfile, strings.concat(
"package main;\nimport commit.codec;\n",
"fn main() i32 = { return cable.value(); };\n"));
@@ -8285,6 +8329,10 @@ fn runtimepath(relative: str) str = {
runcommand(root, strings.concat("name-commit-fixed-run-", stages[si]),
commitfixedrun, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
assert(has(readfile(strings.concat(commitworks[si],
"/commit.codec.wwi")),
"//ww:module commit.codec\npackage cable;\n"));
assert(same(commitcoldbytes, readfile(commitcoldbins[si])));
let commitbinbytes: str = readfile(commitfixedbins[si]);
if (si == 0) { commitbinref = strings.dup(commitbinbytes); }
else { assert(same(commitbinref, commitbinbytes)); };
@@ -8614,10 +8662,12 @@ fn runtimepath(relative: str) str = {
let one: str = strings.concat(source, "/pkg/one");
let two: str = strings.concat(source, "/pkg/two");
let positives: []str = ["default", "alias", "reuse", "repeatok",
"repeatedfiles"];
"repeatedfiles", "blankalias", "blankrepeat", "blankdefault",
"blankexplicit", "blankfiles"];
let negatives: []str = ["barefn", "baretype", "baredef", "bareconst",
"barevar", "aliasbad", "aliasleak", "repeatdup", "aliasdup",
"defaultalias", "declcollision", "unusedalias", "blankalias"];
"defaultalias", "declcollision", "unusedalias", "blankqual",
"blankbare", "blankordinaryunused"];
let dirs: []str = [one, two];
let i: i32 = 0;
for (i < positives.len) {
@@ -8673,6 +8723,24 @@ fn runtimepath(relative: str) str = {
"fn second() i32 = { return right.value(); };\n"));
writefile(strings.concat(source, "/cmd/repeatedfiles/main.ww"),
"package main;\nfn main() i32 = { return first() + second(); };\n");
writefile(strings.concat(source, "/cmd/blankalias/main.ww"),
"package main;\nimport _ pkg.one;\nfn main() i32 = { return 0; };\n");
writefile(strings.concat(source, "/cmd/blankrepeat/main.ww"),
strings.concat("package main;\nimport _ pkg.one;\n",
"import _ pkg.one;\nfn main() i32 = { return 0; };\n"));
writefile(strings.concat(source, "/cmd/blankdefault/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/blankexplicit/main.ww"),
strings.concat("package main;\nimport _ pkg.one;\n",
"import stable pkg.one;\n",
"fn main() i32 = { return stable.value(); };\n"));
writefile(strings.concat(source, "/cmd/blankfiles/a.ww"),
"package main;\nimport _ pkg.one;\n");
writefile(strings.concat(source, "/cmd/blankfiles/b.ww"),
"package main;\nimport _ pkg.one;\n");
writefile(strings.concat(source, "/cmd/blankfiles/main.ww"),
"package main;\nfn main() i32 = { return 0; };\n");
let bareexprs: []str = ["value()", "take(7)", "DEFINED", "CONSTANT",
"VARIABLE"];
@@ -8716,22 +8784,29 @@ fn runtimepath(relative: str) str = {
"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");
writefile(strings.concat(source, "/cmd/blankqual/main.ww"),
strings.concat("package main;\nimport _ pkg.one;\n",
"fn main() i32 = { return wire.value(); };\n"));
writefile(strings.concat(source, "/cmd/blankbare/main.ww"),
strings.concat("package main;\nimport _ pkg.one;\n",
"fn main() i32 = { return value(); };\n"));
writefile(strings.concat(source, "/cmd/blankordinaryunused/main.ww"),
strings.concat("package main;\nimport _ pkg.one;\nimport pkg.one;\n",
"fn main() i32 = { return 0; };\n"));
let stages: []str = ["ww", "ww_ww"];
let expected: []i32 = [83, 83, 40, 33, 22];
let binaryrefs: []str = ["", "", "", "", ""];
let expected: []i32 = [83, 83, 40, 33, 22, 0, 0, 11, 11, 0];
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"];
"\"pkg.one\" imported as stable and not used", "undefined: wire",
"undefined: value", "\"pkg.one\" imported as wire and not used"];
let exactdiags: []str = [
strings.concat(
"$WORK/cmd.barefn.unit.new:3:1: error: \"pkg.one\" imported as wire and not used\n",
@@ -8777,8 +8852,7 @@ fn runtimepath(relative: str) str = {
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) {
@@ -8820,6 +8894,26 @@ fn runtimepath(relative: str) str = {
let repeatedunit: str = readfile(strings.concat(work,
"/cmd.repeatedfiles.unit.ww"));
assert(occurrences(repeatedunit, "pkg.one;") == 2);
let blankunit: str = readfile(strings.concat(work,
"/cmd.blankalias.unit.ww"));
assert(has(blankunit, "import _ pkg.one;"));
let blankwwi: str = readfile(strings.concat(work,
"/cmd.blankalias.wwi"));
assert(!has(blankwwi, "import _") && !has(blankwwi, "__ww.."));
let blankrepeat: str = readfile(strings.concat(work,
"/cmd.blankrepeat.unit.ww"));
assert(occurrences(blankrepeat, "import _ pkg.one;") == 2);
assert(occurrences(blankrepeat,
"//ww:direct-export pkg.one ") == 1);
let blankfiles: str = readfile(strings.concat(work,
"/cmd.blankfiles.unit.ww"));
assert(occurrences(blankfiles, "import _ pkg.one;") == 2);
assert(occurrences(blankfiles,
"//ww:direct-export pkg.one ") == 1);
let blankdispatch: str = readfile(strings.concat(work,
"/cmd.blankdefault.init.unit.ww"));
assert(occurrences(blankdispatch,
"//ww:init-call __ww..pkg.p.pkg.one.v0.r0.init") == 1);
i = 0;
for (i < negatives.len) {

36
test/sep/limitexec.c Normal file
View File

@@ -0,0 +1,36 @@
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <unistd.h>
/* Test-only deterministic launcher for allocation-failure observers. Keeping
* setrlimit in a repository-built helper avoids depending on util-linux paths
* or shell-specific ulimit syntax. */
int
main(int argc, char **argv)
{
if (argc < 3) {
fputs("usage: sep-limitexec bytes program [arg ...]\n", stderr);
return 2;
}
errno = 0;
char *end = NULL;
unsigned long long value = strtoull(argv[1], &end, 10);
if (errno != 0 || end == argv[1] || *end != '\0') {
fputs("sep-limitexec: invalid limit\n", stderr);
return 2;
}
struct rlimit limit;
limit.rlim_cur = (rlim_t)value;
limit.rlim_max = (rlim_t)value;
if (setrlimit(RLIMIT_AS, &limit) != 0) {
fputs("sep-limitexec: setrlimit failed\n", stderr);
return 2;
}
execv(argv[2], &argv[2]);
fputs("sep-limitexec: exec failed\n", stderr);
return 127;
}

1421
test/sep/sepinit_test.ww Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -140,6 +140,7 @@ main(void)
"import io;",
"import io.bufio;",
"import stable io.bufio;",
"import _ io.bufio;",
"def MAX: i32 = 4096;",
"export def MAX: i32 = 4096;",
"type point = struct { x: i32, y: i32 };",
@@ -149,6 +150,7 @@ main(void)
"type arr = [16]u8;",
"fn nop() void = {};",
"fn init() void = {}; fn init() void = {};",
"export fn id(x: i32) i32 = { return x; };",
"fn add(a: i32, b: i32) i32 = { return a + b; };",
"@symbol(\"malloc\") fn cmalloc(n: u64) *void;",

View File

@@ -49,6 +49,15 @@ struct row { const char *src, *expect; };
static const struct row rows[] = {
{ "fn main() void = {};", "ok" },
{ "fn init() void = {}; fn init() void = {};", "ok" },
{ "fn init(x: i32) void = {};", "func init must have no arguments and no return values" },
{ "fn init() i32 = { return 1; };", "func init must have no arguments and no return values" },
{ "fn init() void;", "func init must have a body" },
{ "export fn init() void = {};", "func init cannot be exported" },
{ "fn init() void = {}; fn f() void = { init(); };", "undefined: init" },
{ "fn make() i32 = { return 7; }; let state: i32 = make();", "ok" },
{ "fn left() i32 = { return right; }; let first: i32 = left(); let right: i32 = 3;", "ok" },
{ "fn readb() i32 = { return b; }; let a: i32 = readb(); let b: i32 = a;", "initialization cycle for a" },
{ "fn id(x: i32) i32 = { return x; };", "ok" },
{ "fn add(a: i32, b: i32) i32 = { return a + b; };", "ok" },
{ "def MAX: i32 = 4096;", "ok" },

View File

@@ -1,9 +1,9 @@
//ww:run-exit 42
package main;
type bs = struct { rbuf: []u8, mark: i32 };
fn init(b: *bs, rbuf: []u8) void = { b.rbuf = rbuf; b.mark = 9; };
fn setup(b: *bs, rbuf: []u8) void = { b.rbuf = rbuf; b.mark = 9; };
fn main() i32 = {
let raw: [8]u8; raw[0] = 0u8; let b: bs; init(&b, raw[0:5]);
let raw: [8]u8; raw[0] = 0u8; let b: bs; setup(&b, raw[0:5]);
if (b.rbuf.len != 5) { return 1; }; if (b.rbuf.cap != 8) { return 2; };
if (b.mark != 9) { return 3; }; return 42;
};

View File

@@ -16,7 +16,7 @@ export fn main() i32 = {
if (sel == 0) { src = &mstB.vt; };
let rbuf: [4]u8;
let wbuf: [4]u8;
let bc: bufio.stream = bufio.init(src, rbuf[0:4], wbuf[0:4]);
let bc: bufio.stream = bufio.newstream(src, rbuf[0:4], wbuf[0:4]);
let vs: io.stream = &bc.vt;
let out: [4]u8;
let rd = io.read(vs, out[0:4]);

View File

@@ -10,7 +10,7 @@ export fn main() i32 = {
let msrc: io.stream = &mst.vt;
let rbuf: [4]u8;
let wbuf: [4]u8;
let bc: bufio.stream = bufio.init(msrc, rbuf[0:4], wbuf[0:4]);
let bc: bufio.stream = bufio.newstream(msrc, rbuf[0:4], wbuf[0:4]);
let vs: io.stream = &bc.vt;
if (!bufio.isbuffered(vs)) { return 91; };
if (bufio.isbuffered(msrc)) { return 92; };

View File

@@ -10,7 +10,7 @@ export fn main() i32 = {
let msrc: io.stream = &mst.vt;
let rbuf: [4]u8;
let wbuf: [4]u8;
let b: bufio.stream = bufio.init(msrc, rbuf[0:4], wbuf[0:4]);
let b: bufio.stream = bufio.newstream(msrc, rbuf[0:4], wbuf[0:4]);
let vsbuf: io.stream = &b.vt;
let buf2: [4]u8;
let st2: memio.stream = memio.fixed(buf2[0:4]);

View File

@@ -12,7 +12,7 @@ export fn main() i32 = {
let msrc: io.stream = &mst.vt;
let rbuf: [8]u8;
let wbuf: [4]u8;
let b: bufio.stream = bufio.init(msrc, rbuf[0:8], wbuf[0:4]);
let b: bufio.stream = bufio.newstream(msrc, rbuf[0:8], wbuf[0:4]);
let vs: io.stream = &b.vt;
let out: [4]u8;
let rd1 = io.read(vs, out[0:3]);

View File

@@ -10,7 +10,7 @@ export fn main() i32 = {
let sinkvs: io.stream = &sink.vt;
let rb: [4]u8;
let wb: [8]u8;
let b: bufio.stream = bufio.init(sinkvs, rb[0:4], wb[0:8]);
let b: bufio.stream = bufio.newstream(sinkvs, rb[0:4], wb[0:8]);
let bvs: io.stream = &b.vt;
let semi: [1]u8;
semi[0] = 59u8;

View File

@@ -11,7 +11,7 @@ export fn main() i32 = {
let vs: io.stream = &mst.vt;
let rbuf: [4]u8;
let wbuf: [4]u8;
let b: bufio.stream = bufio.init(vs, rbuf[0:4], wbuf[0:4]);
let b: bufio.stream = bufio.newstream(vs, rbuf[0:4], wbuf[0:4]);
let bvs: io.stream = &b.vt;
let out: [4]u8;
let rd = io.read(bvs, out[0:3]);

View File

@@ -10,7 +10,7 @@ export fn main() i32 = {
let msrc: io.stream = &mst.vt;
let rbuf: [8]u8;
let wbuf: [8]u8;
let b: bufio.stream = bufio.init(msrc, rbuf[0:8], wbuf[0:8]);
let b: bufio.stream = bufio.newstream(msrc, rbuf[0:8], wbuf[0:8]);
let vs: io.stream = &b.vt;
let payload: [5]u8;
payload[0] = 104u8; payload[1] = 105u8;

View File

@@ -2,7 +2,7 @@
package main;
import math.random;
export fn main() int = {
let g: random.random = random.init(1u64);
let g: random.random = random.fromseed(1u64);
let x: u32 = random.u32n(&g, 1u32);
if (x >= 1u32) { return 1; };
return 0;

View File

@@ -2,7 +2,7 @@
package main;
import math.random;
export fn main() int = {
let g: random.random = random.init(1u64);
let g: random.random = random.fromseed(1u64);
let x: u32 = random.u32n(&g, 10u32);
if (x >= 10u32) { return 1; };
return 0;

View File

@@ -2,7 +2,7 @@
package main;
import math.random;
export fn main() int = {
let g: random.random = random.init(1u64);
let g: random.random = random.fromseed(1u64);
let x: u64 = random.u64n(&g, 1u64);
if (x >= 1u64) { return 1; };
return 0;

View File

@@ -2,7 +2,7 @@
package main;
import math.random;
export fn main() int = {
let g: random.random = random.init(1u64);
let g: random.random = random.fromseed(1u64);
let x: u64 = random.u64n(&g, 10u64);
if (x >= 10u64) { return 1; };
return 0;