7.2 KiB
Globals Surface, Identity, and Module Boundaries Decision
Status: Accepted
Date: 2026-03-22
Related Agenda: docs/compiler/pbs/agendas/19.1. PBS Globals Surface, Identity, and Module Boundaries Agenda.md
Context
PBS v1 needed a precise language and linking policy for runtime globals before lifecycle, published entrypoint, and lowering work under topic 19 could proceed safely.
The open problem was not merely syntax. It also required closing:
- how globals differ from
declare const, - how globals participate in value visibility and import flow,
- how module ownership of storage is preserved,
- how dependency order between globals is modeled,
- and which diagnostics are mandatory when that model is violated.
Important fixed inputs already existed:
declare constis compile-time only and does not denote mutable module storage,- top-level executable statements remain forbidden,
- the VM already exposes global-slot access,
- and
mod.barrelremains the visibility authority for module exports.
Decision
PBS adopts the following policy for runtime globals in v1:
- PBS introduces a distinct top-level declaration form:
declare global Name: T = expr;
declare globalis not a variant ofdeclare const.declare constremains reserved to immutable values and must not be reused as runtime module storage.declare globalrequires:- an explicit type annotation,
- and an explicit initializer in all ordinary v1 cases.
- Globals participate in the value namespace, but remain a distinct declaration category.
mod.barrelmust expose globals through explicitglobalentries:mod global Name;pub global Name;
- PBS does not introduce global re-export in this line of work.
- Import of a global preserves the storage identity of the original owner module.
- Imports may use aliasing when needed, but aliasing changes only the local visible name, never the canonical storage owner.
- Shadowing between visible
fn,service,global, andconstnames is a compile-time error and must be resolved with aliasing. - Lookup precedence remains:
- locals,
- then struct/class members,
- then globals, including globals introduced by import.
- Global dependency order is defined by a deterministic dependency graph:
- every read of another global in a global initializer creates a dependency edge,
- imports and aliases preserve canonical owner identity in that graph,
- source-file order must not affect dependency resolution.
- Cycles between globals are compile-time errors.
- Modules and globals must share the same structural dependency-analysis kernel through a refactor of the existing module dependency algorithm into:
DependencyGraphAnaliser- located in infra
util.structures.
- What is shared is the structural graph analysis kernel:
- topological ordering,
- cycle detection,
- deterministic traversal support.
- What is not shared is domain semantics:
- each domain remains responsible for constructing canonical nodes, edges, and diagnostics.
Global Initializer Policy
PBS v1 adopts the following initializer policy for declare global:
- The initializer exists only to materialize the initial module storage value.
- Admissible forms in v1 include:
- primitive literals and simple value operations,
- value-bearing member access at this stage,
new Struct(...),- and reads of other globals compatible with the dependency graph.
- Top-level
fncalls are not permitted in a global initializer in v1. some(...)andnoneare not permitted in a global initializer in v1.ifandswitchare not permitted directly in the declaration initializer in v1.- Richer procedural setup belongs to later lifecycle stages rather than the declaration initializer itself.
Rationale
This decision intentionally keeps global declarations narrow and explicit.
That choice:
- prevents semantic collapse between immutable constants and mutable runtime storage,
- keeps import and visibility rules legible,
- makes dependency analysis deterministic and explainable,
- avoids hiding lifecycle logic inside declaration expressions,
- and prepares a clean handoff to later topic
19work on module init, project init, and published entrypoint behavior.
The decision also rejects implicit complexity:
- no silent reuse of
const, - no global re-export,
- no ambiguous cross-category name merging,
- and no procedural
fn-driven initialization insidedeclare global.
Mandatory Diagnostics
PBS must provide, at minimum, the following diagnostics for this policy:
global initializer uses unsupported form- emitted at the invalid subexpression inside a global initializer.
global dependency cycle detected- emitted on the local participating global,
- and should include the canonical cycle path when available.
imported symbol shadows existing visible symbol; alias required- emitted when an imported
fn,service,global, orconstcollides with an already-visible symbol of those categories.
- emitted when an imported
global import must resolve through a global barrel entry- emitted when import resolution would otherwise degrade a global into another category.
Invariants
declare constanddeclare globalremain semantically distinct.- Runtime storage ownership remains attached to the canonical owner module.
- Global dependency order is semantic, not textual.
- Alias spelling must not change canonical global identity.
- The dependency-analysis kernel may be shared structurally, but semantic graph construction remains domain-owned.
Explicit Non-Decisions
- This decision does not define module init or project init behavior.
- This decision does not define
[INIT],[FRAME], or lifecycle markers. - This decision does not define published entrypoint or
FRAME_RETownership. - This decision does not define the final IR representation of globals.
- This decision does not define host-call admissibility during lifecycle hooks.
Spec Impact
This decision should feed at least:
docs/compiler/pbs/specs/3. Core Syntax Specification.mddocs/compiler/pbs/specs/4. Static Semantics Specification.mddocs/compiler/pbs/specs/11. AST Specification.mddocs/compiler/pbs/specs/12. Diagnostics Specification.md
It also constrains future topic 19 work in:
docs/compiler/pbs/agendas/19.2. PBS Lifecycle Markers, Program Init, and Frame Root Semantics Agenda.mddocs/compiler/pbs/agendas/19.3. Published Entrypoint, Synthetic Wrapper, and FRAME_RET Ownership Agenda.mddocs/compiler/pbs/agendas/19.4. Globals and Lifecycle Lowering to IRBackend/IRVM Agenda.md
Validation Notes
At minimum, validation for this decision should include:
- accepted fixtures for
declare globalwith primitive, member-value,new, and dependent-global initializers; - rejection fixtures for forbidden initializer forms such as top-level
fn,some(...),if, andswitch; - import fixtures proving alias-based disambiguation;
- negative fixtures for cross-category collisions;
- dependency fixtures proving deterministic ordering independent of source-file order;
- cycle fixtures proving deterministic detection and diagnostics for intra-module and inter-module cycles.