test: prove ordinary import binding modes

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

View File

@@ -4159,7 +4159,7 @@ Hex encoding keeps arbitrary legal filesystem bytes inside one comment. The
metadata makes ordinary-to-vendor changes and vendor symlink retargeting
invalidate the importer even when its source bytes and both already-warm export
bytes happen to match. It contains no dependency body and does not alter source
positions. The current workdir formats are build 14 and test 13. An equivalent
positions. The current workdir formats are build 15 and test 14. An equivalent
warm request remains a package-production no-op; an export change propagates
only through ordinary direct-export comparison.
@@ -4509,15 +4509,16 @@ source-file binding is named `wire`. The path leaf `codec` is not installed as
another qualifier, and a sibling source file receives no `wire` binding unless
that file has its own import.
The implemented representation keeps five facts distinct:
The implemented representation keeps six facts distinct:
1. source import spelling, including its source file, line, and column;
2. contextually expanded canonical import identity;
3. canonical physical directory;
4. the one declared package name read from eligible source clauses and `.wwi`
package markers; and
5. the source-file-local default qualifier binding from that declaration to
the canonical target.
package markers;
5. the optional explicit alias written at that import occurrence; and
6. the effective source-file-local qualifier, selected from the explicit alias
when present and otherwise from the imported declaration.
Both drivers retain one dynamically allocated `sepbind` occurrence for every
real import site. The occurrence stores the source spelling and position plus a
@@ -4541,14 +4542,15 @@ declared name are no longer collapsed into one token.
`w6c` and `w6c_ww` validate every direct export's leading canonical owner
against its paired `--import` path. After all direct exports are parsed, they
build canonical-path-to-declared-name metadata from those interfaces, apply any
vendor `--import-map` only to canonical identity, and bind the imported
declaration as the default qualifier of each primary source import. The
checkers and code generators select bindings by source-section ID and canonical
owner. Qualified and retained bare-import lookups mark only that file's binding
used. Two files may consequently bind the same name to different canonical
vendor `--import-map` only to canonical identity, and bind each primary source
import through its explicit alias or, when absent, the imported declaration.
The checkers and code generators select bindings by source-section ID and
canonical owner. Only a qualified lookup through that effective binding marks
the owning file's occurrence used; imported declarations are never a bare-name
fallback. Two files may consequently bind the same name to different canonical
packages, while the graph still contains one edge/action for each target.
Within one file, two imports that produce the same default qualifier are a
Within one file, two imports that produce the same effective qualifier are a
redeclared binding; the later unused binding is also reported. An unused import
is reported at its own import position even if a sibling file uses the same
qualifier or canonical dependency. A package-scope declaration collides with
@@ -4590,30 +4592,32 @@ identities and archive closures. A generated dispatcher privately binds its
tested target through
`--test-target-package <canonical-path>` so a command variant declared `main`
does not collide with the dispatcher's own synthesized `main`; this is
compiler-generated wiring, not source alias syntax. Zero-test dispatchers mark
their compiler-owned target/support metadata imports consumed. That private
binding is installed or marked consumed only in the generated dispatcher's
source section; an import from an earlier test-file section neither supplies
nor satisfies it.
compiler-generated wiring distinct from ordinary source aliases. Zero-test
dispatchers mark their compiler-owned target/support metadata imports consumed.
That private binding is installed or marked consumed only in the generated
dispatcher's source section; an import from an earlier test-file section
neither supplies nor satisfies it.
Vendor expansion changes only canonical identity and physical selection. A
source spelling such as `lib.codec` can resolve to
`domain.app.vendor.lib.codec`, while the vendored package's declaration, for
example `package wire`, supplies the file-local qualifier. The driver emits one
sorted semantic `--import-map lib.codec domain.app.vendor.lib.codec` and one
direct export input despite repeated import occurrences in separate files.
The expanded identity continues to own symbols, `.wwi`, archive, voucher, and
link inputs.
example `package wire`, supplies the default file-local qualifier. An explicit
alias overrides only that qualifier. The driver emits one sorted semantic
`--import-map lib.codec domain.app.vendor.lib.codec` and one direct export input
despite repeated import occurrences in separate files. The expanded identity
continues to own symbols, `.wwi`, archive, voucher, and link inputs.
Canonical action interning remains the directory/path/variant model of sections
11.7 and 11.14. Independent file bindings never clone an action, and a declared
name never enters an artifact basename or storage locator. The name is semantic
content in the owner unit and export. Changing only a dependency's declaration
therefore keeps the same action identity but changes its export bytes, causes
each direct importer to be reconsidered, and stops propagation after an
importer's regenerated export is unchanged. An identical warm request remains
a producer no-op. Build workdir format 14 and test format 13 prevent reuse of
older vouchers that lack these semantics.
name or source alias never enters an artifact basename or storage locator.
Changing a dependency's declaration keeps the same action identity and causes
each direct importer to be reconsidered. A default-bound importer may then fail
because its old qualifier disappeared; an explicitly aliased importer keeps its
binding, regenerates a canonical semantic export, and stops reverse propagation
when those bytes are unchanged. An alias-only source edit rebuilds its owner but
likewise leaves canonical export identity unchanged. An identical warm request
remains a producer no-op. Build workdir format 15 and test format 14 prevent
reuse of older vouchers that lack these semantics.
Compiler argv still contains exactly the sorted, deduplicated `.wwi` exports of
direct canonical dependencies; no transitive `.wwi` and no qualifier-derived
@@ -4634,10 +4638,11 @@ Responsibility is intentionally split as follows:
publication. Their storage, diagnostics, call positions, and allocation
failures are isomorphic.
- `cmd/w6c`/`cmd/wcc` and `selfhost/cmd/w6c`/`selfhost/cmd/wcc` own export
owner/name reading and writing, file-local binding installation, collision
and unused-import diagnostics, name/type lookup, canonical symbol ownership,
and generated-dispatcher private qualification. Cstage and WWstage emit
byte-identical applicable interfaces, assembly, archives, and binaries.
owner/name reading and writing, ordinary/aliased parse facts, file-local
binding installation, collision and unused-import diagnostics, name/type
lookup, canonical symbol ownership, and generated-dispatcher private
qualification. Cstage and WWstage emit byte-identical applicable interfaces,
assembly, archives, and binaries.
- `cmd/wwtest` remains only the test-command dispatcher.
The behavior follows pinned official Go 1.26.5 source at commit
@@ -4684,11 +4689,9 @@ The behavior follows pinned official Go 1.26.5 source at commit
[lines 628659](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L659),
[`cmd/go/internal/test/test.go`, lines 11331226](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1133-L1226)).
The deliberately retained source grammar is `import dotted.path;`. Quoted Go
imports, explicit aliases, dot imports, and blank imports are not implemented.
Because bindings already retain source ownership and position separately from
canonical identity and declared name, adding explicit aliases later will not
require another package-wide namespace redesign.
Section 11.19 completes ordinary explicit aliases while retaining dotted,
unquoted paths. Grouped, quoted, dot, and blank imports remain deliberately
unimplemented.
The focused native observer
`declared_name_identity_and_file_import_scope` generates every tree
@@ -4700,6 +4703,204 @@ exports and archive-only links, warm no-op behavior, declared-name invalidation,
rejection-state preservation, normalized argv, artifact/binary identity,
allocation-bearing runtime behavior, and request/product-order independence.
### 11.19 Implemented ordinary and explicitly aliased file-scoped imports
WW now implements the two ordinary binding modes for its local dotted import
model:
```ww
import acme.codec; // effective qualifier is the declared package name
import stable acme.codec; // effective qualifier is exactly stable
```
If canonical package `acme.codec` declares `package wire`, the first form
exposes only `wire.Name`; the second exposes only `stable.Name`. Neither form
also exposes `codec.Name`, the unused alternative qualifier, or bare `Name`.
WW has no dot-import form, so an ordinary import never inserts the dependency's
exported declarations into unqualified lookup. Builtins, lexical declarations,
and same-package declarations retain ordinary bare lookup.
This is the dotted-path counterpart of Go's independent local name and quoted
path, without adopting quoted paths. The syntax AST stores the optional source
alias independently from the original dotted spelling, canonical expanded
identity, imported declared name, effective qualifier, owning source section,
and source position. `Sym.use_alias` remains the older checker coexistence bit
for a declaration that shares a leaf with a package qualifier; it is not the
source-language alias fact.
#### Pinned Go evidence
The reference is official Go 1.26.5 source at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`:
- `go/ast.ImportSpec` stores `Name` and `Path` independently, and `Pos` selects
the alias position when one exists
([`go/ast/ast.go`, lines 908915 and 939955](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/ast/ast.go#L908-L955)).
`parseImportSpec` parses the optional local name separately from the path
([`go/parser/parser.go`, lines 25092546](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/parser/parser.go#L2509-L2546)).
- `go/types` creates one child scope per source file, chooses an explicit alias
when present and the imported package's declared name otherwise, inserts one
ordinary package-name object, and inserts bare exports only for an explicit
dot import
([`go/types/resolver.go`, lines 237350](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L237-L350)).
Package/file collisions and unused occurrences are handled at
[`resolver.go`, lines 463480](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L463-L480)
and
[`resolver.go`, lines 701735](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L701-L735).
The production compiler mirrors those rules at
[`cmd/compile/internal/types2/resolver.go`, lines 223335](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L223-L335),
[lines 472489](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L472-L489),
and
[lines 706740](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L706-L740).
- Qualified selection marks the exact file-local package-name object used;
bare imported declarations are associated only with the dot-import table
([`go/types/call.go`, lines 682693](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/call.go#L682-L693),
[`go/types/typexpr.go`, lines 2031 and 7986](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/typexpr.go#L20-L86)).
Compiler diagnostics are stably sorted by source position before printing
([`cmd/compile/internal/base/print.go`, lines 7092](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/base/print.go#L70-L92)).
- Official testdata permits one path under distinct names, including default
plus aliases, while keeping every occurrence independently subject to unused
checking
([`internal/types/testdata/check/importdecl0/importdecl0a.go`, lines 2952](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/importdecl0/importdecl0a.go#L29-L52),
[`test/import.go`, lines 723](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/test/import.go#L7-L23),
[`test/import1.go`, lines 718](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/test/import1.go#L7-L18)).
- Canonical package path and declared name are independent in
[`go/types/package.go`, lines 2640](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/package.go#L26-L40).
Unified export writes and restores canonical path and name independently
([`cmd/compile/internal/noder/writer.go`, lines 430465](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/writer.go#L430-L465),
[`cmd/compile/internal/importer/ureader.go`, lines 157196](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/importer/ureader.go#L157-L196),
[`cmd/compile/internal/noder/reader.go`, lines 342376](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/noder/reader.go#L342-L376)).
- `go/build` keeps production, internal-test, and external-test files/imports
separate and constructs their source import sets
([`go/build/build.go`, lines 436505](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L436-L505),
[lines 9391040](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L939-L1040),
[lines 10611063](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L1061-L1063)).
`cmd/go` interns by canonical import path, performs internal/vendor checks at
every real site, rejects imported `main`, and builds distinct test variants
([`cmd/go/internal/load/pkg.go`, lines 633636](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L633-L636),
[lines 757806](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L757-L806),
[lines 20242047](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L2024-L2047),
[`cmd/go/internal/load/test.go`, lines 175293](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L175-L293),
[lines 421484](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L421-L484)).
- Build actions are keyed by operation and canonical package object, consume
canonical direct dependencies, and link the reachable canonical closure
([`cmd/go/internal/work/action.go`, lines 202206](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L202-L206),
[lines 437447](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L437-L447),
[lines 628708](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L708),
[lines 919968](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L919-L968),
[lines 10341068](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L1034-L1068)).
Root construction and test execution retain those canonical production and
generated-test objects
([`cmd/go/internal/work/build.go`, lines 495558](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L495-L558),
[`cmd/go/internal/test/test.go`, lines 11331226 and 12971366](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1133-L1366)).
Go's dot-import branch is negative evidence only: it demonstrates that bare
foreign declarations require a distinct explicit mode. WW does not implement
that mode.
#### Scope, duplicate, collision, and usage rules
Every import occurrence owns its spelling, optional alias, position, source
section, and used bit. The effective qualifier is installed only in that
section. A qualified type, value, function, def, const, or variable lookup maps
the effective qualifier to canonical identity and marks that exact occurrence
used. A failed bare lookup marks nothing. A sibling file cannot use or satisfy
the occurrence, while two files may independently reuse one alias for different
canonical packages.
In one source file, equal effective qualifiers are duplicate bindings. The
later occurrence remains independently unused; at one source position the
duplicate diagnostic precedes its unused diagnostic. A later bare undefined
name is printed after the earlier unused-import diagnostic. Equal canonical
paths are otherwise not a conflict: distinct aliases, or default plus explicit
alias, are accepted when their effective names differ and each occurrence is
used. A package-scope declaration colliding with a file import is rejected in
the existing deterministic reconciliation pass. The unused wording follows
Go's leaf comparison: a binding equal to the path leaf says `imported and not
used`; any other binding, including an unusual default declared name, says
`imported as <name> and not used`.
`_` is rejected at parse time as `blank import alias _ is not implemented`.
Grouped imports, quoted paths, dot imports, and blank imports are deliberately
deferred rather than partially implemented.
#### Graph, export, artifact, and persistence identity
The imports-only parser and full parser share one import-spec routine and retain
alias and dotted path separately. Both drivers sort and resolve occurrences by
the dotted spelling, perform contextual internal and nearest-first vendor checks
at every real site, and intern the expanded canonical action. `sepbind` and
`--import-map` continue to mean source dotted spelling to expanded vendor
identity; neither contains the alias. Repeated occurrences remain separate
scope facts but form one sorted canonical edge/action.
The compiler independently reads the direct dependency's declared name. It
installs the explicit alias when present or that declared name otherwise, while
keeping the canonical owner on declarations, symbols, and code generation. A
source alias cannot bypass imported-`main` rejection. Production, internal-test,
external-test, support, and generated-main identities remain isolated, and an
import found only in a test file reaches only its corresponding test variant.
The coordinator remains responsible only for package/test classification and
submitting those roots; it does not parse or rewrite imports.
`.wwi` data never exports a local alias as package identity. Qualified exported
type and constant references are normalized to a deterministic compiler-private
qualifier `__wwi_` followed by the lowercase hexadecimal bytes of the canonical
path. Matching import records still name the canonical path, and transitive fact
sections carry the same canonical private spelling. The reader restores the
real declared name from the direct owner's metadata while treating those
private names as semantic placeholders. Thus two source aliases for the same
canonical type produce the same interface bytes, even if two dependencies have
the same declared name.
The source/import and interface protocol change advances persistent build
workdirs to format 15 and test workdirs to format 14. Older unit vouchers are
invalidated before reuse, so an interface written with historical implicit-dot
or declared-name spelling cannot preserve a stale qualifier under the new
checker.
Compiler argv remains exactly one sorted `--import <canonical-path> <direct.wwi>`
pair per direct dependency plus the exact required vendor import maps. No
transitive `.wwi` is passed. Symbols, objects, archives, vouchers, stamps,
persistent directories, and product basenames remain canonical-action owned.
Linker argv remains root plus reachable archives and native/runtime inputs only;
it contains neither `.wwi` files nor alias-derived archive names.
A dependency declared-name change invalidates its semantic export and
reconsiders every direct importer. Default-bound unchanged source loses the old
qualifier and is rejected cleanly. Explicitly aliased source stays valid; after
its canonical interface regenerates unchanged, invalidation stops before
unaffected reverse dependencies. An alias-only source edit rebuilds the edited
owner but likewise cannot rename symbols or alter canonical export identity, so
unchanged semantic bytes stop reverse rebuilding.
Parser, identity, alias, declaration, binding, and scope failures occur before
publication. Compiler-owned failures may start the compiler, but staged unit,
interface, assembly, object, archive, voucher, stamp, status, and product state
is discarded or invalidated under section 11.18's partial-commit protocol.
#### Stage and observer ownership
The C and WW parsers retain identical alias/path/position facts. The Cstage and
WWstage drivers resolve only the dotted path; the compiler mains restore the
declared name and choose the effective binding; the checkers own duplicate,
collision, usage, and file-scope lookup; the interface writers own canonical
normalization; and code generators consume checker-stamped canonical types and
module ownership. WWstage resolves all top-level function signatures in their
declaring package before checking bodies, matching Cstage and preventing a
consumer from reinterpreting a later declaration's bare same-package types.
The native `explicit_import_alias_binding_modes` observer owns the focused
ordinary/alias/bare-negative, duplicate, repeated-path, file-scope, canonical
artifact, `.wwi`, and rejection-state matrix. The existing
`declared_name_identity_and_file_import_scope` observer owns default-versus-
stable-alias invalidation and propagation. Existing directory, command-test,
vendor, recursive, exact-argv, link-closure, persistent-workdir, and
partial-commit observers retain their broader variant and action ownership.
Every applicable proof runs Cstage and WWstage from independent cold roots and
compares diagnostics, normalized tool arguments, artifacts, binaries, and
runtime output.
## 12. Candidate architectures and hard-gate decision
Five candidates were developed as coherent systems, not as feature bins.