195 lines
7.6 KiB
Markdown
195 lines
7.6 KiB
Markdown
# Lifecycle Markers, Program Init, and Frame Root Semantics Decision
|
|
|
|
Status: Accepted
|
|
Date: 2026-03-22
|
|
Related Agenda: `docs/compiler/pbs/agendas/19.2. PBS Lifecycle Markers, Program Init, and Frame Root Semantics Agenda.md`
|
|
|
|
## Context
|
|
|
|
After closing the globals surface and module-boundary model, PBS v1 needed a precise lifecycle policy for:
|
|
|
|
- module bootstrap,
|
|
- project bootstrap,
|
|
- frame-root identification,
|
|
- host-call admissibility during init,
|
|
- and failure behavior at boot.
|
|
|
|
The key requirement was to keep initialization expressive enough for real setup work, while preserving deterministic semantics and preventing the declaration initializer of `global` from becoming a general procedural execution surface.
|
|
|
|
Important fixed inputs already existed:
|
|
|
|
- `declare global` lowers into module-owned runtime storage,
|
|
- global initializers remain declarative and restricted,
|
|
- top-level `fn` calls are not allowed inside `declare global` initializers,
|
|
- `FRAME_SYNC` remains the only normative safepoint,
|
|
- and PBS v1 remains deterministic and single-threaded.
|
|
|
|
## Decision
|
|
|
|
PBS adopts the following lifecycle-marker and bootstrap policy in v1:
|
|
|
|
1. PBS introduces the reserved userland attributes:
|
|
- `[Init]`
|
|
- `[Frame]`
|
|
2. `[Frame]` is mandatory for an executable project.
|
|
3. `[Frame]` is unique per executable project.
|
|
4. `[Init]` is optional.
|
|
5. `[Init]` may appear in ordinary userland source both for module-local bootstrap and for project bootstrap.
|
|
6. Both `[Init]` and `[Frame]` require the same signature shape:
|
|
- `fn name() -> void`
|
|
7. Recommended canonical source forms are:
|
|
|
|
```pbs
|
|
[Init]
|
|
fn init() -> void { ... }
|
|
```
|
|
|
|
```pbs
|
|
[Frame]
|
|
fn frame() -> void { ... }
|
|
```
|
|
|
|
## Module Init and Project Init Policy
|
|
|
|
PBS adopts the following bootstrap layering:
|
|
|
|
1. Every module has exactly one synthetic module init.
|
|
2. `declare global` always lowers into that synthetic module init.
|
|
3. Each file may declare at most one `[Init]`.
|
|
4. A file may declare `[Init]` even when it declares no globals.
|
|
5. For one file:
|
|
- that file's globals are materialized first,
|
|
- then that file's `[Init]`, if present, executes.
|
|
6. File-level init fragments are then composed into the one synthetic module init.
|
|
7. The `[Init]` colocated in the same file as `[Frame]` is the project init.
|
|
8. `[Init]` in other files remains module init.
|
|
9. The distinction between module init and project init is one of ordering and semantic role, not one of different source syntax.
|
|
10. There is therefore at most one project init per executable project.
|
|
11. Project init uses the same signature shape:
|
|
- `fn init() -> void`
|
|
|
|
## Bootstrap Order
|
|
|
|
PBS adopts the following logical bootstrap order:
|
|
|
|
1. materialize globals for each file according to dependency order,
|
|
2. execute that file's `[Init]`, if present,
|
|
3. compose those steps into one synthetic module init,
|
|
4. after required module work is complete, execute project init, if present,
|
|
5. then enter the first `frame()`.
|
|
|
|
Within one module:
|
|
|
|
1. file ordering must follow the order derived from the globals `DependencyGraphAnaliser`,
|
|
2. and when two files have no dependency edge between them, the compiler must still use a deterministic stable order.
|
|
|
|
## Host Interaction During Init
|
|
|
|
PBS adopts the following init-time host-call policy:
|
|
|
|
1. Loops are permitted in module init and project init.
|
|
2. Host calls are forbidden by default during init.
|
|
3. The only host calls admissible during init are host methods marked with `[InitAllowed]`.
|
|
4. `[InitAllowed]` is a reserved SDK attribute, not a userland attribute.
|
|
5. `[InitAllowed]` has no arguments in v1.
|
|
6. `[InitAllowed]` is valid only on SDK host methods.
|
|
7. `[InitAllowed]` is invalid on userland `fn`, `global`, `service`, or any non-host-method surface.
|
|
8. `[InitAllowed]` does not distinguish module init from project init.
|
|
9. The difference between those phases is ordering and semantic role, not a separate host-permission class.
|
|
10. Admissibility of `[InitAllowed]` must be checked at compile time.
|
|
|
|
Illustrative shape:
|
|
|
|
```pbs
|
|
declare host Runtime {
|
|
[Host(module = "runtime", name = "random_u32", version = 1)]
|
|
[Capability(name = "runtime")]
|
|
[InitAllowed]
|
|
fn random_u32() -> int;
|
|
}
|
|
```
|
|
|
|
## Failure Policy
|
|
|
|
PBS adopts a fail-fast boot policy:
|
|
|
|
1. Failure during module init aborts boot.
|
|
2. Failure during project init aborts boot.
|
|
3. No automatic retry occurs on the next frame.
|
|
4. Implementations must surface a coherent failure message identifying the failing boot phase.
|
|
|
|
## Rationale
|
|
|
|
This decision keeps the declaration initializer of `global` simple, while still allowing practical bootstrap work in later lifecycle stages.
|
|
|
|
That balance matters because:
|
|
|
|
- arrays and derived data often require loops during setup,
|
|
- module bootstrap must remain expressive enough for real projects,
|
|
- host interaction during boot must still be tightly controlled,
|
|
- and the runtime model must remain deterministic and explainable.
|
|
|
|
Colocation of project init with frame was chosen deliberately as a source-organization rule.
|
|
It avoids introducing a second project-level marker while keeping project bootstrap discoverable in source.
|
|
|
|
## Mandatory Diagnostics
|
|
|
|
PBS must provide, at minimum, the following diagnostics for this policy:
|
|
|
|
1. `project init must be colocated with frame`
|
|
2. `multiple project init functions detected`
|
|
3. `multiple frame functions detected`
|
|
4. `missing frame function for executable project`
|
|
5. `init function must have signature fn name() -> void`
|
|
6. `frame function must have signature fn name() -> void`
|
|
7. `host call not allowed during init`
|
|
8. `InitAllowed is valid only on SDK host methods`
|
|
9. `boot failed during module init`
|
|
10. `boot failed during project init`
|
|
11. `multiple module init functions detected`
|
|
12. `Init attribute target invalid`
|
|
|
|
## Invariants
|
|
|
|
1. `[Frame]` remains unique per executable project.
|
|
2. Project init is identified by colocation with `[Frame]`.
|
|
3. Each file contributes at most one init fragment.
|
|
4. Each module contributes exactly one synthetic module init.
|
|
5. Host-call admissibility during init is explicit and compile-time validated.
|
|
6. Boot ordering remains deterministic.
|
|
|
|
## Explicit Non-Decisions
|
|
|
|
1. This decision does not define the published synthetic wrapper entrypoint.
|
|
2. This decision does not define `FRAME_RET` ownership.
|
|
3. This decision does not define the final IR encoding of bootstrap.
|
|
4. This decision does not define cartridge manifest publication details.
|
|
|
|
## Spec Impact
|
|
|
|
This decision should feed at least:
|
|
|
|
1. `docs/compiler/pbs/specs/3. Core Syntax Specification.md`
|
|
2. `docs/compiler/pbs/specs/4. Static Semantics Specification.md`
|
|
3. `docs/compiler/pbs/specs/9. Dynamic Semantics Specification.md`
|
|
4. `docs/compiler/pbs/specs/12. Diagnostics Specification.md`
|
|
|
|
It also constrains future topic `19` work in:
|
|
|
|
1. `docs/compiler/pbs/agendas/19.3. Published Entrypoint, Synthetic Wrapper, and FRAME_RET Ownership Agenda.md`
|
|
2. `docs/compiler/pbs/agendas/19.4. Globals and Lifecycle Lowering to IRBackend/IRVM Agenda.md`
|
|
3. `docs/compiler/pbs/agendas/19.5. Diagnostics, Manifest Propagation, and Conformance Coverage Agenda.md`
|
|
|
|
## Validation Notes
|
|
|
|
At minimum, validation for this decision should include:
|
|
|
|
1. accepted fixtures for `[Init]` and `[Frame]` with correct signatures;
|
|
2. accepted fixtures for file-level `[Init]` without globals;
|
|
3. accepted fixtures for loops inside module init and project init;
|
|
4. rejection fixtures for invalid marker targets and invalid signatures;
|
|
5. rejection fixtures for illegal host calls during init;
|
|
6. accepted fixtures for SDK host calls marked `[InitAllowed]`;
|
|
7. failure fixtures that distinguish module-init boot failure from project-init boot failure;
|
|
8. ordering fixtures proving deterministic file ordering within one module.
|