test: prove local vendor package semantics

This commit is contained in:
2026-08-13 19:18:05 +09:00
parent b84fb3ff25
commit 5d50788e46
2 changed files with 1204 additions and 45 deletions

View File

@@ -2794,16 +2794,17 @@ package main;
import lib.math;
```
An import is translated from dots to path separators and resolved only as a
directory package through the entry package's directory, explicit `-I` roots in
command order, and the toolchain source-library root. A same-named `.ww` file is
neither a match nor a shadow for an import, so a later root containing the
directory wins over an earlier file decoy. There is no network, manifest, or
imported-file fallback. Explicit single-file CLI roots retain their raw-unit
compatibility path. The loader uses the compiler frontend's imports-only parser,
unions duplicate imports, byte-sorts direct edges, interns resolved directories
by filesystem identity, and reports self-imports and stable cycle chains before
compilation.
A source import is translated from dots to path separators, expanded through
the nearest eligible local `vendor` directory described in section 11.16, then
falls back to directory lookup through the entry package's directory, explicit
`-I` roots in command order, and the toolchain source-library root. A same-named
`.ww` file is neither a match nor a shadow for an import, so a later root
containing the directory wins over an earlier file decoy. There is no network,
manifest, or imported-file fallback. Explicit single-file CLI roots retain
their raw-unit compatibility path. The loader uses the compiler frontend's
imports-only parser, unions duplicate imports, byte-sorts direct edges, interns
canonical directory actions, and reports self-imports and stable cycle chains
before compilation.
A directory package consists of its immediate regular non-symlink `.ww` files,
excluding `*_test.ww`, in byte-sorted filename order. Every selected file must
@@ -2813,8 +2814,9 @@ instead declares `package main` to validate command kind while retaining its
complete canonical import identity. A source import of a command package from a
different directory is rejected; the one same-directory exception is an
external `main_test` variant's canonical import of its production action. Two
logical identities for one physical directory are rejected rather than compiled
twice.
ordinary logical identities for one physical directory are rejected rather
than compiled twice; section 11.16 records the deliberate exception for
distinct expanded vendor routes that converge through symlinks.
Packages compile serially in dependency-first postorder. The compiler emits the
existing deterministic `.wwi` interface for every directory-package action,
@@ -2828,10 +2830,14 @@ their names. Checked fixed array dimensions are emitted as numeric type facts,
so a public layout never requires exposing the private constant spelling that
produced its length.
A package compilation unit contains only that package's own byte-sorted sources
and deterministic `//ww:module-reset` separators. Each **direct** import is a
separate `--import <canonical-path> <dependency.wwi>` compiler input, sorted by
canonical path and deduplicated by the loader; no transitive `.wwi` is passed.
A package compilation unit contains only that package's own byte-sorted sources,
deterministic `//ww:module-reset` separators, and sorted driver-private
resolution metadata; it never contains a dependency source body. Each
**direct** import is a separate
`--import <canonical-path> <dependency.wwi>` compiler input, sorted by canonical
path and deduplicated by the loader; a source spelling expanded through
`vendor` additionally receives the non-dependency `--import-map` described in
section 11.16. No transitive `.wwi` is passed.
Origin-tagged facts inside those direct artifacts are compiler data, not source
imports: a source qualifier is visible only when its owning package directly
imports it, and private members, transitive-only qualifiers, bare values, and
@@ -2909,9 +2915,9 @@ publication spelling, `FILE.wwi.new`, so an incomplete archive/export pair is
never caused by a late path-overflow failure. Package loading, cycle detection,
and closure validation retain diagnostic precedence over this publication-only
check, and `-S` does not validate a publication path it never consumes.
Build workdir format 12 invalidates older build-mode unit vouchers before reuse
because root compiler argv changed; test workdir format 11 is unchanged because
test variants still link only their explicit generated-main action. Thereafter
Build workdir format 13 and test workdir format 12 invalidate older unit
vouchers before reuse because source binding and vendor-directory identity now
participate in compiler argv and persistent unit semantics. Thereafter
an equivalent warm library build invokes no tools, a private dependency change
stops at its unchanged export, and an export change recompiles its direct
importer under the existing propagation rule.
@@ -3082,12 +3088,13 @@ export and emits a root `.wwi/.a`.
The reserved support action and an ordinary source-imported package `test` may
coexist only because the former is explicitly rebound to the compiler-only
qualifier `__wwtest`. This is the sole directory-alias exception and cannot be
created by a source import. All ordinary production actions, including an
external test's colocated production dependency, use canonical directory
identity and the global bidirectional import-path checks above. There is no
role-based tolerance for duplicate ordinary import identities and no late
product-closure ambiguity to resolve.
qualifier `__wwtest`. This is the sole role-based directory alias and cannot be
created by a source import. The separate expanded-vendor-route exception in
section 11.16 is canonical source-tree identity, not a role alias. All ordinary
production actions, including an external test's colocated production
dependency, use canonical directory identity and the global bidirectional
import-path checks above. There is no role-based tolerance for duplicate
ordinary import identities and no late product-closure ambiguity to resolve.
The selected internal/external variants and a production action reached by
their imports or test-runtime closure are the sanctioned graph nodes that may
@@ -3098,7 +3105,8 @@ archive is omitted from that product's final link while the production action's
dependency archives remain in the closure. The external product includes the
production archive. Variant-only archives are never linked into another
product. All ordinary logical and physical package-identity collision checks
remain unchanged.
remain unchanged; only distinct expanded vendor routes receive the section
11.16 symlink-convergence exception.
Both stage linkers receive the generated-main archive first, followed by the
complete reverse-topological reachable package-archive closure, runtime, and
@@ -3236,9 +3244,9 @@ stamp. A warm invocation byte-compares all applicable live executables before
considering any committed unit reusable. A missing or changed driver copy
invalidates every `.unit.ww` voucher before compilation; old artifacts may
remain recoverable, but none can be reused without a freshly committed unit.
The workdir format revisions are 8 for ordinary builds and 9 for tests. The
identity-finalization change bumps both formats so no leaf-keyed unit voucher
can be reused as a full-path package action.
That slice introduced workdir format revisions 8 for ordinary builds and 9 for
tests. Later package-identity slices supersede those revisions; the current
formats are recorded in section 11.16.
This closes a real hidden-input boundary. The driver, rather than `w6c`, owns
canonical directory interning, source-derived graph construction, owner-only
@@ -3278,14 +3286,16 @@ or a manifest.
### 11.11 Implemented directory-only source-import slice
Cstage and WWstage now use a directory-only locator for every parsed source
import, including imports selected only by a package-test variant and the
compiler-generated test-support edge. The loader makes one ordered pass for
`<root>/<import-path>/`; it never probes `<root>/<import-path>.ww`. Unit
composition consequently writes only the owning package's byte-sorted source
files and never copies a dependency interface or imported source body. Missing
imports retain the importing source position and the same stable diagnostic in
both stages.
Cstage and WWstage now use directory packages for every parsed source import,
including imports selected only by a package-test variant and real imports in
the test-support package. Source imports first probe the bounded vendor
candidates in section 11.16, then make one ordered fallback pass for
`<root>/<import-path>/`; they never probe `<root>/<import-path>.ww`. The
compiler-generated edge to test support remains synthetic. Unit composition
consequently writes only the owning package's byte-sorted source files and
never copies a dependency interface or imported source body. Missing imports
retain the importing source position and the same stable diagnostic in both
stages.
Root selection remains a separate compatibility boundary. A literal `.ww` CLI
target, or a bare CLI target found as `<root>/<name>.ww` after the global
@@ -3331,9 +3341,17 @@ manifest, schema, package database, or network lookup is involved.
Both drivers construct those arguments directly from the package node's sorted,
deduplicated outgoing edges. They never walk grandchildren for compilation.
Every `.unit.ww` contains only the node's byte-sorted source files and reset
separators, while executable linking independently walks the full reachable
package closure and passes archives, never interfaces. The same path handles an
Every `.unit.ww` contains the node's byte-sorted source files, reset separators,
and any sorted driver-private vendor/import-map voucher comments, but no
dependency body. When exact source spelling differs from a selected expanded
canonical identity, the drivers also pass sorted, unique
`--import-map <source-spelling> <canonical-import-path>` triples. A map must
preserve the import leaf and target an ordinary direct `--import`; it adds no
export input or graph edge. The compilers rewrite only matching imports in the
primary source list before merging interfaces, leaving the source qualifier,
position, imported interface metadata, and generated/synthetic imports
untouched. Executable linking independently walks the full reachable package
closure and passes archives, never interfaces. The same path handles an
ordinary package, the production-plus-internal-test variant, the external test
package and its reused production package, compiler-generated test main, and
the reserved test-support package. Persistent workdirs compare a newly emitted
@@ -3540,7 +3558,8 @@ The SHA-256 byte input is exactly:
WW package paths and host paths cannot contain NUL, so that boundary is
unambiguous. Variant and role are present in both the digest input and the
visible locator tag. The digest is only an action-storage address: units still
begin with `//ww:module-reset <complete-path>`, exports begin with
begin with `//ww:module-reset <complete-path>` and may append deterministic
driver-private resolution comments, while exports begin with
`//ww:module <complete-path>`, compiler imports carry the complete path, and
qualified declarations use it in generated symbols. A selected executable
entry retains its intentional bare linker spelling. User-selected `-o`
@@ -3556,9 +3575,10 @@ existing regular `.unit.ww` voucher against the requested complete semantic
owner before stale-tool invalidation or reuse; a missing voucher is cold state,
while a malformed, non-regular, or wrong-owner voucher is a pre-tool error.
Thus ordinary preferred-name collisions are resolved, and a digest collision
cannot silently alias two live or warm package actions. The workdir format is
now build version 10 and test version 11 so an older flat-layout voucher is
never accepted as current state.
cannot silently alias two live or warm package actions. That storage slice
introduced build version 10 and test version 11 so an older flat-layout voucher
was never accepted as current state. Section 11.16 records the current
superseding formats.
The persisted semantic owner is deliberately the complete canonical import
path, not the canonical host directory: host location must not enter compiler
@@ -3988,6 +4008,206 @@ normalized tool arguments across independent cold Cstage and WWstage work
roots. Test variants additionally compare their unit/export/archive bytes and
exercise byte-equivalent runtime output in both stages.
### 11.16 Implemented manifest-free local vendor-directory imports
The local package loader now expands only imports parsed from source. For a
source package whose resolved lexical route is `<root>/domain/app`, an import
of `lib.math` probes these directory packages in order:
```text
<root>/domain/app/vendor/lib/math
<root>/domain/vendor/lib/math
<root>/vendor/lib/math
<ordinary ordered-root lookup for lib/math>
```
The walk stops at that edge's applicable active source root. It never walks an
arbitrary filesystem ancestor and never acquires a boundary from another
requested product. Each resolution context carries the current package's
lexical route and source-root boundary. An explicitly identified root derives
the boundary by removing and round-trip validating exactly its dotted identity
components. An unbound literal root chooses the first precedence-valid strict
ancestor in its own ordered search roots, or the selected directory itself.
An ordinary child records the exact root that selected it; a vendored child
inherits the parent's boundary. These values are contextual resolution state,
not package/action identity.
Before source scanning, a literal directory whose lexical route is
representable below that boundary binds the complete relative dotted identity,
including any `vendor` components. A truly rootless literal keeps its reversible
`__wwlocal` identity and cannot be coalesced with a same-physical vendored
action. Thus selecting a vendored directory directly remains legal without
letting product order donate its action identity to or from a source import.
A candidate shadows outer and ordinary candidates only when its directory
contains an observed non-directory name ending in `.ww`. This deliberately
includes `_test.ww` and the bare name `.ww`, matching Go's suffix probe; an
actual subdirectory named `x.ww` and an unreadable candidate with no observed
source do not shadow. Once a candidate is selected, normal package enumeration
reports its real errors, including a test-only or otherwise production-empty
directory, rather than falling through.
Source retains only the effective spelling, such as `lib.math`. Selection
assigns the target the complete expanded canonical identity, such as
`domain.app.vendor.lib.math`, and separately canonicalizes its physical
directory. The action key is that expanded identity and canonical directory,
plus the existing variant/role. Different physical vendor copies are distinct;
different expanded vendor routes remain distinct even when symlinks converge
on one physical directory; repeated resolutions of the same pair reuse one
action. Source spelling, importer context, product order, output name, declared
leaf, and allocation/discovery order do not enter that key.
The final exact non-terminal dotted component named `vendor` determines the
effective suffix and owner. A nested path uses its final `vendor`; `vendorx` is
ordinary; and a path ending exactly in `vendor` names an ordinary package.
After resolution and action interning, every source edge first performs vendor
visibility and then verifies source spelling. An allowed importer that directly
spells an expanded path receives the source-position diagnostic:
```text
<expanded-path> must be imported as <effective-suffix>
```
An outside importer receives, with visibility taking diagnostic precedence:
```text
use of vendored package not allowed
```
Directly selecting a directory below `vendor` remains legal because a command
root is not a source import.
Visibility derives the owner from the current edge's resolved lexical vendor
route and only then canonicalizes that owner physically. It never strips
components from the already-canonical target, whose symlink shape may have a
different depth. The importer is its canonical physical directory. Equality or
a real `/`-component descendant is allowed; a raw string prefix is not.
Consequently an importer reached through a symlink is judged by its physical
containment, while a vendor target symlinked to a differently shaped physical
directory keeps the owner established by the lexical vendor route. The check
runs after intern/reuse on every source edge, so an action loaded by an allowed
importer cannot authorize a later forbidden spelling or importer.
Cstage represents the resolution with `route` and `source_root` in
`sepcontext`, the bounded `sep_resolve_source_import` and vendor helpers, stable
integer action references in typed source bindings, and transient
`{package,context}` loader children. WWstage uses the isomorphic `sepcontext`,
`sepresolvesourceimport`, typed bindings, and transient child vector. The
package-global dependency set remains the sorted/deduplicated canonical action
set used for compilation and linking; contextual child traversal never creates
a second universe or contaminates action identity. All new storage grows with
checked allocation, and package/context vectors continue to expose only stable
integer references across growth.
A compile still receives one sorted, deduplicated
`--import <expanded-path> <dependency.wwi>` triple for each direct dependency.
When source spelling differs, the driver additionally supplies the sorted,
unique auxiliary mapping:
```text
--import-map <source-spelling> <expanded-path>
```
Both compilers require the map target to be an existing direct `--import`,
require source keys to be sorted and unique, require the final import leaf to
remain unchanged, and require a matching import in the primary source input.
After parsing only that primary input and before prepending imported interfaces,
the compiler replaces its semantic import key while preserving its source leaf,
position, and spelling bytes. Imported `.wwi` nodes and synthetic sources are
never rewritten. Thus the map adds no dependency or export input, expanded
identity flows into self-contained `.wwi` ownership and symbols, and direct
exports still have no transitive leakage. Linking remains independent: each
executable consumes its root archive and complete reachable archive closure,
never `.wwi` or import-map inputs.
Persistent units record resolution identity in ignored, deterministic comments:
```text
//ww:vendor-dir <hex-canonical-package-directory>
//ww:import-map <source-spelling> <expanded-path> <hex-canonical-target-directory>
```
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 13 and test 12. An equivalent
warm request remains a package-production no-op; an export change propagates
only through ordinary direct-export comparison.
Production, same-package internal-test, external `_test`, and real source
imports inside the test-support package all use this source-edge resolver.
Generated-main-to-variant, generated-main-to-support, and coordinator product
edges remain synthetic and receive no map or retroactive source legality.
`cmd/wwtest` remains a dispatcher. `internal/wwpackage` still discovers and
classifies source groups and submits one command-global union; it performs no
vendor resolution. Its only related state change is transactional cleanup: if
it created a cold hashed request workdir and the driver rejects before a tool
stamp exists, it removes that empty request directory.
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.
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
IDs, a package database, a CAS, registry access, or network lookup, and it is
separate from the future locked vendor store in section 4.6.
The rule follows the pinned official Go 1.26.5 source at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`:
- source-derived imports are expanded and the expanded vendor path becomes the
canonical import path
([`pkg.go`, lines 658668](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L658-L668));
- resolution and canonical package-cache reuse occur before the contextual
internal/vendor checks, which still run with the importing position on every
source edge
([`pkg.go`, lines 722796](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L722-L796));
- vendor lookup walks importer ancestors nearest-first to the applicable root,
requires a source-bearing directory, and records the expanded identity
([`pkg.go`, lines 12131263](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1213-L1263));
- the source-bearing probe accepts any non-directory `.go` suffix and ignores
directory-read failure
([`pkg.go`, lines 14181429](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1418-L1429));
- command roots remain legal, while an expanded source spelling must use its
effective suffix
([`pkg.go`, lines 15931617](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1593-L1617));
- vendor ownership uses component-aware physical containment and symlink
expansion
([`pkg.go`, lines 16201667](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1620-L1667));
- the final exact non-terminal `vendor` component controls the rule
([`pkg.go`, lines 16701688](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L1670-L1688));
- production, internal-test, and external-test imports retain distinct raw
source spellings/positions but each resolves canonically, while generated
test-main wiring is synthetic
([`test.go`, lines 85173](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L85-L173),
[`test.go`, lines 175266](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L175-L266),
[`test.go`, lines 272373](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L272-L373),
[`go/build/build.go`, lines 415493](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L415-L493)); and
- action caching consumes the already-resolved package graph, while output
publication remains a later concern
([`action.go`, lines 437455](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L437-L455),
[`action.go`, lines 628659](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L628-L659),
[`build.go`, lines 470558](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/build.go#L470-L558)).
The native observer `vendor_directory_import_resolution` generates every source
tree temporarily and runs both stages from independent cold work roots. It
proves nearest/outer/root/ordinary selection and the active-root boundary;
source-bearing versus empty candidates; distinct and reused actions, including
same-physical symlink targets; exact spelling/visibility diagnostics and
product-order reversal; final-component, terminal-name, component-prefix, and
symlink behavior; direct vendored roots; production/internal/external/support
source imports and synthetic-main isolation; owner-only sorted units, exact
direct exports/import maps, archive-only link closures, runtime output,
stage-equal normalized argv/artifacts/binaries, warm no-op production, and cold
and warm rejection-state preservation.
## 12. Candidate architectures and hard-gate decision
Five candidates were developed as coherent systems, not as feature bins.