test: prove package initialization semantics

This commit is contained in:
2026-08-14 19:02:49 +09:00
parent c6bec0914d
commit 763bbc8f25
24 changed files with 2243 additions and 349 deletions

View File

@@ -246,7 +246,8 @@ does not return (e.g. a call to `abort`).
```
SourceFile = PackageClause { ImportDecl } { TopDecl } .
PackageClause = "package" ident ";" .
ImportDecl = "import" ( ImportPath | ident ImportPath ) ";" .
ImportDecl = "import" ( ImportPath | ImportName ImportPath ) ";" .
ImportName = ident .
ImportPath = ident { "." ident } .
```
@@ -263,8 +264,18 @@ ImportPath = ident { "." ident } .
Neither form exposes an imported declaration as a bare `Name`; ordinary
unqualified lookup remains limited to lexical, builtin, and same-package
declarations.
- `import _ acme.codec;` is a blank side-effect import. The lone `_` creates no
qualifier, exposes no bare declaration, and is never diagnosed as unused.
It is nevertheless a real import occurrence: resolution and all missing,
self-import, cycle, `internal`, vendor, and imported-command checks use the
dotted path, and the dependency participates in executable and test package
initialization. Repeated blank occurrences and blank plus default/explicit
named occurrences of one path are valid; every named occurrence remains
independently subject to duplicate-binding and unused checks.
The package dependency graph is the sorted, deduplicated union of the real
imports in all eligible files. Self-import is rejected.
imports in all eligible files. Occurrences retain their owning file and
position, but equal canonical targets create one graph edge/package action.
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
@@ -272,8 +283,8 @@ ImportPath = ident { "." ident } .
- Only names marked `export` (§5) are visible across module boundaries.
Import paths remain unquoted and dotted. Grouped imports, quoted import paths,
dot imports, and blank imports are not implemented. In particular, `_` is
rejected as an alias rather than being treated as a blank import.
and dot imports are not implemented. `_` is reserved here for the blank form;
it is not an ordinary alias.
---
@@ -298,23 +309,42 @@ The `=` between signature and body is required. A bodiless fn (`;`
terminator) declares an external symbol (C FFI); the bare `...` C-style
variadic is legal only on a bodiless declaration (§6.3).
At package scope, exactly `fn init() void = Block;` declares a special package
initializer. It must have a body, no parameters, no result, no `export`, and no
attribute. Multiple init declarations are permitted and retain owner-file and
source order. `init` is not inserted into ordinary package scope: it cannot be
called as `init()`, selected as `pkg.init`, exported, or used by another kind
of declaration. Package-variable initialization completes before these
functions run.
### 5.2 `let`
```
LetDecl = "let" ident ":" Type [ "=" Expr ] ";" .
```
A mutable binding. Without an initialiser the storage is uninitialised.
A mutable binding. Without an initialiser, package-level storage is
zero-backed; an uninitialized function-local binding retains the existing
uninitialized-local rule.
A `static` qualifier (`static let …`) gives function-local storage
static lifetime.
For a package-level `let` with an initializer, values representable by the
static-data emitter are installed statically. Every other otherwise valid
initializer is evaluated exactly once at runtime. Checked references through
package functions contribute variable-dependency edges; dependencies precede
dependents, and source declaration order breaks ready ties. Initialization
cycles are errors. Runtime package lets execute after imported package tasks and
before the package's init functions. `const` and `def` are not broadened by
this runtime path.
### 5.3 `const`
`const` introduces an immutable binding of the same shape as `let`.
TODO(spec): pin the exact `const` vs `def` boundary against `cmd/wcc`
(both keywords exist; this text takes `def` = compile-time constant
[Hare's `def`] and `const` = immutable runtime binding — confirm).
`const` introduces an immutable storage binding of the same declaration shape
as `let`; assignment to it is rejected. At package scope its initializer stays
within the existing static-data forms. A `const` is not entered into the
runtime package-variable schedule described above; use mutable `let` when an
otherwise valid initializer requires runtime evaluation.
### 5.4 `def`
@@ -561,6 +591,15 @@ the program. Two placements, following the Go `foo` / `foo_test` model:
};
```
Production, production-plus-white-box-test, external black-box-test, test
support, and generated test main are distinct package actions. Before a test
function runs, the generated test product initializes its exact reachable
variant graph dependency-first and once per canonical action. A white-box
variant replaces the colocated production task rather than initializing both;
an external variant depends on ordinary production. Imports, runtime lets, and
init declarations found only in `*_test.ww` never enter an ordinary production
build.
---
## 11. Concurrency (reserved)