fix: reject bare import bindings
This commit is contained in:
@@ -9545,6 +9545,221 @@ No format bump. Build workdir format remains `18`, test workdir format remains
|
||||
`19`, semantic storage format remains `3`, and no test-result cache is
|
||||
introduced.
|
||||
|
||||
### 11.48 Implemented selector-only effective import bindings
|
||||
|
||||
Pinned Go's applicable semantic rule is that an imported package's effective
|
||||
name denotes a package-name object, not a value or type, and that object may be
|
||||
used only to qualify a selector. A bare value occurrence is rejected as
|
||||
`use of package BINDING not in selector`; a bare type occurrence is rejected as
|
||||
`BINDING (package name) is not a type`. A bare occurrence does not mark the
|
||||
import used, while a selector does. These rules and diagnostics are **behavior
|
||||
directly implemented or asserted by pinned Go**.
|
||||
|
||||
The rule honestly applies within WW's local, dotted-import, manifest-free
|
||||
model. WW already resolves every ordinary import to an effective default or
|
||||
explicit file-local qualifier and already represents qualified value and type
|
||||
selectors. Treating that existing qualifier as a package-name object closes a
|
||||
checker hole without adding quoted, grouped, dot, generalized, network, module,
|
||||
manifest, registry, or source-expression import machinery. Applying Go's
|
||||
package-name-object rule to WW's already representable import binding is
|
||||
**behavior derived from the pinned implementation**.
|
||||
|
||||
#### Pinned evidence and fact classification
|
||||
|
||||
The sole semantic authority is official Go 1.26.5 at
|
||||
`c19862e5f8415b4f24b189d065ed739517c548ba`:
|
||||
|
||||
- [`ident` in `cmd/compile/internal/types2/typexpr.go`, lines
|
||||
18–60](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/typexpr.go#L18-L60)
|
||||
resolves an identifier to its object and, in a type context, rejects an
|
||||
object that is not a type name as `NAME (KIND) is not a type`;
|
||||
- [the package-name value branch in that file, lines
|
||||
89–92](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/typexpr.go#L89-L92)
|
||||
rejects a package name outside a selector as
|
||||
`use of package NAME not in selector`;
|
||||
- [`objectKind` in `cmd/compile/internal/types2/object.go`, lines
|
||||
675–680](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/object.go#L675-L680)
|
||||
names a `PkgName` object's kind `package name`;
|
||||
- [selector checking in `cmd/compile/internal/types2/call.go`, lines
|
||||
672–735](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/call.go#L672-L735),
|
||||
especially lines 681–691, admits a package name in the selector position and
|
||||
marks that exact package-name object used;
|
||||
- [import declaration in `cmd/compile/internal/types2/resolver.go`, lines
|
||||
248–340](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L248-L340)
|
||||
creates the `PkgName` in file scope, and
|
||||
[`unusedImports`, lines
|
||||
706–740](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L706-L740)
|
||||
diagnoses each nonblank package-name object not marked by a selector;
|
||||
- [compiler diagnostic sorting in `cmd/compile/internal/base/print.go`, lines
|
||||
70–92](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/base/print.go#L70-L92)
|
||||
preserves stable source-position ordering; and
|
||||
- [binary-expression checking in `cmd/compile/internal/types2/expr.go`, lines
|
||||
788–802](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/expr.go#L788-L802)
|
||||
checks both operands before returning for either invalid result, so two bare
|
||||
package-name operands retain two independently owned diagnostics.
|
||||
|
||||
Those source branches are **behavior directly implemented or asserted by
|
||||
pinned Go**. Official assertions are
|
||||
[`test/fixedbugs/issue11361.go`, lines
|
||||
7–11](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/test/fixedbugs/issue11361.go#L7-L11),
|
||||
which pairs an unused import with its bare package-value error;
|
||||
[`src/internal/types/testdata/check/builtins0.go`, lines
|
||||
613–617](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/builtins0.go#L613-L617),
|
||||
which rejects a package name passed as a value; and
|
||||
[`src/internal/types/testdata/check/decls1.go`, lines
|
||||
45–73](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/decls1.go#L45-L73),
|
||||
especially line 66, which asserts `math (package name) is not a type`.
|
||||
|
||||
From those pinned mechanisms it follows that a bare-only named import receives
|
||||
both its unused-import diagnostic and its context-specific package-name
|
||||
diagnostic; a legal selector removes only the unused diagnostic and never
|
||||
excuses a distinct bare occurrence; an explicit alias supplies the displayed
|
||||
name; and a sibling file without its own binding follows ordinary name lookup.
|
||||
Those conclusions are **behavior derived from the pinned implementation**.
|
||||
|
||||
#### Fresh four-axis audit and direct pre-fix measurements
|
||||
|
||||
The fresh audit also confirmed multiple named source operands, explicit
|
||||
`*_test.ww` build operands, shared top-level test-process state and fatal-abort
|
||||
behavior, Go's regular-expression `-run` matching, and lexical shadowing of import
|
||||
bindings as applicable but unselected differences. Ordinary directory
|
||||
production selection was aligned. Grouped, quoted, and dot imports remain
|
||||
inapplicable to WW's deliberately narrower syntax. Those classifications are
|
||||
audit conclusions, not claims that this slice closes the unselected behavior.
|
||||
|
||||
Before this slice, the following observations were **directly measured WW
|
||||
behavior**. With a dependency declared `package wire` and exporting a value and
|
||||
type, both stages accepted legal `wire.Value` and `wire.Number` controls and
|
||||
produced byte-identical 8,201-byte executables with SHA-256
|
||||
`7fb0e95229aa22714d880070388ee5169697543076536267a8c709429cce0fcb`;
|
||||
both ran with status 41. A bare-only value caused only the generated-unit
|
||||
unused-import diagnostic in each stage. Once a legal selector marked the import
|
||||
used, Cstage let a separate bare value reach the linker as
|
||||
`undefined reference to 'wire'`, while WWstage let it reach code generation as
|
||||
`cgident: unresolvable identifier (rule 7)`. An explicit alias reproduced the
|
||||
same stage split using that alias. Reversing occurrence order or adding a
|
||||
second bare occurrence did not restore checker ownership.
|
||||
|
||||
A bare type plus a legal selector was reported by both stages as
|
||||
`unknown type 'wire'`, rather than the pinned package-name diagnostic. With no
|
||||
selector, Cstage also retained the unused-import error, while WWstage emitted
|
||||
only the unknown-type error. A sibling source that did not import `wire` let a
|
||||
bare value reach the linker or code generator rather than following ordinary
|
||||
undefined-name checking. Blank-import controls did follow ordinary undefined
|
||||
lookup, missing targets retained missing-package precedence, and the valid
|
||||
qualified/blank control retained artifact-byte and runtime parity. All cold
|
||||
failed work directories examined were empty and no output was published.
|
||||
|
||||
The same checker escape was measured in same-package, external-test, honest
|
||||
test-only, production-test, and imported-dependency paths: the test coordinator
|
||||
reported `FAIL`, but Cstage failed in the linker while WWstage failed in code
|
||||
generation. These status, stream, artifact, and cleanup results are **directly
|
||||
measured WW behavior** from the pre-change Cstage/WWstage probes; they are not
|
||||
attributed to Go.
|
||||
|
||||
The directly measured pre-fix result across the permanent axes was therefore:
|
||||
|
||||
- **Go-like build:** legal selector controls loaded, compiled, linked,
|
||||
published, and ran byte-identically, but a bare package name escaped semantic
|
||||
checking and failed at different backend phases;
|
||||
- **Go-like test:** every applicable generated test source role reached the
|
||||
ordinary package failure result, but its checker diagnostic and failure phase
|
||||
differed between stages;
|
||||
- **Go-like package:** effective qualifiers were already source-local during
|
||||
legal selector resolution, but sibling and bare lookup could escape that
|
||||
source-local package-name boundary; and
|
||||
- **Go-like import:** unused accounting recognized some selector uses, but a
|
||||
bare import binding was not consistently classified as a non-value,
|
||||
non-type package-name object.
|
||||
|
||||
#### Ownership and complete four-axis contract
|
||||
|
||||
The true semantic owner is identifier and type-name resolution in the Cstage
|
||||
and WWstage semantic checkers. The checker must distinguish a package-name
|
||||
object belonging to the current source from ordinary lexical or package-scope
|
||||
objects before generic undefined, type, code-generation, or link recovery. A
|
||||
selector remains the only construct that consumes such an object and marks its
|
||||
import used. A bare type name must not accidentally mark the import used. Both
|
||||
implementations must produce the same normalized diagnostics at the same source
|
||||
positions and retain ordinary undefined or unknown-type behavior when the
|
||||
current file has no binding.
|
||||
|
||||
- **Go-like build:** every selected source is rejected during semantic checking
|
||||
before compiler output, assembly, archive, link, or installation when it
|
||||
contains a bare package-name object. Legal selectors retain the existing
|
||||
build graph, actions, artifact bytes, runtime result, and publication rules.
|
||||
- **Go-like test:** production, same-package, external-test, and honest
|
||||
test-only sources use the same checker rule. Rejection precedes test-process
|
||||
construction and execution; discovery, filters, descriptors, result
|
||||
accounting, fatal/skip behavior, timeouts, and process cleanup do not change.
|
||||
- **Go-like package:** the binding remains owned by exactly its source file.
|
||||
Declared package names, canonical package and variant identity, selected
|
||||
source membership, exported declarations, initialization, and symbol naming
|
||||
do not change.
|
||||
- **Go-like import:** default and explicit named bindings become consistently
|
||||
selector-only package-name objects. Selectors alone satisfy unused-import
|
||||
accounting. Blank imports create no binding, rejected effective `init`
|
||||
imports install none, and missing-target resolution keeps precedence.
|
||||
|
||||
#### Lifecycle, parity, proof, and formats
|
||||
|
||||
Loading and source eligibility, filename ordering, package-clause validation,
|
||||
canonical dotted resolution, contextual local/vendor expansion, source-role
|
||||
classification, and graph-edge construction remain unchanged. The diagnostic
|
||||
is a property of an already resolved file-local binding; physical directories
|
||||
remain loader and presentation metadata and never acquire package, import,
|
||||
graph, action, artifact, symbol, `.wwi`, publication, or persistence identity.
|
||||
|
||||
Semantic rejection occurs within the compiler action before code generation,
|
||||
so no assembler, archiver, linker, test runtime, or installer may run for the
|
||||
invalid action.
|
||||
Diagnostic source order and precedence must remain stable: missing-package,
|
||||
invalid effective-`init`, blank-no-binding, import collision, and ordinary
|
||||
undefined-name paths keep their established owners; a valid binding receives
|
||||
the exact package-name diagnostic and, unless a selector separately used it,
|
||||
its independent unused-import diagnostic. Repeated invalid bare occurrences
|
||||
are diagnosed independently.
|
||||
|
||||
Cold rejection must publish no executable, archive, interface, retained test
|
||||
binary, or partial semantic action. Warm rejection must preserve the previous
|
||||
public product and complete committed work generation byte-for-byte. A later
|
||||
valid request must recover through existing invalidation and reuse rules.
|
||||
Neither rejected source text nor its physical directory may create a new
|
||||
identity or persistence key. Valid controls must retain comparable Cstage and
|
||||
WWstage diagnostic and artifact-byte parity.
|
||||
|
||||
The invalid consumer's compiler action starts and rejects during semantic
|
||||
checking, before its code-generation or downstream producer boundary. Already
|
||||
valid dependency producers may also have completed before that rejection.
|
||||
Assembler, archiver, linker, installer, test-runtime, and runtime-failure paths
|
||||
for the invalid consumer are therefore unreachable. Existing producer/runtime
|
||||
failure, rollback, publication transactions, parallel-product isolation,
|
||||
concurrent-request locking, cancellation, interruption escalation, child
|
||||
ownership, and descendant cleanup remain unchanged for other actions. Rejection
|
||||
must remove request-owned stages and leave no active `.new`, `.install`,
|
||||
`.wwtxn.*`, adjacent `.sepwork`, capture, result, scratch, or tool-stage
|
||||
transaction residue.
|
||||
|
||||
The WW-native `bare_import_bindings_require_selectors` observer is required to
|
||||
prove exact cross-stage status and normalized diagnostic parity for bare value
|
||||
and type contexts, explicit aliases, selector order, repeated occurrences,
|
||||
unused accounting, sibling-file isolation, blank/`init`/missing controls,
|
||||
ordinary and imported builds, builtin-spelled package callees (`len`, `size`,
|
||||
and `align`), nested value/type contexts, and every applicable test source role.
|
||||
In particular, parser-shaped `size`/`align` type arguments must keep type
|
||||
checking after the callee is classified as a package-name object and must never
|
||||
escape through generic internal-expression recovery. The observer must also
|
||||
prove valid artifact-byte/runtime parity, cold empty rollback, warm prior state
|
||||
preservation, invalidation and recovery, and residue cleanup. Broader
|
||||
producer/runtime failure, concurrency, interruption, and descendant-process
|
||||
behavior retain their existing owners because this slice adds no such
|
||||
boundary. These are proof requirements; validation and final post-change byte
|
||||
measurements are recorded only after they are run.
|
||||
|
||||
No format bump. Build workdir format remains `18`, test workdir format remains
|
||||
`19`, semantic storage format remains `3`, and no test-result cache is
|
||||
introduced.
|
||||
|
||||
## 12. Candidate architectures and hard-gate decision
|
||||
|
||||
Five candidates were developed as coherent systems, not as feature bins.
|
||||
|
||||
20
docs/spec.md
20
docs/spec.md
@@ -323,11 +323,25 @@ ImportPath = ident { "." ident } .
|
||||
package declares `package wire;`, the importing file sees its exported names
|
||||
as `wire.Name`; `codec.Name` is not an additional binding. An explicit alias
|
||||
replaces only that visible qualifier: `import stable acme.codec;` exposes
|
||||
`stable.Name`, not `wire.Name` or `codec.Name`. Both kinds of binding are
|
||||
scoped to that source file. A sibling file must declare its own import.
|
||||
`stable.Name`, not `wire.Name` or `codec.Name`. Each effective default or
|
||||
explicit named binding is a file-local package-name object usable only as
|
||||
the left qualifier of a selector. A bare occurrence in a value context is
|
||||
rejected as `use of package BINDING not in selector`; a bare occurrence in
|
||||
a type context is rejected as `BINDING (package name) is not a type`. Such a
|
||||
rejected bare occurrence does not count as use of the import for
|
||||
unused-import accounting. A legal selector does count as use, but does not
|
||||
excuse any separate bare occurrence. Both kinds of named binding are scoped
|
||||
to that source file. A sibling file must declare its own import; without one,
|
||||
its otherwise equal spelling follows ordinary undefined-name or unknown-type
|
||||
lookup rather than package-name diagnostics. Builtin spelling does not alter
|
||||
the object: an import bound as `len`, `size`, `align`, or another builtin name
|
||||
remains a selector-only package-name object.
|
||||
Neither form exposes an imported declaration as a bare `Name`; ordinary
|
||||
unqualified lookup remains limited to lexical, builtin, and same-package
|
||||
declarations.
|
||||
declarations. A blank import creates no package-name object, an effective
|
||||
`init` import is rejected before installing one, and a missing target fails
|
||||
during import resolution, so none of those cases acquires the bare-package
|
||||
diagnostics or satisfies a named import's unused accounting.
|
||||
- An import whose effective file-local qualifier is `init` is invalid. This
|
||||
includes both `import init acme.codec;` and an unaliased import whose target
|
||||
declares `package init;`. Each resolved occurrence is rejected at its first
|
||||
|
||||
@@ -583,6 +583,35 @@ 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.
|
||||
|
||||
The focused dual-stage `bare_import_bindings_require_selectors` observer is the
|
||||
acceptance owner for the rule that an effective default or explicit import
|
||||
binding is a file-local package-name object, not a value or type. Its generated
|
||||
matrix must prove exact `use of package BINDING not in selector` value
|
||||
diagnostics and `BINDING (package name) is not a type` type diagnostics; an
|
||||
unused diagnostic for every named binding that has only bare occurrences; and
|
||||
the absence of that unused diagnostic once a legal selector independently uses
|
||||
the binding. It must cover occurrence order, repeated bare occurrences,
|
||||
explicit aliases, builtin-spelled bindings including `len`, `size`, and `align`,
|
||||
qualified value and type controls, nested value and type contexts, sibling-file
|
||||
isolation, blank imports, rejected `init` bindings, and missing-target
|
||||
precedence. Type-shaped arguments parsed for `size` and `align` must retain type
|
||||
checking when either spelling is instead an import binding; neither stage may
|
||||
fall into an internal expression-kind diagnostic.
|
||||
|
||||
The same observer must exercise ordinary root and imported builds plus
|
||||
production, same-package test, external-test, and honest test-only source
|
||||
roles. Cstage and WWstage must agree on status and normalized stdout/stderr,
|
||||
including diagnostic order and source position, and valid controls must produce
|
||||
byte-identical comparable artifacts and equal runtime results. Its lifecycle
|
||||
matrix must include cold rejection with no public or retained product, warm
|
||||
preservation of every prior committed work-file and public byte, imported
|
||||
dependency failure, invalidation followed by rejection, subsequent valid
|
||||
recovery, and exact transaction-residue checks. Legal selector controls retain
|
||||
ordinary graph/action, compilation, linking, publication, persistence, and
|
||||
reuse behavior. Producer/runtime failure, concurrency, interruption, process
|
||||
topology, and descendant cleanup remain with their established observers
|
||||
because this checker-owned rejection creates no producer or runtime boundary.
|
||||
|
||||
The focused dual-stage `effective_init_imports_never_enter_binding_recovery`
|
||||
observer is
|
||||
the acceptance owner for imports whose effective file-local qualifier is
|
||||
|
||||
Reference in New Issue
Block a user