15 KiB
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:
3. Core Syntax Specification.md4. Static Semantics Specification.md5. Manifest, Stdlib, and SDK Resolution Specification.md6. VM-owned vs Host-backed.md6.1. Intrinsics and Builtin Types Specification.md6.2. Host ABI Binding and Loader Resolution Specification.md8. Stdlib Environment Packaging and Loading Specification.md18. Standard Library Surface Specification.md- 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.md4. Static Semantics Specification.md5. Manifest, Stdlib, and SDK Resolution Specification.md6. VM-owned vs Host-backed.md6.1. Intrinsics and Builtin Types Specification.md6.2. Host ABI Binding and Loader Resolution Specification.md8. Stdlib Environment Packaging and Loading Specification.md18. 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.barrelis the single source of module visibility.- Imports target modules, not files.
- Reserved stdlib project spaces are resolved only from the selected stdlib environment.
- Only
pubsymbols may be imported from another module. - 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. Module Scope Construction
6.1 Module collection
For one module:
- the compiler collects top-level declarations from all
.pbsfiles in that module, - forms one module-level declaration space,
- and only then applies
mod.barrelto determinemodandpubvisibility.
Rules:
- declaration existence is not derived from barrel entries,
- module-internal top-level declaration availability does not depend on source-file order,
- and
mod.barrelis 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:
- nearest local lexical bindings,
- parameters in the current lexical function scope,
- visible module-local values,
- 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:
- visible module-local type declarations,
- visible imported type declarations,
- builtin simple types
int,float,bool, andstras 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:
- visible module-local callables,
- 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:
- visible module-local host owners,
- 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 nameX,import { X as Y } from @project:path;introduces only the visible local nameY,- 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
Xis exported and importable from that module, - and introduces
Xinto 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
fconsists only of those overload signatures offthat are exported through that module'smod.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
Vec2orColor.
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:
syntaxmanifest/import resolutionlinkingstatic 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:
- 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,
- duplicate canonical builtin identities in one resolved environment,
- duplicate canonical host identities in one resolved environment.
12.5 Static semantics
static semantics owns:
- duplicate local declarations,
- declaration validity,
- type checking,
- member lookup once the receiver type is known,
- 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:
- import of a non-exported builtin type shell,
- import of a non-exported builtin constant shell,
- import of a non-exported host owner shell,
- import of any non-exported name from a resolved module,
- unresolved barrel entries against module declarations,
- local-versus-import same-namespace collisions,
- imported same-name collisions from different canonical origins,
- local-versus-import function-name collisions,
- duplicate visible builtin type declarations that claim the same canonical builtin identity in one resolved environment,
- duplicate visible builtin constants that claim the same canonical builtin constant identity in one resolved environment,
- duplicate visible host owners that claim the same canonical host identity in one resolved environment,
- member lookup on a builtin receiver where the imported builtin shell does not declare the requested field or intrinsic member,
- 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:
- name lookup is deterministic across all relevant scopes,
- barrel and module linking behavior is explicit,
- reserved stdlib shell resolution is explicit for host-backed and VM-owned surfaces,
- cross-module callable visibility and ambiguity behavior are normatively defined,
- and the phase ownership of source-facing name-resolution failures is explicit enough for diagnostics and conformance.