prometeu-studio/docs/compiler/pbs/decisions/Name Resolution - Linking Phase Boundary Decision.md
2026-03-24 13:42:37 +00:00

201 lines
7.6 KiB
Markdown

# Name Resolution - Linking Phase Boundary Decision
Status: Accepted (Implemented)
Cycle: Initial name-resolution closure pass
## 1. Context
PBS v1 needs an explicit phase boundary around source-level name resolution so that:
- frontend implementation does not invent phase ownership,
- diagnostics can attribute failures consistently,
- conformance can test rejection classes coherently,
- and later lowering work does not have to guess whether a failure belongs to syntax, linking, or typing.
Earlier closure already established rules for:
- module scope construction,
- import naming and aliasing,
- builtin shell visibility,
- callable-set visibility across modules.
The remaining open question was how to classify the failure classes that arise while assembling visible declarations across files and modules.
## 2. Decision
PBS v1 adopts the following normative phase baseline for name resolution and related frontend failures:
1. `syntax` is a distinct normative phase.
2. `manifest/import resolution` is a distinct normative phase.
3. `linking` is a distinct normative phase.
4. `static semantics` is a distinct normative phase.
5. `syntax` owns malformed import grammar and other token-shape or grammar-shape failures.
6. `manifest/import resolution` owns failures to resolve project space, reserved stdlib space, dependency graph, module path, or environment source.
7. `linking` owns assembly of visible declarations across files and modules.
8. `linking` therefore owns:
- import of non-exported names,
- unresolved barrel entries against module declarations,
- collisions between visible imported names from different canonical origins,
- local-versus-import visibility collisions,
- callable-origin conflicts across module boundaries,
- and duplicate canonical builtin or host identities in one resolved environment.
9. `static semantics` owns declaration validity, type checking, member lookup once the receiver type is known, and overload resolution within an already-formed visible callable set.
10. Member lookup failure on an already-typed builtin receiver belongs to `static semantics`.
11. Ambiguity between different imported origins must be rejected in `linking` before any callsite analysis.
## 3. Phase Roles
### 3.1 Syntax
`syntax` owns failures that arise before module or declaration assembly.
Examples:
- malformed import declaration shape,
- malformed alias syntax,
- malformed declaration grammar,
- unexpected tokens.
### 3.2 Manifest and Import Resolution
`manifest/import resolution` owns failures of locating and loading the source of a module or reserved environment.
Examples:
- unresolved `@project:*` dependency target,
- unresolved reserved stdlib module,
- invalid project-space selection,
- failure to load the module source from the selected environment.
### 3.3 Linking
`linking` owns the assembly of the visible declaration space after modules have been found and loaded.
Examples:
- imported name is not exported by the target module,
- `mod.barrel` entry does not resolve against declarations of the module,
- two imports expose the same visible name from different canonical origins,
- a local visible declaration collides with an imported visible declaration,
- callable-origin conflicts across module boundaries,
- duplicate canonical builtin identities in one resolved environment,
- duplicate canonical host identities in one resolved environment.
### 3.4 Static Semantics
`static semantics` owns validation and typing of the already-linked visible program.
Examples:
- duplicate local declarations,
- invalid declaration validity conditions,
- type mismatch,
- member lookup on a known receiver type,
- overload resolution inside one already-visible callable set.
## 4. Linking As A Real Normative Phase
PBS keeps `linking` as a distinct normative phase rather than collapsing it fully into `static semantics`.
Reasoning:
- some failures arise only when assembling visible declarations across files and modules,
- those failures are neither pure grammar failures nor ordinary local typing failures,
- and naming `linking` gives diagnostics and conformance a stable vocabulary for them.
This does not require every implementation to expose one identical internal compiler pass graph.
It only requires the externally visible failure ownership to map back to this normative phase model.
## 5. Barrel Matching And Visible Assembly
`mod.barrel` matching belongs to `linking`.
This means:
- failure of a barrel item to resolve to a declaration of the module is not merely a syntax issue,
- and it is not deferred until later typing or lowering.
Barrel matching is part of assembling the module's visible declaration contract.
## 6. Imported-Origin Conflicts
Conflicts between different imported origins are rejected in `linking` immediately.
This means:
- the implementation must not wait for a specific callsite or use-site,
- overload resolution is not allowed to rescue ambiguity between different imported origins,
- and visible declaration conflicts are resolved structurally before later typing work proceeds.
## 7. Builtin Receiver Lookup
Member lookup on a builtin receiver whose type is already known remains `static semantics`.
The reason is:
- by that point the relevant shell visibility work has already completed,
- and the remaining question is ordinary typed member validity rather than module assembly.
## 8. Invariants
- The phase model exposed to users and conformance must remain deterministic.
- `linking` remains the phase for visible declaration assembly across module boundaries.
- Callsite analysis must not be used to resolve imported-origin conflicts.
- `static semantics` operates on an already-linked visible declaration space.
- Syntax and manifest/import resolution remain narrower than linking and do not absorb visible-name conflicts that arise only after module loading.
## 9. Explicit Non-Decisions
This decision record does not yet close:
- the full diagnostics-code taxonomy,
- whether implementations expose one explicit user-facing `linking` label or a mapped equivalent wording class,
- the final artifact-facing phase vocabulary after lowering and verification specs close,
- and the exact implementation architecture of one compiler pipeline.
## 10. Spec Impact
This decision should feed at least:
- `docs/general/specs/14. Name Resolution and Module Linking Specification.md`
- `docs/pbs/specs/12. Diagnostics Specification.md`
- `docs/general/specs/13. Conformance Test Specification.md`
It should also constrain future work in:
- `docs/pbs/specs/13. Lowering IRBackend Specification.md`
- `docs/pbs/decisions/Name Resolution - Scope, Lookup, and Imports Decision.md`
- `docs/pbs/decisions/Name Resolution - Builtin Shells and Host Owners Decision.md`
- `docs/pbs/decisions/Name Resolution - Callable Sets and Cross-Module Linking Decision.md`
## 11. Validation Notes
The intended phase split is:
- `syntax` parses declaration and import shape,
- `manifest/import resolution` finds and loads modules,
- `linking` assembles visible names across modules and barrels,
- `static semantics` validates and types the already-linked visible program.
Illustrative examples:
```pbs
import { Missing } from @a:m;
```
If `@a:m` resolves successfully but does not export `Missing`, this is a `linking` failure.
```pbs
import { f } from @a:m;
import { f } from @b:n;
```
If the canonical origins differ, this is a `linking` failure and is rejected before any callsite analysis.
```pbs
import { Vec2 } from @core:math;
value.zero()
```
If `value` is already known to have builtin type `Vec2` and `.zero` is not a valid member on that receiver type, the failure is `static semantics`.