test: prove case-fold collision preflight
This commit is contained in:
@@ -311,11 +311,21 @@ and matches its final single `linux` token, exactly as Go does.
|
||||
Selection is additive, not replacement-based: every matching file belongs to
|
||||
the package. The production variant then excludes `*_test.ww`; internal and
|
||||
external test classification uses only the already platform-selected test
|
||||
files. An excluded file creates no source occurrence, import, dependency edge,
|
||||
package/action/variant identity, compiler input, export, archive member, link
|
||||
input, artifact, status, or persistence dependency. Adding or editing one is a
|
||||
producer no-op. Adding, removing, or editing a selected file changes the owning
|
||||
unit normally.
|
||||
files. After those decisions, distinct selected basenames in one canonical
|
||||
directory MUST NOT be equal under Go 1.26.5's Unicode simple-fold comparison.
|
||||
The check spans the production, internal-test, and external-test selections of
|
||||
one `ww test` product without merging those source units. An ordinary
|
||||
`ww build` sees production names only. Exact basename reuse by another action
|
||||
view of that directory is not a collision.
|
||||
|
||||
An excluded file creates no source occurrence, collision, import, dependency
|
||||
edge, package/action/variant identity, compiler input, export, archive member,
|
||||
link input, artifact, status, or persistence dependency. Adding or editing one
|
||||
is a producer no-op. Adding, removing, or editing a selected noncolliding file
|
||||
changes the owning unit normally. This applicability is deliberately narrower
|
||||
than Go's `Package.AllFiles`: WW omits wrong-platform and `*_test.ww` names
|
||||
from an ordinary build because those files are not loaded in WW's
|
||||
fixed-target, manifest-free source model.
|
||||
|
||||
WW implements no source-level build expressions, user tags, target descriptor,
|
||||
`UseAllFiles` escape, `+tag` replacement scheme, or manifest-defined selector.
|
||||
@@ -5588,6 +5598,154 @@ source-level build expressions, quoted/grouped/dot imports, and targets other
|
||||
than the separately specified fixed `linux/amd64` filename selection. None is
|
||||
used to define canonical package or directory-product identity.
|
||||
|
||||
### 11.23 Implemented case-fold collision preflight
|
||||
|
||||
This slice pins the collision semantics to Go 1.26.5, tag commit
|
||||
`c19862e5f8415b4f24b189d065ed739517c548ba`. The official command loader uses
|
||||
an exact import cache at `src/cmd/go/internal/load/pkg.go:633-636,757-775`,
|
||||
performs contextual and vendor resolution before cache lookup at
|
||||
`:840-911,974-1005`, owns one command-global folded import table at `:1725`,
|
||||
and rejects a second fold-equivalent import spelling at `:1950-1959`. Its
|
||||
selected-name collision is over `Package.AllFiles` (`:149-194`) and is applied
|
||||
at `:1991-2000`. `src/cmd/go/internal/str/str.go:32-89` defines the pinned
|
||||
`ToFold` and `FoldDup` algorithms; the direct, transitive, and filename
|
||||
expectations are in
|
||||
`src/cmd/go/testdata/script/list_case_collision.txt:1-41`. Filename discovery
|
||||
and package/test classification are ordered by
|
||||
`src/go/build/build.go:859-914,1005-1036,1076-1082,1450-1469`.
|
||||
|
||||
Before this slice, a Linux case-sensitive filesystem let distinct directories
|
||||
such as `domain.Foo` and `domain.foo`, expanded vendor identities, and selected
|
||||
files such as `File.ww` and `file.ww` build as unrelated packages or sources.
|
||||
Both Cstage and WWstage did so byte-identically. A symlink making the two import
|
||||
spellings reach one directory happened to trip the older exact
|
||||
directory/identity check, but that host-physical consequence was neither the Go
|
||||
diagnostic nor the required package-graph rule.
|
||||
|
||||
#### Exact identity and request-only folded keys
|
||||
|
||||
Canonical package identity remains the exact, case-sensitive effective dotted
|
||||
identity. It is still the key used by action interning, source import maps,
|
||||
`.wwi` ownership, mangled symbols, storage digests, archives, diagnostics, and
|
||||
link closure. Default bindings, explicit aliases, blank imports, declared
|
||||
package names, path leaves, artifacts, and physical directories do not replace
|
||||
it. Exact repeated occurrences therefore continue to form one canonical edge
|
||||
and one package action.
|
||||
|
||||
The loader graph additionally owns a checked, dynamically grown, request-only
|
||||
table from a simple-fold key to the first exact package representative. Every
|
||||
ordinary effective identity registers after contextual local/vendor expansion
|
||||
and before exact action reuse or physical-directory conflict checks. Exact
|
||||
reuse succeeds. A distinct spelling with the same key rejects as
|
||||
`ww: case-insensitive import collision: "A" and "a"`; WW byte-sorts the two
|
||||
exact spellings so root, request, import, and discovery order cannot select a
|
||||
different diagnostic. It never stores, interns, looks up, or publishes the
|
||||
folded spelling as package identity.
|
||||
|
||||
WW dotted import components are ASCII by construction: the C lexer/parser
|
||||
accept them through `cmd/wcc/lex.c:53-63` and
|
||||
`cmd/wcc/parse.c:1318-1353`, and the self-hosted syntax path mirrors that in
|
||||
`lib/ww/syntax/lex.ww` and `lib/ww/syntax/decl.ww`. Context-derived identities
|
||||
are revalidated, and arbitrary local filesystem bytes are escaped into ASCII.
|
||||
The general Unicode fold routine is nevertheless shared with filename
|
||||
preflight; for package identities its observable domain reduces exactly to
|
||||
ASCII case folding.
|
||||
|
||||
Vendor imports register the fully expanded canonical identity, not the short
|
||||
source spelling. Thus `lib.Foo` and `lib.foo` reached below one vendor owner
|
||||
collide as, for example, `domain.app.vendor.lib.Foo` and
|
||||
`domain.app.vendor.lib.foo`. Reaching one physical directory through two such
|
||||
identities changes neither ownership nor the diagnostic. The physical
|
||||
directory remains a resolution fact, never a substitute identity.
|
||||
|
||||
Production, same-package test (`ptest`), external test (`pxtest`), and
|
||||
product-scoped recompiled actions share their one exact ordinary package
|
||||
representative and do not re-register as different packages. External
|
||||
compiler identity may still carry `_test`; that action path is not the folded
|
||||
package key. Copy-on-write clones copy exact identity and do not register.
|
||||
Generated main has no ordinary import identity and does not register. Ordinary
|
||||
toolchain package `test` registers like any real package; only the reserved
|
||||
synthetic `__wwtest` role stays outside the ordinary representative table.
|
||||
|
||||
#### Selected source basenames
|
||||
|
||||
Directory enumeration first excludes leading-dot, leading-underscore,
|
||||
wrong-platform, and variant-ineligible files, validates the selected regular
|
||||
sources, and byte-sorts their names. Before delegation, the coordinator performs
|
||||
its required package-clause classification and parses selected production files
|
||||
to reject `@test` declarations outside `*_test.ww`. The delegated driver then
|
||||
registers each selected basename in a second request-only table scoped by
|
||||
canonical physical directory. Repeated views of the exact same selected
|
||||
basename across `p`, `ptest`, or `pxtest` are accepted. Distinct fold-equivalent
|
||||
basenames reject as
|
||||
`ww: case-insensitive file name collision: "File.ww" and "file.ww"` before the
|
||||
driver's graph-import scan or any producer. The preflight is not an earlier
|
||||
replacement for the coordinator-owned source validation parse.
|
||||
|
||||
One `ww test` request shares that directory scope across production,
|
||||
same-package test, external test, same-only, external-only, and mixed test-only
|
||||
actions while preserving their separate compilation units. This catches a
|
||||
collision crossing classifications, such as production `X_TEST.ww` and test
|
||||
`x_test.ww`. An ordinary `ww build` excludes `*_test.ww` before registration.
|
||||
Hidden, underscore-prefixed, and wrong-platform files never register and
|
||||
therefore create no collision or invalidation.
|
||||
|
||||
This last ordering is WW's explicit applicability boundary rather than a claim
|
||||
that every upstream `AllFiles` member is selected here. Go includes test files
|
||||
and some ignored Go files in `AllFiles`, so its ordinary build can diagnose a
|
||||
broader set. WW intentionally follows its existing fixed-target source
|
||||
eligibility and build/test isolation: files it does not load have no graph or
|
||||
persistence effect.
|
||||
|
||||
Filesystem basenames are arbitrary non-NUL bytes, so their fold keys reproduce
|
||||
the pinned Unicode 15.0 `unicode.SimpleFold` minimum-cycle behavior without
|
||||
locale or normalization. Each malformed UTF-8 byte contributes one U+FFFD to
|
||||
the temporary key, as Go string ranging does; diagnostics preserve the exact
|
||||
original byte and quote it as `\xNN`. Printable Unicode remains UTF-8, other
|
||||
nonprinting runes use Go-style `\u` or `\U` escapes, and composed/decomposed
|
||||
Unicode spellings are not normalized.
|
||||
|
||||
#### Tool, artifact, transaction, and persistence ownership
|
||||
|
||||
The coordinator owns initial eligibility and production/test classification;
|
||||
the delegated driver loader owns both fold checks. The language parser still
|
||||
owns the exact import occurrence and qualifier. The driver owns per-site self,
|
||||
internal, vendor, and imported-main structural legality, while the compiler
|
||||
checker owns file-local binding, use, and visibility. The export writer/reader
|
||||
owns exact canonical `.wwi` data. Compiler, assembler, archiver, and linker
|
||||
protocols did not change. Successful neighboring units, `.wwi`, assembly,
|
||||
objects, archives, generated mains, binaries, and exact tool argv therefore
|
||||
remain byte-identical in Cstage and WWstage.
|
||||
|
||||
All root and reachable dependency loading, fold registration, and final exact
|
||||
identity binding finish before scratch acquisition, support or generated-main
|
||||
producer work, compilation, assembly, archive construction, or linking. A
|
||||
collision invokes none of those tools and creates no unit, `.wwi`, assembly,
|
||||
object, archive, main, binary, result, status, voucher, stamp, or `.new` stage.
|
||||
Request-wide publication remains transactional: committed sibling and
|
||||
dependency bytes survive a newly introduced collision, and removing the
|
||||
colliding source restores precise warm reuse.
|
||||
|
||||
The fold tables live only for one command and are freed at graph teardown.
|
||||
Entries become live only after every owned string and vector allocation
|
||||
succeeds, so allocation failure cannot publish a partial table or make cleanup
|
||||
traverse uninitialized entries. Successful action/unit/storage content did not
|
||||
change; build workdir format stays `18`, test workdir format stays `19`, and
|
||||
semantic storage stays `3`. A format bump would only discard valid exact-key
|
||||
artifacts and is therefore not used.
|
||||
|
||||
Native proof extends `package_graph_diagnostics_are_stable`,
|
||||
`platform_filename_source_selection`, and
|
||||
`vendor_directory_import_resolution`, with command-global allocation failure
|
||||
retained in `allocation_failure_is_command_global`. The matrix covers direct,
|
||||
transitive, reversed, recursive, same-directory, and vendor-expanded imports;
|
||||
exact-repeat acceptance; reversed vendor import and product order;
|
||||
production/internal/external/test-only filenames; reversed creation and
|
||||
direct/recursive collision-diagnostic parity; cross-classification, Unicode,
|
||||
invalid UTF-8, ignored files, and absence of normalization; zero-tool
|
||||
rejection; multi-product publication isolation; cold/warm add-remove reuse;
|
||||
exact artifact preservation; and Cstage/WWstage diagnostic and byte identity.
|
||||
|
||||
## 12. Candidate architectures and hard-gate decision
|
||||
|
||||
Five candidates were developed as coherent systems, not as feature bins.
|
||||
|
||||
23
docs/spec.md
23
docs/spec.md
@@ -269,8 +269,14 @@ ImportPath = ident { "." ident } .
|
||||
are ignored. Eligible names are byte-sorted before source validation.
|
||||
Production excludes selected `*_test.ww`; test variants classify only those
|
||||
selected test files. An excluded file contributes no declarations, imports,
|
||||
package edge, action, export, artifact, initialization, test, or persistent
|
||||
invalidation.
|
||||
filename collision, package edge, action, export, artifact, initialization,
|
||||
test, or persistent invalidation. After eligibility, two distinct selected
|
||||
basenames in one canonical directory that are equal under Go 1.26.5 Unicode
|
||||
simple folding are rejected after the coordinator's required package-clause
|
||||
classification and production `@test` validation parses, but before the
|
||||
delegated graph-import scan or tools. An ordinary build compares production
|
||||
names only; one test product compares its production, same-package test, and
|
||||
external-test selections without merging their units.
|
||||
- `import acme.codec;` loads the canonical package `acme.codec`. If that
|
||||
package declares `package wire;`, the importing file sees its exported names
|
||||
as `wire.Name`; `codec.Name` is not an additional binding. An explicit alias
|
||||
@@ -294,6 +300,12 @@ ImportPath = ident { "." ident } .
|
||||
edge. Test-only occurrences never enter ordinary production. Self-import is
|
||||
rejected, except that toolchain-owned external-test self wiring is rebound to
|
||||
the effective augmented package action after ordinary per-site validation.
|
||||
- Canonical import identity is exact and case-sensitive. After contextual local
|
||||
or vendor expansion, two distinct effective identities that are equal under
|
||||
Go 1.26.5 simple folding are a request-wide structural error. Folding is only
|
||||
a temporary collision key: it never changes lookup, action identity, `.wwi`
|
||||
ownership, symbols, artifacts, storage, or diagnostics. Repeated occurrences
|
||||
of the same exact identity remain valid and deduplicate normally.
|
||||
- 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
|
||||
@@ -618,6 +630,13 @@ under test binds to the augmented white-box action when it exists; affected
|
||||
transitive importers are copied and rewired so ordinary and augmented package
|
||||
state do not coexist in the linked closure.
|
||||
|
||||
Those variants are action distinctions over exact package representatives, not
|
||||
new ordinary package identities for case-fold comparison. Production,
|
||||
same-package test, external test, and recompiled copies of one exact canonical
|
||||
package therefore do not collide with each other. Their selected source units
|
||||
remain separate, while the directory-owned filename preflight spans the
|
||||
production/internal/external selections applicable to that test product.
|
||||
|
||||
Before a test function runs, the one generated product initializes its exact
|
||||
effective graph dependency-first and once per canonical action. Imports,
|
||||
runtime lets, and init declarations found only in `*_test.ww` never enter an
|
||||
|
||||
@@ -242,6 +242,22 @@ directory entry order. A recursive pattern omits a directory with no eligible
|
||||
source, while an explicit wrong-target-only build rejects it as having no WW
|
||||
package source. There is no source-level build-expression or user-tag mode.
|
||||
|
||||
After that eligibility boundary and the coordinator's required package-clause
|
||||
classification and production `@test` validation parses, the delegated loader
|
||||
performs selected-basename Go 1.26.5 simple-fold preflight before its graph
|
||||
import scan. During graph loading, effective canonical imports register only
|
||||
after contextual local/vendor expansion; exact identity reuse is accepted, but
|
||||
two distinct fold-equivalent identities reject the entire shared plan.
|
||||
Selected basenames register in one canonical-directory scope. A production
|
||||
build registers production names only. A test plan shares the filename scope
|
||||
across production, internal-test, and external-test actions, including
|
||||
same-only, external-only, and mixed directories, without combining their source
|
||||
units. Hidden, underscore-prefixed, wrong-platform, and otherwise ineligible
|
||||
files never register. Package variants, product-scoped recompiled copies,
|
||||
support, and generated main remain action wiring over exact identities and do
|
||||
not manufacture package collisions. The fold preflight does not replace or
|
||||
precede those coordinator-owned source-validation parses.
|
||||
|
||||
Separate compilation is the only driver build path; no compatibility mode
|
||||
switch remains.
|
||||
|
||||
@@ -294,6 +310,24 @@ have the same `ww: out of memory` diagnostic, invoke no compiler, assembler, or
|
||||
linker, and leave an empty caller work directory with no output, status, or
|
||||
staging path.
|
||||
|
||||
The same observer now owns selected-basename folding: ASCII production,
|
||||
internal, external, same-only, external-only, mixed, and cross-classification
|
||||
collisions; printable Unicode Kelvin folding; malformed UTF-8 replacement-rune
|
||||
keys with exact `\xNN` diagnostics; accepted composed/decomposed neighbors;
|
||||
ordinary-build isolation from test collisions; ignored folded pairs; reversed
|
||||
creation order; and direct/recursive collision-diagnostic parity. It proves zero
|
||||
compiler/assembler/linker calls and an empty workdir, including no
|
||||
test-support/main artifact, on cold rejection. A committed warm package then
|
||||
gains and loses a colliding file; the failed middle request preserves every
|
||||
unit, `.wwi`, assembly, object, archive, binary, tool record, and stamp, and
|
||||
removal restores exact reuse.
|
||||
`package_graph_diagnostics_are_stable` provides the corresponding direct,
|
||||
transitive, reversed-root, recursive, same-directory, exact-repeat, and warm
|
||||
import proof. `vendor_directory_import_resolution` proves folding uses the
|
||||
expanded vendor identity under reversed imports and product descriptors, even
|
||||
when both spellings reach one physical directory, and that multi-product
|
||||
rejection publishes no output, status, tool state, or staging residue.
|
||||
|
||||
`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,
|
||||
@@ -353,6 +387,13 @@ real directory test products always run, while no-selected-test directories do
|
||||
not create a process. The byte-identity and bootstrap gates keep building on
|
||||
fresh scratch. `make clean` reclaims every workdir under `out/`.
|
||||
|
||||
Fold keys and first-spelling tables are request-only loader state and are never
|
||||
stored in a workdir. They do not alter successful unit bytes, action/storage
|
||||
keys, or tool records, so build format 18, test format 19, and semantic storage
|
||||
format 3 remain unchanged. Structural fold rejection occurs before staging and
|
||||
cannot mutate a committed generation; a later request with the collision
|
||||
removed evaluates the original exact-key reuse contract.
|
||||
|
||||
On a package or tree target, `ww build/test -w DIR` forwards that exact caller
|
||||
path as the one command-global semantic-action store; neither pattern spelling
|
||||
nor package-group name derives a persistent subdirectory. The delegated driver
|
||||
|
||||
Reference in New Issue
Block a user