integrated 14 into specs
This commit is contained in:
parent
1cacbd4f9a
commit
4b44674907
@ -141,5 +141,3 @@ This workshop should produce:
|
|||||||
- `docs/pbs/specs/13. Conformance Test Specification.md`
|
- `docs/pbs/specs/13. Conformance Test Specification.md`
|
||||||
- `docs/pbs/specs/14. Name Resolution and Module Linking Specification.md`
|
- `docs/pbs/specs/14. Name Resolution and Module Linking Specification.md`
|
||||||
- `docs/pbs/agendas/11. Diagnostics Agenda.md`
|
- `docs/pbs/agendas/11. Diagnostics Agenda.md`
|
||||||
- `docs/pbs/agendas/14.4. Name Resolution Workshop 4 - Linking Phase Boundary.md`
|
|
||||||
|
|
||||||
|
|||||||
@ -114,5 +114,3 @@ Rationale:
|
|||||||
- `docs/pbs/specs/12. IR and Lowering Specification.md`
|
- `docs/pbs/specs/12. IR and Lowering Specification.md`
|
||||||
- `docs/pbs/specs/15. Bytecode and PBX Mapping Specification.md`
|
- `docs/pbs/specs/15. Bytecode and PBX Mapping Specification.md`
|
||||||
- `docs/pbs/agendas/12. IR and Lowering Agenda.md`
|
- `docs/pbs/agendas/12. IR and Lowering Agenda.md`
|
||||||
- `docs/pbs/agendas/14.4. Name Resolution Workshop 4 - Linking Phase Boundary.md`
|
|
||||||
|
|
||||||
|
|||||||
@ -1,117 +0,0 @@
|
|||||||
# PBS Name Resolution and Module Linking Agenda
|
|
||||||
|
|
||||||
Status: Active
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
Drive the closure of `14. Name Resolution and Module Linking Specification.md` so frontend implementation can proceed without inventing lookup or linking rules.
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
This is the main unresolved frontend-facing spec. The current skeleton still leaves open:
|
|
||||||
|
|
||||||
- exact lookup order across locals, top-level declarations, imports, builtin shells, and reserved surfaces,
|
|
||||||
- shadowing and duplicate rules by namespace,
|
|
||||||
- import alias and barrel-export matching behavior,
|
|
||||||
- cross-module callable and overload visibility,
|
|
||||||
- and the resolution model for builtin and host-backed stdlib shells.
|
|
||||||
|
|
||||||
Without this closure, parser and AST work can proceed only as exploratory implementation rather than normative frontend work.
|
|
||||||
|
|
||||||
## Decisions To Produce
|
|
||||||
|
|
||||||
1. Decide deterministic lookup order for every relevant namespace.
|
|
||||||
2. Decide shadowing, duplicate, and ambiguity rules for local, module, and imported names.
|
|
||||||
3. Decide import alias resolution and barrel matching rules in enough detail for implementation.
|
|
||||||
4. Decide the resolution model for builtin shells, builtin constants, intrinsic members, and host owners.
|
|
||||||
5. Decide which rejections belong to static semantics and which belong to a distinct linking phase.
|
|
||||||
|
|
||||||
## Core Questions
|
|
||||||
|
|
||||||
1. What is the exact lookup order in value position, type position, callable position, and host-owner position?
|
|
||||||
2. Do shadowing rules differ between locals, imports, top-level declarations, and builtin surfaces?
|
|
||||||
3. How are callable overload sets formed across local module declarations and imports?
|
|
||||||
4. When two imported modules make the same name visible, what is the exact ambiguity and rejection rule?
|
|
||||||
5. Are reserved stdlib shells resolved as ordinary visible declarations with metadata, or do they have any special priority?
|
|
||||||
|
|
||||||
## Proposed Workshop Sequence
|
|
||||||
|
|
||||||
### Workshop 1: Scope, Lookup, and Imports
|
|
||||||
|
|
||||||
Purpose:
|
|
||||||
|
|
||||||
- close module scope construction,
|
|
||||||
- close lookup order by namespace,
|
|
||||||
- and close import alias and ambiguity rules.
|
|
||||||
|
|
||||||
Reference:
|
|
||||||
|
|
||||||
- `docs/pbs/agendas/14.1. Name Resolution Workshop 1 - Scope, Lookup, and Imports.md`
|
|
||||||
|
|
||||||
### Workshop 2: Builtin Shells and Host Owners
|
|
||||||
|
|
||||||
Purpose:
|
|
||||||
|
|
||||||
- close how reserved stdlib shells enter resolution,
|
|
||||||
- and close lookup and collision policy for builtin types, builtin constants, intrinsic members, and host owners.
|
|
||||||
|
|
||||||
Expected decisions:
|
|
||||||
|
|
||||||
- reserved-shell visibility model,
|
|
||||||
- builtin and host-owner lookup interaction,
|
|
||||||
- and canonical identity versus local aliasing boundaries.
|
|
||||||
|
|
||||||
### Workshop 3: Callable Sets and Cross-Module Linking
|
|
||||||
|
|
||||||
Purpose:
|
|
||||||
|
|
||||||
- close overload-set formation across module boundaries,
|
|
||||||
- and decide ambiguity and rejection rules for imported callable sets.
|
|
||||||
|
|
||||||
Expected decisions:
|
|
||||||
|
|
||||||
- callable-set merge rules,
|
|
||||||
- barrel-matching consequences for imported overloads,
|
|
||||||
- and cross-module ambiguity failures.
|
|
||||||
|
|
||||||
### Workshop 4: Linking Phase Boundary
|
|
||||||
|
|
||||||
Purpose:
|
|
||||||
|
|
||||||
- decide which rules belong to static semantics versus a distinct linking phase.
|
|
||||||
|
|
||||||
Expected decisions:
|
|
||||||
|
|
||||||
- phase split or collapse,
|
|
||||||
- rejection ownership by phase,
|
|
||||||
- and inputs needed by `11`, `12`, and frontend conformance.
|
|
||||||
|
|
||||||
## Expected Spec Material
|
|
||||||
|
|
||||||
The resulting spec work should be able to add or close sections for:
|
|
||||||
|
|
||||||
- scope construction,
|
|
||||||
- namespace-specific lookup order,
|
|
||||||
- import and alias resolution,
|
|
||||||
- barrel export matching,
|
|
||||||
- overload-set visibility across modules,
|
|
||||||
- builtin and host-shell resolution,
|
|
||||||
- linking-phase versus static-phase rejection boundaries,
|
|
||||||
- and deterministic cross-module failure cases.
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
|
|
||||||
- Redefining grammar already fixed in core syntax.
|
|
||||||
- Designing runtime loader behavior.
|
|
||||||
- Freezing one symbol-table implementation.
|
|
||||||
- Designing IDE auto-import behavior.
|
|
||||||
|
|
||||||
## Inputs
|
|
||||||
|
|
||||||
- `docs/pbs/specs/3. Core Syntax Specification.md`
|
|
||||||
- `docs/pbs/specs/4. Static Semantics Specification.md`
|
|
||||||
- `docs/pbs/specs/5. Manifest, Stdlib, and SDK Resolution Specification.md`
|
|
||||||
- `docs/pbs/specs/6.1. Intrinsics and Builtin Types Specification.md`
|
|
||||||
- `docs/pbs/specs/8. Stdlib Environment Packaging and Loading Specification.md`
|
|
||||||
- `docs/pbs/specs/14. Name Resolution and Module Linking Specification.md`
|
|
||||||
- `docs/pbs/specs/18. Standard Library Surface Specification.md`
|
|
||||||
@ -1,184 +0,0 @@
|
|||||||
# PBS Name Resolution Workshop 1
|
|
||||||
|
|
||||||
Status: Active
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
Run the first focused discussion for `14. Name Resolution and Module Linking Specification.md` on the smallest set of decisions that unlocks normative frontend work:
|
|
||||||
|
|
||||||
- scope construction,
|
|
||||||
- namespace-specific lookup order,
|
|
||||||
- import visibility,
|
|
||||||
- and ambiguity rejection.
|
|
||||||
|
|
||||||
## Why This Slice First
|
|
||||||
|
|
||||||
This slice is the best first cut because it does not require full lowering closure, but it does unblock:
|
|
||||||
|
|
||||||
- parser and binder architecture,
|
|
||||||
- symbol-table modeling,
|
|
||||||
- deterministic frontend diagnostics,
|
|
||||||
- and the later discussion of overload visibility and reserved stdlib shells.
|
|
||||||
|
|
||||||
It should intentionally avoid trying to close every linking edge case in one meeting.
|
|
||||||
|
|
||||||
## Proposed Meeting Order
|
|
||||||
|
|
||||||
1. Confirm already-settled inputs that are not up for debate.
|
|
||||||
2. Close scope-construction rules inside one module.
|
|
||||||
3. Close lookup order by namespace.
|
|
||||||
4. Close import visibility and alias rules.
|
|
||||||
5. Close ambiguity and duplicate rejection rules.
|
|
||||||
6. Record deferred items for a second workshop.
|
|
||||||
|
|
||||||
## Already-Settled Inputs To Reconfirm
|
|
||||||
|
|
||||||
The meeting should start by explicitly reaffirming:
|
|
||||||
|
|
||||||
- `mod.barrel` is the single source of module visibility.
|
|
||||||
- Imports target modules, not files.
|
|
||||||
- Only `pub` names are importable from another module.
|
|
||||||
- PBS has distinct type, value, callable, and host-owner namespaces.
|
|
||||||
- Builtin simple types `int`, `float`, `bool`, and `str` are always present in type position.
|
|
||||||
- Reserved stdlib spaces resolve from the selected stdlib environment, not from ordinary dependencies.
|
|
||||||
|
|
||||||
These points already come from existing specs and should not be reopened in this workshop.
|
|
||||||
|
|
||||||
## Decisions To Produce
|
|
||||||
|
|
||||||
1. Scope construction inside a module.
|
|
||||||
2. Lookup order in value, type, callable, and host-owner position.
|
|
||||||
3. Import alias behavior and collisions.
|
|
||||||
4. Ambiguity rules when multiple imports expose the same visible name.
|
|
||||||
5. The phase boundary between syntax, static semantics, and linking for these failures.
|
|
||||||
|
|
||||||
## Candidate Decisions
|
|
||||||
|
|
||||||
### 1. Scope Construction Inside One Module
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- File-local top-level declarations are collected first from all `.pbs` files in the module.
|
|
||||||
- `mod.barrel` is then applied as a visibility filter over the already-collected top-level declarations.
|
|
||||||
- Top-level declaration availability inside the same module does not depend on source-file order.
|
|
||||||
- Local block scopes nest normally over module scope.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- This matches the existing module model.
|
|
||||||
- It prevents file-order-dependent lookup.
|
|
||||||
- It keeps barrel as a visibility mechanism, not as the source of declaration existence.
|
|
||||||
|
|
||||||
### 2. Lookup Order By Namespace
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
Value position:
|
|
||||||
|
|
||||||
- nearest local bindings,
|
|
||||||
- parameters,
|
|
||||||
- visible `declare const`,
|
|
||||||
- visible imported values,
|
|
||||||
- service singleton values introduced by visible service declarations.
|
|
||||||
|
|
||||||
Type position:
|
|
||||||
|
|
||||||
- nearest type declarations visible in module scope,
|
|
||||||
- visible imported type declarations,
|
|
||||||
- builtin simple types.
|
|
||||||
|
|
||||||
Callable position:
|
|
||||||
|
|
||||||
- nearest callable declarations visible in module scope,
|
|
||||||
- then visible imported callable declarations,
|
|
||||||
- with overload sets formed after visibility filtering.
|
|
||||||
|
|
||||||
Host-owner position:
|
|
||||||
|
|
||||||
- visible host owners in module scope,
|
|
||||||
- then visible imported host owners.
|
|
||||||
|
|
||||||
Open point for discussion:
|
|
||||||
|
|
||||||
- whether builtin simple types should be treated as implicit outermost scope or as a final fallback outside ordinary visible declarations.
|
|
||||||
|
|
||||||
### 3. Import Alias Rules
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- `import @project:mod;` imports the module itself as a module-qualified reference surface only if another spec later defines such a surface.
|
|
||||||
- For current v1 name resolution, named imports are the only source of imported visible names.
|
|
||||||
- `import { X } from ...;` introduces `X` in the matching namespace of the exported declaration.
|
|
||||||
- `import { X as Y } from ...;` introduces only `Y` locally.
|
|
||||||
- Alias spelling affects only the local visible name, never canonical builtin or host identity.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- Keeps current source behavior conservative.
|
|
||||||
- Avoids inventing module-as-value semantics accidentally.
|
|
||||||
- Matches the existing distinction between source-visible spelling and canonical metadata identity.
|
|
||||||
|
|
||||||
### 4. Collision And Ambiguity Rules
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- Two local declarations in the same namespace that produce the same non-overload identity are duplicates.
|
|
||||||
- A local declaration and an imported declaration with the same visible name in the same namespace are a deterministic conflict unless one rule explicitly allows shadowing.
|
|
||||||
- Two imported declarations with the same visible name in the same namespace are a deterministic ambiguity unless they are the same declaration reached through the same canonical module resolution.
|
|
||||||
- Callable overload sets may merge only when their callable identities remain distinct after tuple-shape identity rules are applied.
|
|
||||||
- Non-callable namespaces do not merge by name.
|
|
||||||
|
|
||||||
Open point for discussion:
|
|
||||||
|
|
||||||
- whether local declarations should fully shadow imports in value, type, callable, and host-owner namespaces, or whether PBS should reject such shadowing to keep lookup simpler for beginners.
|
|
||||||
|
|
||||||
### 5. Diagnostic Phase Boundary
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- malformed import syntax remains syntax-phase.
|
|
||||||
- unresolved module path remains manifest/import-resolution phase.
|
|
||||||
- import of non-exported symbol remains linking-phase or manifest/import-resolution-adjacent phase, but not syntax-phase.
|
|
||||||
- duplicate local declarations remain static-semantics phase.
|
|
||||||
- ambiguous visible names after import resolution remain linking-phase unless the project later collapses linking into static semantics explicitly.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- This preserves a clean split between parsing, declaration validity, and cross-module resolution.
|
|
||||||
- It gives `11. Diagnostics Specification.md` a concrete input rather than forcing that spec to invent phases in isolation.
|
|
||||||
|
|
||||||
## Questions To Resolve In The Room
|
|
||||||
|
|
||||||
1. Do local top-level declarations shadow imported names, or is any such collision rejected?
|
|
||||||
2. Are parameters and local `let` bindings one combined nearest-scope value layer, or should parameters be modeled separately for diagnostics only?
|
|
||||||
3. Can imported overloads and local overloads merge into one callable set, or must that be rejected?
|
|
||||||
4. Is `import @project:module;` currently just side-effect-free validation, or should it create any usable source-visible surface in v1?
|
|
||||||
5. Do builtin simple types behave like reserved final fallback names or like ordinary always-visible type declarations?
|
|
||||||
|
|
||||||
## Expected Outputs
|
|
||||||
|
|
||||||
This workshop should produce:
|
|
||||||
|
|
||||||
1. a decision record for scope and lookup order,
|
|
||||||
2. a decision record for import alias and ambiguity policy,
|
|
||||||
3. a concrete update plan for `14. Name Resolution and Module Linking Specification.md`,
|
|
||||||
4. and a list of deferred issues for Workshop 2.
|
|
||||||
|
|
||||||
## Explicit Deferrals For Workshop 2
|
|
||||||
|
|
||||||
The following topics should be deferred unless they become unavoidable:
|
|
||||||
|
|
||||||
- reserved stdlib shell priority versus ordinary imports,
|
|
||||||
- member lookup on builtin receivers,
|
|
||||||
- contract and method dispatch lookup details,
|
|
||||||
- barrel matching of callable overload sets across module boundaries,
|
|
||||||
- and whether linking is its own normative phase or is folded fully into static semantics.
|
|
||||||
|
|
||||||
## Inputs
|
|
||||||
|
|
||||||
- `docs/pbs/specs/3. Core Syntax Specification.md`
|
|
||||||
- `docs/pbs/specs/4. Static Semantics Specification.md`
|
|
||||||
- `docs/pbs/specs/5. Manifest, Stdlib, and SDK Resolution Specification.md`
|
|
||||||
- `docs/pbs/specs/14. Name Resolution and Module Linking Specification.md`
|
|
||||||
- `docs/pbs/specs/18. Standard Library Surface Specification.md`
|
|
||||||
- `docs/pbs/agendas/14. Name Resolution and Module Linking Agenda.md`
|
|
||||||
@ -1,159 +0,0 @@
|
|||||||
# PBS Name Resolution Workshop 2
|
|
||||||
|
|
||||||
Status: Active
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
Run the second focused discussion for `14. Name Resolution and Module Linking Specification.md` on reserved stdlib shells and host-owner resolution:
|
|
||||||
|
|
||||||
- builtin type shells,
|
|
||||||
- builtin constants,
|
|
||||||
- intrinsic member surfaces,
|
|
||||||
- and `declare host` owners imported from reserved stdlib modules.
|
|
||||||
|
|
||||||
## Why This Slice Second
|
|
||||||
|
|
||||||
This slice should come after Workshop 1 because it builds on already-closed rules for:
|
|
||||||
|
|
||||||
- module scope construction,
|
|
||||||
- ordinary import visibility,
|
|
||||||
- aliasing,
|
|
||||||
- and namespace-specific lookup order.
|
|
||||||
|
|
||||||
It also prepares the ground for later lowering by making the resolution contract explicit without prematurely defining artifact shape.
|
|
||||||
|
|
||||||
## Proposed Meeting Order
|
|
||||||
|
|
||||||
1. Reconfirm already-settled ownership and stdlib-loading rules.
|
|
||||||
2. Close the visibility model for builtin and host shells.
|
|
||||||
3. Close lookup behavior for builtin types, builtin constants, and host owners.
|
|
||||||
4. Close member resolution on builtin receivers.
|
|
||||||
5. Close collision and ambiguity rules involving reserved shells.
|
|
||||||
6. Record deferred callable-linking issues for Workshop 3.
|
|
||||||
|
|
||||||
## Already-Settled Inputs To Reconfirm
|
|
||||||
|
|
||||||
The meeting should explicitly reaffirm:
|
|
||||||
|
|
||||||
- reserved stdlib project spaces include `@core:*` and `@sdk:*`,
|
|
||||||
- reserved stdlib spaces resolve only from the selected stdlib environment,
|
|
||||||
- source-visible builtin names resolve through imported builtin shell declarations,
|
|
||||||
- builtin intrinsic members are not imported as free functions,
|
|
||||||
- canonical builtin and host identities come from metadata rather than local alias spelling,
|
|
||||||
- and host-owner namespace remains distinct from type, value, and callable namespaces.
|
|
||||||
|
|
||||||
These are already fixed elsewhere and should not be reopened here.
|
|
||||||
|
|
||||||
## Decisions To Produce
|
|
||||||
|
|
||||||
1. Visibility rules for builtin shells and host owners imported from reserved stdlib modules.
|
|
||||||
2. Lookup rules for builtin types, builtin constants, intrinsic members, and host owners.
|
|
||||||
3. Collision and ambiguity rules when reserved shells overlap with ordinary user-visible declarations.
|
|
||||||
4. Diagnostic ownership for reserved-shell resolution failures.
|
|
||||||
5. Deferred boundary between source resolution and later lowering.
|
|
||||||
|
|
||||||
## Candidate Decisions
|
|
||||||
|
|
||||||
### 1. Reserved Shells Enter Resolution As Ordinary Visible Declarations
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- Imported builtin type shells become visible in the type namespace like other imported type declarations.
|
|
||||||
- Imported builtin constants become visible in the value namespace like other imported constants.
|
|
||||||
- Imported host owners become visible in the host-owner namespace like other imported host declarations.
|
|
||||||
- Reserved metadata changes canonical lowering identity, not the ordinary namespace they enter.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- This keeps the source model uniform.
|
|
||||||
- It avoids a hidden parallel lookup system just for reserved stdlib surfaces.
|
|
||||||
- It matches the current specs that describe shells as imported declarations with metadata.
|
|
||||||
|
|
||||||
### 2. Builtin Simple Types Remain Separate From Imported Builtin Shells
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- `int`, `float`, `bool`, and `str` remain always-available builtin simple types.
|
|
||||||
- Imported builtin shells such as `Vec2` or `Color` are ordinary visible type declarations and do not share special priority with builtin simple types.
|
|
||||||
- User-authored declarations still cannot reuse builtin simple type names.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- Separates the small predeclared core from stdlib-delivered builtin shells.
|
|
||||||
- Prevents `@core:*` imports from acting like implicit language keywords.
|
|
||||||
|
|
||||||
### 3. Intrinsic Members Resolve Through Receiver Type, Never Through Free Lookup
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- Builtin intrinsic methods are resolved only during member lookup on a builtin-typed receiver.
|
|
||||||
- They do not appear in top-level callable lookup.
|
|
||||||
- They do not become visible through named import by method name.
|
|
||||||
- Alias changes to the builtin type affect only source spelling of the owner type, not intrinsic canonical identity.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- This matches the existing builtin and intrinsics spec.
|
|
||||||
- It prevents accidental confusion between member semantics and free functions.
|
|
||||||
|
|
||||||
### 4. Collision Policy For Reserved Shells
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- Namespace collisions are handled by the same visibility rules used for ordinary declarations.
|
|
||||||
- A user-visible imported builtin type colliding with another visible type name is a deterministic conflict unless an already-closed shadowing rule says otherwise.
|
|
||||||
- A host owner colliding with a type or value name is not a conflict by itself because host-owner namespace is distinct.
|
|
||||||
- Duplicate canonical builtin identities in one resolved environment remain rejection cases even if local aliases differ.
|
|
||||||
|
|
||||||
Open point for discussion:
|
|
||||||
|
|
||||||
- whether conflicts involving reserved shells should be rejected more aggressively than ordinary import conflicts to preserve beginner clarity.
|
|
||||||
|
|
||||||
### 5. Diagnostic Ownership
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- failure to resolve the reserved module remains manifest/import-resolution phase,
|
|
||||||
- import of a non-exported builtin or host shell remains linking or resolution-phase rejection,
|
|
||||||
- member lookup failure on a builtin receiver remains static-semantics or linking-phase depending on the final phase model,
|
|
||||||
- duplicate canonical builtin or host identities in one resolved environment remain deterministic compile-time rejection.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- This keeps module loading, visibility, and member resolution separate.
|
|
||||||
- It gives `11` a concrete discussion input later.
|
|
||||||
|
|
||||||
## Questions To Resolve In The Room
|
|
||||||
|
|
||||||
1. Should reserved shells be completely ordinary imported declarations at source level, or do any of them need special lookup priority?
|
|
||||||
2. Can an ordinary user declaration shadow an imported builtin shell, or should that always be rejected?
|
|
||||||
3. Is member lookup on builtin receivers purely a type-driven operation after receiver typing, or does name resolution need a separate reserved-shell step?
|
|
||||||
4. Should `@sdk:*` be allowed to expose builtin shells, or should builtin shells remain policy-preferred under `@core:*` only?
|
|
||||||
5. Which failures around canonical builtin identity belong to resolution, static semantics, or environment validation?
|
|
||||||
|
|
||||||
## Expected Outputs
|
|
||||||
|
|
||||||
This workshop should produce:
|
|
||||||
|
|
||||||
1. a decision record for reserved-shell visibility and lookup,
|
|
||||||
2. a decision record for collision policy involving builtin and host shells,
|
|
||||||
3. a concrete update plan for the reserved-shell sections of `14`,
|
|
||||||
4. and a deferred list for cross-module callable-set discussion in Workshop 3.
|
|
||||||
|
|
||||||
## Explicit Deferrals For Workshop 3
|
|
||||||
|
|
||||||
The following topics should be deferred unless unavoidable:
|
|
||||||
|
|
||||||
- imported callable-set merging across modules,
|
|
||||||
- overload resolution across ordinary and reserved callable surfaces,
|
|
||||||
- contract dispatch lookup,
|
|
||||||
- and whether linking is fully distinct from static semantics.
|
|
||||||
|
|
||||||
## Inputs
|
|
||||||
|
|
||||||
- `docs/pbs/specs/5. Manifest, Stdlib, and SDK Resolution Specification.md`
|
|
||||||
- `docs/pbs/specs/6.1. Intrinsics and Builtin Types Specification.md`
|
|
||||||
- `docs/pbs/specs/14. Name Resolution and Module Linking Specification.md`
|
|
||||||
- `docs/pbs/specs/18. Standard Library Surface Specification.md`
|
|
||||||
- `docs/pbs/agendas/14. Name Resolution and Module Linking Agenda.md`
|
|
||||||
- `docs/pbs/agendas/14.1. Name Resolution Workshop 1 - Scope, Lookup, and Imports.md`
|
|
||||||
@ -1,160 +0,0 @@
|
|||||||
# PBS Name Resolution Workshop 3
|
|
||||||
|
|
||||||
Status: Active
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
Run the third focused discussion for `14. Name Resolution and Module Linking Specification.md` on callable visibility and cross-module linking:
|
|
||||||
|
|
||||||
- imported callable sets,
|
|
||||||
- overload-set formation across module boundaries,
|
|
||||||
- barrel matching consequences for imported overloads,
|
|
||||||
- and deterministic ambiguity rejection.
|
|
||||||
|
|
||||||
## Why This Slice Third
|
|
||||||
|
|
||||||
This slice should come after Workshops 1 and 2 because it depends on already-closed rules for:
|
|
||||||
|
|
||||||
- scope construction,
|
|
||||||
- namespace-specific lookup order,
|
|
||||||
- import aliasing,
|
|
||||||
- reserved-shell visibility,
|
|
||||||
- and ordinary collision handling.
|
|
||||||
|
|
||||||
It is also the last major source-facing step before deciding whether linking is a separate normative phase.
|
|
||||||
|
|
||||||
## Proposed Meeting Order
|
|
||||||
|
|
||||||
1. Reconfirm callable identity and barrel visibility rules that are already fixed.
|
|
||||||
2. Close how imported callable declarations become visible.
|
|
||||||
3. Close overload-set formation across local and imported declarations.
|
|
||||||
4. Close ambiguity and conflict rules for cross-module callable sets.
|
|
||||||
5. Close barrel-matching consequences for imported overload visibility.
|
|
||||||
6. Record phase-boundary questions for Workshop 4.
|
|
||||||
|
|
||||||
## Already-Settled Inputs To Reconfirm
|
|
||||||
|
|
||||||
The meeting should explicitly reaffirm:
|
|
||||||
|
|
||||||
- callable identity ignores parameter labels and output labels,
|
|
||||||
- overloaded functions differ by input/output tuple shape rather than parameter spelling,
|
|
||||||
- only `pub` callable entries are importable from another module,
|
|
||||||
- each exported overload must appear explicitly in `mod.barrel`,
|
|
||||||
- and `mod.barrel` matches callables by callable identity rather than by label spelling alone.
|
|
||||||
|
|
||||||
These points are already fixed and should not be reopened here.
|
|
||||||
|
|
||||||
## Decisions To Produce
|
|
||||||
|
|
||||||
1. Visibility rules for imported callable declarations and callable sets.
|
|
||||||
2. Merge or no-merge policy between local and imported overloads.
|
|
||||||
3. Ambiguity and rejection rules for callable sets visible from multiple modules.
|
|
||||||
4. Barrel consequences for callable visibility across modules.
|
|
||||||
5. Diagnostic ownership for callable-linking failures.
|
|
||||||
|
|
||||||
## Candidate Decisions
|
|
||||||
|
|
||||||
### 1. Imported Callables Enter Ordinary Callable Lookup
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- `import { f } from @project:mod;` introduces the exported callable set named `f` into local callable lookup.
|
|
||||||
- If the source module exports multiple overloads of `f`, the imported surface initially brings in that visible overload set, not only one declaration.
|
|
||||||
- Alias spelling changes only the local visible callable name, not callable identity.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- This keeps imported callables aligned with existing callable namespace rules.
|
|
||||||
- It avoids inventing per-overload import syntax before it is needed.
|
|
||||||
|
|
||||||
### 2. Local And Imported Overloads Do Not Merge Implicitly
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- A local callable set and an imported callable set with the same visible name do not silently merge.
|
|
||||||
- Such a collision is a deterministic ambiguity unless both sets resolve to the same canonical underlying declarations.
|
|
||||||
- Overload merging is allowed only within one declaration origin set already sanctioned by module visibility and barrel rules.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- Silent merging across modules makes beginner reasoning harder.
|
|
||||||
- It also makes diagnostics and compatibility more fragile.
|
|
||||||
- Rejecting cross-origin merges keeps overload sets traceable.
|
|
||||||
|
|
||||||
Open point for discussion:
|
|
||||||
|
|
||||||
- whether there is any narrow exception for identical re-exported declarations reached through more than one import path.
|
|
||||||
|
|
||||||
### 3. Cross-Module Callable Ambiguity Is Rejected Before Overload Resolution
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- If two different imported modules make the same callable name visible locally, and they do not denote the same canonical declarations, the program is rejected before ordinary overload resolution.
|
|
||||||
- Overload resolution should not be used as a tie-breaker between different imported origin sets.
|
|
||||||
- Callable ambiguity is resolved structurally by import visibility, not opportunistically by the later callsite.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- This keeps lookup deterministic and simple.
|
|
||||||
- It avoids context-sensitive import ambiguity that changes across callsites.
|
|
||||||
|
|
||||||
### 4. Barrel Visibility Defines The Imported Callable Set Boundary
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- Only overloads exported through the source module's `mod.barrel` are part of the importable callable set.
|
|
||||||
- Non-exported local overloads in the source module do not participate in imported callable visibility.
|
|
||||||
- A named import of `f` from another module is therefore a request for that module's exported `f` set, filtered by barrel visibility.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- This makes cross-module callable linking a direct consequence of barrel policy rather than an extra ad hoc rule.
|
|
||||||
|
|
||||||
### 5. Diagnostic Ownership
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- duplicate callable declarations inside one module remain static-semantics rejection,
|
|
||||||
- failure of a barrel callable entry to resolve inside its own module remains a module-linking or static-semantics-adjacent rejection depending on the final phase split,
|
|
||||||
- cross-module callable-name ambiguity remains linking-phase rejection,
|
|
||||||
- and overload-resolution failure at a callsite remains static semantics rather than module linking.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- This preserves a useful distinction between visible-set formation and callsite selection.
|
|
||||||
|
|
||||||
## Questions To Resolve In The Room
|
|
||||||
|
|
||||||
1. Should local and imported callable sets ever merge automatically under the same name?
|
|
||||||
2. Should two imported callable sets with the same name be rejected immediately, or only when actually referenced?
|
|
||||||
3. If the same declaration becomes reachable through two import paths, should that still be rejected or canonicalized as one visible origin?
|
|
||||||
4. Is there any need for per-overload import control in v1, or is barrel-level export enough?
|
|
||||||
5. Where exactly is the line between callable-set formation and ordinary overload resolution?
|
|
||||||
|
|
||||||
## Expected Outputs
|
|
||||||
|
|
||||||
This workshop should produce:
|
|
||||||
|
|
||||||
1. a decision record for imported callable-set visibility,
|
|
||||||
2. a decision record for callable ambiguity and merge policy,
|
|
||||||
3. a concrete update plan for the callable-linking sections of `14`,
|
|
||||||
4. and a narrow carry-forward list for phase-boundary cleanup in Workshop 4.
|
|
||||||
|
|
||||||
## Explicit Deferrals For Workshop 4
|
|
||||||
|
|
||||||
The following topics should be deferred unless unavoidable:
|
|
||||||
|
|
||||||
- whether linking is a formally distinct phase or folded into static semantics,
|
|
||||||
- diagnostic naming and code stability,
|
|
||||||
- source-to-artifact mapping concerns,
|
|
||||||
- and backend consequences of callable linking.
|
|
||||||
|
|
||||||
## Inputs
|
|
||||||
|
|
||||||
- `docs/pbs/specs/3. Core Syntax Specification.md`
|
|
||||||
- `docs/pbs/specs/4. Static Semantics Specification.md`
|
|
||||||
- `docs/pbs/specs/14. Name Resolution and Module Linking Specification.md`
|
|
||||||
- `docs/pbs/agendas/14. Name Resolution and Module Linking Agenda.md`
|
|
||||||
- `docs/pbs/agendas/14.1. Name Resolution Workshop 1 - Scope, Lookup, and Imports.md`
|
|
||||||
- `docs/pbs/agendas/14.2. Name Resolution Workshop 2 - Builtin Shells and Host Owners.md`
|
|
||||||
|
|
||||||
@ -1,144 +0,0 @@
|
|||||||
# PBS Name Resolution Workshop 4
|
|
||||||
|
|
||||||
Status: Active
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
Run the fourth focused discussion for `14. Name Resolution and Module Linking Specification.md` on phase ownership:
|
|
||||||
|
|
||||||
- what belongs to syntax,
|
|
||||||
- what belongs to static semantics,
|
|
||||||
- what belongs to module/linking,
|
|
||||||
- and whether PBS keeps linking as a distinct normative phase at all.
|
|
||||||
|
|
||||||
## Why This Slice Last
|
|
||||||
|
|
||||||
This slice should come last because it is mostly a boundary-setting exercise over decisions already made in Workshops 1 through 3.
|
|
||||||
|
|
||||||
Trying to close it earlier would force abstract phase arguments before the actual failure classes were concrete.
|
|
||||||
|
|
||||||
## Proposed Meeting Order
|
|
||||||
|
|
||||||
1. Reconfirm the concrete failure categories identified in earlier workshops.
|
|
||||||
2. Group those failures by source-only, module-resolution, and cross-module visibility concerns.
|
|
||||||
3. Decide whether PBS names linking as a distinct normative phase.
|
|
||||||
4. Decide how this boundary feeds diagnostics, conformance, and frontend implementation claims.
|
|
||||||
5. Record any residual editorial cleanup for the spec integration pass.
|
|
||||||
|
|
||||||
## Failure Classes To Sort
|
|
||||||
|
|
||||||
This workshop should explicitly sort at least the following classes:
|
|
||||||
|
|
||||||
- malformed import syntax,
|
|
||||||
- unresolved module path,
|
|
||||||
- import of non-exported symbol,
|
|
||||||
- duplicate local declarations,
|
|
||||||
- ambiguous imported names,
|
|
||||||
- callable-set ambiguity across modules,
|
|
||||||
- builtin-receiver member lookup failure,
|
|
||||||
- duplicate canonical builtin or host identities in one resolved environment,
|
|
||||||
- and barrel entries that fail to resolve against module declarations.
|
|
||||||
|
|
||||||
## Decisions To Produce
|
|
||||||
|
|
||||||
1. Whether linking is a distinct normative phase in PBS v1.
|
|
||||||
2. Ownership of the major failure classes already identified.
|
|
||||||
3. The minimum phase vocabulary needed by diagnostics and conformance.
|
|
||||||
4. The frontend implementation consequences of the chosen phase model.
|
|
||||||
|
|
||||||
## Candidate Decisions
|
|
||||||
|
|
||||||
### 1. Keep Linking As A Distinct Normative Phase
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- syntax owns grammar and token-shape failures,
|
|
||||||
- static semantics owns declaration validity, typing, and callsite selection,
|
|
||||||
- linking owns cross-file and cross-module visible-set formation and barrel/import matching.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- This gives a clean home to failures that are neither purely syntactic nor ordinary local typing.
|
|
||||||
- It matches the practical compiler architecture implied by module loading and barrel visibility.
|
|
||||||
- It gives `11` and `13` a usable phase vocabulary.
|
|
||||||
|
|
||||||
Alternative to discuss:
|
|
||||||
|
|
||||||
- collapse linking into static semantics and treat "linking" only as an implementation detail.
|
|
||||||
|
|
||||||
### 2. Module-Visibility And Import Failures Belong To Linking
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- unresolved exported-name lookup after the target module is loaded belongs to linking,
|
|
||||||
- ambiguous imported visible names belong to linking,
|
|
||||||
- callable-set ambiguity across modules belongs to linking,
|
|
||||||
- and unresolved barrel entries belong to linking or module-linking rather than to syntax.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- These failures arise from assembling visible declarations across files or modules, not from parsing isolated declarations.
|
|
||||||
|
|
||||||
### 3. Callsite Selection Remains Static Semantics
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- once visible callable sets are formed, overload resolution at a callsite remains static semantics,
|
|
||||||
- member lookup driven by the known receiver type also remains static semantics unless it fails earlier due to unresolved imported shell visibility.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- This keeps typing and callable selection together.
|
|
||||||
- It prevents linking from becoming a catch-all bucket for any failure that mentions imports.
|
|
||||||
|
|
||||||
### 4. Diagnostics And Conformance Need Only A Small Stable Vocabulary
|
|
||||||
|
|
||||||
Candidate direction:
|
|
||||||
|
|
||||||
- the minimum normative phase vocabulary is:
|
|
||||||
syntax,
|
|
||||||
static semantics,
|
|
||||||
manifest/import resolution,
|
|
||||||
linking,
|
|
||||||
and host-binding/capability admission.
|
|
||||||
- tools may subdivide internally, but they should map back to that stable external vocabulary.
|
|
||||||
|
|
||||||
Rationale:
|
|
||||||
|
|
||||||
- This is enough for source-facing determinism without overfitting to one compiler pipeline.
|
|
||||||
|
|
||||||
## Questions To Resolve In The Room
|
|
||||||
|
|
||||||
1. Is there real value in naming linking as its own normative phase, or does that add conceptual weight without enough payoff?
|
|
||||||
2. Should unresolved barrel entries be considered module-internal static invalidity or linking invalidity?
|
|
||||||
3. Should builtin-receiver member lookup failure always stay in static semantics once receiver type is known?
|
|
||||||
4. Do diagnostics and conformance need the word `linking`, or only a broader `resolution` bucket?
|
|
||||||
5. Does the chosen phase model help or hinder future lowering and verifier specs?
|
|
||||||
|
|
||||||
## Expected Outputs
|
|
||||||
|
|
||||||
This workshop should produce:
|
|
||||||
|
|
||||||
1. a decision record for the PBS phase model around name resolution and linking,
|
|
||||||
2. a sorted matrix of failure classes by phase,
|
|
||||||
3. a concrete update plan for `14`,
|
|
||||||
4. and explicit follow-ups for `11` and `13`.
|
|
||||||
|
|
||||||
## Explicit Deferrals
|
|
||||||
|
|
||||||
The following topics should be deferred unless unavoidable:
|
|
||||||
|
|
||||||
- detailed diagnostic code design,
|
|
||||||
- artifact-level failure surfacing,
|
|
||||||
- verifier and loader phase internals,
|
|
||||||
- and optimizer or backend pipeline naming.
|
|
||||||
|
|
||||||
## Inputs
|
|
||||||
|
|
||||||
- `docs/pbs/specs/11. Diagnostics Specification.md`
|
|
||||||
- `docs/pbs/specs/13. Conformance Test Specification.md`
|
|
||||||
- `docs/pbs/specs/14. Name Resolution and Module Linking Specification.md`
|
|
||||||
- `docs/pbs/agendas/14. Name Resolution and Module Linking Agenda.md`
|
|
||||||
- `docs/pbs/agendas/14.1. Name Resolution Workshop 1 - Scope, Lookup, and Imports.md`
|
|
||||||
- `docs/pbs/agendas/14.2. Name Resolution Workshop 2 - Builtin Shells and Host Owners.md`
|
|
||||||
- `docs/pbs/agendas/14.3. Name Resolution Workshop 3 - Callable Sets and Cross-Module Linking.md`
|
|
||||||
@ -0,0 +1,168 @@
|
|||||||
|
# Name Resolution - Builtin Shells and Host Owners Decision
|
||||||
|
|
||||||
|
Status: Proposed
|
||||||
|
Cycle: Initial name-resolution closure pass
|
||||||
|
|
||||||
|
## 1. Context
|
||||||
|
|
||||||
|
PBS v1 needs a deterministic rule for how reserved stdlib shells participate in source-level name resolution.
|
||||||
|
|
||||||
|
The open questions were:
|
||||||
|
|
||||||
|
- whether builtin shells and host owners enter lookup as ordinary imported declarations or as special compiler-only names,
|
||||||
|
- whether builtin intrinsic methods participate in free callable lookup,
|
||||||
|
- whether reserved shells receive special collision priority,
|
||||||
|
- whether builtin shells belong under `@core:*` or `@sdk:*` by policy,
|
||||||
|
- and which phase owns failures related to builtin member lookup and canonical identity duplication.
|
||||||
|
|
||||||
|
Important fixed inputs already existed:
|
||||||
|
|
||||||
|
- reserved stdlib project spaces are resolved from the selected stdlib environment,
|
||||||
|
- source-visible builtin names resolve through imported builtin shell declarations,
|
||||||
|
- canonical builtin and host identities come from metadata rather than local spelling,
|
||||||
|
- builtin intrinsic methods are not imported as free functions,
|
||||||
|
- and host-owner namespace remains distinct from type, value, and callable namespaces.
|
||||||
|
|
||||||
|
## 2. Decision
|
||||||
|
|
||||||
|
PBS v1 adopts the following baseline for builtin shells and host-owner resolution:
|
||||||
|
|
||||||
|
1. Source-visible declarations exposed by reserved builtin shells are imported into their ordinary corresponding namespaces.
|
||||||
|
2. This means builtin-exposed types enter type lookup as imported type declarations, builtin-exposed constants enter value lookup as imported values, and host owners enter host-owner lookup as imported host-owner declarations.
|
||||||
|
3. `declare builtin type` itself remains a reserved stdlib/toolchain declaration form rather than ordinary user-authored source syntax.
|
||||||
|
4. Reserved-shell metadata exists to drive canonical identity and later lowering, not to create special source-level lookup priority.
|
||||||
|
5. Builtin simple types `int`, `float`, `bool`, and `str` remain separate always-available reserved type names and are not treated as ordinary imported builtin shells.
|
||||||
|
6. Builtin intrinsic methods never participate in free lookup.
|
||||||
|
7. Builtin intrinsic methods are resolved only through member lookup on a receiver already known to have the corresponding builtin type.
|
||||||
|
8. Reserved builtin shells and host owners follow the same collision policy as ordinary imported declarations: same visible name in the same namespace is an error unless another already-closed rule explicitly permits otherwise.
|
||||||
|
9. By policy, builtin shells are preferred under `@core:*`.
|
||||||
|
10. Member lookup failure on an already-typed builtin receiver belongs to static semantics.
|
||||||
|
11. Duplicate canonical builtin identities or duplicate canonical host identities in one resolved environment belong normatively to linking rather than to syntax or ordinary static semantics.
|
||||||
|
|
||||||
|
## 3. Reserved Shells As Imported Declarations
|
||||||
|
|
||||||
|
Reserved shells are source-visible through ordinary import and visibility rules.
|
||||||
|
|
||||||
|
This means:
|
||||||
|
|
||||||
|
- the user imports the shell from a reserved stdlib module,
|
||||||
|
- the imported shell becomes visible in the matching namespace,
|
||||||
|
- and source-level lookup treats that visible declaration like other imported declarations of the same namespace.
|
||||||
|
|
||||||
|
The compiler still recognizes the reserved metadata attached to those declarations, but that metadata does not create a parallel hidden namespace.
|
||||||
|
|
||||||
|
## 4. Builtin Simple Types Versus Imported Builtin Shells
|
||||||
|
|
||||||
|
PBS distinguishes:
|
||||||
|
|
||||||
|
- the tiny predeclared builtin simple-type set: `int`, `float`, `bool`, `str`,
|
||||||
|
- and imported builtin shells such as `Vec2`, `Color`, or similar stdlib-exposed types.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- builtin simple types remain always available without import,
|
||||||
|
- imported builtin shells are not promoted to that same status,
|
||||||
|
- and the two categories must not be conflated in source-level lookup.
|
||||||
|
|
||||||
|
## 5. Intrinsic Member Lookup
|
||||||
|
|
||||||
|
Builtin intrinsic members are resolved only through receiver-member lookup.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- they do not appear in top-level callable lookup,
|
||||||
|
- they are not introduced by import as free functions,
|
||||||
|
- and aliasing the owner builtin type changes only the local visible owner spelling, not the intrinsic's canonical identity.
|
||||||
|
|
||||||
|
This keeps member semantics uniform and avoids special free-function treatment for reserved surfaces.
|
||||||
|
|
||||||
|
## 6. Collision Policy
|
||||||
|
|
||||||
|
Reserved shells receive no special collision privilege.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- if an imported builtin shell declaration and another visible declaration produce the same visible name in the same namespace, the program is rejected under the ordinary collision rules already chosen,
|
||||||
|
- if a host owner shares a spelling with a type or value, that alone is not a conflict because host-owner namespace remains distinct,
|
||||||
|
- and reserved-shell status does not authorize silent shadowing.
|
||||||
|
|
||||||
|
## 7. Reserved Project-Space Policy
|
||||||
|
|
||||||
|
For source-level policy:
|
||||||
|
|
||||||
|
- builtin shells are preferred under `@core:*`,
|
||||||
|
- host-facing capability surfaces remain the natural fit for `@sdk:*`.
|
||||||
|
|
||||||
|
This is a policy direction for clarity and separation of concerns.
|
||||||
|
It does not change the underlying ownership model governed by other specs.
|
||||||
|
|
||||||
|
## 8. Failure Ownership Baseline
|
||||||
|
|
||||||
|
The current baseline is:
|
||||||
|
|
||||||
|
- failure to resolve a reserved stdlib module remains manifest/import resolution,
|
||||||
|
- import of a non-exported reserved shell remains linking,
|
||||||
|
- member lookup failure on an already-typed builtin receiver remains static semantics,
|
||||||
|
- duplicate canonical builtin or host identities in one resolved environment remain linking failures.
|
||||||
|
|
||||||
|
Implementations may internally perform some of these checks during environment validation, but the normative external phase ownership is linking where the linking-phase decision assigns it.
|
||||||
|
|
||||||
|
## 9. Invariants
|
||||||
|
|
||||||
|
- Reserved-shell metadata does not create special source-level lookup priority.
|
||||||
|
- Builtin intrinsic methods are never free functions in source-level lookup.
|
||||||
|
- Builtin simple types remain separate from stdlib-delivered builtin shells.
|
||||||
|
- Host owners remain in the host-owner namespace only.
|
||||||
|
- Ordinary collision rules apply to reserved-shell imports unless a later explicit rule says otherwise.
|
||||||
|
- Canonical identity conflicts are linking problems, not syntax problems.
|
||||||
|
|
||||||
|
## 10. Explicit Non-Decisions
|
||||||
|
|
||||||
|
This decision record does not yet close:
|
||||||
|
|
||||||
|
- the full stdlib-surface policy of `18. Standard Library Surface Specification.md`,
|
||||||
|
- and the final lowering consequences in `12. IR and Lowering Specification.md`.
|
||||||
|
|
||||||
|
## 11. Spec Impact
|
||||||
|
|
||||||
|
This decision should feed at least:
|
||||||
|
|
||||||
|
- `docs/pbs/specs/14. Name Resolution and Module Linking Specification.md`
|
||||||
|
- `docs/pbs/specs/18. Standard Library Surface Specification.md`
|
||||||
|
- `docs/pbs/specs/11. Diagnostics Specification.md`
|
||||||
|
|
||||||
|
It should also constrain future work in:
|
||||||
|
|
||||||
|
- `docs/pbs/specs/12. IR and Lowering Specification.md`
|
||||||
|
|
||||||
|
## 12. Validation Notes
|
||||||
|
|
||||||
|
The intended baseline is:
|
||||||
|
|
||||||
|
- reserved builtin shells are imported like ordinary declarations into the matching namespace,
|
||||||
|
- builtin methods are found only through receiver-member lookup,
|
||||||
|
- builtin simple types remain always available without import,
|
||||||
|
- `@core:*` is the preferred home for builtin shells,
|
||||||
|
- and duplicate canonical builtin or host identities are rejected at linking time.
|
||||||
|
|
||||||
|
Illustrative examples:
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
import { Vec2 } from @core:math;
|
||||||
|
```
|
||||||
|
|
||||||
|
`Vec2` becomes an imported visible type declaration with builtin metadata.
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
import { Vec2 } from @core:math;
|
||||||
|
Vec2.zero()
|
||||||
|
```
|
||||||
|
|
||||||
|
If `zero` is a builtin intrinsic member, it resolves through builtin member lookup on `Vec2`-typed receiver use and not through free callable lookup by the name `zero`.
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
import { Vec2 } from @core:math;
|
||||||
|
declare const Vec2: int = 1;
|
||||||
|
```
|
||||||
|
|
||||||
|
This is rejected as an ordinary same-namespace collision rather than resolved by reserved-shell priority.
|
||||||
@ -0,0 +1,173 @@
|
|||||||
|
# Name Resolution - Callable Sets and Cross-Module Linking Decision
|
||||||
|
|
||||||
|
Status: Proposed
|
||||||
|
Cycle: Initial name-resolution closure pass
|
||||||
|
|
||||||
|
## 1. Context
|
||||||
|
|
||||||
|
PBS v1 needs a deterministic rule for how callable names cross module boundaries.
|
||||||
|
|
||||||
|
The open questions were:
|
||||||
|
|
||||||
|
- what exactly `import { f } from @project:path;` imports when `f` has multiple exported signatures,
|
||||||
|
- whether callable sets from different origins may merge,
|
||||||
|
- whether same-name imported callables from different origins are tolerated until callsite analysis,
|
||||||
|
- whether local and imported callable names may coexist under one visible function name,
|
||||||
|
- and how `mod.barrel` bounds the callable set that becomes importable.
|
||||||
|
|
||||||
|
Earlier closure already fixed important inputs:
|
||||||
|
|
||||||
|
- `mod.barrel` is the single source of module visibility,
|
||||||
|
- only `pub` names may be imported from another module,
|
||||||
|
- local function names and imported function names with the same visible name are already being treated as collisions in this closure pass,
|
||||||
|
- and callable identity is determined by callable shape rather than parameter-label spelling alone.
|
||||||
|
|
||||||
|
## 2. Decision
|
||||||
|
|
||||||
|
PBS v1 adopts the following baseline for callable sets and cross-module linking:
|
||||||
|
|
||||||
|
1. `import { f } from @project:path;` imports the exported callable set named `f` from the target module.
|
||||||
|
2. The imported callable set for `f` consists only of those overload signatures of `f` that are exported through that module's `mod.barrel`.
|
||||||
|
3. Non-exported overloads in the target module do not participate in the imported callable set.
|
||||||
|
4. Two imports of the same visible callable name are tolerated when they resolve to the same canonical underlying callable-set origin after module resolution.
|
||||||
|
5. Such same-origin duplicate imports are redundant but not errors.
|
||||||
|
6. Two imports of the same visible callable name from different canonical origins are rejected immediately.
|
||||||
|
7. The program must not wait until a callsite to reject different-origin callable-name collisions.
|
||||||
|
8. A module-local function name and an imported function name with the same visible name are rejected immediately.
|
||||||
|
9. Automatic merging of callable sets across different origins is not permitted in this closure pass.
|
||||||
|
10. Ordinary overload resolution occurs only after one unambiguous visible callable set has already been formed.
|
||||||
|
|
||||||
|
## 3. Imported Callable Set
|
||||||
|
|
||||||
|
The meaning of:
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
import { f } from @project:path;
|
||||||
|
```
|
||||||
|
|
||||||
|
is:
|
||||||
|
|
||||||
|
- resolve the target module,
|
||||||
|
- collect the exported callable entries named `f` from that module,
|
||||||
|
- form the callable set consisting only of those exported overloads,
|
||||||
|
- and make that callable set visible locally under the imported visible name.
|
||||||
|
|
||||||
|
This import does not:
|
||||||
|
|
||||||
|
- reach non-exported overloads,
|
||||||
|
- merge with hidden module-internal overloads,
|
||||||
|
- or import one overload at a time independently from the exported callable name.
|
||||||
|
|
||||||
|
## 4. Same-Origin Versus Different-Origin Imports
|
||||||
|
|
||||||
|
### 4.1 Same origin
|
||||||
|
|
||||||
|
If the same visible callable name is imported more than once and both imports resolve to the same canonical underlying callable-set origin:
|
||||||
|
|
||||||
|
- the duplicate import is tolerated,
|
||||||
|
- but it remains redundant.
|
||||||
|
|
||||||
|
### 4.2 Different origin
|
||||||
|
|
||||||
|
If the same visible callable name is imported from different canonical origins:
|
||||||
|
|
||||||
|
- the program is rejected immediately,
|
||||||
|
- and one of the imports must be removed or aliased.
|
||||||
|
|
||||||
|
The implementation must not:
|
||||||
|
|
||||||
|
- merge the callable sets,
|
||||||
|
- defer the conflict to overload resolution,
|
||||||
|
- or allow callsite context to decide which imported origin wins.
|
||||||
|
|
||||||
|
## 5. Local Versus Imported Callable Names
|
||||||
|
|
||||||
|
If a module-local function name and an imported function name are the same visible callable name:
|
||||||
|
|
||||||
|
- the program is rejected immediately,
|
||||||
|
- and the implementation must not merge the local and imported callable sets.
|
||||||
|
|
||||||
|
This preserves deterministic visibility and keeps callable origin traceable.
|
||||||
|
|
||||||
|
## 6. Barrel Boundary
|
||||||
|
|
||||||
|
`mod.barrel` defines the exact boundary of the callable set that may cross module boundaries.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- only overloads exported through `mod.barrel` are part of the importable callable set,
|
||||||
|
- overloads with the same source-level function name but not exported through `mod.barrel` are invisible to importing modules,
|
||||||
|
- and imported callable-set visibility is therefore a direct consequence of the source module's barrel contract.
|
||||||
|
|
||||||
|
## 7. Overload Resolution Boundary
|
||||||
|
|
||||||
|
This decision deliberately separates:
|
||||||
|
|
||||||
|
- callable-set formation,
|
||||||
|
- and overload resolution at a callsite.
|
||||||
|
|
||||||
|
The implementation must first form one unambiguous visible callable set.
|
||||||
|
Only then may ordinary overload resolution operate on the candidate signatures of that set.
|
||||||
|
|
||||||
|
Callsite context must not be used to resolve ambiguity between different imported callable origins.
|
||||||
|
|
||||||
|
## 8. Invariants
|
||||||
|
|
||||||
|
- Imported callable visibility is bounded by barrel export.
|
||||||
|
- Different callable origins must not merge automatically.
|
||||||
|
- Same-origin duplicate imports may be tolerated as redundancy, not as new callable contributions.
|
||||||
|
- Local and imported function names do not coexist under one merged visible callable name in this closure pass.
|
||||||
|
- Overload resolution happens after visible callable-set formation, not instead of it.
|
||||||
|
|
||||||
|
## 9. Explicit Non-Decisions
|
||||||
|
|
||||||
|
This decision record does not yet close:
|
||||||
|
|
||||||
|
- per-overload import syntax,
|
||||||
|
- aliasing policy for callable imports beyond the already-closed generic alias rules,
|
||||||
|
- and any lowering consequences in `12. IR and Lowering Specification.md`.
|
||||||
|
|
||||||
|
## 10. Spec Impact
|
||||||
|
|
||||||
|
This decision should feed at least:
|
||||||
|
|
||||||
|
- `docs/pbs/specs/14. Name Resolution and Module Linking Specification.md`
|
||||||
|
- `docs/pbs/specs/11. Diagnostics Specification.md`
|
||||||
|
- `docs/pbs/specs/13. Conformance Test Specification.md`
|
||||||
|
|
||||||
|
It should also constrain future work in:
|
||||||
|
|
||||||
|
- `docs/pbs/specs/12. IR and Lowering Specification.md`
|
||||||
|
|
||||||
|
## 11. Validation Notes
|
||||||
|
|
||||||
|
The intended baseline is:
|
||||||
|
|
||||||
|
- callable imports operate on exported callable names,
|
||||||
|
- all exported overloads of that name come together from one module,
|
||||||
|
- same-origin duplicate imports are tolerated,
|
||||||
|
- different-origin same-name imports are rejected immediately,
|
||||||
|
- and local/import callable collisions are also rejected immediately.
|
||||||
|
|
||||||
|
Illustrative examples:
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
import { f } from @a:m;
|
||||||
|
import { f } from @a:m;
|
||||||
|
```
|
||||||
|
|
||||||
|
This is tolerated as redundant same-origin duplication.
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
import { f } from @a:m;
|
||||||
|
import { f } from @b:n;
|
||||||
|
```
|
||||||
|
|
||||||
|
This is rejected immediately because the visible callable names match but the canonical origins differ.
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
fn f(a: int) -> int { ... }
|
||||||
|
import { f } from @a:m;
|
||||||
|
```
|
||||||
|
|
||||||
|
This is rejected as a local-versus-import callable collision.
|
||||||
@ -0,0 +1,200 @@
|
|||||||
|
# Name Resolution - Linking Phase Boundary Decision
|
||||||
|
|
||||||
|
Status: Proposed
|
||||||
|
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/pbs/specs/14. Name Resolution and Module Linking Specification.md`
|
||||||
|
- `docs/pbs/specs/11. Diagnostics Specification.md`
|
||||||
|
- `docs/pbs/specs/13. Conformance Test Specification.md`
|
||||||
|
|
||||||
|
It should also constrain future work in:
|
||||||
|
|
||||||
|
- `docs/pbs/specs/12. IR and Lowering 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`.
|
||||||
@ -0,0 +1,270 @@
|
|||||||
|
# Name Resolution - Scope, Lookup, and Imports Decision
|
||||||
|
|
||||||
|
Status: Proposed
|
||||||
|
Cycle: Initial name-resolution closure pass
|
||||||
|
|
||||||
|
## 1. Context
|
||||||
|
|
||||||
|
PBS v1 needs a deterministic frontend-visible rule for:
|
||||||
|
|
||||||
|
- how module scope is formed,
|
||||||
|
- how lookup works by namespace,
|
||||||
|
- what imports actually introduce into local scope,
|
||||||
|
- how collisions between local and imported names are handled,
|
||||||
|
- and which failures belong to syntax, manifest/import resolution, static semantics, or linking.
|
||||||
|
|
||||||
|
Existing specs already fix important inputs:
|
||||||
|
|
||||||
|
- `mod.barrel` is the single source of module visibility,
|
||||||
|
- imports target modules, not files,
|
||||||
|
- only `pub` names may be imported from another module,
|
||||||
|
- PBS has distinct type, value, callable, and host-owner namespaces,
|
||||||
|
- builtin simple types `int`, `float`, `bool`, and `str` are always available in type position,
|
||||||
|
- and reserved stdlib project spaces resolve only from the selected stdlib environment.
|
||||||
|
|
||||||
|
The remaining goal of this decision is to close the minimum name-resolution baseline needed for normative frontend work around ordinary scope construction, ordinary lookup, and import naming.
|
||||||
|
|
||||||
|
## 2. Decision
|
||||||
|
|
||||||
|
PBS v1 adopts the following baseline for scope construction, lookup, and imports:
|
||||||
|
|
||||||
|
1. Top-level declarations of a module are collected across all `.pbs` files in the module before visibility filtering is applied.
|
||||||
|
2. `mod.barrel` is a visibility filter over existing module declarations, not the source of declaration existence.
|
||||||
|
3. Module-internal top-level availability does not depend on source-file order.
|
||||||
|
4. Local block scopes nest normally over module scope.
|
||||||
|
5. In value position, lookup prefers the nearest lexical value binding before any module-level or imported value.
|
||||||
|
6. Parameters and local `let` bindings participate in the same nearest lexical value-scope layer for lookup purposes.
|
||||||
|
7. In type position, visible module-local type declarations are considered before imported type declarations, and builtin simple types remain always-available reserved type names outside ordinary import competition.
|
||||||
|
8. In callable position, visible module-local callable declarations are considered before imported callable declarations.
|
||||||
|
9. In host-owner position, visible module-local host owners are considered before imported host owners.
|
||||||
|
10. The local visible name introduced by an import is always the post-alias name when an alias is present.
|
||||||
|
11. `import { X } from @project:path;` introduces the imported exported name `X` into the matching namespace.
|
||||||
|
12. `import { X as Y } from @project:path;` introduces only the local visible name `Y` into the matching namespace.
|
||||||
|
13. Alias spelling changes only the local visible name, never canonical builtin identity or canonical host identity.
|
||||||
|
14. `import { * } from @project:path;` is the whole-module import form for bringing the target module's exported names into local visibility under their exported names.
|
||||||
|
15. A collision between a module-local declaration and an imported visible name in the same namespace is a deterministic error rather than silent shadowing.
|
||||||
|
16. A collision between two imported visible names in the same namespace is not an error only when both imports denote the same canonical underlying declaration after module resolution.
|
||||||
|
17. If two imported visible names in the same namespace come from different canonical underlying declarations, the program is rejected and one of the imports must be aliased or removed.
|
||||||
|
18. `import { * } from @project:path;` does not create a first-class module object, module namespace value, or other source-visible binding by itself; it only introduces the exported names of the target module.
|
||||||
|
19. A module-local function and an imported function with the same visible name produce a deterministic error in this closure pass.
|
||||||
|
20. Non-callable namespaces do not merge by name.
|
||||||
|
|
||||||
|
## 3. Scope Construction
|
||||||
|
|
||||||
|
### 3.1 Module collection
|
||||||
|
|
||||||
|
For one module:
|
||||||
|
|
||||||
|
- the compiler collects top-level declarations from all `.pbs` files in that module,
|
||||||
|
- forms one module-level declaration space,
|
||||||
|
- then applies `mod.barrel` to determine `mod` and `pub` visibility.
|
||||||
|
|
||||||
|
This means:
|
||||||
|
|
||||||
|
- declaration existence is not derived from barrel entries,
|
||||||
|
- and module-internal declaration availability is not ordered by file traversal.
|
||||||
|
|
||||||
|
### 3.2 Lexical scope
|
||||||
|
|
||||||
|
Inside executable bodies:
|
||||||
|
|
||||||
|
- lexical block scope is nested normally,
|
||||||
|
- nearest local bindings win within value lookup,
|
||||||
|
- and lexical nesting remains independent from cross-module visibility.
|
||||||
|
|
||||||
|
## 4. Lookup By Namespace
|
||||||
|
|
||||||
|
### 4.1 Value position
|
||||||
|
|
||||||
|
Value-position lookup follows this order:
|
||||||
|
|
||||||
|
1. nearest local lexical bindings,
|
||||||
|
2. parameters in the current lexical function scope,
|
||||||
|
3. visible module-local values,
|
||||||
|
4. visible imported values.
|
||||||
|
|
||||||
|
For lookup purposes, parameters and local `let` bindings are one nearest lexical value layer.
|
||||||
|
The distinction between them may still matter for diagnostics wording.
|
||||||
|
|
||||||
|
### 4.2 Type position
|
||||||
|
|
||||||
|
Type-position lookup follows this order:
|
||||||
|
|
||||||
|
1. visible module-local type declarations,
|
||||||
|
2. visible imported type declarations,
|
||||||
|
3. builtin simple types `int`, `float`, `bool`, and `str` as always-available reserved type names.
|
||||||
|
|
||||||
|
Builtin simple types are not treated as ordinary imported declarations and do not participate in ordinary import competition.
|
||||||
|
|
||||||
|
### 4.3 Callable position
|
||||||
|
|
||||||
|
Callable-position lookup follows this order:
|
||||||
|
|
||||||
|
1. visible module-local callables,
|
||||||
|
2. visible imported callables.
|
||||||
|
|
||||||
|
If a visible module-local function name and a visible imported function name are the same, the program is rejected in this closure pass rather than merged.
|
||||||
|
|
||||||
|
### 4.4 Host-owner position
|
||||||
|
|
||||||
|
Host-owner lookup follows this order:
|
||||||
|
|
||||||
|
1. visible module-local host owners,
|
||||||
|
2. visible imported host owners.
|
||||||
|
|
||||||
|
Host-owner lookup remains separate from type, value, and callable lookup.
|
||||||
|
|
||||||
|
## 5. Import Surface
|
||||||
|
|
||||||
|
### 5.1 Named import
|
||||||
|
|
||||||
|
`import { X } from @project:path;`:
|
||||||
|
|
||||||
|
- resolves the target module,
|
||||||
|
- checks that `X` is exported and importable from that module,
|
||||||
|
- and introduces `X` into the corresponding namespace locally.
|
||||||
|
|
||||||
|
### 5.2 Aliased import
|
||||||
|
|
||||||
|
`import { X as Y } from @project:path;`:
|
||||||
|
|
||||||
|
- resolves the same exported declaration as the non-aliased form,
|
||||||
|
- but introduces only `Y` as the local visible name.
|
||||||
|
|
||||||
|
Alias spelling does not change canonical identity governed elsewhere.
|
||||||
|
|
||||||
|
### 5.3 Whole-module import
|
||||||
|
|
||||||
|
`import { * } from @project:path;`:
|
||||||
|
|
||||||
|
- validates and resolves the target module,
|
||||||
|
- introduces the target module's exported visible names under their exported spellings,
|
||||||
|
- but does not create a module-valued binding,
|
||||||
|
- does not create a namespace object,
|
||||||
|
- and does not authorize qualified member access by itself.
|
||||||
|
|
||||||
|
If PBS later wants module-object or namespace-qualified source semantics, that must be added explicitly rather than inferred from this form.
|
||||||
|
|
||||||
|
## 6. Collision Policy
|
||||||
|
|
||||||
|
### 6.1 Local versus imported
|
||||||
|
|
||||||
|
If a module-local declaration and an imported declaration produce the same visible name in the same namespace:
|
||||||
|
|
||||||
|
- the program is rejected,
|
||||||
|
- and the implementation must not silently shadow the imported declaration or the local declaration.
|
||||||
|
|
||||||
|
This includes function names.
|
||||||
|
In this closure pass, a local function and an imported function with the same visible name are rejected rather than merged.
|
||||||
|
|
||||||
|
### 6.2 Imported versus imported
|
||||||
|
|
||||||
|
If two imports produce the same visible name in the same namespace:
|
||||||
|
|
||||||
|
- the program is not rejected if both imports resolve to the same canonical underlying declaration after module resolution,
|
||||||
|
- but the duplicate import is still redundant,
|
||||||
|
- and the program is rejected if the imports resolve to different canonical underlying declarations.
|
||||||
|
|
||||||
|
### 6.3 Namespace separation
|
||||||
|
|
||||||
|
Names in different namespaces do not collide merely by spelling.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
- a host owner and a type declaration do not collide by spelling alone,
|
||||||
|
- because host-owner namespace remains distinct from type namespace.
|
||||||
|
|
||||||
|
## 7. Relationship To Later Name-Resolution Decisions
|
||||||
|
|
||||||
|
This decision is intentionally limited to:
|
||||||
|
|
||||||
|
- ordinary scope construction,
|
||||||
|
- ordinary namespace lookup,
|
||||||
|
- import naming,
|
||||||
|
- and ordinary collision policy.
|
||||||
|
|
||||||
|
Later name-resolution decisions close:
|
||||||
|
|
||||||
|
- reserved builtin shells and host owners,
|
||||||
|
- callable-set visibility across modules,
|
||||||
|
- and the final phase boundary between syntax, manifest/import resolution, linking, and static semantics.
|
||||||
|
|
||||||
|
## 8. Invariants
|
||||||
|
|
||||||
|
- Name lookup must be deterministic.
|
||||||
|
- Module-internal top-level declaration availability must not depend on file order.
|
||||||
|
- `mod.barrel` remains a visibility mechanism rather than a declaration source.
|
||||||
|
- Imports must not invent first-class module-object semantics accidentally.
|
||||||
|
- The effective visible name of an import is always the post-alias name when an alias is present.
|
||||||
|
- Builtin simple types remain a reserved always-available core type set, distinct from ordinary imported declarations.
|
||||||
|
- Implementations must not assume silent local-over-import shadowing.
|
||||||
|
- Implementations must not merge local and imported function names automatically.
|
||||||
|
|
||||||
|
## 9. Explicit Non-Decisions
|
||||||
|
|
||||||
|
This decision record does not yet close:
|
||||||
|
|
||||||
|
- reserved-shell-specific lookup and collision details,
|
||||||
|
- callable-set import behavior across module boundaries beyond the local-versus-import collision baseline,
|
||||||
|
- and backend-facing lowering consequences of the resolved lookup model.
|
||||||
|
|
||||||
|
## 10. Spec Impact
|
||||||
|
|
||||||
|
This decision should feed at least:
|
||||||
|
|
||||||
|
- `docs/pbs/specs/14. Name Resolution and Module Linking Specification.md`
|
||||||
|
- `docs/pbs/specs/11. Diagnostics Specification.md`
|
||||||
|
- `docs/pbs/specs/13. Conformance Test Specification.md`
|
||||||
|
|
||||||
|
It should also constrain future work in:
|
||||||
|
|
||||||
|
- `docs/pbs/specs/12. IR and Lowering Specification.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`
|
||||||
|
- `docs/pbs/decisions/Name Resolution - Linking Phase Boundary Decision.md`
|
||||||
|
|
||||||
|
## 11. Validation Notes
|
||||||
|
|
||||||
|
The intended baseline is:
|
||||||
|
|
||||||
|
- all top-level declarations in a module exist before barrel filtering,
|
||||||
|
- lookup is namespace-specific and deterministic,
|
||||||
|
- the effective visible import name is the alias name when an alias is present,
|
||||||
|
- whole-module import through `import { * } from @project:path;` introduces exported names rather than a module object,
|
||||||
|
- local/import collisions are rejected rather than shadowed,
|
||||||
|
- and builtin simple types remain reserved always-available names outside ordinary import competition.
|
||||||
|
|
||||||
|
Illustrative examples:
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
import { Foo } from @a:m;
|
||||||
|
declare const Foo: int = 1;
|
||||||
|
```
|
||||||
|
|
||||||
|
This is rejected as a local-versus-import collision in the value namespace.
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
import { f } from @a:m;
|
||||||
|
import { f } from @b:n;
|
||||||
|
```
|
||||||
|
|
||||||
|
This is rejected because the visible imported names match but the canonical origins differ.
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
fn f(a: int) -> int { ... }
|
||||||
|
import { f } from @a:m;
|
||||||
|
```
|
||||||
|
|
||||||
|
This is rejected as a local-versus-import collision in callable position.
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
import { * } from @a:m;
|
||||||
|
```
|
||||||
|
|
||||||
|
This introduces the exported visible names of `@a:m`, but does not introduce a source-visible module object in v1.
|
||||||
|
|
||||||
|
```pbs
|
||||||
|
import { A as aaa } from @a:m;
|
||||||
|
```
|
||||||
|
|
||||||
|
The visible local declaration name is `aaa`, not `A`.
|
||||||
@ -1,29 +1,37 @@
|
|||||||
# PBS Name Resolution and Module Linking Specification
|
# PBS Name Resolution and Module Linking Specification
|
||||||
|
|
||||||
Status: Draft v0 (Skeleton)
|
Status: Draft v1 (Normative)
|
||||||
Applies to: scope formation, name lookup, imports, exports, barrel visibility, cross-module resolution, and semantic linking of PBS programs
|
Applies to: scope formation, name lookup, imports, exports, barrel visibility, cross-module resolution, and semantic linking of PBS programs
|
||||||
|
|
||||||
## 1. Purpose
|
## 1. Purpose
|
||||||
|
|
||||||
This document will define the normative name-resolution and semantic-linking model for PBS.
|
This document defines the normative name-resolution and semantic-linking model for PBS v1.
|
||||||
|
|
||||||
|
Its purpose is to make the visible declaration space of a PBS program deterministic enough that:
|
||||||
|
|
||||||
|
- frontend implementations do not invent lookup or collision rules,
|
||||||
|
- diagnostics can classify failures consistently,
|
||||||
|
- conformance can test source-facing rejection classes,
|
||||||
|
- and lowering begins from an already-linked visible program.
|
||||||
|
|
||||||
## 2. Scope
|
## 2. Scope
|
||||||
|
|
||||||
This document is intended to define:
|
This document defines:
|
||||||
|
|
||||||
- scope formation and lookup order,
|
- module scope construction,
|
||||||
- namespace separation and collision rules,
|
- lookup order by namespace,
|
||||||
- import and export resolution,
|
- import and alias resolution,
|
||||||
- barrel-mediated visibility and module linking,
|
- barrel-mediated visibility,
|
||||||
- cross-file and cross-module symbol resolution,
|
- reserved stdlib shell resolution for builtin and host-backed surfaces,
|
||||||
- resolution of reserved stdlib shells for host-backed and VM-owned surfaces,
|
- callable visibility across modules,
|
||||||
- linkage-time rejection conditions before lowering.
|
- and linking-time rejection conditions before lowering.
|
||||||
|
|
||||||
This document does not define:
|
This document does not define:
|
||||||
|
|
||||||
- runtime execution behavior,
|
- runtime execution behavior,
|
||||||
- optimizer or backend internals,
|
- optimizer or backend internals,
|
||||||
- full PBX binary linking.
|
- full PBX binary linking,
|
||||||
|
- or loader/runtime artifact behavior after lowering.
|
||||||
|
|
||||||
## 3. Authority and Precedence
|
## 3. Authority and Precedence
|
||||||
|
|
||||||
@ -34,8 +42,10 @@ Normative precedence:
|
|||||||
3. `5. Manifest, Stdlib, and SDK Resolution Specification.md`
|
3. `5. Manifest, Stdlib, and SDK Resolution Specification.md`
|
||||||
4. `6. VM-owned vs Host-backed.md`
|
4. `6. VM-owned vs Host-backed.md`
|
||||||
5. `6.1. Intrinsics and Builtin Types Specification.md`
|
5. `6.1. Intrinsics and Builtin Types Specification.md`
|
||||||
6. `18. Standard Library Surface Specification.md`
|
6. `6.2. Host ABI Binding and Loader Resolution Specification.md`
|
||||||
7. This document
|
7. `8. Stdlib Environment Packaging and Loading Specification.md`
|
||||||
|
8. `18. Standard Library Surface Specification.md`
|
||||||
|
9. This document
|
||||||
|
|
||||||
If a rule here conflicts with higher-precedence specs, it is invalid.
|
If a rule here conflicts with higher-precedence specs, it is invalid.
|
||||||
|
|
||||||
@ -48,6 +58,7 @@ This document depends on, at minimum:
|
|||||||
- `5. Manifest, Stdlib, and SDK Resolution Specification.md`
|
- `5. Manifest, Stdlib, and SDK Resolution Specification.md`
|
||||||
- `6. VM-owned vs Host-backed.md`
|
- `6. VM-owned vs Host-backed.md`
|
||||||
- `6.1. Intrinsics and Builtin Types Specification.md`
|
- `6.1. Intrinsics and Builtin Types Specification.md`
|
||||||
|
- `6.2. Host ABI Binding and Loader Resolution Specification.md`
|
||||||
- `8. Stdlib Environment Packaging and Loading Specification.md`
|
- `8. Stdlib Environment Packaging and Loading Specification.md`
|
||||||
- `18. Standard Library Surface Specification.md`
|
- `18. Standard Library Surface Specification.md`
|
||||||
|
|
||||||
@ -63,97 +74,300 @@ The following inputs are already fixed elsewhere and must not be contradicted he
|
|||||||
- Reserved stdlib/interface modules may expose compile-time-only shells for both host-backed and VM-owned surfaces.
|
- Reserved stdlib/interface modules may expose compile-time-only shells for both host-backed and VM-owned surfaces.
|
||||||
- Source-visible builtin names are resolved through imported builtin shell declarations rather than by hardcoded source spelling alone.
|
- Source-visible builtin names are resolved through imported builtin shell declarations rather than by hardcoded source spelling alone.
|
||||||
- Canonical builtin identity and canonical intrinsic identity are governed by builtin metadata rather than by the imported PBS-visible declaration name alone.
|
- Canonical builtin identity and canonical intrinsic identity are governed by builtin metadata rather than by the imported PBS-visible declaration name alone.
|
||||||
|
- Canonical host identity is governed by host metadata rather than by imported owner spelling alone.
|
||||||
|
|
||||||
## 6. Initial Section Targets
|
## 6. Module Scope Construction
|
||||||
|
|
||||||
At minimum, the completed document should contain normative sections for:
|
### 6.1 Module collection
|
||||||
|
|
||||||
1. scope construction,
|
For one module:
|
||||||
2. lookup order by namespace,
|
|
||||||
3. import and alias resolution,
|
|
||||||
4. barrel export matching and linking,
|
|
||||||
5. reserved stdlib shell resolution for builtin and host-backed surfaces,
|
|
||||||
6. duplicate and shadowing rules,
|
|
||||||
7. cross-module resolution failures.
|
|
||||||
|
|
||||||
## 7. TODO
|
- the compiler collects top-level declarations from all `.pbs` files in that module,
|
||||||
|
- forms one module-level declaration space,
|
||||||
The following items remain to be closed in future agenda discussion.
|
- and only then applies `mod.barrel` to determine `mod` and `pub` visibility.
|
||||||
|
|
||||||
- Exact lookup order across local bindings, top-level declarations, imports, and reserved intrinsic surfaces.
|
|
||||||
- Whether shadowing rules differ by namespace or declaration kind.
|
|
||||||
- Whether semantic linking is fully part of static semantics or split into a distinct phase contract.
|
|
||||||
- Exact rejection model for ambiguous cross-module overload visibility and barrel-linked callable sets.
|
|
||||||
- Whether stdlib/interface-module linking imposes extra restrictions beyond ordinary modules.
|
|
||||||
- Whether builtin shell declarations may be synthesized by the toolchain in addition to being provided by stdlib modules.
|
|
||||||
|
|
||||||
## 8. Non-Goals
|
|
||||||
|
|
||||||
- Reopening the already-settled import surface syntax.
|
|
||||||
- Defining runtime loader behavior.
|
|
||||||
- Freezing one compiler symbol-table implementation.
|
|
||||||
|
|
||||||
## 9. Exit Criteria
|
|
||||||
|
|
||||||
This document is ready to move beyond skeleton status only when:
|
|
||||||
|
|
||||||
1. name lookup is deterministic across all relevant scopes,
|
|
||||||
2. barrel and module linking behavior is explicit,
|
|
||||||
3. reserved stdlib shell resolution is explicit for host-backed and VM-owned surfaces,
|
|
||||||
4. cross-module ambiguity and failure cases are normatively defined,
|
|
||||||
5. the document no longer relies on unresolved `TODO` items for ordinary v1 resolution behavior.
|
|
||||||
|
|
||||||
## 10. Reserved Stdlib Shell Resolution
|
|
||||||
|
|
||||||
Reserved stdlib modules may expose compile-time-only declarations that do not
|
|
||||||
behave like ordinary user-authored implementation bodies.
|
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
|
|
||||||
- `declare host` surfaces exported from reserved stdlib modules resolve in the host-owner namespace.
|
- declaration existence is not derived from barrel entries,
|
||||||
- `declare builtin type` surfaces exported from reserved stdlib modules resolve in the type namespace.
|
- module-internal top-level declaration availability does not depend on source-file order,
|
||||||
- builtin constants exported from reserved stdlib modules resolve in the value namespace.
|
- and `mod.barrel` is a visibility filter over existing declarations rather than a declaration source.
|
||||||
- builtin intrinsic methods are not imported as free functions; they are resolved through the imported builtin type declaration of the receiver type.
|
|
||||||
- canonical host identity is not derived from the imported host owner spelling,
|
|
||||||
- and canonical builtin/intrinsic identity is not derived solely from the imported builtin type spelling.
|
|
||||||
|
|
||||||
Example:
|
### 6.2 Lexical scope
|
||||||
|
|
||||||
- importing `Vec2` from `@core:math` may introduce the PBS-visible type name `Vec2`,
|
Inside executable bodies:
|
||||||
- while the declaration's metadata may still lower that type to canonical builtin identity `("vec2", 1)`.
|
|
||||||
|
|
||||||
## 11. Import and Barrel Rules for Builtin Shells
|
- lexical block scope nests normally over module scope,
|
||||||
|
- nearest local bindings win within value lookup,
|
||||||
|
- and lexical nesting remains independent from cross-module visibility.
|
||||||
|
|
||||||
Builtin shells follow ordinary module visibility rules at the source level and
|
## 7. Lookup Order By Namespace
|
||||||
special metadata rules at lowering time.
|
|
||||||
|
Lookup in PBS remains namespace-based.
|
||||||
|
|
||||||
|
### 7.1 Value position
|
||||||
|
|
||||||
|
Value-position lookup follows this order:
|
||||||
|
|
||||||
|
1. nearest local lexical bindings,
|
||||||
|
2. parameters in the current lexical function scope,
|
||||||
|
3. visible module-local values,
|
||||||
|
4. visible imported values.
|
||||||
|
|
||||||
|
For lookup purposes, parameters and local `let` bindings participate in one nearest lexical value layer.
|
||||||
|
|
||||||
|
### 7.2 Type position
|
||||||
|
|
||||||
|
Type-position lookup follows this order:
|
||||||
|
|
||||||
|
1. visible module-local type declarations,
|
||||||
|
2. visible imported type declarations,
|
||||||
|
3. builtin simple types `int`, `float`, `bool`, and `str` as always-available reserved type names.
|
||||||
|
|
||||||
|
Builtin simple types:
|
||||||
|
|
||||||
|
- are not treated as ordinary imported declarations,
|
||||||
|
- do not participate in ordinary import competition,
|
||||||
|
- and remain always available without import.
|
||||||
|
|
||||||
|
### 7.3 Callable position
|
||||||
|
|
||||||
|
Callable-position lookup follows this order:
|
||||||
|
|
||||||
|
1. visible module-local callables,
|
||||||
|
2. visible imported callables.
|
||||||
|
|
||||||
|
Callable lookup operates only on already-formed visible callable sets as defined later in this document.
|
||||||
|
|
||||||
|
### 7.4 Host-owner position
|
||||||
|
|
||||||
|
Host-owner lookup follows this order:
|
||||||
|
|
||||||
|
1. visible module-local host owners,
|
||||||
|
2. visible imported host owners.
|
||||||
|
|
||||||
|
Host-owner lookup remains separate from type, value, and callable lookup.
|
||||||
|
|
||||||
|
## 8. Import Surface And Naming
|
||||||
|
|
||||||
|
### 8.1 Effective local imported name
|
||||||
|
|
||||||
|
The local visible name introduced by an import is always the post-alias name when an alias is present.
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
|
|
||||||
- a builtin type, builtin constant, or host owner may be imported only if it is exported through the target module's `mod.barrel`,
|
- `import { X } from @project:path;` introduces the visible local name `X`,
|
||||||
- aliasing an imported builtin type changes only the source-visible local name, not the canonical builtin identity declared by metadata,
|
- `import { X as Y } from @project:path;` introduces only the visible local name `Y`,
|
||||||
- aliasing an imported builtin constant changes only the local binding name, not the canonical builtin constant identity declared by metadata,
|
- and alias spelling changes only the local visible name, never canonical builtin identity or canonical host identity.
|
||||||
- aliasing an imported host owner changes only the source-visible local name, not the canonical host identity declared by `Host(...)`,
|
|
||||||
- barrel matching for builtin declarations is declaration-based rather than executable-body based,
|
|
||||||
- and resolution must complete before lowering begins.
|
|
||||||
|
|
||||||
## 12. Lookup Order for Builtin and Intrinsic Surfaces
|
### 8.2 Named import
|
||||||
|
|
||||||
Lookup remains namespace-based.
|
`import { X } from @project:path;`:
|
||||||
|
|
||||||
|
- resolves the target module,
|
||||||
|
- checks that `X` is exported and importable from that module,
|
||||||
|
- and introduces `X` into the corresponding namespace locally.
|
||||||
|
|
||||||
|
### 8.3 Whole-module import
|
||||||
|
|
||||||
|
`import { * } from @project:path;`:
|
||||||
|
|
||||||
|
- resolves the target module,
|
||||||
|
- introduces the target module's exported visible names under their exported spellings,
|
||||||
|
- but does not create a module-valued binding,
|
||||||
|
- does not create a namespace object,
|
||||||
|
- and does not authorize qualified member access by itself.
|
||||||
|
|
||||||
|
If PBS later wants module-object or namespace-qualified source semantics, that must be added explicitly rather than inferred from this import form.
|
||||||
|
|
||||||
|
## 9. Collision And Ambiguity Rules
|
||||||
|
|
||||||
|
### 9.1 Local versus imported
|
||||||
|
|
||||||
|
If a module-local declaration and an imported declaration produce the same visible name in the same namespace:
|
||||||
|
|
||||||
|
- the program is rejected,
|
||||||
|
- and the implementation must not silently shadow either declaration.
|
||||||
|
|
||||||
|
This includes function names.
|
||||||
|
A module-local function and an imported function with the same visible name are rejected rather than merged.
|
||||||
|
|
||||||
|
### 9.2 Imported versus imported
|
||||||
|
|
||||||
|
If two imports produce the same visible name in the same namespace:
|
||||||
|
|
||||||
|
- the program is not rejected only when both imports resolve to the same canonical underlying declaration after module resolution,
|
||||||
|
- such same-origin duplication is redundant but tolerated,
|
||||||
|
- and the program is rejected if the imports resolve to different canonical underlying declarations.
|
||||||
|
|
||||||
|
### 9.3 Namespace separation
|
||||||
|
|
||||||
|
Names in different namespaces do not collide merely by spelling.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
- a host owner and a type declaration do not collide by spelling alone,
|
||||||
|
- because host-owner namespace remains distinct from type namespace.
|
||||||
|
|
||||||
|
### 9.4 Non-callable names do not merge
|
||||||
|
|
||||||
|
Non-callable namespaces do not merge by name.
|
||||||
|
|
||||||
|
## 10. Callable Sets And Cross-Module Linking
|
||||||
|
|
||||||
|
### 10.1 Imported callable set
|
||||||
|
|
||||||
|
`import { f } from @project:path;` imports the exported callable set named `f` from the target module.
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
|
|
||||||
- type-position lookup considers visible builtin type declarations alongside other visible type declarations,
|
- the imported callable set for `f` consists only of those overload signatures of `f` that are exported through that module's `mod.barrel`,
|
||||||
- value-position lookup considers visible builtin constants alongside other visible values,
|
- non-exported overloads in the target module do not participate in the imported callable set,
|
||||||
- callable lookup does not treat builtin intrinsic members as top-level callable declarations,
|
- and import operates on the exported callable name rather than on one overload at a time.
|
||||||
- member lookup on a builtin-typed receiver considers builtin projection fields first as field-like surfaces of that builtin declaration,
|
|
||||||
- method lookup on a builtin-typed receiver considers builtin intrinsic member signatures declared by that builtin shell,
|
|
||||||
- and host-owner lookup remains separate from builtin type lookup.
|
|
||||||
|
|
||||||
This preserves the distinction between:
|
### 10.2 Same-origin versus different-origin callable imports
|
||||||
|
|
||||||
- imported builtin type shells,
|
If the same visible callable name is imported more than once:
|
||||||
- imported host owners,
|
|
||||||
- and compiler-recognized intrinsic method surfaces on existing core forms such as `optional` and enums.
|
- same-origin duplicate imports are tolerated as redundancy when they resolve to the same canonical callable-set origin,
|
||||||
|
- different-origin imports are rejected immediately,
|
||||||
|
- and the implementation must not wait until a callsite to reject different-origin callable-name collisions.
|
||||||
|
|
||||||
|
### 10.3 Local versus imported callable names
|
||||||
|
|
||||||
|
If a module-local function name and an imported function name are the same visible callable name:
|
||||||
|
|
||||||
|
- the program is rejected immediately,
|
||||||
|
- and the implementation must not merge the local and imported callable sets.
|
||||||
|
|
||||||
|
### 10.4 Overload-resolution boundary
|
||||||
|
|
||||||
|
PBS separates:
|
||||||
|
|
||||||
|
- callable-set formation,
|
||||||
|
- and overload resolution at a callsite.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- the implementation must first form one unambiguous visible callable set,
|
||||||
|
- only then may ordinary overload resolution operate on the candidate signatures of that set,
|
||||||
|
- and callsite context must not be used to resolve ambiguity between different imported callable origins.
|
||||||
|
|
||||||
|
## 11. Reserved Stdlib Shell Resolution
|
||||||
|
|
||||||
|
### 11.1 Reserved shells as imported declarations
|
||||||
|
|
||||||
|
Source-visible declarations exposed by reserved builtin shells are imported into their ordinary corresponding namespaces.
|
||||||
|
|
||||||
|
This means:
|
||||||
|
|
||||||
|
- builtin-exposed types enter type lookup as imported type declarations,
|
||||||
|
- builtin-exposed constants enter value lookup as imported values,
|
||||||
|
- and host owners enter host-owner lookup as imported host-owner declarations.
|
||||||
|
|
||||||
|
`declare builtin type` itself remains a reserved stdlib/toolchain declaration form rather than ordinary user-authored source syntax.
|
||||||
|
|
||||||
|
### 11.2 Builtin simple types versus imported builtin shells
|
||||||
|
|
||||||
|
PBS distinguishes:
|
||||||
|
|
||||||
|
- the predeclared builtin simple-type set `int`, `float`, `bool`, `str`,
|
||||||
|
- and imported builtin shells such as `Vec2` or `Color`.
|
||||||
|
|
||||||
|
Imported builtin shells are not promoted to the same always-available status as builtin simple types.
|
||||||
|
|
||||||
|
### 11.3 Intrinsic member lookup
|
||||||
|
|
||||||
|
Builtin intrinsic methods:
|
||||||
|
|
||||||
|
- never participate in free lookup,
|
||||||
|
- are not introduced by import as free functions,
|
||||||
|
- and are resolved only through member lookup on a receiver already known to have the corresponding builtin type.
|
||||||
|
|
||||||
|
Alias changes to the imported builtin type affect only the local visible owner spelling, not the canonical builtin or intrinsic identity.
|
||||||
|
|
||||||
|
### 11.4 Collision policy for reserved shells
|
||||||
|
|
||||||
|
Reserved builtin shells and host owners follow the same collision policy as ordinary imported declarations.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- same visible name in the same namespace is an error unless another already-closed rule explicitly permits otherwise,
|
||||||
|
- reserved-shell status does not authorize silent shadowing,
|
||||||
|
- and host owners do not collide with types or values merely by spelling because host-owner namespace remains separate.
|
||||||
|
|
||||||
|
### 11.5 Reserved project-space policy
|
||||||
|
|
||||||
|
For source-level policy:
|
||||||
|
|
||||||
|
- builtin shells are preferred under `@core:*`,
|
||||||
|
- and host-facing capability surfaces remain the natural fit for `@sdk:*`.
|
||||||
|
|
||||||
|
### 11.6 Canonical identity
|
||||||
|
|
||||||
|
Canonical builtin identity and canonical host identity are not derived from local alias spelling.
|
||||||
|
|
||||||
|
Duplicate canonical builtin identities or duplicate canonical host identities in one resolved environment are rejected as linking failures.
|
||||||
|
|
||||||
|
## 12. Linking Phase Boundary And Deterministic Failures
|
||||||
|
|
||||||
|
### 12.1 Normative phase baseline
|
||||||
|
|
||||||
|
PBS v1 adopts the following normative phase baseline for name resolution and related frontend failures:
|
||||||
|
|
||||||
|
1. `syntax`
|
||||||
|
2. `manifest/import resolution`
|
||||||
|
3. `linking`
|
||||||
|
4. `static semantics`
|
||||||
|
|
||||||
|
### 12.2 Syntax
|
||||||
|
|
||||||
|
`syntax` owns malformed import grammar and other token-shape or grammar-shape failures.
|
||||||
|
|
||||||
|
### 12.3 Manifest and import resolution
|
||||||
|
|
||||||
|
`manifest/import resolution` owns failures to resolve:
|
||||||
|
|
||||||
|
- project space,
|
||||||
|
- reserved stdlib space,
|
||||||
|
- dependency graph target,
|
||||||
|
- module path,
|
||||||
|
- or environment source.
|
||||||
|
|
||||||
|
### 12.4 Linking
|
||||||
|
|
||||||
|
`linking` owns assembly of visible declarations across files and modules.
|
||||||
|
|
||||||
|
At minimum, `linking` owns:
|
||||||
|
|
||||||
|
1. import of non-exported names,
|
||||||
|
2. unresolved barrel entries against module declarations,
|
||||||
|
3. collisions between visible imported names from different canonical origins,
|
||||||
|
4. local-versus-import visibility collisions,
|
||||||
|
5. callable-origin conflicts across module boundaries,
|
||||||
|
6. duplicate canonical builtin identities in one resolved environment,
|
||||||
|
7. duplicate canonical host identities in one resolved environment.
|
||||||
|
|
||||||
|
### 12.5 Static semantics
|
||||||
|
|
||||||
|
`static semantics` owns:
|
||||||
|
|
||||||
|
1. duplicate local declarations,
|
||||||
|
2. declaration validity,
|
||||||
|
3. type checking,
|
||||||
|
4. member lookup once the receiver type is known,
|
||||||
|
5. overload resolution within an already-formed visible callable set.
|
||||||
|
|
||||||
|
Member lookup failure on an already-typed builtin receiver therefore belongs to `static semantics`.
|
||||||
|
|
||||||
|
### 12.6 Imported-origin conflicts are rejected before callsite analysis
|
||||||
|
|
||||||
|
Conflicts between different imported origins are rejected in `linking` immediately.
|
||||||
|
|
||||||
|
The implementation must not:
|
||||||
|
|
||||||
|
- wait for a specific callsite,
|
||||||
|
- use overload resolution to rescue imported-origin ambiguity,
|
||||||
|
- or defer visible declaration conflicts to later typing work.
|
||||||
|
|
||||||
## 13. Deterministic Resolution Failures
|
## 13. Deterministic Resolution Failures
|
||||||
|
|
||||||
@ -162,8 +376,30 @@ At minimum, a conforming implementation must reject:
|
|||||||
1. import of a non-exported builtin type shell,
|
1. import of a non-exported builtin type shell,
|
||||||
2. import of a non-exported builtin constant shell,
|
2. import of a non-exported builtin constant shell,
|
||||||
3. import of a non-exported host owner shell,
|
3. import of a non-exported host owner shell,
|
||||||
4. ambiguous cross-module visibility of builtin declarations after barrel resolution,
|
4. import of any non-exported name from a resolved module,
|
||||||
5. duplicate visible builtin type declarations that claim the same canonical builtin identity in one resolved environment,
|
5. unresolved barrel entries against module declarations,
|
||||||
6. duplicate visible builtin constants that claim the same canonical builtin constant identity in one resolved environment,
|
6. local-versus-import same-namespace collisions,
|
||||||
7. member lookup on a builtin receiver where the imported builtin shell does not declare the requested field or intrinsic member,
|
7. imported same-name collisions from different canonical origins,
|
||||||
8. any resolution path that attempts to derive host or builtin canonical identity from alias spelling alone.
|
8. local-versus-import function-name collisions,
|
||||||
|
9. duplicate visible builtin type declarations that claim the same canonical builtin identity in one resolved environment,
|
||||||
|
10. duplicate visible builtin constants that claim the same canonical builtin constant identity in one resolved environment,
|
||||||
|
11. duplicate visible host owners that claim the same canonical host identity in one resolved environment,
|
||||||
|
12. member lookup on a builtin receiver where the imported builtin shell does not declare the requested field or intrinsic member,
|
||||||
|
13. any resolution path that attempts to derive host or builtin canonical identity from alias spelling alone.
|
||||||
|
|
||||||
|
## 14. Non-Goals
|
||||||
|
|
||||||
|
- Reopening the already-settled import surface syntax.
|
||||||
|
- Defining runtime loader behavior.
|
||||||
|
- Freezing one compiler symbol-table implementation.
|
||||||
|
- Freezing one internal compiler pass graph.
|
||||||
|
|
||||||
|
## 15. Exit Criteria
|
||||||
|
|
||||||
|
This document is ready to move beyond its current draft status only when:
|
||||||
|
|
||||||
|
1. name lookup is deterministic across all relevant scopes,
|
||||||
|
2. barrel and module linking behavior is explicit,
|
||||||
|
3. reserved stdlib shell resolution is explicit for host-backed and VM-owned surfaces,
|
||||||
|
4. cross-module callable visibility and ambiguity behavior are normatively defined,
|
||||||
|
5. and the phase ownership of source-facing name-resolution failures is explicit enough for diagnostics and conformance.
|
||||||
|
|||||||
537
docs/roadmaps/lsp/LSP - base PRs.md
Normal file
537
docs/roadmaps/lsp/LSP - base PRs.md
Normal file
@ -0,0 +1,537 @@
|
|||||||
|
# PR — lsp-base (LSP MVP)
|
||||||
|
|
||||||
|
**Branch:** `pr-08-lsp-mvp`
|
||||||
|
|
||||||
|
## Briefing
|
||||||
|
|
||||||
|
Queremos um **LSP mínimo funcional** que já permita trabalhar PBS no VSCode com:
|
||||||
|
|
||||||
|
* erros aparecendo (diagnostics)
|
||||||
|
* navegação básica (goto definition)
|
||||||
|
* visão estrutural (document/workspace symbols)
|
||||||
|
|
||||||
|
Regras-chave (MVP):
|
||||||
|
|
||||||
|
* `didChange` será **full-text**
|
||||||
|
* rebuild será **coarse** (recompila o projeto inteiro) sempre que qualquer arquivo muda
|
||||||
|
* sem incremental analysis ainda
|
||||||
|
* comentários extensivos com exemplos se necessário e em inglês sempre
|
||||||
|
|
||||||
|
Este PR não inclui semantic tokens nem completion (virão nos PRs seguintes).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Alvo (Features)
|
||||||
|
|
||||||
|
### LSP endpoints
|
||||||
|
|
||||||
|
* ✅ `initialize`
|
||||||
|
* ✅ `textDocument/didOpen`
|
||||||
|
* ✅ `textDocument/didChange` (FULL)
|
||||||
|
* ✅ `textDocument/didClose`
|
||||||
|
* ✅ `textDocument/documentSymbol`
|
||||||
|
* ✅ `workspace/symbol`
|
||||||
|
* ✅ `textDocument/definition`
|
||||||
|
* ✅ `textDocument/publishDiagnostics`
|
||||||
|
|
||||||
|
### Modelo de build
|
||||||
|
|
||||||
|
* `AnalysisDb` em memória
|
||||||
|
* `FileDb` (uri → texto)
|
||||||
|
* `rebuild()` recompila **workspace inteiro** e produz um snapshot
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Design (como deve funcionar)
|
||||||
|
|
||||||
|
### 1) AnalysisDb: estado e snapshot
|
||||||
|
|
||||||
|
**Objetivo:** separar “estado de arquivos” de “resultado de análise”.
|
||||||
|
|
||||||
|
Estruturas recomendadas:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub struct AnalysisDb {
|
||||||
|
pub file_db: FileDb,
|
||||||
|
pub revision: u64,
|
||||||
|
pub active_rebuild: Option<RebuildHandle>,
|
||||||
|
pub last_good: Option<AnalysisSnapshot>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AnalysisSnapshot {
|
||||||
|
pub diagnostics: Vec<Diagnostic>,
|
||||||
|
pub symbols: SymbolIndex, // index por Project/Module (coarse)
|
||||||
|
pub node_to_symbol: NodeToSymbol, // para definition
|
||||||
|
pub def_index: DefIndex, // opcional se você já tiver
|
||||||
|
pub ast: AstArena, // opcional (mas útil para nodes)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> Observação: use os tipos reais do seu compiler. O importante é ter um “snapshot” único que o LSP consulta.
|
||||||
|
|
||||||
|
### 2) Fluxo de rebuild
|
||||||
|
|
||||||
|
* `didOpen/didChange`:
|
||||||
|
|
||||||
|
* atualizar `file_db` com texto atual
|
||||||
|
* incrementar `revision`
|
||||||
|
* disparar `request_rebuild()` (coalescing)
|
||||||
|
|
||||||
|
* `request_rebuild()`:
|
||||||
|
|
||||||
|
* cancela rebuild anterior se houver
|
||||||
|
* agenda um rebuild assíncrono (tokio task)
|
||||||
|
* no fim, se não estiver cancelado e se `revision` não mudou, grava `last_good` e publica diagnostics
|
||||||
|
|
||||||
|
### 3) Diagnostics
|
||||||
|
|
||||||
|
* O compiler já deve produzir diagnostics com `Span { file, start, end }` em bytes.
|
||||||
|
* Para publicar, converter:
|
||||||
|
|
||||||
|
* `Span` → `lsp_types::Range` via `TextIndex` (já existe do refactor)
|
||||||
|
|
||||||
|
Regra:
|
||||||
|
|
||||||
|
* Diagnóstico sem `Span` (ou span inválido) deve ser **ignorado** ou degradado para range 0..0.
|
||||||
|
|
||||||
|
### 4) Definition
|
||||||
|
|
||||||
|
Ao receber `textDocument/definition`:
|
||||||
|
|
||||||
|
1. converter `Position` → byte offset (com `TextIndex`)
|
||||||
|
2. encontrar `NodeId` no ponto
|
||||||
|
3. resolver `NodeId -> SymbolId` via `node_to_symbol`
|
||||||
|
4. pegar `Symbol.decl_span`
|
||||||
|
5. converter `decl_span` → `Location`
|
||||||
|
|
||||||
|
> Se `NodeId` não existir ou não resolver símbolo: retornar `None`.
|
||||||
|
|
||||||
|
### 5) documentSymbol / workspaceSymbol
|
||||||
|
|
||||||
|
* `documentSymbol`: filtrar símbolos cujo `decl_span.file` == arquivo da request.
|
||||||
|
* `workspaceSymbol`: busca textual simples (contains/case-insensitive) nos nomes de símbolos (coarse) e retorna top N.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tarefas de implementação (checklist técnico)
|
||||||
|
|
||||||
|
### Capabilities em `initialize`
|
||||||
|
|
||||||
|
Declarar no server:
|
||||||
|
|
||||||
|
* `textDocumentSync: Full`
|
||||||
|
* `definitionProvider: true`
|
||||||
|
* `documentSymbolProvider: true`
|
||||||
|
* `workspaceSymbolProvider: true`
|
||||||
|
* `diagnosticProvider`: use publishDiagnostics (push)
|
||||||
|
|
||||||
|
### didOpen
|
||||||
|
|
||||||
|
* upsert texto
|
||||||
|
* request_rebuild
|
||||||
|
|
||||||
|
### didChange (Full)
|
||||||
|
|
||||||
|
* receber texto completo do doc
|
||||||
|
* upsert texto
|
||||||
|
* request_rebuild
|
||||||
|
|
||||||
|
### didClose
|
||||||
|
|
||||||
|
* remover do file_db (ou marcar fechado)
|
||||||
|
* publicar diagnostics vazios para o arquivo fechado
|
||||||
|
|
||||||
|
### Conversions
|
||||||
|
|
||||||
|
* `uri <-> FileId`: FileDb precisa mapear URI para FileId estável.
|
||||||
|
* `Span -> Range`: usar `TextIndex` do texto atual do arquivo.
|
||||||
|
* `Position -> byte`: usar `TextIndex`.
|
||||||
|
|
||||||
|
### Node lookup
|
||||||
|
|
||||||
|
Você precisa de uma função no snapshot (ou util) tipo:
|
||||||
|
|
||||||
|
* `fn node_at(file: FileId, byte: u32) -> Option<NodeId>`
|
||||||
|
|
||||||
|
MVP aceitável:
|
||||||
|
|
||||||
|
* se você ainda não tiver um índice de nodes por span, pode:
|
||||||
|
|
||||||
|
* usar AST arena e fazer busca linear na árvore (aceitável no coarse MVP)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Test Harness (mínimo, mas real)
|
||||||
|
|
||||||
|
### Opção A (preferida): tests com `tower-lsp` client in-process
|
||||||
|
|
||||||
|
Criar um teste `tests/lsp_mvp.rs`:
|
||||||
|
|
||||||
|
* sobe o backend LSP em memória
|
||||||
|
* envia:
|
||||||
|
|
||||||
|
* `initialize`
|
||||||
|
* `didOpen` com um fixture PBS (2 arquivos)
|
||||||
|
* espera `publishDiagnostics` (pode interceptar via `Client` mock)
|
||||||
|
* chama `definition` em uma posição de uso e verifica que retorna `Location` do `decl_span`
|
||||||
|
|
||||||
|
**Aceite:**
|
||||||
|
|
||||||
|
* definition retorna arquivo correto
|
||||||
|
* range bate com span convertido
|
||||||
|
|
||||||
|
### Opção B (mais simples): unit tests nos adaptadores
|
||||||
|
|
||||||
|
Se o harness LSP demorar, pelo menos criar:
|
||||||
|
|
||||||
|
* `span_to_range_uses_utf16()`
|
||||||
|
* `position_to_byte_roundtrip()`
|
||||||
|
* `definition_resolves_to_decl_span()` usando snapshot fake.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checklist de aceite (obrigatório)
|
||||||
|
|
||||||
|
* [ ] `cargo test -q` passa no workspace
|
||||||
|
* [ ] VSCode: abrir arquivo `.pbs` mostra diagnostics (pelo menos 1 erro sintático)
|
||||||
|
* [ ] VSCode: `Go to Definition` funciona para símbolo resolvido
|
||||||
|
* [ ] VSCode: Outline mostra `documentSymbol`
|
||||||
|
* [ ] Mudanças em um arquivo disparam rebuild e atualizam diagnostics
|
||||||
|
* [ ] Unicode: diagnostics não ficam “desalinhados” (teste manual com `ação`/emoji)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fora de escopo (explicitamente)
|
||||||
|
|
||||||
|
* semantic tokens
|
||||||
|
* completion
|
||||||
|
* references/rename
|
||||||
|
* hover/signatureHelp
|
||||||
|
* incremental analysis e cancelation avançada
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# PR — lsp-hightlight-base (Semantic Tokens — lexer-first)
|
||||||
|
|
||||||
|
**Branch:** `pr-12a-lsp-semantic-lexer`
|
||||||
|
|
||||||
|
## Briefing
|
||||||
|
|
||||||
|
Queremos **highlight no VSCode via LSP**, sem depender de resolver e sem TextMate.
|
||||||
|
|
||||||
|
Estratégia:
|
||||||
|
|
||||||
|
* Implementar `textDocument/semanticTokens/full`.
|
||||||
|
* Gerar tokens **lexer-first**: comments/strings/numbers/keywords/identifiers.
|
||||||
|
* Converter spans (bytes) para LSP positions (UTF-16) usando `TextIndex`.
|
||||||
|
* comentários extensivos com exemplos se necessário e em inglês sempre
|
||||||
|
|
||||||
|
Isso entrega um highlight “bom o suficiente” e muito estável, mesmo com arquivo com erro de parse.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Alvo (Features)
|
||||||
|
|
||||||
|
* ✅ `textDocument/semanticTokens/full`
|
||||||
|
* ✅ `SemanticTokensLegend` consistente
|
||||||
|
* ✅ tokens derivados do lexer
|
||||||
|
|
||||||
|
Opcional (não obrigatório neste PR):
|
||||||
|
|
||||||
|
* `semanticTokens/range`
|
||||||
|
* `semanticTokens/full/delta`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
### 1) Legend fixa
|
||||||
|
|
||||||
|
Escolher um conjunto pequeno de token types:
|
||||||
|
|
||||||
|
* `comment`
|
||||||
|
* `string`
|
||||||
|
* `number`
|
||||||
|
* `keyword`
|
||||||
|
* `operator` (opcional)
|
||||||
|
* `variable` (para identifiers genéricos)
|
||||||
|
|
||||||
|
> Não inventar muitos tipos agora; fácil expandir depois.
|
||||||
|
|
||||||
|
### 2) Fonte de tokens
|
||||||
|
|
||||||
|
Implementar uma função no analysis/compiler layer (ou no lsp crate) que, dado:
|
||||||
|
|
||||||
|
* `FileId`
|
||||||
|
* `text: &str`
|
||||||
|
retorna `Vec<(Span, TokenType, TokenModifiers)>`.
|
||||||
|
|
||||||
|
**Importante:**
|
||||||
|
|
||||||
|
* Spans são em bytes.
|
||||||
|
* Devem ser **não sobrepostos** e preferencialmente em ordem.
|
||||||
|
|
||||||
|
### 3) Conversão para formato LSP (deltas)
|
||||||
|
|
||||||
|
LSP semantic tokens usa encoding em deltas:
|
||||||
|
|
||||||
|
* `deltaLine`, `deltaStartChar`, `length`, `tokenType`, `tokenModifiers`
|
||||||
|
|
||||||
|
Algoritmo:
|
||||||
|
|
||||||
|
1. Converter `Span.start` e `Span.end` em `(line, utf16_col)`.
|
||||||
|
2. Calcular `length` em UTF-16 units para o trecho (start..end).
|
||||||
|
3. Ordenar por `(line, col)`.
|
||||||
|
4. Emitir deltas.
|
||||||
|
|
||||||
|
Regra:
|
||||||
|
|
||||||
|
* Se `Span` cruza linhas, **quebrar** em múltiplos tokens por linha (MVP seguro).
|
||||||
|
|
||||||
|
### 4) Robustez
|
||||||
|
|
||||||
|
* Token inválido (end < start, ou fora do texto) deve ser ignorado.
|
||||||
|
* Se o arquivo não estiver no `FileDb`, retornar tokens vazios.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tarefas de implementação
|
||||||
|
|
||||||
|
### Server capabilities
|
||||||
|
|
||||||
|
No `initialize`, anunciar:
|
||||||
|
|
||||||
|
* `semanticTokensProvider` com:
|
||||||
|
|
||||||
|
* `legend`
|
||||||
|
* `full: true`
|
||||||
|
* `range: false` (por enquanto)
|
||||||
|
|
||||||
|
### Handler
|
||||||
|
|
||||||
|
Implementar `semantic_tokens_full(params)`:
|
||||||
|
|
||||||
|
* pegar `uri`
|
||||||
|
* buscar texto no `file_db`
|
||||||
|
* gerar tokens do lexer
|
||||||
|
* converter com `TextIndex`
|
||||||
|
* retornar `SemanticTokensResult::Tokens`
|
||||||
|
|
||||||
|
### Lexer tokens
|
||||||
|
|
||||||
|
Se você já tem lexer com spans:
|
||||||
|
|
||||||
|
* mapear tokens para os tipos (keyword/string/comment/number/identifier)
|
||||||
|
* keywords: pode ser por enum do token ou por tabela
|
||||||
|
|
||||||
|
Se o lexer não marca keyword vs ident:
|
||||||
|
|
||||||
|
* fallback: parse por string e classifica keywords via `HashSet<&'static str>`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testes
|
||||||
|
|
||||||
|
### Unit tests (obrigatórios)
|
||||||
|
|
||||||
|
1. `semantic_tokens_legend_is_stable()`
|
||||||
|
|
||||||
|
* garante que legend não muda sem intenção.
|
||||||
|
|
||||||
|
2. `semantic_tokens_are_sorted_and_non_negative()`
|
||||||
|
|
||||||
|
* gera tokens em um fixture com 2 linhas
|
||||||
|
* valida que deltas nunca ficam negativos e que ordem é válida.
|
||||||
|
|
||||||
|
3. `semantic_tokens_unicode_length_utf16()`
|
||||||
|
|
||||||
|
* texto com `ação🙂`
|
||||||
|
* valida que `length` corresponde a UTF-16 (emoji conta como 2).
|
||||||
|
|
||||||
|
### Teste manual (aceite)
|
||||||
|
|
||||||
|
* Abrir `.pbs` no VSCode
|
||||||
|
* Verificar:
|
||||||
|
|
||||||
|
* strings e comentários coloridos
|
||||||
|
* keywords coloridas
|
||||||
|
* números coloridos
|
||||||
|
* identifiers coloridos (mesmo que genérico)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checklist de aceite
|
||||||
|
|
||||||
|
* [ ] `cargo test -q` passa
|
||||||
|
* [ ] VSCode: arquivos PBS ficam coloridos via LSP (sem TextMate)
|
||||||
|
* [ ] Unicode não quebra offsets
|
||||||
|
* [ ] Arquivo com erro de parse ainda tem highlight (lexer-first)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fora de escopo
|
||||||
|
|
||||||
|
* semantic tokens semântico (type vs fn vs var) — virá em `PR-12b`
|
||||||
|
* `range`/`delta`
|
||||||
|
* completion
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# PR — lsp-completion-base (Completion mínimo)
|
||||||
|
|
||||||
|
**Branch:** `pr-11a-lsp-completion-min`
|
||||||
|
|
||||||
|
## Briefing
|
||||||
|
|
||||||
|
Queremos autocomplete **mínimo mas útil** para conseguir escrever SDK/ECS em PBS sem fricção.
|
||||||
|
|
||||||
|
Princípio:
|
||||||
|
|
||||||
|
* Não depende de scope facts, nem type facts.
|
||||||
|
* Usa somente:
|
||||||
|
|
||||||
|
* keywords/builtins
|
||||||
|
* símbolos top-level do módulo atual
|
||||||
|
* exports/imports visíveis no projeto (coarse)
|
||||||
|
|
||||||
|
Isso é suficiente para começar a programar e evoluir o LSP depois.
|
||||||
|
|
||||||
|
* comentários extensivos com exemplos se necessário e em inglês sempre
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Alvo (Features)
|
||||||
|
|
||||||
|
* ✅ `textDocument/completion`
|
||||||
|
* ✅ `completionItem/resolve` (opcional; pode retornar item já completo)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
### 1) Buckets e ordenação
|
||||||
|
|
||||||
|
Retornar `CompletionList` com itens nesta prioridade:
|
||||||
|
|
||||||
|
1. Keywords (`fn`, `let`, `mutate`, `declare`, `struct`, `storage`, `if`, `else`, `for`, `return`, etc.)
|
||||||
|
2. Builtins/funções globais (ex.: `alloc`, `box`, `unbox`, `range`, etc. — conforme spec do PBS)
|
||||||
|
3. Símbolos do módulo atual (top-level)
|
||||||
|
4. Símbolos “workspace visible” (exports/imports), limitado (ex.: top 200)
|
||||||
|
|
||||||
|
**Regra de ranking simples:**
|
||||||
|
|
||||||
|
* locals (não teremos) > módulo atual > workspace
|
||||||
|
|
||||||
|
### 2) Contexto
|
||||||
|
|
||||||
|
Para completion mínimo, só precisamos:
|
||||||
|
|
||||||
|
* `uri` do documento
|
||||||
|
* `position` (para pegar prefixo)
|
||||||
|
|
||||||
|
Prefixo:
|
||||||
|
|
||||||
|
* converter `Position` -> byte
|
||||||
|
* extrair texto até o cursor e identificar o “token parcial” (regex simples `[A-Za-z_][A-Za-z0-9_]*$`)
|
||||||
|
|
||||||
|
### 3) Fonte dos símbolos
|
||||||
|
|
||||||
|
Usar o `AnalysisSnapshot` (produzido na PR-08) para expor:
|
||||||
|
|
||||||
|
* `fn symbols_in_file(file: FileId) -> Vec<SymbolId>`
|
||||||
|
* `fn symbols_in_module(project: ProjectId, module: ModuleId) -> Vec<SymbolId>`
|
||||||
|
* `fn workspace_symbols() -> impl Iterator<Item = (name, kind, decl_span)>`
|
||||||
|
|
||||||
|
MVP aceitável:
|
||||||
|
|
||||||
|
* `workspace_symbols()` pode ser um vetor “flatten” pré-calculado no snapshot.
|
||||||
|
|
||||||
|
### 4) CompletionItem
|
||||||
|
|
||||||
|
Mapeamento para `CompletionItemKind`:
|
||||||
|
|
||||||
|
* functions -> `FUNCTION`
|
||||||
|
* types -> `CLASS` (ou `STRUCT` se quiser)
|
||||||
|
* modules -> `MODULE`
|
||||||
|
* variables/constants -> `VARIABLE` / `CONSTANT`
|
||||||
|
|
||||||
|
Campos:
|
||||||
|
|
||||||
|
* `label`: nome
|
||||||
|
* `detail`: (opcional) `"fn"/"type"/"module"`
|
||||||
|
* `sortText`: prefixado para impor bucket (ex.: `"1_"`, `"2_"`)
|
||||||
|
* `filterText`: label
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tarefas de implementação
|
||||||
|
|
||||||
|
### Capabilities
|
||||||
|
|
||||||
|
No `initialize`:
|
||||||
|
|
||||||
|
* `completionProvider: { resolveProvider: false, triggerCharacters: [".", ":"]? }`
|
||||||
|
|
||||||
|
> Para completion mínimo, nem precisa trigger chars. Pode deixar default.
|
||||||
|
|
||||||
|
### Handler `completion`
|
||||||
|
|
||||||
|
* buscar texto e `TextIndex`
|
||||||
|
* calcular prefixo
|
||||||
|
* montar lista de candidatos por bucket
|
||||||
|
* filtrar por prefixo (case-sensitive ou insensitive; escolha e documente)
|
||||||
|
* limitar tamanho
|
||||||
|
* retornar `CompletionResponse::List`
|
||||||
|
|
||||||
|
### Builtins/keywords
|
||||||
|
|
||||||
|
Criar tabelas estáticas em `prometeu-lsp`:
|
||||||
|
|
||||||
|
* `static KEYWORDS: &[&str] = ...`
|
||||||
|
* `static BUILTINS: &[&str] = ...`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Testes
|
||||||
|
|
||||||
|
### Unit tests (obrigatórios)
|
||||||
|
|
||||||
|
1. `completion_extracts_prefix()`
|
||||||
|
|
||||||
|
* valida regex de prefixo
|
||||||
|
|
||||||
|
2. `completion_includes_keywords()`
|
||||||
|
|
||||||
|
* chama handler com texto vazio e posição 0
|
||||||
|
* garante que `fn` aparece
|
||||||
|
|
||||||
|
3. `completion_filters_by_prefix()`
|
||||||
|
|
||||||
|
* prefixo `"ra"` deve sugerir `range` (se builtin)
|
||||||
|
|
||||||
|
4. (se possível) `completion_includes_module_symbols()`
|
||||||
|
|
||||||
|
* usando snapshot fake ou fixture compilado pela infra da PR-08
|
||||||
|
|
||||||
|
### Teste manual (aceite)
|
||||||
|
|
||||||
|
* Abrir arquivo PBS e digitar `ra` -> aparece `range`
|
||||||
|
* Digitar nome de tipo/fn do SDK -> aparece suggestion
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Checklist de aceite
|
||||||
|
|
||||||
|
* [ ] `cargo test -q` passa
|
||||||
|
* [ ] VSCode: autocomplete sugere keywords e builtins
|
||||||
|
* [ ] VSCode: autocomplete sugere símbolos do módulo atual
|
||||||
|
* [ ] Não trava com rebuild coarse
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fora de escopo
|
||||||
|
|
||||||
|
* locals em scope
|
||||||
|
* members (`obj.`)
|
||||||
|
* signatureHelp
|
||||||
|
* hover
|
||||||
219
docs/roadmaps/lsp/LSP - roadmap remain.md
Normal file
219
docs/roadmaps/lsp/LSP - roadmap remain.md
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
# LSP Roadmap — do “base usable” até “LSP completo”
|
||||||
|
|
||||||
|
> Este documento é o mapa incremental pós `lsp-base`, `lsp-hightlight-base`, `lsp-completion-base`.
|
||||||
|
> A ideia é manter o LSP evoluindo sem rework, enquanto SDK/ECS/Packer avançam em paralelo.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Estado atual (após os 3 PRs base)
|
||||||
|
|
||||||
|
### ✅ Já temos
|
||||||
|
|
||||||
|
* Diagnostics (publishDiagnostics)
|
||||||
|
* Definition (goto definition)
|
||||||
|
* DocumentSymbol / WorkspaceSymbol
|
||||||
|
* Semantic tokens (lexer-first)
|
||||||
|
* Completion mínimo (keywords/builtins/top-level/module/workspace)
|
||||||
|
|
||||||
|
### ❌ Ainda não temos
|
||||||
|
|
||||||
|
* references / rename
|
||||||
|
* hover / signatureHelp
|
||||||
|
* completion com locals em scope
|
||||||
|
* semantic tokens semântico (type vs fn vs var)
|
||||||
|
* incremental analysis real (por arquivo / cancelation)
|
||||||
|
* debug map pc→span integrado com traps
|
||||||
|
* code actions / formatting
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Próximos PRs recomendados (ordem sugerida)
|
||||||
|
|
||||||
|
## PR-09 — References + Rename (seguro)
|
||||||
|
|
||||||
|
### Objetivo
|
||||||
|
|
||||||
|
Habilitar `references`, `prepareRename`, `rename` com regras de segurança.
|
||||||
|
|
||||||
|
### Pré-requisitos
|
||||||
|
|
||||||
|
* `RefIndex` completo (SymbolId -> usos em spans)
|
||||||
|
* `NodeToSymbol` estável
|
||||||
|
* `TriviaIndex` (comments/strings) para não renomear spans proibidos
|
||||||
|
|
||||||
|
### Regras de segurança
|
||||||
|
|
||||||
|
* Não renomear símbolo não-resolvido
|
||||||
|
* Não renomear em comentário/string
|
||||||
|
* Renomear deve gerar `WorkspaceEdit` apenas para spans válidos
|
||||||
|
|
||||||
|
### Testes
|
||||||
|
|
||||||
|
* Fixture com 2 arquivos: rename atualiza todas as refs
|
||||||
|
* Fixture: rename em comentário não altera nada
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PR-10 — Hover + SignatureHelp
|
||||||
|
|
||||||
|
### Objetivo
|
||||||
|
|
||||||
|
* `hover`: mostrar tipo + docstring (docstring opcional)
|
||||||
|
* `signatureHelp`: para call nodes
|
||||||
|
|
||||||
|
### Pré-requisitos
|
||||||
|
|
||||||
|
* Type facts básicos: `NodeId -> TypeId`
|
||||||
|
* Modelo de assinatura: para functions (params, retorno)
|
||||||
|
|
||||||
|
### Testes
|
||||||
|
|
||||||
|
* Hover em símbolo mostra tipo
|
||||||
|
* SignatureHelp em chamada mostra params
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PR-11b — Completion com locals em scope
|
||||||
|
|
||||||
|
### Objetivo
|
||||||
|
|
||||||
|
Autocomplete útil para código real:
|
||||||
|
|
||||||
|
* locals/params
|
||||||
|
* shadowing correto
|
||||||
|
|
||||||
|
### Pré-requisitos
|
||||||
|
|
||||||
|
* Binder/Scope facts:
|
||||||
|
|
||||||
|
* `Position -> ScopeId`
|
||||||
|
* `ScopeId -> bindings (name -> SymbolId/TypeId)`
|
||||||
|
|
||||||
|
### Testes
|
||||||
|
|
||||||
|
* Dentro de bloco, sugere variável local
|
||||||
|
* Shadowing: sugere a mais interna
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PR-12b — Semantic tokens resolver-backed (semântico)
|
||||||
|
|
||||||
|
### Objetivo
|
||||||
|
|
||||||
|
Melhorar highlight:
|
||||||
|
|
||||||
|
* diferenciar `type` vs `function` vs `variable` vs `parameter` vs `module`
|
||||||
|
|
||||||
|
### Pré-requisitos
|
||||||
|
|
||||||
|
* `NodeAtPosition` confiável
|
||||||
|
* `NodeToSymbol` confiável
|
||||||
|
* `SymbolKind` consistente
|
||||||
|
|
||||||
|
### Testes
|
||||||
|
|
||||||
|
* Identificador de tipo recebe token `type`
|
||||||
|
* Função recebe token `function`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PR-13 — Formatting + Code Actions (opcional)
|
||||||
|
|
||||||
|
### Objetivo
|
||||||
|
|
||||||
|
* `textDocument/formatting` (nem que seja formatter simples/estável)
|
||||||
|
* code actions básicas:
|
||||||
|
|
||||||
|
* organizar imports
|
||||||
|
* criar stub de função/struct
|
||||||
|
|
||||||
|
### Pré-requisitos
|
||||||
|
|
||||||
|
* AST pretty printer (ou formatter incremental)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PR-14 — Incremental analysis + cancelation (de verdade)
|
||||||
|
|
||||||
|
### Objetivo
|
||||||
|
|
||||||
|
Sair do rebuild coarse:
|
||||||
|
|
||||||
|
* recompilar somente arquivo/módulo afetado
|
||||||
|
* cancelamento real de builds longos
|
||||||
|
* snapshots por versão
|
||||||
|
|
||||||
|
### Pré-requisitos
|
||||||
|
|
||||||
|
* Graph de dependências por módulo
|
||||||
|
* Cache de parse/resolve por arquivo
|
||||||
|
* `revision` por doc
|
||||||
|
|
||||||
|
### Testes
|
||||||
|
|
||||||
|
* Editar arquivo A não recompila projeto inteiro
|
||||||
|
* Cancel: digitar rápido não aplica resultados velhos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PR-15 — Debug map (pc→span) + integração com traps
|
||||||
|
|
||||||
|
### Objetivo
|
||||||
|
|
||||||
|
Quando a VM gerar trap/runtime error:
|
||||||
|
|
||||||
|
* mapear `pc` para `Span`
|
||||||
|
* mostrar erro com localização exata
|
||||||
|
|
||||||
|
### Pré-requisitos
|
||||||
|
|
||||||
|
* SourceMap gerado no backend bytecode
|
||||||
|
* runtime expõe `pc`/contexto
|
||||||
|
|
||||||
|
### Testes
|
||||||
|
|
||||||
|
* programa que gera trap aponta para linha/col corretos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Backlog adicional (nice to have)
|
||||||
|
|
||||||
|
* `workspace/didChangeWatchedFiles` (reagir a mudanças fora do editor)
|
||||||
|
* `textDocument/codeLens` (ex.: run test / run main)
|
||||||
|
* `textDocument/inlayHint`
|
||||||
|
* `workspace/diagnostic` pull-mode (se quiser)
|
||||||
|
* semanticTokens `range` e `delta` para performance
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Interação com SDK/ECS/Packer
|
||||||
|
|
||||||
|
### Com os 3 PRs base, você já consegue:
|
||||||
|
|
||||||
|
* escrever SDK/ECS em PBS com feedback imediato
|
||||||
|
* navegar rapidamente pelo código
|
||||||
|
* manter arquivos grandes com highlight
|
||||||
|
* usar completion para nomes de APIs
|
||||||
|
|
||||||
|
### Enquanto isso, em paralelo:
|
||||||
|
|
||||||
|
* packer pode evoluir (não depende do LSP)
|
||||||
|
* quando packer estabilizar, podemos adicionar code actions/codelens para “build/pack/run” direto no VSCode
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Definição de “LSP completo” (para Prometeu view-ready)
|
||||||
|
|
||||||
|
Para chamar de "completo" (na sua visão de produto):
|
||||||
|
|
||||||
|
* ✅ diagnostics
|
||||||
|
* ✅ definition
|
||||||
|
* ✅ symbols
|
||||||
|
* ✅ highlight semântico
|
||||||
|
* ✅ completion com scope + members
|
||||||
|
* ✅ hover + signatureHelp
|
||||||
|
* ✅ references + rename
|
||||||
|
* ✅ incremental + cancel
|
||||||
|
* ✅ debug map integrado com traps
|
||||||
|
|
||||||
|
> A ordem acima é incremental por valor percebido e por dependências.
|
||||||
0
docs/roadmaps/packer/.gitkeep
Normal file
0
docs/roadmaps/packer/.gitkeep
Normal file
564
docs/roadmaps/packer/Prometeu Packer.md
Normal file
564
docs/roadmaps/packer/Prometeu Packer.md
Normal file
@ -0,0 +1,564 @@
|
|||||||
|
# Prometeu Packer (prometeu-packer) — Specification (Draft)
|
||||||
|
|
||||||
|
> **Status:** Draft / Community-facing
|
||||||
|
>
|
||||||
|
> This document specifies the **Prometeu Packer**, the tooling responsible for **asset management** and producing two build artifacts:
|
||||||
|
>
|
||||||
|
> * `build/assets.pa` — the ROM payload containing packed asset bytes
|
||||||
|
> * `build/asset_table.json` — a machine-readable descriptor of the packed assets
|
||||||
|
>
|
||||||
|
> The Packer is deliberately **agnostic of cartridge building**. A separate **Cartridge Builder** (outside the Packer) consumes `build/asset_table.json` to generate the final `cartridge/manifest.json`, copy `assets.pa` to the cartridge directory, and zip the cartridge into a distributable format.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Goals and Non-Goals
|
||||||
|
|
||||||
|
### 1.1 Goals
|
||||||
|
|
||||||
|
1. **Be the guardian of sanity** in a constantly mutating `assets/` workspace.
|
||||||
|
|
||||||
|
* Users may be disorganized.
|
||||||
|
* The directory may contain WIP, junk, unused files, duplicates, outdated exports.
|
||||||
|
* The Packer must help users identify and fix mistakes.
|
||||||
|
|
||||||
|
2. Provide a robust, deterministic, **tooling-grade** asset pipeline.
|
||||||
|
|
||||||
|
* Stable asset identity.
|
||||||
|
* Deterministic packing order.
|
||||||
|
* Reproducible output bytes.
|
||||||
|
|
||||||
|
3. Support both **raw (direct) assets** and **virtual assets**.
|
||||||
|
|
||||||
|
* Raw assets: the payload in ROM is exactly the source bytes.
|
||||||
|
* Virtual assets: the payload is derived from multiple inputs via a build pipeline (e.g., PNG + palettes → `TILES` payload).
|
||||||
|
|
||||||
|
4. Produce an output descriptor (`build/asset_table.json`) suitable for:
|
||||||
|
|
||||||
|
* a Cartridge Builder to generate the runtime manifest
|
||||||
|
* CI checks
|
||||||
|
* a future IDE / GUI tooling
|
||||||
|
|
||||||
|
5. Provide an extensive **diagnostics chain** (doctor) with structured error codes and suggested fixes.
|
||||||
|
|
||||||
|
### 1.2 Non-Goals
|
||||||
|
|
||||||
|
* The Packer **does not**:
|
||||||
|
|
||||||
|
* generate `cartridge/manifest.json`
|
||||||
|
* decide preload slots
|
||||||
|
* copy files into `cartridge/`
|
||||||
|
* compile PBS bytecode
|
||||||
|
* zip cartridges
|
||||||
|
|
||||||
|
These responsibilities belong to a separate **Cartridge Builder**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Repository / Project Layout (Golden Pipeline)
|
||||||
|
|
||||||
|
The Prometeu project uses the following canonical structure:
|
||||||
|
|
||||||
|
* `src/` — PBS scripts
|
||||||
|
* `assets/` — mutable asset workspace (WIP allowed)
|
||||||
|
* `build/` — generated artifacts and caches
|
||||||
|
* `cartridge/` — final cartridge directory (produced by Cartridge Builder)
|
||||||
|
* `prometeu.json` — project description (dependencies, version, etc.)
|
||||||
|
* `sdk/` — SDK/tooling and libraries
|
||||||
|
|
||||||
|
The Packer owns the asset workspace metadata under:
|
||||||
|
|
||||||
|
* `assets/.prometeu/` — Packer control directory (registry, cache, quarantine)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Crate Topology
|
||||||
|
|
||||||
|
### 3.1 Crates
|
||||||
|
|
||||||
|
* **`prometeu-packer`**
|
||||||
|
|
||||||
|
* **lib**: `prometeu_packer_core`
|
||||||
|
* **bin**: `prometeu-packer`
|
||||||
|
|
||||||
|
* a thin CLI wrapper delegating to `prometeu_packer_core::run()`
|
||||||
|
|
||||||
|
* **`prometeu` dispatcher**
|
||||||
|
|
||||||
|
* provides a wrapper command **`prometeup`** (or integrated subcommand)
|
||||||
|
* delegates to `prometeu-packer` for packer operations
|
||||||
|
|
||||||
|
### 3.2 Design Principle
|
||||||
|
|
||||||
|
Treat the Packer like the compiler: core library + CLI wrapper.
|
||||||
|
|
||||||
|
* The core library enables future integrations (IDE, GUI, watch mode) without shelling out.
|
||||||
|
* CLI is a stable interface for users and CI.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Mental Model: A “Git-like” Asset Workspace
|
||||||
|
|
||||||
|
The Packer treats `assets/` like a **dirty working tree**.
|
||||||
|
|
||||||
|
* `assets/` can contain *anything*.
|
||||||
|
* Only the assets registered in the Packer registry are considered part of the build.
|
||||||
|
|
||||||
|
This is analogous to Git:
|
||||||
|
|
||||||
|
* working tree (chaos) vs index (truth)
|
||||||
|
|
||||||
|
The **source of truth** for “what counts” is the registry:
|
||||||
|
|
||||||
|
* `assets/.prometeu/index.json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Core Concepts
|
||||||
|
|
||||||
|
### 5.1 Managed Asset
|
||||||
|
|
||||||
|
A **managed asset** is an entry in `assets/.prometeu/index.json` pointing to an **asset root directory** that contains an anchor file:
|
||||||
|
|
||||||
|
* `asset.json` (the asset specification)
|
||||||
|
|
||||||
|
Everything else is free-form.
|
||||||
|
|
||||||
|
### 5.2 Asset Identity
|
||||||
|
|
||||||
|
Each asset has stable identity:
|
||||||
|
|
||||||
|
* `asset_id: u32` — stable within the project (used by runtime/tooling)
|
||||||
|
* `asset_uuid: string` — globally unique stable identifier (useful for IDE and future migrations)
|
||||||
|
|
||||||
|
Names and paths may change, but identity remains.
|
||||||
|
|
||||||
|
### 5.3 Asset Types (Bank Targets)
|
||||||
|
|
||||||
|
Assets ultimately target a **bank type** in the runtime:
|
||||||
|
|
||||||
|
* `TILES`
|
||||||
|
* `SOUNDS`
|
||||||
|
* (future) `SPRITESHEET`, `MAP`, `FONT`, `RAW_BLOB`, etc.
|
||||||
|
|
||||||
|
The Packer does **not** define bank memory semantics. It defines the *ROM payload* and its metadata.
|
||||||
|
|
||||||
|
### 5.4 Raw vs Virtual Assets
|
||||||
|
|
||||||
|
* **Raw assets**: ROM payload equals the source bytes.
|
||||||
|
* **Virtual assets**: ROM payload is derived from input(s) via deterministic build steps.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
* PNG + palette files → `TILES` payload (indexed pixels + packed palettes)
|
||||||
|
* WAV → PCM16LE payload
|
||||||
|
* Multiple PNGs → atlas spritesheet
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Directory Structure and Control Files
|
||||||
|
|
||||||
|
### 6.1 Workspace
|
||||||
|
|
||||||
|
`assets/` is a mutable workspace:
|
||||||
|
|
||||||
|
* users may create nested organization trees
|
||||||
|
* junk files are allowed
|
||||||
|
|
||||||
|
### 6.2 Control Directory
|
||||||
|
|
||||||
|
The Packer stores its truth + tools state in:
|
||||||
|
|
||||||
|
```
|
||||||
|
assets/
|
||||||
|
.prometeu/
|
||||||
|
index.json
|
||||||
|
cache/
|
||||||
|
fingerprints.json
|
||||||
|
build-cache.json
|
||||||
|
quarantine/
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
* `index.json` — registry of managed assets
|
||||||
|
* `cache/` — fingerprints and incremental build cache
|
||||||
|
* `quarantine/` — optional area to move detected junk (only by explicit user action)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Asset Specification: `asset.json`
|
||||||
|
|
||||||
|
`asset.json` describes:
|
||||||
|
|
||||||
|
1. **the output ROM payload** expected by runtime
|
||||||
|
2. **the build pipeline** (for virtual assets)
|
||||||
|
3. **metadata** needed by runtime/builder
|
||||||
|
|
||||||
|
This spec is modular: **each asset format** (e.g. `TILES/indexed_v1`) has its own dedicated specification document.
|
||||||
|
|
||||||
|
### 7.1 Common Fields (All Assets)
|
||||||
|
|
||||||
|
* `schema_version`
|
||||||
|
* `name`
|
||||||
|
* `type` (bank type)
|
||||||
|
* `codec` (e.g. `RAW`; future: compression)
|
||||||
|
* `inputs` (for virtual assets)
|
||||||
|
* `output` (format + required metadata)
|
||||||
|
* `build` (optional pipeline configuration)
|
||||||
|
|
||||||
|
### 7.2 Virtual Asset Pipeline Declaration
|
||||||
|
|
||||||
|
Virtual assets must be declared in a way that is:
|
||||||
|
|
||||||
|
* deterministic
|
||||||
|
* fully materialized (no silent inference)
|
||||||
|
* explicit about defaults (defaults may exist, but must be written into `asset.json` or build outputs)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Build Artifacts Produced by the Packer
|
||||||
|
|
||||||
|
### 8.1 `build/assets.pa`
|
||||||
|
|
||||||
|
**`assets.pa`** is the ROM asset payload used by the runtime.
|
||||||
|
|
||||||
|
**Definition:** a contiguous binary blob where each managed asset contributes a payload region.
|
||||||
|
|
||||||
|
#### Key Properties
|
||||||
|
|
||||||
|
* Deterministic asset order (by `asset_id`)
|
||||||
|
* Offsets are recorded in `build/asset_table.json`
|
||||||
|
* Alignment rules (configurable by packer, default: no alignment unless required by a format)
|
||||||
|
|
||||||
|
**Note:** `assets.pa` is intentionally simple.
|
||||||
|
|
||||||
|
* No internal header is required.
|
||||||
|
* The authoritative structure comes from the descriptor (`asset_table.json`).
|
||||||
|
|
||||||
|
Future versions may introduce chunk tables, but v1 keeps ROM simple.
|
||||||
|
|
||||||
|
### 8.2 `build/asset_table.json`
|
||||||
|
|
||||||
|
**`asset_table.json`** is the canonical descriptor output of the Packer.
|
||||||
|
|
||||||
|
It contains:
|
||||||
|
|
||||||
|
* `assets_pa` file info (size, hash)
|
||||||
|
* `asset_table[]` entries describing each payload slice
|
||||||
|
* optional diagnostics/warnings
|
||||||
|
|
||||||
|
#### Asset Table Entry
|
||||||
|
|
||||||
|
An entry describes a ROM slice and its runtime meaning:
|
||||||
|
|
||||||
|
* `asset_id` — stable u32
|
||||||
|
* `asset_uuid` — stable UUID string
|
||||||
|
* `asset_name` — stable user-facing name
|
||||||
|
* `bank_type` — e.g. `TILES`, `SOUNDS`
|
||||||
|
* `offset` — byte offset in `assets.pa`
|
||||||
|
* `size` — bytes stored in ROM
|
||||||
|
* `decoded_size` — bytes after decode (equal to `size` when `codec=RAW`)
|
||||||
|
* `codec` — `RAW` (future: compression)
|
||||||
|
* `metadata` — format-specific metadata needed by runtime/builder
|
||||||
|
|
||||||
|
Additional tooling fields:
|
||||||
|
|
||||||
|
* `source_root` — path to asset dir
|
||||||
|
* `inputs` — resolved input paths
|
||||||
|
* `source_hashes` — stable fingerprints of inputs
|
||||||
|
|
||||||
|
`asset_table.json` is machine-readable and designed for:
|
||||||
|
|
||||||
|
* cartridge builder consumption
|
||||||
|
* IDE visualization
|
||||||
|
* debugging
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Determinism Rules
|
||||||
|
|
||||||
|
1. Asset packing order MUST be deterministic.
|
||||||
|
|
||||||
|
* Default: increasing `asset_id`
|
||||||
|
|
||||||
|
2. All derived outputs MUST be deterministic.
|
||||||
|
|
||||||
|
* No random seeds unless explicitly declared
|
||||||
|
* Any seed must be written to output metadata
|
||||||
|
|
||||||
|
3. Default values MUST be materialized.
|
||||||
|
|
||||||
|
* If the packer infers something, it must be written into `asset.json` (via `--fix`) or recorded in build outputs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Diagnostics and the “Sanity Guardian” Chain
|
||||||
|
|
||||||
|
The Packer provides structured diagnostics:
|
||||||
|
|
||||||
|
* `code` — stable diagnostic code
|
||||||
|
* `severity` — `error | warning | info`
|
||||||
|
* `path` — affected file
|
||||||
|
* `message` — human friendly
|
||||||
|
* `help` — extended context
|
||||||
|
* `fixes[]` — suggested automated or manual fixes
|
||||||
|
|
||||||
|
### 10.1 Diagnostic Classes
|
||||||
|
|
||||||
|
1. **Registered Errors** (break build)
|
||||||
|
|
||||||
|
* registry entry missing anchor file
|
||||||
|
* `asset.json` invalid
|
||||||
|
* missing inputs
|
||||||
|
* format/metadata mismatch
|
||||||
|
|
||||||
|
2. **Workspace Warnings** (does not break build)
|
||||||
|
|
||||||
|
* orphaned `asset.json` (not registered)
|
||||||
|
* unused large files
|
||||||
|
* duplicate inputs by hash
|
||||||
|
|
||||||
|
3. **Policy Hints** (optional)
|
||||||
|
|
||||||
|
* naming conventions
|
||||||
|
* missing preview
|
||||||
|
|
||||||
|
### 10.2 `doctor` Modes
|
||||||
|
|
||||||
|
* `doctor` (default) — validate registry only (fast)
|
||||||
|
* `doctor --workspace` — deep scan workspace (slow)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Incremental Build, Cache, and Fingerprints
|
||||||
|
|
||||||
|
The Packer maintains fingerprints of inputs:
|
||||||
|
|
||||||
|
* size
|
||||||
|
* mtime
|
||||||
|
* strong hash (sha256)
|
||||||
|
|
||||||
|
Stored in:
|
||||||
|
|
||||||
|
* `assets/.prometeu/cache/fingerprints.json`
|
||||||
|
|
||||||
|
This enables:
|
||||||
|
|
||||||
|
* detecting changes
|
||||||
|
* rebuild only what changed
|
||||||
|
* producing stable reports
|
||||||
|
|
||||||
|
The cache must never compromise determinism.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Quarantine and Garbage Collection
|
||||||
|
|
||||||
|
### 12.1 Quarantine
|
||||||
|
|
||||||
|
The Packer can optionally move suspected junk to:
|
||||||
|
|
||||||
|
* `assets/.prometeu/quarantine/`
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
* Quarantine is **never automatic** without user consent.
|
||||||
|
* Packer must explain exactly what will be moved.
|
||||||
|
|
||||||
|
### 12.2 Garbage Collection (`gc`)
|
||||||
|
|
||||||
|
The Packer can report unused files:
|
||||||
|
|
||||||
|
* files not referenced by any registered asset
|
||||||
|
* orphaned asset dirs
|
||||||
|
|
||||||
|
Actions:
|
||||||
|
|
||||||
|
* list candidates
|
||||||
|
* optionally move to quarantine
|
||||||
|
* never delete without explicit user request
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. CLI Commands (Comprehensive)
|
||||||
|
|
||||||
|
> The CLI is a stable interface; all commands are implemented by calling `prometeu_packer_core`.
|
||||||
|
|
||||||
|
### 13.1 `prometeu packer init`
|
||||||
|
|
||||||
|
Creates the control directory and initial registry:
|
||||||
|
|
||||||
|
* creates `assets/.prometeu/index.json`
|
||||||
|
* creates caches directory
|
||||||
|
|
||||||
|
### 13.2 `prometeu packer add <path> [--name <name>] [--type <TILES|SOUNDS|...>]`
|
||||||
|
|
||||||
|
Registers a new managed asset.
|
||||||
|
|
||||||
|
* does not require moving files
|
||||||
|
* can create an asset root directory if desired
|
||||||
|
* generates `asset.json` with explicit defaults
|
||||||
|
* allocates `asset_id` and `asset_uuid`
|
||||||
|
|
||||||
|
Variants:
|
||||||
|
|
||||||
|
* `add --dir` creates a dedicated asset root dir
|
||||||
|
* `add --in-place` anchors next to the file
|
||||||
|
|
||||||
|
### 13.3 `prometeu packer adopt`
|
||||||
|
|
||||||
|
Scans workspace for unregistered `asset.json` anchors and offers to register them.
|
||||||
|
|
||||||
|
* default: dry-run list
|
||||||
|
* `--apply` registers them
|
||||||
|
|
||||||
|
### 13.4 `prometeu packer forget <name|id|uuid>`
|
||||||
|
|
||||||
|
Removes an asset from the registry without deleting files.
|
||||||
|
|
||||||
|
Useful for WIP and cleanup.
|
||||||
|
|
||||||
|
### 13.5 `prometeu packer rm <name|id|uuid> [--delete]`
|
||||||
|
|
||||||
|
Removes the asset from the registry.
|
||||||
|
|
||||||
|
* default: no deletion
|
||||||
|
* `--delete` can remove the asset root dir (dangerous; must confirm in UI tooling, or require a force flag in CLI)
|
||||||
|
|
||||||
|
### 13.6 `prometeu packer list`
|
||||||
|
|
||||||
|
Lists managed assets:
|
||||||
|
|
||||||
|
* id, uuid, name
|
||||||
|
* type
|
||||||
|
* status (ok/error)
|
||||||
|
|
||||||
|
### 13.7 `prometeu packer show <name|id|uuid>`
|
||||||
|
|
||||||
|
Shows detailed information:
|
||||||
|
|
||||||
|
* resolved inputs
|
||||||
|
* metadata
|
||||||
|
* fingerprints
|
||||||
|
* last build summary
|
||||||
|
|
||||||
|
### 13.8 `prometeu packer doctor [--workspace] [--strict] [--fix]`
|
||||||
|
|
||||||
|
Runs diagnostics:
|
||||||
|
|
||||||
|
* `--workspace` deep scan
|
||||||
|
* `--strict` warnings become errors
|
||||||
|
* `--fix` applies safe automatic fixes (materialize defaults, normalize paths)
|
||||||
|
|
||||||
|
### 13.9 `prometeu packer build [--out build/assets.pa] [--table build/asset_table.json]`
|
||||||
|
|
||||||
|
Builds:
|
||||||
|
|
||||||
|
* `build/assets.pa`
|
||||||
|
* `build/asset_table.json`
|
||||||
|
|
||||||
|
Key behaviors:
|
||||||
|
|
||||||
|
* validates registry before packing
|
||||||
|
* packs assets deterministically
|
||||||
|
* for virtual assets, runs build pipelines
|
||||||
|
* records all offsets and metadata
|
||||||
|
|
||||||
|
### 13.10 `prometeu packer watch`
|
||||||
|
|
||||||
|
Watches registered inputs and registry changes.
|
||||||
|
|
||||||
|
* emits events (future)
|
||||||
|
* rebuilds incrementally
|
||||||
|
|
||||||
|
`watch` is optional in v0 but recommended.
|
||||||
|
|
||||||
|
### 13.11 `prometeu packer gc [--workspace] [--quarantine]`
|
||||||
|
|
||||||
|
Reports unused files.
|
||||||
|
|
||||||
|
* default: report only
|
||||||
|
* `--quarantine` moves candidates to quarantine
|
||||||
|
|
||||||
|
### 13.12 `prometeu packer quarantine <path> [--restore]`
|
||||||
|
|
||||||
|
Moves or restores files into/from quarantine.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Virtual Assets (Deep Explanation)
|
||||||
|
|
||||||
|
Virtual assets are a major capability.
|
||||||
|
|
||||||
|
### 14.1 Why Virtual Assets
|
||||||
|
|
||||||
|
* Most runtime formats should be derived from human-friendly authoring formats.
|
||||||
|
* Example:
|
||||||
|
|
||||||
|
* author uses `source.png` and palette files
|
||||||
|
* runtime expects indexed pixels + packed RGB565 palettes
|
||||||
|
|
||||||
|
### 14.2 Virtual Asset Contract
|
||||||
|
|
||||||
|
* Inputs are explicit.
|
||||||
|
* Build steps are deterministic.
|
||||||
|
* Outputs match a well-defined runtime payload format.
|
||||||
|
|
||||||
|
### 14.3 Examples of Future Virtual Assets
|
||||||
|
|
||||||
|
* `TILES/indexed_v1`: PNG + palette files → indexed pixels + packed palettes
|
||||||
|
* `SOUNDS/pcm16le_v1`: WAV → PCM16LE
|
||||||
|
* `SPRITESHEET/atlas_v1`: multiple PNG frames → atlas + metadata
|
||||||
|
|
||||||
|
Each `output.format` must have its own dedicated spec.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. Integration with Cartridge Builder
|
||||||
|
|
||||||
|
The Cartridge Builder should:
|
||||||
|
|
||||||
|
1. Compile PBS into bytecode (e.g. `program.pbc` / `program.pbx`)
|
||||||
|
2. Call `prometeu packer build`
|
||||||
|
3. Consume `build/asset_table.json` and produce `cartridge/manifest.json`
|
||||||
|
4. Copy artifacts into `cartridge/`
|
||||||
|
5. Zip the cartridge into a distributable format (`.crt` / `.rom` / `.pro`)
|
||||||
|
|
||||||
|
The packer never touches `cartridge/`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. Compatibility and Versioning
|
||||||
|
|
||||||
|
* `assets/.prometeu/index.json` has `schema_version`
|
||||||
|
* `asset.json` has `schema_version`
|
||||||
|
* `build/asset_table.json` has `schema_version`
|
||||||
|
|
||||||
|
The Packer must be able to migrate older schema versions or emit actionable diagnostics.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 17. Security and Trust Model
|
||||||
|
|
||||||
|
* The Packer is offline tooling.
|
||||||
|
* It must never execute untrusted scripts.
|
||||||
|
* It should treat external inputs as untrusted data.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 18. Implementation Notes (Non-Normative)
|
||||||
|
|
||||||
|
* Rust implementation with a core crate + CLI wrapper.
|
||||||
|
* Prefer structured JSON serde models.
|
||||||
|
* Use stable diagnostic codes.
|
||||||
|
* Keep the build deterministic.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Appendix A — Glossary
|
||||||
|
|
||||||
|
* **ROM (`assets.pa`)**: packed asset payload used by runtime
|
||||||
|
* **Descriptor (`asset_table.json`)**: mapping from logical assets to ROM slices
|
||||||
|
* **Managed asset**: registered asset with stable identity and anchor file
|
||||||
|
* **Virtual asset**: derived asset built from multiple inputs
|
||||||
|
* **Quarantine**: safe area for suspected junk
|
||||||
|
* **Doctor**: diagnostic command to keep sanity
|
||||||
Loading…
x
Reference in New Issue
Block a user