# Name Resolution - Builtin Shells and Host Owners Decision Status: Accepted (Implemented) 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 `13. Lowering IRBackend Specification.md`. ## 11. Spec Impact This decision should feed at least: - `docs/general/specs/14. Name Resolution and Module Linking Specification.md` - `docs/general/specs/18. Standard Library Surface Specification.md` - `docs/pbs/specs/12. Diagnostics Specification.md` It should also constrain future work in: - `docs/pbs/specs/13. Lowering IRBackend 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.