test: prove declared package import semantics

This commit is contained in:
2026-08-14 03:08:16 +09:00
parent 6efe9b70d4
commit a841278333
11 changed files with 1001 additions and 131 deletions

View File

@@ -2634,10 +2634,13 @@ commit and are implementation evidence, not requirements for the replacement.
### 11.2 Conflated identities and accidental behavior
This table records the baseline that the implemented slices below replaced;
sections 11.611.18 are authoritative where they conflict with it.
| Concept that must be separate | Current conflation or accident |
|---|---|
| package identity | Dotted import spelling is graph key, module/symbol prefix, artifact basename, and link identity. |
| declared name | A directory's leaf is checked against it for imports, but root directories and literal file roots receive different validation. `.wwi` itself retains only the leaf package name. |
| package identity | Before the local-package slices, dotted import spelling was simultaneously graph key, module/symbol prefix, artifact basename, and link identity. The implemented loader now separates source spelling, expanded canonical identity, physical directory, and storage locator. |
| declared name | Before section 11.18, imported directories required their declared name to equal the import-path leaf and `.wwi` retained only that leaf. The implemented compiler/export path now carries the declaration independently. |
| filesystem location | Ordered search roots silently choose/shadow a location; the same physical directory may be compiled under two import identities, while duplicate locations for one spelling produce no collision diagnostic. Paths are lexical, not content identities. |
| package versus file | Directories create separate-compilation nodes; files disappear into owners. The same `package` syntax means two compilation models. |
| artifact versus identity | `<dotted-path>.wwi/.s/.o/.a` names artifacts; root aliases to `__root`, which can collide with a real import. |
@@ -2802,19 +2805,22 @@ falls back to directory lookup through the entry package's directory, explicit
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.
imports-only parser, retains every real import occurrence with its owning
source file and position, byte-sorts and deduplicates the resulting canonical
direct edges, interns canonical directory actions, and reports self-imports and
stable cycle chains before compilation. Occurrence retention makes contextual
`internal` and `vendor` checks run at every import site; it does not duplicate
package actions or compiler inputs.
A directory package consists of its immediate `.ww` entries whose basenames do
not begin `.` or `_`: regular files and symlinks targeting regular files are
included under the entry name, while symlinks targeting directories are
ignored. The production variant excludes `*_test.ww`; each variant retains
byte-sorted filename order. Every selected file must
declare the same package. An ordinary importable directory's declaration must
equal the final component of its import path. A selected command directory
instead declares `package main` to validate command kind while retaining its
complete canonical import identity. A source import of a command package from a
byte-sorted filename order. Every selected production file must declare the
same package name, but that declaration is independent of the directory name
and every component of the canonical import path. A selected command directory
declares `package main` 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
ordinary logical identities for one physical directory are rejected rather
@@ -2842,9 +2848,11 @@ 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
bare types remain compiler errors. In `-c` package mode the compiler parses each
imports: a source qualifier is visible only in the source file that directly
imports it. The qualifier is the imported export's declared package name, not
the source spelling or import-path leaf. Private members, transitive-only
qualifiers, bare values, and bare types remain compiler errors. In `-c` package
mode the compiler parses each
export independently, then coalesces repeated exported type/constant facts with
the same origin, kind, and name, preserving one nominal type identity across
diamonds; raw non-package `w6c` retains its existing one-source behavior. The
@@ -2855,8 +2863,9 @@ boundary is live in production Cstage and WWstage compilers and drivers.
A selected production root is one normal package action. Its finalized
canonical import identity tags its owner-only unit; it receives only direct
exports, emits `.wwi`, `.o`, and a deterministic `.a`, and is compiled exactly
once. The declared package name validates the last component of that identity
for ordinary importable packages. After loading and identity finalization, the
once. The declared package name is semantic package content but never validates,
shortens, aliases, or replaces that identity. After loading and identity
finalization, the
declaration also selects the terminal build action: `package main` is a command
and every other valid declaration is a compile-only library. A command root
receives the narrow compiler `--entry` flag, which controls bare `main` codegen,
@@ -2869,8 +2878,8 @@ package declaration to classify and remains a raw command unit; it does not
participate in canonical directory-package interning.
The package driver still uses the parser-only `--command-package` marker for
command test variants that must remain ordinary archive code. Neither marker
changes export identity, and imported interfaces remain under strict leaf
validation. The linkers seed `main` before archive selection, so the existing
changes export identity, and imported interfaces retain independent canonical
owner and declared-name records. The linkers seed `main` before archive selection, so the existing
WWAR member protocol needs no special root object or format change.
Publication is separate from that semantic action choice. `ww build -o lib.a
@@ -2885,7 +2894,7 @@ action identity. The historical action-selecting `-p` exception is removed and
rejected as an unknown build flag. Assembly-only `-S` still stops before object,
archive, publication, or link production. A literal directory is
reverse-resolved through the active source roots or receives the deterministic
local identity described below; its declared leaf can never invent or truncate
local identity described below; its declaration can never invent or truncate
that identity. Two cold builds with identical inputs are required to produce
byte-identical requested products. Compiler intrinsics keep their package-mode
runtime ABI independent of transitive source interfaces (for example, `alloc`
@@ -2918,7 +2927,7 @@ 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 13 and test workdir format 12 invalidate older unit
Build workdir format 14 and test workdir format 13 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
@@ -2956,21 +2965,28 @@ package variant:
files followed by byte-sorted matching `package p` test files. It is distinct
from production so its private production declarations and test declarations
remain isolated to that test product.
- The external variant selects only matching `package p_test` files. Its
`import p` is a direct edge to the same ordinary production action used by
- When production sources establish `p`, the external variant selects only
matching `package p_test` files, where `p` is the production declaration
rather than an import-path leaf. A test-only directory may establish its own
one consistent test package name, matching pinned `go/build` classification.
A real source import of the production package uses its canonical import
path and is a direct edge to the same ordinary production action used by
ordinary products and transitive imports; there is no external-production
role or artifact.
- Generated main is a separate package action whose owner-only generated unit
declares `package main` and imports exactly the selected variant and test
support. It consumes those direct `.wwi` files, emits its own `.wwi/.o/.a`,
and alone receives compiler `-T --entry`.
and alone receives compiler `-T --entry`. Its target edge also receives the
compiler-private `--test-target-package <canonical-path>` binding. That
private canonical-path qualifier prevents a tested command declared `main`
from colliding with generated main; it is not user alias syntax.
The authoritative directory-action identity is the triple **(canonical
filesystem directory, canonical ordinary import path, semantic variant)**.
The semantic variants are production, internal production-plus-test, and
external `_test`; generated main and the reserved test-support alias are
explicit non-directory action classes. Source package name validates the leaf
of the bound import path but is not itself a second identity. Requested-root
explicit non-directory action classes. The declared package name is stored
separately and is not part of canonical directory/path interning. Requested-root
state, discovery role, product ordinal, output path, persistent artifact key,
and discovery order never enter the triple.
@@ -2999,17 +3015,19 @@ invocation, each still-unbound directory is finalized by this exact algorithm:
root cannot rename an explicitly resolved package.
4. If no active root can represent the directory, bind the reserved,
non-source-importable identity
`__wwlocal.p<escaped-canonical-absolute-directory>.<declared-leaf>`. The
`__wwlocal.p<escaped-canonical-absolute-directory>`. The
escape is injective and reversible over path bytes: ASCII letters and digits
are copied, `_` becomes `_u`, `/` becomes `_s`, and every other byte becomes
`_xHH` with lowercase hexadecimal. Source imports of `__wwlocal` or any of
its children are rejected, so this command-local identity creates no alias.
The selected full identity's final component is then validated against the
ordinary declared package name (or against the ordinary leaf obtained by
removing `_test` for the external variant). The one command-kind rule is that a
selected command family declares `main`/`main_test` while keeping the finalized
ordinary identity unchanged. A source import of that command from another
The selected full identity is never validated against the ordinary declared
package name. Production and internal variants retain the production
declaration; when production exists, an external variant is admitted only as
`<production-declared-name>_test`. A test-only directory instead establishes
one consistent test package declaration itself. The one command-kind rule is that a selected
command family declares `main`/`main_test` while keeping the finalized ordinary
identity unchanged. A source import of that command from another
directory rejects as a program before tools; a colocated external command test
may reuse the canonical production action. There is no fallback from an empty
import path to a declaration name. Relative, absolute, and symlink spellings
@@ -3027,9 +3045,10 @@ The derivation and diagnostics are implemented symmetrically in
variant descriptors, preserves an explicitly resolved identity only for one
direct request, and forwards a caller's `-w` semantic-action store unchanged.
It never derives identity or persistent layout from a pattern traversal prefix
and does not add that prefix to import search. `w6c` and `wcc` continue to
consume and validate the finalized dotted identity; neither tool performs
directory lookup or introduces a package registry.
and does not add that prefix to import search. `w6c` and `wcc` consume the
finalized dotted identity as export/symbol owner while reading the declared
name independently from export data; neither tool performs directory lookup or
introduces a package registry.
Artifact publication follows the semantic action instead of product order:
production uses the full finalized ordinary identity, internal appends
@@ -3352,11 +3371,13 @@ 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
target an ordinary direct `--import`; it adds no export input or graph edge.
The compilers rewrite only the matching primary import's canonical semantic key
before merging interfaces. They then obtain that target's declared package name
from its direct export and install it as the default qualifier in the owning
source file. Source spelling and position remain intact; generated/synthetic
imports use their explicit compiler-owned bindings. 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
@@ -3533,14 +3554,13 @@ and storage-address derivation. The complete `path`, together with the
semantic `variant` and `role`, is the persisted/compiler owner carried through
source-import edges, module-reset and export ownership, compiler `--import`
arguments, generated-main construction, and symbol qualification; `storage`
participates in none of those identities. The reversible outside-root form
remains
`__wwlocal.p<escaped-canonical-absolute-directory>.<declared-leaf>` and is
participates in none of those identities. The reversible outside-root form is
`__wwlocal.p<escaped-canonical-absolute-directory>` and is
allocated to its exact length. It is neither truncated nor replaced by a
digest, and `__wwlocal` remains unavailable to source imports. Package
declarations continue to validate the selected package kind and leaf; they do
not supply a missing identity and do not alter a command package's canonical
path.
declarations classify package kind and contribute semantic export content; they
do not validate a path leaf, supply a missing identity, or alter a command
package's canonical path.
Short actions retain their established `.unit.ww`, `.wwi`, `.s`, `.o`, and
`.a` basenames when the basename plus `.unit.new` fits the 255-byte supported
@@ -3712,6 +3732,9 @@ The Cstage representation is exact and deliberately small:
`depcap`;
- each `seppkg.context_state` is a lazily extended, zero-filled
`unsigned char *` with `context_cap`;
- each package owns a dynamically grown import-occurrence vector recording
kind, source spelling, source file, line, column, and stable dependency
action index; its separate dependency vector remains sorted/deduplicated;
- parsed `sepproduct` values are a dynamically allocated vector, and each
product stores its support-action index directly; and
- package-load frames and topological-DFS frames are temporary dynamic vectors,
@@ -3720,7 +3743,8 @@ The Cstage representation is exact and deliberately small:
The WWstage representation is isomorphic. `sepgraph.pkg: []seppkg` and
`sepgraph.context: []sepcontext` use allocated slice length as capacity and keep
separate `n`/`ncontext` logical counts. Every `seppkg` owns a dynamically grown
`deps: []i32` with `ndeps` and a lazily zero-extended `contextstate: []u8`.
`bindings: []sepbind`, a dynamically grown `deps: []i32` with `ndeps`, and a
lazily zero-extended `contextstate: []u8`.
Products, load frames, and topological frames use typed dynamically allocated
slices. `internal/wwpackage` continues to construct one union command for all
selected directory-test groups, but now checks the complete
@@ -4051,7 +4075,9 @@ 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,
directory. The target's declared name independently supplies the default
qualifier in each importing source file. 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
@@ -4110,12 +4136,13 @@ unique auxiliary mapping:
```
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
require source keys to be sorted and unique, and require a matching import in
the primary source input. No leaf-equality condition exists. After parsing the
primary input and before prepending imported interfaces, the compiler replaces
its semantic import key while preserving source position and spelling. It then
reads the expanded target's declared name from the direct `.wwi` and installs
that name only in the declaring source file. 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,
@@ -4132,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 13 and test 12. An equivalent
positions. The current workdir formats are build 14 and test 13. An equivalent
warm request remains a package-production no-op; an export change propagates
only through ordinary direct-export comparison.
@@ -4320,7 +4347,8 @@ and cannot make one variant visible to another.
Vendor selection remains distinct from vendor import resolution. Selecting a
directory below `vendor`, literally or through an explicitly vendor-rooted
pattern, keeps its complete canonical local identity; it is never shortened to
the suffix after `vendor`. A generic recursive pattern does not expose vendored
the suffix after `vendor`, and its declaration never renames it. A generic
recursive pattern does not expose vendored
descendants as ordinary short command roots. Independently, an allowed real
source import still searches nearest-first below local `vendor`, creates the
expanded identity described in section 11.16, supplies the required
@@ -4355,8 +4383,8 @@ files. A compiler receives exactly the sorted, deduplicated `.wwi` exports of
direct source dependencies and any required vendor import-map binding. Each
executable link receives its root archive and complete reachable archive
closure, never a `.wwi`. Canonical duplicate roots reuse the same action; the
pattern text and the product receiving an output do not affect semantic or
persistent identity.
pattern text, declared qualifier, and product receiving an output do not affect
canonical or persistent action ownership.
Selection and package-clause validation finish before the coordinator creates
its removable temporary plan, and the coordinator never creates persistent
@@ -4457,6 +4485,208 @@ rejection-state preservation. The existing
diamond and long-closure observers independently prove sorted/deduplicated
direct `.wwi` cardinality and archive-only reachable link closures.
### 11.18 Implemented canonical identity, declared-name, and file-import-scope slice
Directory-package identity and source naming are now independent throughout
local build and test. For example:
```ww
// canonical import identity: acme.codec
package wire;
```
is imported with the existing dotted syntax:
```ww
package main;
import acme.codec;
export fn main() i32 = { return wire.value(); };
```
The package action, exports, symbols, archives, dependency edges, persistent
storage ownership, and link closure remain owned by `acme.codec`. Only the
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:
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.
Both drivers retain one dynamically allocated `sepbind` occurrence for every
real import site. The occurrence stores the source spelling and position plus a
stable action index. Contextual `internal` and vendor resolution is therefore
rechecked for every source import even if its target action already exists. A
separate package dependency vector unions those occurrences, deduplicates by
canonical action, and sorts by complete canonical identity/variant/role. That
vector alone supplies dependency traversal and compiler `--import` arguments.
Patterns, qualifiers, declared names, export closure facts, and generated edges
never create source dependency edges.
The owner-only composed unit preserves byte-sorted source boundaries with one
`//ww:module-reset <canonical-owner>` separator per file. Parser nodes carry a
source-section ID as well as canonical owner and declared package name. Each
real `N_USE` therefore belongs to one source section. `.wwi` emission repeats
canonical-owner/package markers as needed for contributing source sections and
retains imports only with the declarations from the file that owned them.
Interfaces remain source-like transitional data, but canonical owner and
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
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
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
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.
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
different name. Any ordinary source import of a package declared `main` is
rejected as `package <canonical-path> is a program, not an importable package`,
regardless of its path leaf. The one pinned loader exception is an external
test's exact same-directory import of the command production: it is rewired to
the forced-library test copy. WW admits only that canonical colocated edge.
Test naming is likewise declaration-based. Production and internal-test
variants use the production declared name. When production files exist,
external files must declare `<production-declared-name>_test`; a test-only
directory may establish one consistent package name from its test files, as in
pinned `go/build`. External action identity remains the canonical production
identity plus the existing external variant suffix where that production
exists. Imports found only in internal or external test files belong only to
that variant. Support and generated-main actions retain their isolated
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.
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.
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.
Compiler argv still contains exactly the sorted, deduplicated `.wwi` exports of
direct canonical dependencies; no transitive `.wwi` and no qualifier-derived
path appears. Linker argv still contains only the executable root archive and
complete reachable archive closure plus runtime/native inputs. Publication and
completion occur only after the corresponding action/product succeeds.
Responsibility is intentionally split as follows:
- `internal/wwpackage` classifies production, internal, and external test files
from their declarations, derives the allowed external name from the
production declaration, expands request patterns, and submits variant roots.
It does not resolve imports, choose qualifiers, or create dependency edges.
- `cmd/ww/main.c` and `selfhost/cmd/ww/main.ww` own per-site parsing and
contextual resolution, canonical directory/action interning, declared-name
consistency, imported-command rejection, sorted dependency union, exact tool
argv, variant/generated-main construction, persistence, linking, and
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.
- `cmd/wwtest` remains only the test-command dispatcher.
The behavior follows pinned official Go 1.26.5 source at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`:
- `go/build.Package` stores `Dir`, `Name`, `ImportPath`, production files, and
test files independently, while package-clause consistency is checked during
file classification
([`go/build/build.go`, lines 436493](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L436-L493),
[`go/build/build.go`, lines 9391049](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L939-L1049)).
- The loader interns by resolved `ImportPath`, keeps `ImportPath` and `Name`
separate, constructs edges from source imports, and rejects an imported
package whose `Name == "main"` except for the exact same-directory test case
([`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)).
- The test loader builds distinct production/internal/external/generated-main
packages, derives external `Name` from `p.Name + "_test"`, handles the
same-directory command self-import, and rewrites it to the library-form test
copy
([`cmd/go/internal/load/test.go`, lines 144203](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L144-L203),
[lines 228293](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L228-L293),
[lines 421472](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/test.go#L421-L472)).
- `go/types.Package` stores independent path and name. The resolver creates one
child scope per source file, uses the imported package's declared name when
no alias is present, inserts imports into that file scope, reconciles them
with package declarations, and reports unused imports
([`go/types/package.go`, lines 2640](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/package.go#L26-L40),
[`go/types/resolver.go`, lines 237350](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L237-L350),
[lines 463480](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L463-L480),
[lines 701735](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/types/resolver.go#L701-L735)).
The production compiler mirrors those rules
([`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 473486](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L473-L486),
[lines 706740](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L706-L740)).
- Compiler export import reconstructs and interns package descriptors by
canonical path while restoring their independently encoded package names and
imports
([`cmd/compile/internal/importer/ureader.go`, lines 152196](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/importer/ureader.go#L152-L196),
[`go/internal/gcimporter/ureader.go`, lines 224244](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/internal/gcimporter/ureader.go#L224-L244)).
- Work actions consume cached canonical package objects and direct imports,
while test execution builds and links the isolated test action graph
([`cmd/go/internal/work/action.go`, lines 437455](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/action.go#L437-L455),
[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.
The focused native observer
`declared_name_identity_and_file_import_scope` generates every tree
temporarily and exercises both stages from independent cold roots. It proves
the identity/name/qualifier split, file-local collision and unused behavior,
one-edge/action reuse, command and imported-command rules, all test variants,
vendor expansion, recursive/direct selection, owner-only units, exact direct
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.
## 12. Candidate architectures and hard-gate decision
Five candidates were developed as coherent systems, not as feature bins.

View File

@@ -249,13 +249,25 @@ PackageClause = "package" ident ";" .
ImportDecl = "import" ident ";" .
```
- Every source file begins with a package clause. A directory of `.ww`
files sharing one package name compiles as a single module.
- `import foo;` makes module `foo`'s exported names available as
`foo.Name`. Self-import is rejected.
- An executable's entry module is `package main;` with a `fn main`.
- Every source file begins with a package clause. A directory of eligible
`.ww` files sharing one declared package name compiles as one package. The
declared name need not equal the directory name or the final component of its
canonical import identity.
- `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. The import binding
is scoped to that source file. A sibling file must declare its own import.
The package dependency graph is the sorted, deduplicated union of the real
imports in all eligible files. 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
colocated external-test wiring.
- Only names marked `export` (§5) are visible across module boundaries.
The implemented grammar remains dotted and unaliased. Quoted import paths,
explicit aliases, dot imports, and blank imports are not currently accepted.
---
## 5. Declarations

View File

@@ -227,6 +227,20 @@ owns individual `@test` functions.
Separate compilation is the only driver build path; no compatibility mode
switch remains.
`test/package/package_test.ww` also owns the dual-stage
`declared_name_identity_and_file_import_scope` observer. It generates temporary
directory trees proving that canonical import identity, physical directory,
declared package name, and source-file default qualifier remain distinct;
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
declared qualifier; compiler argv contains only direct `.wwi` inputs; and its
named rejected actions leave neither committed nor staged action artifacts or a
published binary. 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.
`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

View File

@@ -2,12 +2,12 @@ package wwfixture;
def protocolversion: i32 = 1;
def corpuscount: i32 = 1759;
def errorcount: i32 = 352;
def compilecount: i32 = 21;
def errorcount: i32 = 351;
def compilecount: i32 = 22;
def runcount: i32 = 209;
def runexitcount: i32 = 1177;
def nativecount: i32 = 3518;
def corpushash: str = "38613db49b4c87a14d87fb9392e5500e17cc0c95767fb31d8eb9bc4fe2bf74c5";
def corpushash: str = "47d731a8fd089ecdef9a1b9b94f3dfc06a733b7e03236014f095b068a67ce176";
type directive = enum i32 {
ERROR = 0,

View File

@@ -21,15 +21,16 @@ package wwi_test;
// qualification of the compiler-private name on both stages.
//
// wwileaf — BUG-C (#11) regression pin: a decl-less / export-less
// primary module's `.wwi` `package` line must carry the module's real
// leaf, NOT the literal default "main". Table-driven over the three
// primary module's `.wwi` must preserve its independently parsed declaration,
// rather than substituting the literal default "main". Table-driven over the three
// decl-less shapes routing the wwi_emit fallback (empty, comment-only,
// nested dotted path a.b.c -> leaf c). Each row drives the REAL
// producer->importer flow on BOTH stages: (a) the dep `.wwi` package
// owner marker is the complete path and its package line equals the real leaf,
// (b) the dep `.wwi` is byte-identical
// across stages, (c) a root importing the dep RESOLVES on both stages
// and the two importer `.s` are byte-identical. The owner units and
// and both importers diagnose the same file-local unused binding after
// obtaining that declared name from export data. The owner units and
// separate exports are fed straight to w6c / w6c_ww (not `ww build`):
// the producer-unit shape `//ww:module-reset <path>` + body is exactly
// what the driver's sep_emit_body emits.
@@ -331,8 +332,8 @@ fn leafrow(tag: str, path: str, leaf: str, body: str, want: str) void = {
"package ", leaf, ";\n",
body));
// Leg a — both stages emit the dep `.wwi`; the `package` leaf is
// the module's real leaf, not the default "main".
// Leg a — both stages emit the dep `.wwi`; the `package` declaration is
// the source declaration supplied independently of its canonical owner.
let cav: []str = [testenv.driver("w6c"), "-c", "-I", cswwi,
"-o", css, prod];
if (!runok(td, "csprod", cav)) { fail(tag, "w6c producer errored"); };
@@ -342,7 +343,7 @@ fn leafrow(tag: str, path: str, leaf: str, body: str, want: str) void = {
let wanthead: str = strings.concat(strings.concat(strings.concat(
"//ww:module ", path), "\npackage "), strings.concat(want, ";\n"));
if (!strings.hasprefix(testenv.readfile(cswwi), wanthead)) {
fail(tag, ".wwi owner or package leaf is incomplete (BUG-C)");
fail(tag, ".wwi owner or declared package name is incomplete (BUG-C)");
};
// Leg b — the dep `.wwi` is byte-identical across stages (rule 10).
@@ -350,9 +351,11 @@ fn leafrow(tag: str, path: str, leaf: str, body: str, want: str) void = {
fail(tag, "w6c vs w6c_ww .wwi differ (rule 10)");
};
// Leg c — a root importing the dep RESOLVES from a separate export on
// both stages (pre-fix: "package main does not match import path"
// REJECT).
// Leg c — a root importing the empty dep obtains its default qualifier from
// the separate export on both stages. With no exported declaration to use,
// Go-style file scope requires the import itself to be rejected as unused;
// the diagnostic proves that the reader recovered the declared name rather
// than substituting the historical default `main` declaration.
testenv.writefile(root, strings.concat(
"//ww:module-reset\n",
"package main;\n",
@@ -360,22 +363,59 @@ fn leafrow(tag: str, path: str, leaf: str, body: str, want: str) void = {
"export fn main() i32 = { return 42; };\n"));
let rcav: []str = [testenv.driver("w6c"), "-c", "--import", path,
cswwi, "-o", rcss, root];
if (!runok(td, "csroot", rcav)) {
fail(tag, "w6c rejected the import (BUG-C)");
};
let rwav: []str = [testenv.driver("w6c_ww"), "-c", "--import", path,
wwwwi, "-o", rwws, root];
if (!runok(td, "wsroot", rwav)) {
fail(tag, "w6c_ww rejected the import (BUG-C)");
let rco: testenv.commandout;
let rwo: testenv.commandout;
testenv.runcommand(td, td, "csroot", rcav, lifetime(), &rco);
testenv.runcommand(td, td, "wsroot", rwav, lifetime(), &rwo);
let (prefix, suffix) = strings.rcut(path, ".");
let leaf: str = suffix;
if (leaf.len == 0) { leaf = path; };
let unused: str;
if (testenv.same(want, leaf)) {
unused = strings.concat(strings.concat("\"", path),
"\" imported and not used");
} else {
unused = strings.concat(strings.concat(strings.concat(
"\"", path), "\" imported as "), strings.concat(want,
" and not used"));
};
if (!testenv.same(testenv.readfile(rcss), testenv.readfile(rwws))) {
fail(tag, "importer .s differ cs vs ww (rule 10)");
if (rco.termination != exec.termination.EXIT || rco.code == 0
|| rwo.termination != exec.termination.EXIT || rwo.code == 0
|| !testenv.same(rco.stderr, rwo.stderr)
|| !testenv.has(rco.stderr, unused)) {
fail(tag, "imported-name or unused-import behavior differs by stage");
};
testenv.clean(td);
};
fn resetfallbackrow() void = {
let td: str = testenv.fresh();
let src: str = strings.concat(td, "/reset.ww");
testenv.writefile(src, strings.concat(
"//ww:module-reset raw.owner\n",
"export fn value() i32 = { return 7; };\n"));
let cs: str = strings.concat(td, "/c.wwi");
let ws: str = strings.concat(td, "/w.wwi");
let cav: []str = [testenv.driver("w6c"), "-c", "-I", cs,
"-o", strings.concat(td, "/c.s"), src];
let wav: []str = [testenv.driver("w6c_ww"), "-c", "-I", ws,
"-o", strings.concat(td, "/w.s"), src];
if (!runok(td, "reset-c", cav) || !runok(td, "reset-w", wav)) {
fail("nested", "a package-less reset owner failed to export");
};
let body: str = testenv.readfile(cs);
if (!testenv.same(body, testenv.readfile(ws))
|| !testenv.has(body, "//ww:module raw.owner\npackage main;\n")
|| !testenv.has(body, "export fn value() i32;")) {
fail("nested", "reset-owner fallback lost its source section");
};
testenv.clean(td);
};
// Each shape lacks any module-tagged decl, so wwi_emit's decl-scan
// misses and the leaf must come from the parse-stamped N_FILE identity.
// misses and the declaration must come from the parse-stamped N_FILE fact.
@test fn wwileaf_empty() void = {
leafrow("empty", "emptymod", "emptymod", "", "emptymod");
};
@@ -386,4 +426,5 @@ fn leafrow(tag: str, path: str, leaf: str, body: str, want: str) void = {
@test fn wwileaf_nested() void = {
leafrow("nested", "a.b.c", "c", "", "c");
resetfallbackrow();
};

View File

@@ -319,8 +319,8 @@ fn workescape(s: str) str = {
return strings.frombytes(out);
};
fn localidentity(dir: str, leaf: str) str = {
let out: []u8 = alloc([], (dir.len * 4 + leaf.len + 16): u64)!;
fn localidentity(dir: str, declaredname: str) str = {
let out: []u8 = alloc([], (dir.len * 4 + 16): u64)!;
let prefix: str = "__wwlocal.p";
let i: i32 = 0;
for (i < prefix.len) { append(out, prefix[i]); i += 1; };
@@ -343,9 +343,6 @@ fn localidentity(dir: str, leaf: str) str = {
};
i += 1;
};
append(out, '.');
i = 0;
for (i < leaf.len) { append(out, leaf[i]); i += 1; };
return strings.frombytes(out);
};
@@ -1241,14 +1238,16 @@ fn hexbytes(value: str) str = {
let externalmaincompile: str = linecontaining(ctrace,
"pkg_test-external-test-main.unit.ww");
assert(same(samemaincompile, strings.concat(
"-T --entry --test-support-module test -c --import pkg ",
"-T --entry --test-support-module test ",
"--test-target-package pkg -c --import pkg ",
sharedwork, "pkg-internal-test.wwi --import test ",
sharedwork, "test.wwi -I ", sharedwork,
"pkg-internal-test-main.wwi -o ", sharedwork,
"pkg-internal-test-main.s ", sharedwork,
"pkg-internal-test-main.unit.ww")));
assert(same(externalmaincompile, strings.concat(
"-T --entry --test-support-module test -c --import pkg_test ",
"-T --entry --test-support-module test ",
"--test-target-package pkg_test -c --import pkg_test ",
sharedwork, "pkg_test-external-test.wwi --import test ",
sharedwork, "test.wwi -I ", sharedwork,
"pkg_test-external-test-main.wwi -o ", sharedwork,
@@ -1495,9 +1494,9 @@ fn hexbytes(value: str) str = {
aliasunit];
runcommand(root, strings.concat("alias-unowned-", aliasstages[i]),
rawav, (30i64 * (time.second: i64)): time.duration, &out);
assert(out.termination == exec.termination.EXIT && out.code != 0);
assert(has(out.stderr, "package")
&& has(out.stderr, "does not match import path"));
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
assert(os.exists(asmout));
let badav: []str = [driver(aliasstages[i]), "-c",
"--test-support-module", "arbitrary", "-o", asmout, aliasunit];
runcommand(root, strings.concat("alias-arbitrary-", aliasstages[i]),
@@ -4226,9 +4225,13 @@ fn hexbytes(value: str) str = {
let leftbody: str = strings.concat(
"package parityleft;\n",
"import ", baseidentity, ";\n",
"import ", baseidentity, ";\n",
"export fn value() i32 = { return ",
"paritybase.value(); };\n");
let leftz: str = strings.concat(
"package parityleft;\n",
"import ", baseidentity, ";\n",
"export fn repeated_binding() i32 = { return ",
"paritybase.value(); };\n");
let rightbody: str = strings.concat(
"package parityright;\n",
"import ", baseidentity, ";\n",
@@ -4242,7 +4245,8 @@ fn hexbytes(value: str) str = {
"parityright.value() + 1; };\n");
writefile(strings.concat(base, "/a.ww"), basea);
writefile(strings.concat(base, "/z.ww"), basez);
writefile(strings.concat(left, "/left.ww"), leftbody);
writefile(strings.concat(left, "/a.ww"), leftbody);
writefile(strings.concat(left, "/z.ww"), leftz);
writefile(strings.concat(right, "/right.ww"), rightbody);
writefile(strings.concat(target, "/main.ww"), rootbody);
@@ -4316,7 +4320,8 @@ fn hexbytes(value: str) str = {
"//ww:module-reset ", baseidentity, "\n", basea,
"\n//ww:module-reset ", baseidentity, "\n", basez, "\n"),
strings.concat("//ww:module-reset ", leftidentity, "\n",
leftbody, "\n"),
leftbody, "\n//ww:module-reset ", leftidentity, "\n",
leftz, "\n"),
strings.concat("//ww:module-reset ", rightidentity, "\n",
rightbody, "\n"),
strings.concat("//ww:module-reset ", rootidentity, "\n", rootbody,
@@ -4425,9 +4430,8 @@ fn hexbytes(value: str) str = {
assert(has(assembly, strings.concat(identities[ai], ".value")));
};
if (ai == 3) {
assert(has(exportf, strings.concat("\npackage paritycommand",
";\n")));
assert(!has(exportf, "\npackage main;\n"));
assert(has(exportf, "\npackage main;\n"));
assert(!has(exportf, "\npackage paritycommand;\n"));
};
if (si == 0) {
referenceexports[ai] = strings.dup(exportf);
@@ -4476,18 +4480,23 @@ fn hexbytes(value: str) str = {
assert(same(linecontaining(ctrace, strings.concat(artifacts[3],
".unit.ww")), rootline));
assert(!has(rootline, strings.concat(artifacts[0], ".wwi")));
// The primary command declaration is accepted only when the compiler
// receives command classification from --entry. Imported interfaces and
// ordinary package compiles retain strict identity-leaf validation.
// Canonical owner and declared name stay independent even in a raw
// package compile. --entry selects link-root code generation; it is not
// permission to replace canonical owner with the declared name.
let rejectwwi: str = strings.concat(root, "/reject-", tags[si], ".wwi");
let rejectasm: str = strings.concat(root, "/reject-", tags[si], ".s");
let rejectav: []str = [driver(compilers[si]), "-c", "-I", rejectwwi,
"-o", rejectasm, strings.concat(work, artifacts[3], ".unit.ww")];
let rejectav: []str = [driver(compilers[si]), "-c", "--import",
leftidentity,
strings.concat(work, artifacts[1], ".wwi"),
"--import", rightidentity,
strings.concat(work, artifacts[2], ".wwi"),
"-I", rejectwwi, "-o", rejectasm,
strings.concat(work, artifacts[3], ".unit.ww")];
runcommand(root, strings.concat("command-without-entry-", tags[si]),
rejectav, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "does not match import path"));
assert(has(out.stderr, rootidentity));
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
assert(os.exists(rejectwwi) && os.exists(rejectasm));
let ownerrejectwwi: str = strings.concat(root, "/owner-reject-",
tags[si], ".wwi");
let ownerrejectasm: str = strings.concat(root, "/owner-reject-",
@@ -4726,11 +4735,11 @@ fn hexbytes(value: str) str = {
"package main_test;\n",
"import ", commandid, ";\n",
"@test fn command_external() void = { ",
"assert(cmdtool.value() == 41); };\n"));
"assert(main.value() == 41); };\n"));
writefile(strings.concat(importer, "/importer.ww"), strings.concat(
"package importer;\n",
"import ", commandid, ";\n",
"export fn value() i32 = { return cmdtool.value(); };\n"));
"export fn value() i32 = { return main.value(); };\n"));
writeexecutable(wrapper, strings.concat(
"#!/bin/sh\n",
"printf 'BEGIN' >> \"$WW_COMMAND_COMPILER_TRACE\"\n",
@@ -4830,8 +4839,12 @@ fn hexbytes(value: str) str = {
strings.concat(artifacts[4], ".unit.new"));
assert(has(internalmainline, strings.concat("<--import><", internalid,
"><", work, artifacts[1], ".wwi>")));
assert(has(internalmainline, strings.concat(
"<--test-target-package><", internalid, ">")));
assert(has(externalmainline, strings.concat("<--import><", externalid,
"><", work, artifacts[2], ".wwi>")));
assert(has(externalmainline, strings.concat(
"<--test-target-package><", externalid, ">")));
assert(occurrences(ctrace, strings.concat("<", work, artifacts[0],
".unit.new>")) == 1);
assert(has(readfile(strings.concat(work, artifacts[0], ".s")),
@@ -4870,7 +4883,7 @@ fn hexbytes(value: str) str = {
};
// The same command action is not generally source-importable. It is
// classified before leaf validation so this Go-like diagnostic is stable,
// classified from its declaration so this Go-like diagnostic is stable,
// and graph failure occurs before any compiler invocation.
clean(trace);
writefile(trace, "");
@@ -7844,6 +7857,547 @@ fn runtimepath(relative: str) str = {
// artifact in place of the fixed <package>.test stem; -o without -c
// has nothing to name (runs execute from the temp root); one name
// cannot fan out over multiple packages (Go's `go test -o` rule).
@test fn declared_name_identity_and_file_import_scope() void = {
let root: str = fresh();
let source: str = strings.concat(root, "/source");
let tools: str = strings.concat(root, "/tools");
let codec: str = strings.concat(source, "/acme/codec");
let bridge: str = strings.concat(source, "/acme/bridge");
let direct: str = strings.concat(source, "/cmd/direct");
let app: str = strings.concat(source, "/cmd/app");
let scopeone: str = strings.concat(source, "/scope/one");
let scopetwo: str = strings.concat(source, "/scope/two");
let scoped: str = strings.concat(source, "/cmd/scoped");
let repeated: str = strings.concat(source, "/cmd/repeated");
let leafbad: str = strings.concat(source, "/cmd/leafbad");
let leak: str = strings.concat(source, "/cmd/leak");
let duplicate: str = strings.concat(source, "/cmd/duplicate");
let collision: str = strings.concat(source, "/cmd/collision");
let crosscollision: str = strings.concat(source, "/cmd/crosscollision");
let conflict: str = strings.concat(source, "/conflict/pkg");
let leafmain: str = strings.concat(source, "/domain/main");
let program: str = strings.concat(source, "/domain/program");
let leafmainuser: str = strings.concat(source, "/cmd/leafmain");
let programuser: str = strings.concat(source, "/cmd/programuser");
let testcodec: str = strings.concat(source, "/testpkg/codec");
let testhelper: str = strings.concat(source, "/testonly/helper");
let vendored: str = strings.concat(source,
"/vend/vendor/short/codec");
let vendorclient: str = strings.concat(source, "/vend/client");
let dirs: []str = [source, tools, codec, bridge, direct, app, scopeone,
scopetwo, scoped, repeated, leafbad, leak, duplicate, collision,
crosscollision, conflict, leafmain, program, leafmainuser, programuser, testcodec,
testhelper, vendored, vendorclient];
let di: i32 = 0;
for (di < dirs.len) { mkdirall(dirs[di]); di += 1; };
let wirebody: str = strings.concat(
"package wire;\n",
"export fn value() i32 = { return 42; };\n");
let cablebody: str = strings.concat(
"package cable;\n",
"export fn value() i32 = { return 42; };\n");
let codecfile: str = strings.concat(codec, "/codec.ww");
writefile(codecfile, wirebody);
writefile(strings.concat(bridge, "/bridge.ww"), strings.concat(
"package bridge;\nimport acme.codec;\n",
// WW retains its direct-import bare-declaration convenience. It is
// useful here because changing only the dependency declaration leaves
// this source unchanged while still forcing its action to reconsider.
"export fn bridged() i32 = { return value(); };\n"));
writefile(strings.concat(direct, "/main.ww"), strings.concat(
"package main;\nimport acme.codec;\n",
"fn main() i32 = { return wire.value(); };\n"));
writefile(strings.concat(app, "/main.ww"), strings.concat(
"package main;\nimport acme.bridge;\n",
"fn main() i32 = { return bridge.bridged(); };\n"));
writefile(strings.concat(scopeone, "/one.ww"),
"package same;\nexport fn value() i32 = { return 20; };\n");
writefile(strings.concat(scopetwo, "/two.ww"),
"package same;\nexport fn value() i32 = { return 22; };\n");
writefile(strings.concat(scoped, "/a.ww"), strings.concat(
"package main;\nimport scope.one;\n",
"fn left() i32 = { return same.value(); };\n"));
writefile(strings.concat(scoped, "/b.ww"), strings.concat(
"package main;\nimport scope.two;\n",
"fn right() i32 = { return same.value(); };\n"));
writefile(strings.concat(scoped, "/main.ww"),
"package main;\nfn main() i32 = { return left() + right(); };\n");
writefile(strings.concat(repeated, "/a.ww"), strings.concat(
"package main;\nimport acme.codec;\n",
"fn first() i32 = { return wire.value(); };\n"));
writefile(strings.concat(repeated, "/b.ww"), strings.concat(
"package main;\nimport acme.codec;\n",
"fn second() i32 = { return wire.value(); };\n"));
writefile(strings.concat(repeated, "/main.ww"), strings.concat(
"package main;\n",
"fn main() i32 = { return first() - second(); };\n"));
writefile(strings.concat(leafbad, "/main.ww"), strings.concat(
"package main;\nimport acme.codec;\n",
"fn main() i32 = { return codec.value(); };\n"));
writefile(strings.concat(leak, "/a.ww"), strings.concat(
"package main;\nimport acme.codec;\n",
"fn anchor() i32 = { return 1; };\n"));
writefile(strings.concat(leak, "/b.ww"),
"package main;\nfn leaked() i32 = { return wire.value(); };\n");
writefile(strings.concat(leak, "/main.ww"),
"package main;\nfn main() i32 = { return leaked(); };\n");
writefile(strings.concat(duplicate, "/main.ww"), strings.concat(
"package main;\nimport scope.one;\nimport scope.two;\n",
"fn main() i32 = { return same.value(); };\n"));
writefile(strings.concat(collision, "/main.ww"), strings.concat(
"package main;\nimport scope.one;\n",
"fn same() i32 = { return 0; };\n",
"fn main() i32 = { return same(); };\n"));
writefile(strings.concat(crosscollision, "/a.ww"), strings.concat(
"package main;\nimport scope.one;\n",
"fn fromdep() i32 = { return same.value(); };\n"));
writefile(strings.concat(crosscollision, "/b.ww"), strings.concat(
"package main;\nfn same() i32 = { return 0; };\n",
"fn main() i32 = { return fromdep(); };\n"));
writefile(strings.concat(conflict, "/a.ww"),
"package first;\nexport fn a() i32 = { return 1; };\n");
writefile(strings.concat(conflict, "/b.ww"),
"package second;\nexport fn b() i32 = { return 2; };\n");
writefile(strings.concat(leafmain, "/utility.ww"),
"package utility;\nexport fn value() i32 = { return 7; };\n");
writefile(strings.concat(program, "/program.ww"),
"package main;\nexport fn value() i32 = { return 9; };\n");
writefile(strings.concat(leafmainuser, "/main.ww"), strings.concat(
"package main;\nimport domain.main;\n",
"fn main() i32 = { return utility.value(); };\n"));
writefile(strings.concat(programuser, "/main.ww"), strings.concat(
"package main;\nimport domain.program;\n",
"fn main() i32 = { return main.value(); };\n"));
writefile(strings.concat(testhelper, "/helper.ww"),
"package helper;\nexport fn value() i32 = { return 1; };\n");
writefile(strings.concat(testcodec, "/codec.ww"), wirebody);
writefile(strings.concat(testcodec, "/internal_test.ww"), strings.concat(
"package wire;\nimport testonly.helper;\n",
"@test fn internal_name_uses_declaration() void = {\n",
" assert(value() + helper.value() == 43);\n};\n"));
writefile(strings.concat(testcodec, "/external_test.ww"), strings.concat(
"package wire_test;\nimport testpkg.codec;\n",
"@test fn external_name_uses_declaration() void = {\n",
" assert(wire.value() == 42);\n};\n"));
writefile(strings.concat(vendored, "/codec.ww"),
"package cable;\nexport fn value() i32 = { return 13; };\n");
writefile(strings.concat(vendorclient, "/a.ww"), strings.concat(
"package main;\nimport short.codec;\n",
"fn vendora() i32 = { return cable.value(); };\n"));
writefile(strings.concat(vendorclient, "/b.ww"), strings.concat(
"package main;\nimport short.codec;\n",
"fn vendorb() i32 = { return cable.value(); };\n"));
writefile(strings.concat(vendorclient, "/main.ww"),
"package main;\nfn main() i32 = { return vendora(); };\n");
let compilerwrapper: str = strings.concat(tools, "/w6c.sh");
writeexecutable(compilerwrapper, strings.concat(
"#!/bin/sh\n",
"printf 'BEGIN' >> \"$WW_NAME_COMPILER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_NAME_COMPILER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_NAME_COMPILER_TRACE\"\n",
"exec \"$WW_NAME_REAL_COMPILER\" \"$@\"\n"));
let stages: []str = ["ww", "ww_ww"];
let compilers: []str = ["w6c", "w6c_ww"];
let assemblers: []str = ["w6a", "w6a_ww"];
let linkers: []str = ["w6l", "w6l_ww"];
let works: []str = [strings.concat(root, "/c-work"),
strings.concat(root, "/ww-work")];
let traces: []str = [strings.concat(root, "/c-compiler"),
strings.concat(root, "/ww-compiler")];
let directbins: []str = [strings.concat(root, "/c-direct"),
strings.concat(root, "/ww-direct")];
let appbins: []str = [strings.concat(root, "/c-app"),
strings.concat(root, "/ww-app")];
let scopedbins: []str = [strings.concat(root, "/c-scoped"),
strings.concat(root, "/ww-scoped")];
let repeatedbins: []str = [strings.concat(root, "/c-repeated"),
strings.concat(root, "/ww-repeated")];
let leafmainbins: []str = [strings.concat(root, "/c-leafmain"),
strings.concat(root, "/ww-leafmain")];
let vendorbins: []str = [strings.concat(root, "/c-vendor"),
strings.concat(root, "/ww-vendor")];
let actions: []str = ["acme.codec", "acme.bridge", "cmd.direct",
"cmd.app", "scope.one", "scope.two", "cmd.scoped",
"cmd.repeated", "domain.main", "cmd.leafmain",
"vend.vendor.short.codec", "vend.client"];
let suffixes: []str = [".unit.ww", ".wwi", ".a"];
let artifactrefs: []str = alloc([], (actions.len * suffixes.len): u64)!;
let ari: i32 = 0;
for (ari < actions.len * suffixes.len) {
append(artifactrefs, "");
ari += 1;
};
let binaryrefs: []str = ["", "", "", "", "", ""];
let diagnosticrefs: []str = ["", "", "", "", "", ""];
let directtraceref: str = "";
let scopedtraceref: str = "";
let repeatedtraceref: str = "";
let changedtraceref: str = "";
let initialwwiref: str = "";
let testoutref: str = "";
let baseenv: []str = os.getenvs();
let si: i32 = 0;
for (si < stages.len) {
rewritefile(codecfile, wirebody);
assert(os.mkdir(works[si], 448i32) == 0);
writefile(traces[si], "");
let env: []str = alloc([], (baseenv.len + 7): u64)!;
let ei: i32 = 0;
for (ei < baseenv.len) {
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
&& !strings.hasprefix(baseenv[ei], "WW_SRCLIB=")
&& !strings.hasprefix(baseenv[ei],
"WW_NAME_COMPILER_TRACE=")
&& !strings.hasprefix(baseenv[ei],
"WW_NAME_REAL_COMPILER=")) {
append(env, baseenv[ei]);
};
ei += 1;
};
append(env, strings.concat("WW_W6C=", compilerwrapper));
append(env, strings.concat("WW_W6A=", driver(assemblers[si])));
append(env, strings.concat("WW_W6L=", driver(linkers[si])));
append(env, strings.concat("WW_SRCLIB=", repo(), "/lib"));
append(env, strings.concat("WW_NAME_COMPILER_TRACE=", traces[si]));
append(env, strings.concat("WW_NAME_REAL_COMPILER=",
driver(compilers[si])));
// A directly selected library keeps canonical action/artifact identity
// even though its declaration is unrelated to the path leaf.
let out: commandout;
let selectav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, codec];
runcommandenv(root, strings.concat("name-select-", stages[si]),
selectav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
assert(os.exists(strings.concat(works[si], "/acme.codec.wwi")));
assert(!os.exists(strings.concat(works[si], "/wire.wwi")));
let initialwwi: str = readfile(strings.concat(works[si],
"/acme.codec.wwi"));
assert(has(initialwwi, "//ww:module acme.codec\npackage wire;\n"));
if (si == 0) { initialwwiref = strings.dup(initialwwi); }
else { assert(same(initialwwiref, initialwwi)); };
rewritefile(traces[si], "");
let directav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", directbins[si], direct];
runcommandenv(root, strings.concat("name-direct-", stages[si]),
directav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
let directtrace: str = readfile(traces[si]);
let directline: str = linecontaining(directtrace,
"cmd.direct.unit.new");
assert(occurrences(directline, "<--import><acme.codec>") == 1);
assert(!has(directline, "wire.wwi"));
let ndirect: str = normalizedtrace(directtrace,
strings.concat(works[si], "/"), directbins[si]);
if (si == 0) { directtraceref = strings.dup(ndirect); }
else { assert(same(directtraceref, ndirect)); };
let runav: []str = [directbins[si]];
runcommand(root, strings.concat("name-direct-run-", stages[si]), runav,
(30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
// The same declared qualifier belongs to each importing source file,
// not to the package-wide namespace.
rewritefile(traces[si], "");
let scopedav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", scopedbins[si], scoped];
runcommandenv(root, strings.concat("name-scoped-", stages[si]),
scopedav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let scopedtrace: str = readfile(traces[si]);
let scopedline: str = linecontaining(scopedtrace,
"cmd.scoped.unit.new");
assert(occurrences(scopedline, "<--import><scope.one>") == 1);
assert(occurrences(scopedline, "<--import><scope.two>") == 1);
let nscoped: str = normalizedtrace(scopedtrace,
strings.concat(works[si], "/"), scopedbins[si]);
if (si == 0) { scopedtraceref = strings.dup(nscoped); }
else { assert(same(scopedtraceref, nscoped)); };
let scopedrun: []str = [scopedbins[si]];
runcommand(root, strings.concat("name-scoped-run-", stages[si]),
scopedrun, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
// Two per-file binding records for one canonical package still form
// exactly one direct graph edge and one compiler input.
rewritefile(traces[si], "");
let repeatedav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", repeatedbins[si], repeated];
runcommandenv(root, strings.concat("name-repeated-", stages[si]),
repeatedav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let repeatedunit: str = readfile(strings.concat(works[si],
"/cmd.repeated.unit.ww"));
assert(occurrences(repeatedunit, "import acme.codec;") == 2);
let repeatedtrace: str = readfile(traces[si]);
let repeatedline: str = linecontaining(repeatedtrace,
"cmd.repeated.unit.new");
assert(occurrences(repeatedline, "<--import><acme.codec>") == 1);
assert(occurrences(repeatedtrace, "acme.codec.unit.new") == 0);
let nrepeated: str = normalizedtrace(repeatedtrace,
strings.concat(works[si], "/"), repeatedbins[si]);
if (si == 0) { repeatedtraceref = strings.dup(nrepeated); }
else { assert(same(repeatedtraceref, nrepeated)); };
let repeatedrun: []str = [repeatedbins[si]];
runcommand(root, strings.concat("name-repeated-run-", stages[si]),
repeatedrun, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
// A path ending in main remains importable when its declaration is not
// main. A different path declaring main is rejected by loaded name.
let leafmainav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", leafmainbins[si], leafmainuser];
runcommandenv(root, strings.concat("name-leafmain-", stages[si]),
leafmainav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let leafmainrun: []str = [leafmainbins[si]];
runcommand(root, strings.concat("name-leafmain-run-", stages[si]),
leafmainrun, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 7);
// Compiler-owned binding failures are deterministic across stages and
// never commit an action voucher/artifact or publish a binary.
let rejectwork: str = strings.concat(root, "/reject-work-", stages[si]);
assert(os.mkdir(rejectwork, 448i32) == 0);
let rejecttargets: []str = [leafbad, leak, duplicate, collision,
crosscollision, programuser];
let rejectactions: []str = ["cmd.leafbad", "cmd.leak",
"cmd.duplicate", "cmd.collision", "cmd.crosscollision",
"cmd.programuser"];
let rejectneedles: []str = ["undefined: codec",
"package 'wire' is not directly imported",
"same redeclared in this block",
"same already declared through import of package same",
"same already declared through import of package same",
"package domain.program is a program, not an importable package"];
let rj: i32 = 0;
for (rj < rejecttargets.len) {
let badbin: str = strings.concat(root, "/reject-bin-", stages[si],
"-", rejectactions[rj]);
let badav: []str = [driver(stages[si]), "build", "-w", rejectwork,
"-I", source, "-o", badbin, rejecttargets[rj]];
runcommandenv(root, strings.concat("name-reject-", stages[si],
"-", rejectactions[rj]), badav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, rejectneedles[rj]));
let normalized: str = normalizedtrace(out.stderr,
strings.concat(rejectwork, "/"), badbin);
if (rj == 2) {
assert(same(normalized, strings.concat(
"$WORK/cmd.duplicate.unit.new:4:1: error: same redeclared in this block\n",
"\t$WORK/cmd.duplicate.unit.new:3:1: other declaration of same\n",
"$WORK/cmd.duplicate.unit.new:4:1: error: \"scope.two\" imported as same and not used\n",
"ww: w6c failed for cmd.duplicate\n")));
};
if (rj == 3) {
assert(same(normalized, strings.concat(
"$WORK/cmd.collision.unit.new:3:1: error: \"scope.one\" imported as same and not used\n",
"$WORK/cmd.collision.unit.new:4:1: error: same already declared through import of package same (\"scope.one\")\n",
"\t$WORK/cmd.collision.unit.new:3:1: other declaration of same\n",
"ww: w6c failed for cmd.collision\n")));
};
if (si == 0) { diagnosticrefs[rj] = strings.dup(normalized); }
else { assert(same(diagnosticrefs[rj], normalized)); };
assert(!os.exists(badbin));
let committed: []str = [".unit.ww", ".wwi", ".s", ".o", ".a",
".unit.new", ".wwi.new", ".s.new", ".o.new", ".a.new"];
let ci: i32 = 0;
for (ci < committed.len) {
assert(!os.exists(strings.concat(rejectwork, "/",
rejectactions[rj], committed[ci])));
ci += 1;
};
rj += 1;
};
let conflictav: []str = [driver(stages[si]), "build", "-w",
rejectwork, "-I", source, conflict];
runcommandenv(root, strings.concat("name-conflict-", stages[si]),
conflictav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "conflicting package names"));
assert(!os.exists(strings.concat(rejectwork,
"/conflict.pkg.unit.ww")));
// Production/internal/external naming uses the declaration, while every
// action and generated-main artifact retains canonical variant identity.
let testwork: str = strings.concat(root, "/test-work-", stages[si]);
assert(os.mkdir(testwork, 448i32) == 0);
rewritefile(traces[si], "");
let testav: []str = [driver(stages[si]), "test", "-w", testwork,
"-I", source, testcodec];
runcommandenv(root, strings.concat("name-test-", stages[si]), testav,
env, (240i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(has(out.stdout, "internal_name_uses_declaration ... ok\n"));
assert(has(out.stdout, "external_name_uses_declaration ... ok\n"));
assert(out.stderr.len == 0);
if (si == 0) { testoutref = strings.dup(out.stdout); }
else { assert(same(testoutref, out.stdout)); };
let productionunit: str = readfile(strings.concat(testwork,
"/testpkg.codec.unit.ww"));
let internalunit: str = readfile(strings.concat(testwork,
"/testpkg.codec-internal-test.unit.ww"));
let externalunit: str = readfile(strings.concat(testwork,
"/testpkg.codec_test-external-test.unit.ww"));
assert(!has(productionunit, "testonly.helper"));
assert(has(internalunit, "import testonly.helper;"));
assert(!has(externalunit, "testonly.helper"));
assert(has(externalunit, "package wire_test;"));
assert(os.exists(strings.concat(testwork,
"/testpkg.codec-internal-test-main.a")));
assert(os.exists(strings.concat(testwork,
"/testpkg.codec_test-external-test-main.a")));
assert(os.exists(strings.concat(testwork, "/test.a")));
assert(!has(readfile(strings.concat(testwork,
"/testpkg.codec-internal-test-main.unit.ww")), "testonly.helper"));
assert(!has(readfile(strings.concat(testwork,
"/testpkg.codec_test-external-test-main.unit.ww")),
"testonly.helper"));
let recursivebuildwork: str = strings.concat(root,
"/recursive-build-", stages[si]);
assert(os.mkdir(recursivebuildwork, 448i32) == 0);
let recursivebuildav: []str = [driver(stages[si]), "build", "-w",
recursivebuildwork, "-I", source,
strings.concat(source, "/testpkg/...")];
runcommandenv(root, strings.concat("name-recursive-build-", stages[si]),
recursivebuildav, env,
(180i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(os.exists(strings.concat(recursivebuildwork,
"/testpkg.codec.wwi")));
let recursivetestwork: str = strings.concat(root,
"/recursive-test-", stages[si]);
assert(os.mkdir(recursivetestwork, 448i32) == 0);
let recursivetestav: []str = [driver(stages[si]), "test", "-w",
recursivetestwork, "-I", source,
strings.concat(source, "/testpkg/...")];
runcommandenv(root, strings.concat("name-recursive-test-", stages[si]),
recursivetestav, env,
(240i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(has(out.stdout, "external_name_uses_declaration ... ok\n"));
// Vendor expansion owns the action/map identity; the loaded declaration
// independently supplies cable as the source-visible qualifier.
rewritefile(traces[si], "");
let vendorav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", vendorbins[si], vendorclient];
runcommandenv(root, strings.concat("name-vendor-", stages[si]),
vendorav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let vendorunit: str = readfile(strings.concat(works[si],
"/vend.client.unit.ww"));
assert(occurrences(vendorunit, "import short.codec;") == 2);
assert(has(vendorunit, strings.concat(
"//ww:import-map short.codec vend.vendor.short.codec ",
hexbytes(vendored), "\n")));
assert(occurrences(vendorunit,
"//ww:import-map short.codec vend.vendor.short.codec ") == 1);
let vendortrace: str = readfile(traces[si]);
let vendorline: str = linecontaining(vendortrace,
"vend.client.unit.new");
assert(occurrences(vendorline,
"<--import><vend.vendor.short.codec>") == 1);
assert(occurrences(vendorline, strings.concat(
"<--import-map><short.codec><vend.vendor.short.codec>")) == 1);
let vendorwwi: str = readfile(strings.concat(works[si],
"/vend.vendor.short.codec.wwi"));
assert(has(vendorwwi, strings.concat(
"//ww:module vend.vendor.short.codec\npackage cable;\n")));
assert(!os.exists(strings.concat(works[si], "/short.codec.a")));
let vendorrun: []str = [vendorbins[si]];
runcommand(root, strings.concat("name-vendor-run-", stages[si]),
vendorrun, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 13);
// Build the bare-import bridge and its command, establish a no-op warm
// rebuild, then change only the dependency's declared name. The direct
// importer is reconsidered; its unchanged export stops propagation.
rewritefile(traces[si], "");
let appav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", appbins[si], app];
runcommandenv(root, strings.concat("name-app-cold-", stages[si]), appav,
env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let apprun: []str = [appbins[si]];
runcommand(root, strings.concat("name-app-run-", stages[si]), apprun,
(30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
rewritefile(traces[si], "");
runcommandenv(root, strings.concat("name-app-warm-", stages[si]), appav,
env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(readfile(traces[si]).len == 0);
rewritefile(codecfile, cablebody);
rewritefile(traces[si], "");
runcommandenv(root, strings.concat("name-app-renamed-", stages[si]),
appav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let changedtrace: str = readfile(traces[si]);
assert(occurrences(changedtrace, "\n") == 2);
assert(occurrences(changedtrace, "/acme.codec.unit.new") == 1);
assert(occurrences(changedtrace, "/acme.bridge.unit.new") == 1);
assert(!has(changedtrace, "/cmd.app.unit.new"));
let nchanged: str = normalizedtrace(changedtrace,
strings.concat(works[si], "/"), appbins[si]);
if (si == 0) { changedtraceref = strings.dup(nchanged); }
else { assert(same(changedtraceref, nchanged)); };
let renamedwwi: str = readfile(strings.concat(works[si],
"/acme.codec.wwi"));
assert(has(renamedwwi, "//ww:module acme.codec\npackage cable;\n"));
assert(!has(renamedwwi, "package wire;"));
runcommand(root, strings.concat("name-app-renamed-run-", stages[si]),
apprun, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
// New semantic artifacts and binaries are byte-identical across the
// independently cold Cstage and WWstage work roots.
ari = 0;
for (ari < actions.len) {
let xi: i32 = 0;
for (xi < suffixes.len) {
let index: i32 = ari * suffixes.len + xi;
let bytes: str = readfile(strings.concat(works[si], "/",
actions[ari], suffixes[xi]));
if (si == 0) { artifactrefs[index] = strings.dup(bytes); }
else { assert(same(artifactrefs[index], bytes)); };
xi += 1;
};
ari += 1;
};
let stagebins: []str = [directbins[si], appbins[si], scopedbins[si],
repeatedbins[si], leafmainbins[si], vendorbins[si]];
let bini: i32 = 0;
for (bini < stagebins.len) {
let bytes: str = readfile(stagebins[bini]);
if (si == 0) { binaryrefs[bini] = strings.dup(bytes); }
else { assert(same(binaryrefs[bini], bytes)); };
bini += 1;
};
si += 1;
};
clean(root);
};
@test fn compile_artifact_naming() void = {
let root: str = fresh();
let pdir: str = strings.concat(root, "/pkg");

View File

@@ -368,7 +368,7 @@ fn writediamond(td: str, reverse: bool) str = {
testenv.writefile(strings.concat(shared, "/a.ww"), asrc);
};
testenv.writefile(strings.concat(left, "/left.ww"), strings.concat(
"package left;\nimport shared;\nimport shared;\n",
"package left;\nimport shared;\n",
"export fn make() shared.token = { return shared.token{value=shared.base() + 1}; };\n"));
testenv.writefile(strings.concat(right, "/right.ww"), strings.concat(
"package right;\nimport shared;\n",
@@ -424,7 +424,7 @@ fn writediamond(td: str, reverse: bool) str = {
let rightiface: str = testenv.readfile(strings.concat(scratch,
"right.wwi"));
if (testenv.occurrences(leftiface, "import shared;") != 1) {
fail("diamond", "duplicate import escaped into export data");
fail("diamond", "direct import is missing or duplicated in export data");
};
if (testenv.occurrences(leftiface, "export type token") != 1
|| testenv.occurrences(rightiface, "export type token") != 1
@@ -907,7 +907,7 @@ fn writediamond(td: str, reverse: bool) str = {
"/.wwtool.w6a")), testenv.readfile(assembler))
|| !testenv.same(testenv.readfile(strings.concat(work,
"/.wwtool.stamp")),
"ww workdir fmt 13 mode build asm 0\n")) {
"ww workdir fmt 14 mode build asm 0\n")) {
fail("driver-identity", "persistent artifacts or identities are incomplete");
};
let coldwwi: str = testenv.readfile(strings.concat(work, "/dep.wwi"));
@@ -1293,7 +1293,7 @@ fn writediamond(td: str, reverse: bool) str = {
"// ROOT_A\n",
"export fn value() i32 = { return dep.value() + 2; };\n"));
testenv.writefile(strings.concat(bar, "/z.ww"), strings.concat(
"package bar;\nimport foo.dep;\n",
"package bar;\n",
"// ROOT_Z\n",
"export fn other() i32 = { return 9; };\n"));
testenv.writefile(strings.concat(commanddir, "/main.ww"), strings.concat(
@@ -1819,16 +1819,21 @@ fn writediamond(td: str, reverse: bool) str = {
@test fn generated_test_hook_is_not_a_user_exemption() void = {
let td: str = testenv.fresh();
let src: str = strings.concat(td, "/case.ww");
let iface: str = strings.concat(td, "/test.wwi");
testenv.writefile(src, strings.concat(
"package probe;\nimport test;\n",
"fn bad() void = { test.run(); };\n",
"@test fn one() void = {};\n"));
testenv.writefile(iface, strings.concat(
"//ww:module test\n",
"package test;\n",
"export fn placeholder() void;\n"));
let compilers: []str = ["w6c", "w6c_ww"];
let i: i32 = 0;
for (i < compilers.len) {
let asm: str = strings.concat(td, "/case-", compilers[i], ".s");
let av: []str = [testenv.driver(compilers[i]), "-T", "-c", "-o",
asm, src];
let av: []str = [testenv.driver(compilers[i]), "-T", "-c",
"--import", "test", iface, "-o", asm, src];
let co: testenv.commandout;
command(td, strings.concat("test_hook_", compilers[i]), av, &co);
if (co.termination != exec.termination.EXIT || co.code == 0

View File

@@ -220,8 +220,8 @@ export fn occurrences(haystack: str, needle: str) i32 = {
// Expected semantic identity for a canonical absolute directory that is not
// representable below an active source root. Keep this independent observer
// byte-for-byte aligned with the documented driver protocol.
export fn localpackageidentity(dir: str, leaf: str) str = {
let out: []u8 = alloc([], (dir.len * 4 + leaf.len + 16): u64)!;
export fn localpackageidentity(dir: str, declaredname: str) str = {
let out: []u8 = alloc([], (dir.len * 4 + 16): u64)!;
let prefix: str = "__wwlocal.p";
let i: i32 = 0;
for (i < prefix.len) { append(out, prefix[i]); i += 1; };
@@ -244,9 +244,6 @@ export fn localpackageidentity(dir: str, leaf: str) str = {
};
i += 1;
};
append(out, '.');
i = 0;
for (i < leaf.len) { append(out, leaf[i]); i += 1; };
return strings.frombytes(out);
};

View File

@@ -1,4 +1,4 @@
//ww:error "does not match import path"
//ww:compile
//ww:module foo
package bar;
export fn x() i32 = { return 0; };

View File

@@ -22,11 +22,12 @@ package direnum_test;
// source order); the two stages' units are byte-identical.
//
// rejects — the authoritative loader-reject table over private trees:
// root-conflict ("conflicting package names"), import-leaf ("does
// not match import path"), root-missing and root-invalid ("invalid
// or missing package clause"), attest-noncanon ("@test declaration
// outside *_test.ww" — Go's test-file contract); per row both stages
// fail, carry the needle, and their captured stderr is byte-identical.
// root-conflict ("conflicting package names"), root-missing and
// root-invalid ("invalid or missing package clause"), attest-noncanon
// ("@test declaration outside *_test.ww" — Go's test-file contract);
// per row both stages fail, carry the needle, and their captured stderr is
// byte-identical. The former import-leaf rejection is now an accepted
// path/name-decoupling case and uses the imported declaration as qualifier.
//
// Dropped C machinery, not assertions: the ww_ww-absent skip gate
// (the Make target declares both drivers) and the unlink/rmdir
@@ -179,7 +180,7 @@ fn rejectpair(label: str, td: str, target: str, needle: str) void = {
"package other;\nfn spare() i32 = { return 0; };\n");
testenv.writefile(strings.concat(importbad, "/entry.ww"),
strings.concat("package main;\nimport wanted;\n",
"fn main() i32 = { return wanted.value(); };\n"));
"fn main() i32 = { return other.value(); };\n"));
testenv.writefile(strings.concat(wanted, "/a.ww"),
"package other;\nexport fn value() i32 = { return 0; };\n");
testenv.writefile(strings.concat(missing, "/a.ww"),
@@ -192,12 +193,28 @@ fn rejectpair(label: str, td: str, target: str, needle: str) void = {
"package attbad;\n",
"@test fn hidden() void = { assert(false); };\n"));
let tags: []str = ["root-conflict", "import-leaf", "root-missing",
let drvs: []str = ["ww", "ww_ww"];
let s: i32 = 0;
for (s < drvs.len) {
let out: str = strings.concat(td, "/import-name-", drvs[s]);
let av: []str = [testenv.driver(drvs[s]), "build", "-o", out,
strings.concat(importbad, "/entry.ww")];
if (runcode(td, strings.concat("import-name-build-", drvs[s]), av)
!= 0) {
fail("import-name", strings.concat(drvs[s], " build failed"));
};
let rav: []str = [out];
if (runcode(td, strings.concat("import-name-run-", drvs[s]), rav)
!= 0) {
fail("import-name", strings.concat(drvs[s], " run failed"));
};
s += 1;
};
let tags: []str = ["root-conflict", "root-missing",
"root-invalid", "attest-noncanon"];
let targets: []str = [rootbad, strings.concat(importbad,
"/entry.ww"), missing, invalid, attbad];
let targets: []str = [rootbad, missing, invalid, attbad];
let needles: []str = ["conflicting package names",
"does not match import path",
"invalid or missing package clause",
"invalid or missing package clause",
"@test declaration outside *_test.ww"];

View File

@@ -203,7 +203,7 @@ fn userow(label: str, apick: str, onecall: str) void = {
let rav: []str = [strings.concat(td, "/main")];
if (runcode(td, strings.concat("run_", drvs[s]), rav) != 0) {
fail(label, strings.concat(drvs[s], " run-exit != 0 (a ",
"file-global first-match hint routes one math.pick() to ",
"cross-file first-match hint routes one math.pick() to ",
"the wrong package -- #40)"));
};
testenv.clean(td);