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/14. Name Resolution and Module Linking Specification.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/15. Bytecode and PBX Mapping Specification.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
|
||||
|
||||
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
|
||||
|
||||
## 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
|
||||
|
||||
This document is intended to define:
|
||||
This document defines:
|
||||
|
||||
- scope formation and lookup order,
|
||||
- namespace separation and collision rules,
|
||||
- import and export resolution,
|
||||
- barrel-mediated visibility and module linking,
|
||||
- cross-file and cross-module symbol resolution,
|
||||
- resolution of reserved stdlib shells for host-backed and VM-owned surfaces,
|
||||
- linkage-time rejection conditions before lowering.
|
||||
- module scope construction,
|
||||
- lookup order by namespace,
|
||||
- import and alias resolution,
|
||||
- barrel-mediated visibility,
|
||||
- reserved stdlib shell resolution for builtin and host-backed surfaces,
|
||||
- callable visibility across modules,
|
||||
- and linking-time rejection conditions before lowering.
|
||||
|
||||
This document does not define:
|
||||
|
||||
- runtime execution behavior,
|
||||
- optimizer or backend internals,
|
||||
- full PBX binary linking.
|
||||
- full PBX binary linking,
|
||||
- or loader/runtime artifact behavior after lowering.
|
||||
|
||||
## 3. Authority and Precedence
|
||||
|
||||
@ -34,8 +42,10 @@ Normative precedence:
|
||||
3. `5. Manifest, Stdlib, and SDK Resolution Specification.md`
|
||||
4. `6. VM-owned vs Host-backed.md`
|
||||
5. `6.1. Intrinsics and Builtin Types Specification.md`
|
||||
6. `18. Standard Library Surface Specification.md`
|
||||
7. This document
|
||||
6. `6.2. Host ABI Binding and Loader Resolution Specification.md`
|
||||
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.
|
||||
|
||||
@ -48,6 +58,7 @@ This document depends on, at minimum:
|
||||
- `5. Manifest, Stdlib, and SDK Resolution Specification.md`
|
||||
- `6. VM-owned vs Host-backed.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`
|
||||
- `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.
|
||||
- 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 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,
|
||||
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.
|
||||
For one module:
|
||||
|
||||
## 7. TODO
|
||||
|
||||
The following items remain to be closed in future agenda discussion.
|
||||
|
||||
- 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.
|
||||
- the compiler collects top-level declarations from all `.pbs` files in that module,
|
||||
- forms one module-level declaration space,
|
||||
- and only then applies `mod.barrel` to determine `mod` and `pub` visibility.
|
||||
|
||||
Rules:
|
||||
|
||||
- `declare host` surfaces exported from reserved stdlib modules resolve in the host-owner namespace.
|
||||
- `declare builtin type` surfaces exported from reserved stdlib modules resolve in the type namespace.
|
||||
- builtin constants exported from reserved stdlib modules resolve in the value namespace.
|
||||
- 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.
|
||||
- declaration existence is not derived from barrel entries,
|
||||
- module-internal top-level declaration availability does not depend on source-file order,
|
||||
- and `mod.barrel` is a visibility filter over existing declarations rather than a declaration source.
|
||||
|
||||
Example:
|
||||
### 6.2 Lexical scope
|
||||
|
||||
- importing `Vec2` from `@core:math` may introduce the PBS-visible type name `Vec2`,
|
||||
- while the declaration's metadata may still lower that type to canonical builtin identity `("vec2", 1)`.
|
||||
Inside executable bodies:
|
||||
|
||||
## 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
|
||||
special metadata rules at lowering time.
|
||||
## 7. Lookup Order By Namespace
|
||||
|
||||
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:
|
||||
|
||||
- a builtin type, builtin constant, or host owner may be imported only if it is exported through the target module's `mod.barrel`,
|
||||
- aliasing an imported builtin type changes only the source-visible local name, not the canonical builtin identity declared by metadata,
|
||||
- aliasing an imported builtin constant changes only the local binding name, not the canonical builtin constant identity declared by metadata,
|
||||
- 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.
|
||||
- `import { X } from @project:path;` introduces the visible local name `X`,
|
||||
- `import { X as Y } from @project:path;` introduces only the visible local name `Y`,
|
||||
- and alias spelling changes only the local visible name, never canonical builtin identity or canonical host identity.
|
||||
|
||||
## 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:
|
||||
|
||||
- type-position lookup considers visible builtin type declarations alongside other visible type declarations,
|
||||
- value-position lookup considers visible builtin constants alongside other visible values,
|
||||
- callable lookup does not treat builtin intrinsic members as top-level callable declarations,
|
||||
- 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.
|
||||
- the imported callable set for `f` consists only of those overload signatures of `f` that are exported through that module's `mod.barrel`,
|
||||
- non-exported overloads in the target module do not participate in the imported callable set,
|
||||
- and import operates on the exported callable name rather than on one overload at a time.
|
||||
|
||||
This preserves the distinction between:
|
||||
### 10.2 Same-origin versus different-origin callable imports
|
||||
|
||||
- imported builtin type shells,
|
||||
- imported host owners,
|
||||
- and compiler-recognized intrinsic method surfaces on existing core forms such as `optional` and enums.
|
||||
If the same visible callable name is imported more than once:
|
||||
|
||||
- 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
|
||||
|
||||
@ -162,8 +376,30 @@ At minimum, a conforming implementation must reject:
|
||||
1. import of a non-exported builtin type shell,
|
||||
2. import of a non-exported builtin constant shell,
|
||||
3. import of a non-exported host owner shell,
|
||||
4. ambiguous cross-module visibility of builtin declarations after barrel resolution,
|
||||
5. duplicate visible builtin type declarations that claim the same canonical builtin identity in one resolved environment,
|
||||
6. duplicate visible builtin constants that claim the same canonical builtin constant identity in one resolved environment,
|
||||
7. member lookup on a builtin receiver where the imported builtin shell does not declare the requested field or intrinsic member,
|
||||
8. any resolution path that attempts to derive host or builtin canonical identity from alias spelling alone.
|
||||
4. import of any non-exported name from a resolved module,
|
||||
5. unresolved barrel entries against module declarations,
|
||||
6. local-versus-import same-namespace collisions,
|
||||
7. imported same-name collisions from different canonical origins,
|
||||
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