# PBS Name Resolution and Module Linking Specification 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 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 defines: - 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, - or loader/runtime artifact behavior after lowering. ## 3. Authority and Precedence Normative precedence: 1. `3. Core Syntax Specification.md` 2. `4. Static Semantics Specification.md` 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. `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. ## 4. Normative Inputs This document depends on, at minimum: - `3. Core Syntax Specification.md` - `4. Static Semantics Specification.md` - `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` ## 5. Already-Settled Inputs The following inputs are already fixed elsewhere and must not be contradicted here: - PBS has distinct type, value, callable, and host-owner namespaces. - `mod.barrel` is the single source of module visibility. - Imports target modules, not files. - Reserved stdlib project spaces are resolved only from the selected stdlib environment. - Only `pub` symbols may be imported from another module. - Reserved stdlib/interface modules may expose compile-time-only shells for both host-backed and VM-owned surfaces, and may expose service facades over non-public host owners. - 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. Module Scope Construction ### 6.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, - and only then applies `mod.barrel` to determine `mod` and `pub` visibility. Rules: - 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. ### 6.2 Lexical scope Inside executable bodies: - lexical block scope nests normally over module scope, - nearest local bindings win within value lookup, - and lexical nesting remains independent from cross-module visibility. ## 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: - `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. ### 8.2 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. ### 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: - 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. ### 10.2 Same-origin versus different-origin callable imports 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 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. 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.