ww imports: require imports before declarations

This commit is contained in:
2026-08-21 19:20:52 +09:00
parent b03bbb1428
commit ee4573bf55
9 changed files with 730 additions and 8 deletions

View File

@@ -8246,6 +8246,187 @@ this presentation rule does not change.
No persisted-byte contract changed. Build workdir format remains `18`, test
workdir format remains `19`, and semantic storage format remains `3`.
### 11.41 Implemented source-file import-section ordering
Every eligible WW source now has one contiguous import section immediately
after its package clause. Once an ordinary top-level declaration begins, the
first `import` in a later section is rejected as
`imports must appear before other declarations`. The parser continues for
recovery: consecutive imports in that late section do not repeat the ordering
diagnostic, while another ordinary declaration followed by another import
starts a separately diagnosed late section.
This is a source-file syntax rule, not a new import form. Existing unquoted
dotted default, explicit-alias, and blank imports are unchanged. Existing
aggregate module/reset boundaries and each constituent package clause begin a
new source section. The boundary bookkeeping remains parser metadata rather
than package, import, graph, action, artifact, symbol, `.wwi`, publication, or
persistence identity.
#### Pinned Go evidence and fact classification
The sole authority is official Go 1.26.5 at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`:
- `syntax.(*parser).fileOrNil` states the source-file grammar as a package
clause, zero or more imports, and then zero or more top-level declarations.
Its recovery loop accepts a later import only to continue parsing and emits
exactly `imports must appear before other declarations` when the preceding
declaration was not an import
([`cmd/compile/internal/syntax/parser.go`, lines 397428](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/syntax/parser.go#L397-L428)).
- The public parser first consumes the initial import section, then applies the
same predecessor check while parsing the rest of the file
([`go/parser/parser.go`, method `(*parser).parseFile`, lines 28872923](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/parser/parser.go#L2887-L2923)).
- Official types testdata requires one diagnostic for a late import followed by
contiguous imports, then another diagnostic when an ordinary declaration
separates a second late section
([`internal/types/testdata/fixedbugs/issue43190.go`, lines 530](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/fixedbugs/issue43190.go#L5-L30)).
Those grammar branches, diagnostic text, error-recovery behavior, and testdata
assertions are **behavior directly implemented or asserted by pinned Go**.
That the state belongs to one source parser, resets at WW's existing aggregate
source boundaries, and must reject before import-graph construction is
**behavior derived from the pinned implementation**.
The rule honestly applies to WW's model because it orders declaration classes
WW already implements. It requires no quoted, grouped, dot, or generalized
import syntax; module or manifest identity; registry, lock, cache, database,
CAS, or network resolution; or source-level build expression.
#### Fresh four-axis audit and direct pre-fix measurements
The bounded audit examined all four permanent axes against the pinned checkout
before selecting this import difference:
- **Go-like build:** pinned linker method
`(*ErrorReporter).errorUnresolved` gives unresolved `main.main` a dedicated
error
([`cmd/link/internal/ld/errors.go`, lines 2967](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/errors.go#L29-L67)),
asserted by `TestUndefinedRelocErrors` and its source fixture
([`cmd/link/internal/ld/ld_test.go`, lines 1945](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/ld_test.go#L19-L45),
[`testdata/issue10978/main.go`, lines 527](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/testdata/issue10978/main.go#L5-L27)).
Both WW stages rejected a selected declared-`main` package without an entry,
using empty stdout and the same linker/driver diagnostics. This applicable
control was aligned.
- **Go-like test:** pinned `testFlags` explicitly permits known test flags
before and after the package list and implements the transition between
package operands and flags
([`cmd/go/internal/test/testflag.go`, lines 219345](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/testflag.go#L219-L345));
official `test_flag.txt` asserts both placements
([lines 14](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_flag.txt#L1-L4)).
In both WW stages, `-run selected` before or after a directory operand ran
exactly the same one of two registered tests and produced identical output.
This applicable control was aligned for WW's supported option set.
- **Go-like package:** pinned `MultiplePackageError` and directory scanning
reject two eligible declarations with different package names
([`go/build/build.go`, lines 538549](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L538-L549)
and [lines 939967](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L939-L967)),
asserted by `TestMultiplePackageImport`
([`go/build/build_test.go`, lines 105133](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build_test.go#L105-L133)).
Both WW stages rejected a `first`/`second` directory before tools with the
identical positioned diagnostic. This applicable control was aligned.
- **Go-like import:** a command source declared a helper, then imported
`audit.dep`, then used that package from `main`. Both WW stages exited 0,
emitted empty build output, produced byte-identical executables (SHA-256
`f28892147ab0ae283dff5ceea7114dbc81142ed48cb9088ee7fb8294a5ce44cd`),
and those executables exited 42. Corresponding owner unit, interface,
assembly, object, archive, and initializer bytes were stage-identical. A
same-package test source with the same late-import shape ran successfully in
both stages with identical 251-byte stdout (SHA-256
`3c42fa9840f485fb21b5b5318a13b89abfe29e94b530db00779262368f4fbeba`)
and empty stderr. This acceptance was the selected difference.
The WW statuses, streams, runtime exits, and artifact hashes are **directly
measured WW behavior**. The cited implementation and testdata facts are
**behavior directly implemented or asserted by pinned Go**. Applying their
per-file ordering state to WW's existing dotted declarations is **behavior
derived from the pinned implementation**.
#### Ownership and final four-axis behavior
`parseimports` and `parsefile` in `cmd/wcc/parse.c`, with their semantic twins
in `lib/ww/syntax/parse.ww`, are the only production owners. Each keeps one
parser-local `previmport` bit. A normal import following a non-import reports
the pinned diagnostic, then sets the bit so adjacent imports remain one
recovery section. Any ordinary declaration clears it. The existing
module-path, module-reset, and package-clause boundaries set it for a new
source section.
The imports-only pass is used by public driver loading and therefore rejects a
selected or imported late source before graph and producer construction. The
full parser independently gives direct `w6c`/`w6c_ww` input and aggregate units
the same rule. The only tracked compatibility fixture that deliberately put a
declaration before its import was reordered; it still proves file-scoped import
binding and declaration installation order with byte-identical Cstage/WWstage
artifacts, without asserting the rejected syntax.
- **Go-like build:** selected and imported late sources now fail during parser
loading, before compiler, assembler, archiver, linker, output planning side
effects, or runtime. Missing-target resolution does not replace the earlier
syntax error. Valid import-first commands still build, publish, and run.
- **Go-like test:** late imports in production, same-package test,
external-test, and test-only sources fail before variant actions, generated
main, test binary, runtime, accounting, or retained publication. The
directory command emits its existing attributable final `FAIL\n`. A valid
import-first test retains and runs normally.
- **Go-like package:** source eligibility and package-clause classification
remain earlier owners. Wrong-platform sources produce no ordering error;
selected package-name conflicts retain their coordinator diagnostic.
Declared names and command/test family classification are unchanged.
- **Go-like import:** a file can no longer introduce a qualifier or side-effect
edge after ordinary declarations. Valid imports retain their exact source
spelling, declared-name qualifier, file scope, contextual/vendor resolution,
canonical identity, visibility checks, cycle checks, and initialization
edges.
Direct post-fix `w6c` and `w6c_ww`, and public `ww build`/`ww_ww build`, reject
the measured source with empty stdout and byte-identical 149-byte stderr
(SHA-256
`791ac87ab0aa2c91228f863ae80a8815aa83edf78c4996c5f11191193e3e4240`).
The diagnostic points to the late import at line 7, column 1. Directory tests
emit byte-identical `FAIL\n` stdout (SHA-256
`4f8e9e45f8a9e1843b81eaf3bdf52a6b778d415d23bf985774a9d34a43f69bd5`)
and byte-identical 314-byte stderr (SHA-256
`82ec52b26eaff053f475ce0773b7aee902e734cd87dc100848aee3772063f5b1`),
with no test body or accounting. A direct three-import recovery probe emits
exactly two stage-identical ordering diagnostics: one for the first of two
contiguous late imports and one after the intervening declaration.
Loading and fixed-target filename selection otherwise do not change. An
excluded `_windows.ww` or `_windows_test.ww` file contributes no parse,
package, import, graph, action, artifact, diagnostic, or invalidation state.
For valid files, graph nodes, action dependencies and scheduling, compiler and
linker arguments, initialization, runtime behavior, result ordering, and
publication remain unchanged. A package canonically named `domain.dep` may
still declare `renamed`; its importer uses `renamed.Name`, and its unit/export
remain owned by `domain.dep`.
Cold rejection creates no work artifact, output, capture, or adjacent scratch.
A warm source reordered into the invalid form preserves the complete committed
unit/interface/assembly/object/archive/initializer generation, tool vouchers,
stamp, and public executable byte for byte. Exact restoration reuses the
committed producers and republishes the same executable. Because rejection
occurs before a producer or test child, producer failure, runtime failure,
signals, timeout, interruption, and process-group cleanup acquire no new path;
their existing owners remain authoritative. Concurrent valid and invalid
requests use independent parser state, workdirs, captures, and outputs. No
`.new`, `.install`, `.wwtxn.*`, cold scratch, test process, or capture residue
survives the observed failure boundaries.
The WW-native `imports_precede_other_top_level_declarations` observer proves
direct compiler parity, exact recovery-section counts, selected and imported
build rejection, syntax-before-resolution precedence, all directory test
source variants, wrong-platform exclusion, valid runtime behavior, declared
name versus canonical identity, cold/warm persistence and rollback, restored
reuse, concurrent isolation, diagnostic equality, retained executable equality,
and intermediate artifact-byte equality. The C parser unit and the existing
`sepimport` observer separately pin the imports-only AST recovery and valid
file-scoped binding regression.
Rejected source creates no persisted byte contract, while valid source bytes
are unchanged. Build workdir format remains `18`, test workdir format remains
`19`, and semantic storage format remains `3`.
## 12. Candidate architectures and hard-gate decision
Five candidates were developed as coherent systems, not as feature bins.

View File

@@ -257,6 +257,17 @@ ImportPath = ident { "." ident } .
use the related `p_test`, and the actions remain separate even though one
canonical directory owns their test product. The declared name need not equal
the directory name or the final component of its canonical import identity.
- Each source file has one contiguous import section immediately after its
package clause. Once a non-import top-level declaration begins, a later
`import` is rejected as `imports must appear before other declarations`.
Parsing continues for recovery: consecutive imports in that late section
produce one ordering diagnostic, while another ordinary declaration followed
by another import starts a separately diagnosed late section. Existing
aggregate module/reset boundaries and constituent package clauses reset this
parser state per source; they do not relax the rule within a source or become
package/import identity.
A filename excluded by the target-selection rule below reaches no parser and
therefore cannot contribute an ordering diagnostic or import edge.
- Directory source eligibility uses Go 1.26.5 filename suffix semantics for
WW's fixed `linux/amd64` target. In the basename stem before the first dot, a
final `_test` token is ignored for platform matching. A final known OS or

View File

@@ -402,6 +402,19 @@ 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.
Within every eligible production, same-package test, external-test, or
test-only source, the package clause is followed by one contiguous import
section and then ordinary top-level declarations. A later import is a parser
error, `imports must appear before other declarations`. The imports-only load
pass diagnoses the first import in each separated late section before graph or
producer construction; the full parser gives direct compiler input the same
result. Consequently an invalid test source creates no variant, generated main,
test binary, runtime process, accounting, retained output, or new persistent
generation. The directory command retains its existing attributable final
`FAIL\n`. Wrong-target files remain excluded before this rule and contribute no
diagnostic. This ordering state is per source and never package, import,
variant, action, artifact, publication, or persistence identity.
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