test: prove package initialization semantics
This commit is contained in:
@@ -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 1133–1226](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 908–915 and 939–955](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/ast/ast.go#L908-L955),
|
||||
[`go/parser/parser.go`, lines 2509–2546](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 2784–2842](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 16–33](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/decl.go#L16-L33),
|
||||
[`go/types/resolver.go`, lines 103–124](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L103-L124),
|
||||
[lines 279–350](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L279-L350),
|
||||
[lines 400–433](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L400-L433),
|
||||
[lines 701–716](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 90–111](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L90-L111),
|
||||
[lines 264–335](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L264-L335),
|
||||
[lines 416–444](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L416-L444)
|
||||
and
|
||||
[lines 706–721](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 9–31 and 43–52](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 12–18](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/test/init.go#L12-L18),
|
||||
[`test/noinit.go`, lines 315–326](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/test/noinit.go#L315-L326),
|
||||
[`internal/types/testdata/check/decls0.go`, lines 40–46](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/decls0.go#L40-L46),
|
||||
and
|
||||
[`decls1.go`, lines 141–146](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 19–185 and 226–335](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/initorder.go#L19-L335),
|
||||
[`cmd/compile/internal/types2/initorder.go`, lines 16–182 and 223–332](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 1408–1619](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 22–89](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 34–145](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/staticinit/sched.go#L34-L145),
|
||||
[`cmd/compile/internal/noder/writer.go`, lines 2717–2773](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/writer.go#L2717-L2773),
|
||||
[`cmd/compile/internal/noder/reader.go`, lines 3288–3345](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/reader.go#L3288-L3345),
|
||||
[lines 3389–3416](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/reader.go#L3389-L3416),
|
||||
[`cmd/compile/internal/pkginit/init.go`, lines 20–145](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 19–39 and 104–180](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/inittask.go#L19-L180),
|
||||
[`cmd/link/internal/ld/heap.go`, lines 56–99](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/heap.go#L56-L99),
|
||||
[`runtime/proc.go`, lines 203–290](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/runtime/proc.go#L203-L290),
|
||||
[lines 8049–8124](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 7–17](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 108–111 and 193–207](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L108-L207),
|
||||
[`os/dir.go`, lines 109–125](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/os/dir.go#L109-L125),
|
||||
[`go/build/build.go`, lines 948–1040](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L948-L1040),
|
||||
[lines 1061–1063](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L1061-L1063),
|
||||
[lines 1512–1518](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 85–101, 175–293, and 421–550](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 1–20](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 437–455, 628–708, and 919–1068](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 1185–1226](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 314–353](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/unified.go#L314-L353),
|
||||
[lines 463–570](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/unified.go#L463-L570),
|
||||
[`cmd/compile/internal/noder/writer.go`, lines 2742–2749](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/writer.go#L2742-L2749),
|
||||
[`cmd/compile/internal/importer/ureader.go`, lines 41–62](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 787–805](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L787-L805),
|
||||
[`cmd/go/internal/work/build.go`, lines 519–558](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L519-L558),
|
||||
[`cmd/go/internal/work/exec.go`, lines 864–884](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/exec.go#L864-L884),
|
||||
[lines 1592–1653](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/exec.go#L1592-L1653),
|
||||
[`cmd/go/internal/work/gc.go`, lines 136–177](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/gc.go#L136-L177),
|
||||
[lines 590–672](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.
|
||||
|
||||
59
docs/spec.md
59
docs/spec.md
@@ -246,7 +246,8 @@ does not return (e.g. a call to `abort`).
|
||||
```
|
||||
SourceFile = PackageClause { ImportDecl } { TopDecl } .
|
||||
PackageClause = "package" ident ";" .
|
||||
ImportDecl = "import" ( 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)
|
||||
|
||||
@@ -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/`.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user