compiler: implement blank package name semantics

This commit is contained in:
2026-08-23 15:33:59 +09:00
parent e67b8e1bc4
commit 0b24b11d65
14 changed files with 2862 additions and 112 deletions

View File

@@ -273,7 +273,8 @@ does not return (e.g. a call to `abort`).
```
SourceFile = PackageClause { ImportDecl } { TopDecl } .
PackageClause = "package" ident ";" .
PackageClause = "package" PackageName ";" .
PackageName = ident .
ImportDecl = "import" ( ImportPath | ImportName ImportPath ) ";" .
ImportName = ident .
ImportPath = ident { "." ident } .
@@ -285,6 +286,101 @@ 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.
- The discard identifier `_` is syntactically valid as `PackageName`, but it
is never a valid declared package name. A complete source with
`package _;` reaches checker initialization, which reports exactly
`invalid package name _` at the underscore token and continues checking the
retained file. The package-clause parser admits the discard token only in
this grammar slot; no other identifier position is broadened. A malformed or
missing package name remains a parser error, and any complete-file syntax
error prevents this checker diagnostic.
Loading may retain `_` transiently to compare declared source families,
record imports, and construct the applicable action, but it is not canonical
package, import, graph, action, symbol, `.wwi`, artifact, publication, or
persistence identity. Direct sources retain `__root`; dotted directories
and providers retain their dotted identities. Selected files with distinct
declared names remain a loader/family conflict. In an all-blank action the
checker emits one blank-name diagnostic per retained package marker in
deterministic source order.
Observable ordering is source eligibility and loader-visible header/family
validation, recursive import loading, the `ww run` main-package check,
eligible dependency producers, complete parent-source parsing, then the
blank-name, reached imported-package, and later checker diagnostics. Thus
missing or invalid imports may precede the parent check; a blank run root is
not `main` and starts no producer; and a full-source syntax error suppresses
the blank-name error. An
ordinary blank source provider cannot publish an interface or archive for an
importer. A supplied or caller-corrupted `.wwi` can nevertheless contain
that spelling and is defensively validated when reached.
Import interfaces are read, owner-checked, and syntax-parsed as separate
lists before the primary source is parsed. Interface structural errors keep
their existing precedence, but a primary syntax error returns before
imported-package semantic checking and therefore suppresses every
blank-provider import diagnostic. After successful primary syntax, compiler
test mode first materializes its required `test` or collision-safe
`__wwtest` support occurrence unless an equivalent primary occurrence
exists. With no matching interface that occurrence retains the external
support fallback; with a matching interface its provider name is validated,
and the reserved spelling does not bypass the check.
Each represented canonical interface package is classified as valid,
missing, conflicting, or invalid, where invalid means one nonconflicting
real declared name `_`. Imported-interface reachability is rooted only at
canonical uses in the primary and compiler-required lists. A reached valid
interface owner may contribute its imported uses transitively; an invalid,
missing, conflicting, unreachable, or ownerless section may not. Before
interface facts are bound or merged, every declaration and use whose owner
is not both reached and valid is discarded. An unused invalid interface is
therefore wholly inert, even when it embeds a valid-origin section that
imports the invalid path: it emits no diagnostic, installs no scope or
declaration, changes no output, and is byte-equivalent to supplying no such
interface. If primary source separately reaches that valid origin, its
retained edge may legitimately reach and diagnose the invalid provider.
A retained use of an invalid provider is marked used. A nonblank use receives
a fake empty-scope package binding under its explicit alias or, without one,
the canonical path leaf; a blank use creates no visible binding. This
recovery prevents qualified values, calls, and types from producing
missing-member, unknown-type, export, or calling-nonfunction cascades.
Ordinary lexical shadowing of a nonblank recovery alias still applies.
Immediately after primary blank-name diagnostics, the first retained use of
each invalid canonical path reports exactly
`could not import PATH (invalid package name: "_")`; later uses of that path
are deduplicated, distinct paths retain occurrence order, and independent
checker errors continue. Default, explicit, and blank import forms all
position this error at the path's first identifier rather than at the alias.
Canonical path, source spelling, alias, declared provider name, owner marker,
placeholder, edge, and binding remain separate facts. A physical `.wwi`
path is observation metadata and a same-named `.ww` file remains an import
decoy.
Ordinary `ww build` still omits a valid visible literal `*_test.ww` after
header loading, so `package _;` in that omitted role has no action or
diagnostic. Under `ww test`, production plus a same-package blank test forms
one augmented internal-test action; the test recompile substitutes for the
separate production node and one compiler invocation diagnoses every
retained blank marker. A test-only blank source has one test-package action.
Mixed valid/blank production and test names retain family-mismatch
precedence, and no failed blank product reaches generated main, link, or
runtime.
Normal blank-package failure publishes no assembly, interface, object,
archive, executable, retained test, or new semantic generation. Existing
request rollback removes owned stages and preserves prior public and
committed bytes; a public build or test consuming a reached corrupted
committed interface likewise publishes no replacement consumer generation,
retained test, or downstream artifact. Exact valid-interface restoration
follows ordinary reuse. Independent requests share no blank-name,
reachability, deduplication, or fake-binding state. Cstage and WWstage have
the same path-positioned diagnostic stream and artifact outcome. The private
in-memory AST adds only path-position fields; AST enum/printing, `.wwi`
schema, build workdir format 18, test workdir format 19, and semantic storage
format 3 do not change. External driver interruption is unchanged; the fixed
`.new` residue and later persistent request poisoning remain open.
- 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`.
@@ -643,10 +739,10 @@ ImportPath = ident { "." ident } .
interruption preserve existing directory contents and remove only
request-created prefixes and stages. Direct external `SIGTERM` of a build
driver is a verified-open exception: both stages preserve public and
committed work bytes and reap their process group, but may leave `.new`
staging files that make a later persistent-work request reject until those
files are removed. A non-directory output retains the single-product
file/archive rule.
committed work bytes, but leave the directly spawned compiler alive and
exactly three fixed-name `.new` staging files; the existing `.unit.new`
makes a later persistent-work request reject. A non-directory output retains
the single-product file/archive rule.
If a lone
command's synthesized default basename already names a directory, loading
and graph validation complete and the build rejects before tools without