From 4b446749077cf4846599fe5b541035e1f4627414 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Tue, 3 Mar 2026 16:11:39 +0000 Subject: [PATCH] integrated 14 into specs --- ...1 - Diagnostic Identity and Attribution.md | 2 - ...hop 1 - Lowering Contract and IR Status.md | 2 - ...me Resolution and Module Linking Agenda.md | 117 ---- ...Workshop 1 - Scope, Lookup, and Imports.md | 184 ------ ...shop 2 - Builtin Shells and Host Owners.md | 159 ----- ... Callable Sets and Cross-Module Linking.md | 160 ----- ...ion Workshop 4 - Linking Phase Boundary.md | 144 ----- ...Builtin Shells and Host Owners Decision.md | 168 ++++++ ... Sets and Cross-Module Linking Decision.md | 173 ++++++ ...ution - Linking Phase Boundary Decision.md | 200 +++++++ ...n - Scope, Lookup, and Imports Decision.md | 270 +++++++++ ...lution and Module Linking Specification.md | 412 ++++++++++--- docs/roadmaps/lsp/LSP - base PRs.md | 537 +++++++++++++++++ docs/roadmaps/lsp/LSP - roadmap remain.md | 219 +++++++ docs/roadmaps/packer/.gitkeep | 0 docs/roadmaps/packer/Prometeu Packer.md | 564 ++++++++++++++++++ 16 files changed, 2455 insertions(+), 856 deletions(-) delete mode 100644 docs/pbs/agendas/14. Name Resolution and Module Linking Agenda.md delete mode 100644 docs/pbs/agendas/14.1. Name Resolution Workshop 1 - Scope, Lookup, and Imports.md delete mode 100644 docs/pbs/agendas/14.2. Name Resolution Workshop 2 - Builtin Shells and Host Owners.md delete mode 100644 docs/pbs/agendas/14.3. Name Resolution Workshop 3 - Callable Sets and Cross-Module Linking.md delete mode 100644 docs/pbs/agendas/14.4. Name Resolution Workshop 4 - Linking Phase Boundary.md create mode 100644 docs/pbs/decisions/Name Resolution - Builtin Shells and Host Owners Decision.md create mode 100644 docs/pbs/decisions/Name Resolution - Callable Sets and Cross-Module Linking Decision.md create mode 100644 docs/pbs/decisions/Name Resolution - Linking Phase Boundary Decision.md create mode 100644 docs/pbs/decisions/Name Resolution - Scope, Lookup, and Imports Decision.md create mode 100644 docs/roadmaps/lsp/LSP - base PRs.md create mode 100644 docs/roadmaps/lsp/LSP - roadmap remain.md create mode 100644 docs/roadmaps/packer/.gitkeep create mode 100644 docs/roadmaps/packer/Prometeu Packer.md diff --git a/docs/pbs/agendas/11.1. Diagnostics Workshop 1 - Diagnostic Identity and Attribution.md b/docs/pbs/agendas/11.1. Diagnostics Workshop 1 - Diagnostic Identity and Attribution.md index f43a2238..db6b68cc 100644 --- a/docs/pbs/agendas/11.1. Diagnostics Workshop 1 - Diagnostic Identity and Attribution.md +++ b/docs/pbs/agendas/11.1. Diagnostics Workshop 1 - Diagnostic Identity and Attribution.md @@ -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` - diff --git a/docs/pbs/agendas/12.1. IR and Lowering Workshop 1 - Lowering Contract and IR Status.md b/docs/pbs/agendas/12.1. IR and Lowering Workshop 1 - Lowering Contract and IR Status.md index 80a9f2a9..8194f2d6 100644 --- a/docs/pbs/agendas/12.1. IR and Lowering Workshop 1 - Lowering Contract and IR Status.md +++ b/docs/pbs/agendas/12.1. IR and Lowering Workshop 1 - Lowering Contract and IR Status.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` - diff --git a/docs/pbs/agendas/14. Name Resolution and Module Linking Agenda.md b/docs/pbs/agendas/14. Name Resolution and Module Linking Agenda.md deleted file mode 100644 index 0595d72d..00000000 --- a/docs/pbs/agendas/14. Name Resolution and Module Linking Agenda.md +++ /dev/null @@ -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` diff --git a/docs/pbs/agendas/14.1. Name Resolution Workshop 1 - Scope, Lookup, and Imports.md b/docs/pbs/agendas/14.1. Name Resolution Workshop 1 - Scope, Lookup, and Imports.md deleted file mode 100644 index 4ae65e41..00000000 --- a/docs/pbs/agendas/14.1. Name Resolution Workshop 1 - Scope, Lookup, and Imports.md +++ /dev/null @@ -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` diff --git a/docs/pbs/agendas/14.2. Name Resolution Workshop 2 - Builtin Shells and Host Owners.md b/docs/pbs/agendas/14.2. Name Resolution Workshop 2 - Builtin Shells and Host Owners.md deleted file mode 100644 index 52c0ca95..00000000 --- a/docs/pbs/agendas/14.2. Name Resolution Workshop 2 - Builtin Shells and Host Owners.md +++ /dev/null @@ -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` diff --git a/docs/pbs/agendas/14.3. Name Resolution Workshop 3 - Callable Sets and Cross-Module Linking.md b/docs/pbs/agendas/14.3. Name Resolution Workshop 3 - Callable Sets and Cross-Module Linking.md deleted file mode 100644 index bcaf8082..00000000 --- a/docs/pbs/agendas/14.3. Name Resolution Workshop 3 - Callable Sets and Cross-Module Linking.md +++ /dev/null @@ -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` - diff --git a/docs/pbs/agendas/14.4. Name Resolution Workshop 4 - Linking Phase Boundary.md b/docs/pbs/agendas/14.4. Name Resolution Workshop 4 - Linking Phase Boundary.md deleted file mode 100644 index 1e06025b..00000000 --- a/docs/pbs/agendas/14.4. Name Resolution Workshop 4 - Linking Phase Boundary.md +++ /dev/null @@ -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` diff --git a/docs/pbs/decisions/Name Resolution - Builtin Shells and Host Owners Decision.md b/docs/pbs/decisions/Name Resolution - Builtin Shells and Host Owners Decision.md new file mode 100644 index 00000000..d2467e06 --- /dev/null +++ b/docs/pbs/decisions/Name Resolution - Builtin Shells and Host Owners Decision.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. diff --git a/docs/pbs/decisions/Name Resolution - Callable Sets and Cross-Module Linking Decision.md b/docs/pbs/decisions/Name Resolution - Callable Sets and Cross-Module Linking Decision.md new file mode 100644 index 00000000..b0feae3a --- /dev/null +++ b/docs/pbs/decisions/Name Resolution - Callable Sets and Cross-Module Linking Decision.md @@ -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. diff --git a/docs/pbs/decisions/Name Resolution - Linking Phase Boundary Decision.md b/docs/pbs/decisions/Name Resolution - Linking Phase Boundary Decision.md new file mode 100644 index 00000000..b7e061a2 --- /dev/null +++ b/docs/pbs/decisions/Name Resolution - Linking Phase Boundary Decision.md @@ -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`. diff --git a/docs/pbs/decisions/Name Resolution - Scope, Lookup, and Imports Decision.md b/docs/pbs/decisions/Name Resolution - Scope, Lookup, and Imports Decision.md new file mode 100644 index 00000000..8b23a9ce --- /dev/null +++ b/docs/pbs/decisions/Name Resolution - Scope, Lookup, and Imports Decision.md @@ -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`. diff --git a/docs/pbs/specs/14. Name Resolution and Module Linking Specification.md b/docs/pbs/specs/14. Name Resolution and Module Linking Specification.md index 7adc9a40..9809b87d 100644 --- a/docs/pbs/specs/14. Name Resolution and Module Linking Specification.md +++ b/docs/pbs/specs/14. Name Resolution and Module Linking Specification.md @@ -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. diff --git a/docs/roadmaps/lsp/LSP - base PRs.md b/docs/roadmaps/lsp/LSP - base PRs.md new file mode 100644 index 00000000..e3f52eb9 --- /dev/null +++ b/docs/roadmaps/lsp/LSP - base PRs.md @@ -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, + pub last_good: Option, +} + +pub struct AnalysisSnapshot { + pub diagnostics: Vec, + 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` + +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` +* `fn symbols_in_module(project: ProjectId, module: ModuleId) -> Vec` +* `fn workspace_symbols() -> impl Iterator` + +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 diff --git a/docs/roadmaps/lsp/LSP - roadmap remain.md b/docs/roadmaps/lsp/LSP - roadmap remain.md new file mode 100644 index 00000000..f502768e --- /dev/null +++ b/docs/roadmaps/lsp/LSP - roadmap remain.md @@ -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. diff --git a/docs/roadmaps/packer/.gitkeep b/docs/roadmaps/packer/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/docs/roadmaps/packer/Prometeu Packer.md b/docs/roadmaps/packer/Prometeu Packer.md new file mode 100644 index 00000000..294e1cfd --- /dev/null +++ b/docs/roadmaps/packer/Prometeu Packer.md @@ -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 [--name ] [--type ]` + +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 ` + +Removes an asset from the registry without deleting files. + +Useful for WIP and cleanup. + +### 13.5 `prometeu packer rm [--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 ` + +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 [--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