diff --git a/files/Borrow Mutate - Compiler GC.md b/files/Borrow Mutate - Compiler GC.md new file mode 100644 index 00000000..aa5ec3bc --- /dev/null +++ b/files/Borrow Mutate - Compiler GC.md @@ -0,0 +1,198 @@ +Mini Spec — Borrow/Mutate Views on a GC Heap (Prometeu) + +This document defines an optional “Rust-flavored” access discipline for heap objects without using RC/HIP. It is compatible with a GC-based heap, first-class functions (closures), and deterministic coroutines. + +Goals + +Preserve the feel of borrow (shared read) and mutate (exclusive write). + +Prevent common aliasing bugs in user code. + +Keep the runtime simple and GC-friendly. + +Avoid restrictions that would block closures/coroutines. + +Non-goals + +Not a full Rust borrow checker. + +Not a memory management mechanism (GC owns lifetimes). + +Not a general concurrency/locking system. + +1. Concepts + 1.1 Heap handle + +A heap object is referenced by a handle. Handles can be stored, copied, captured, and returned freely (GC-managed). + +1.2 Views + +A view is a temporary capability derived from a handle: + +BorrowView: shared read-only view + +MutView: exclusive mutable view + +Views are stack-only and lexically scoped. + +2. Core Rules (Language Semantics) + 2.1 Creating views + +Views can be created only by dedicated constructs: + +borrow(h) { ... } produces a BorrowView for the duration of the block + +mutate(h) { ... } produces a MutView for the duration of the block + +2.2 View locality + +Views must never escape their lexical scope: + +A view cannot be: + +stored into heap objects + +captured by closures + +returned from functions + +yielded across coroutine suspension points + +stored into globals + +(Think: “views are ephemeral; handles are persistent.”) + +2.3 Access permissions + +Within a view scope: + +BorrowView permits reads only + +MutView permits reads and writes + +Outside a view scope: + +direct field access is not permitted; you must open a view scope again. + +2.4 Exclusivity + +At any program point, for a given handle h: + +Multiple BorrowView(h) may coexist. + +Exactly one MutView(h) may exist, and it excludes all borrows. + +This is enforced at compile time (required) and optionally at runtime in debug mode (see §5). + +2.5 Re-entrancy + +Within a mutate(h) scope: + +nested borrow(h) is allowed (it is trivially compatible) + +nested mutate(h) is disallowed + +Within a borrow(h) scope: + +nested mutate(h) is disallowed + +3. Interaction with Closures and Coroutines + 3.1 Closures + +Closures may capture handles freely. + +Closures must not capture views. + +If code inside borrow/mutate creates a closure, it may capture the handle, but not the view. + +3.2 Coroutines + +Coroutines may store handles in their stacks/locals. + +Views must not live across suspension: + +A coroutine cannot yield while a view is active. + +A coroutine cannot sleep while a view is active. + +This rule guarantees that view lifetimes remain local and deterministic. + +4. Compiler Requirements (Static Enforcement) + +The compiler enforces this feature by tracking view lifetimes as lexical regions. + +Minimum checks: + +A view cannot be assigned to a variable with heap lifetime. + +A view cannot appear in a closure capture set. + +A view cannot be returned. + +A view cannot be live at FRAME_SYNC, YIELD, SLEEP, or any safe-point boundary defined by the language. + +Exclusivity rules per handle region (borrow vs mutate overlaps) must hold. + +If a rule is violated: compile error (fatal). + +5. Optional Runtime Checking (Debug Mode) + +Runtime checking is optional and intended for debugging or untrusted bytecode scenarios. + +5.1 Object header borrow-state + +Each heap object may maintain: + +readers: u16 + +writer: bool + +Runtime actions: + +entering borrow increments readers if no writer + +leaving borrow decrements readers + +entering mutate requires readers==0 and writer==false, then sets writer=true + +leaving mutate sets writer=false + +Violations trap with a specific diagnostic. + +5.2 Build profiles + +Release builds may omit this entirely (zero overhead). + +Debug builds may enable it. + +6. Lowering to Bytecode (Suggested) + +This feature does not require dedicated opcodes in the minimal design. + +Recommended lowering strategy: + +borrow/mutate are compile-time constructs. + +Field reads/writes lower to existing heap access opcodes (FIELD_GET/FIELD_SET or equivalent), but only permitted while a view is considered “active” by the compiler. + +In debug-runtime-check mode, the compiler may insert explicit enter/exit markers (or syscalls) to enable runtime enforcement. + +7. Rationale + +GC removes the need for RC/HIP, but aliasing mistakes still exist. Views provide a clean and didactic discipline: + +Handles: long-lived, capturable, GC-managed. + +Views: short-lived, local, safe access windows. + +This keeps the VM simple while enabling expressive language features (closures, coroutines) without lifetime headaches. + +8. Summary + +Borrow/Mutate are access disciplines, not memory management. + +Views are stack-only and non-escapable. + +Closures and coroutines freely use handles, but never views. + +Enforcement is compile-time, with optional runtime checks in debug mode. \ No newline at end of file diff --git a/files/Hard Reset FE API.md b/files/Hard Reset FE API.md deleted file mode 100644 index 0448406c..00000000 --- a/files/Hard Reset FE API.md +++ /dev/null @@ -1,52 +0,0 @@ -# Phase 03 – Rigid Frontend API & PBS Leak Containment (Junie PR Templates) - -> Goal: **finish Phase 03 with JVM-like discipline** by making the **Backend (BE) the source of truth** and forcing the PBS Frontend (FE) to implement a **strict, minimal, canonical** contract (`frontend-api`). -> -> Strategy: **surgical PRs** that (1) stop PBS types from leaking, (2) replace stringy protocols with canonical models, and (3) make imports/exports/overloads deterministic across deps. - -# Notes / Operating Rules (for Junie) - -1. **BE is the source of truth**: `frontend-api` defines canonical models; FE conforms. -2. **No string protocols** across layers. Strings may exist only as *display/debug*. -3. **No FE implementation imports from other FE implementations**. -4. **No BE imports PBS modules** (hard boundary). -5. **Overload resolution is signature-based** (arity alone is not valid). - ---- - -## PR-03.07 — Phase 03 cleanup: remove legacy compatibility branches and document boundary - -### Title - -Remove legacy string protocol branches and document FE/BE boundary rules - -### Briefing / Context - -After canonical models are in place, we must delete compatibility code paths (`alias/module`, `svc:` prefixes, prefix matching, etc.) to prevent regressions. - -### Target - -* No legacy synthetic module path support. -* No string prefix matching for overloads. -* Documentation: “BE owns the contract; FE implements it.” - -### Scope - -* Delete dead code. -* Add `docs/phase-03-frontend-api.md` (or in-crate docs) summarizing invariants. -* Add CI/lints to prevent BE from importing PBS modules. - -### Checklist - -* [ ] Remove legacy branches. -* [ ] Add boundary docs. -* [ ] Add lint/CI guard. - -### Tests - -* Full workspace tests. -* Golden tests. - -### Risk - -Low/Medium. Mostly deletion + docs, but could expose hidden dependencies. \ No newline at end of file diff --git a/files/Hard Reset.md b/files/Hard Reset.md deleted file mode 100644 index d97132cf..00000000 --- a/files/Hard Reset.md +++ /dev/null @@ -1,179 +0,0 @@ -# Prometeu Industrial-Grade Refactor Plan (JVM-like) - -**Language policy:** All implementation notes, code comments, commit messages, PR descriptions, and review discussion **must be in English**. - -**Reset policy:** This is a **hard reset**. We do **not** keep compatibility with the legacy bytecode/linker/verifier behaviors. No heuristics, no “temporary support”, no string hacks. - -**North Star:** A JVM-like philosophy: - -* Control-flow is **method-local** and **canonical**. -* The linker resolves **symbols** and **tables**, not intra-function branches. -* A **single canonical layout/decoder/spec** is used across compiler/linker/verifier/VM. -* Any invalid program fails with clear diagnostics, not panics. - ---- - -## Phase 3 — JVM-like Symbol Identity: Signature-based Overload & Constant-Pool Mindset - -### PR-09 (3 pts) — Overload resolution rules (explicit, deterministic) - -**Briefing** - -Once overload exists, resolution rules must be explicit. - -**Target** - -Implement a deterministic overload resolver based on exact type match (no implicit hacks). - -**Scope** - -* Exact-match resolution only (initially). -* Clear diagnostic when ambiguous or missing. - -**Requirements Checklist** - -* [ ] No best-effort fallback. - -**Completion Tests** - -* [ ] Ambiguous call produces a clear diagnostic. -* [ ] Missing overload produces a clear diagnostic. - ---- - -## Phase 4 — Eliminate Stringly-Typed Protocols & Debug Hacks - -### PR-10 (5 pts) — Replace `origin: Option` and all string protocols with structured enums - -**Briefing** - -String prefixes like `svc:` and `@dep:` are fragile and non-industrial. - -**Target** - -All origins and external references become typed data. - -**Scope** - -* Replace string origins with enums. -* Update lowering/collector/output accordingly. - -**Requirements Checklist** - -* [ ] No `.starts_with('@')`, `split(':')` protocols. - -**Completion Tests** - -* [ ] Grep-based test/lint step fails if forbidden patterns exist. - ---- - -### PR-11 (5 pts) — DebugInfo V1: structured function metadata (no `name@offset+len`) - -**Briefing** - -Encoding debug metadata in strings is unacceptable. - -**Target** - -Introduce a structured debug info format that stores offset/len as fields. - -**Scope** - -* Add `DebugFunctionInfo { func_idx, name, code_offset, code_len }`. -* Remove all parsing of `@offset+len`. -* Update orchestrator/linker/emit to use structured debug info. - -**Requirements Checklist** - -* [ ] No code emits or parses `@offset+len`. - -**Completion Tests** - -* [ ] A test that fails if any debug name contains `@` pattern. -* [ ] Debug info roundtrip test. - ---- - -## Phase 5 — Hardening: Diagnostics, Error Handling, and Regression Shields - -### PR-12 (3 pts) — Replace panics in critical build pipeline with typed errors + diagnostics - -**Briefing** - -`unwrap/expect` in compiler/linker transforms user errors into crashes. - -**Target** - -Introduce typed errors and surface diagnostics. - -**Scope** - -* Replace unwraps in: - - * symbol resolution - * import/export linking - * entrypoint selection -* Ensure clean error return with context. - -**Requirements Checklist** - -* [ ] No panic paths for invalid user programs. - -**Completion Tests** - -* [ ] Invalid program produces diagnostics, not panic. - ---- - -### PR-13 (3 pts) — Add regression test suite: link-order invariance + opcode-change immunity - -**Briefing** - -We need a system immune to opcode churn. - -**Target** - -Add tests that fail if: - -* linker steps bytes manually -* decoder/spec drift exists -* link order changes semantics - -**Scope** - -* Link-order invariance tests. -* Spec coverage tests. -* Optional: lightweight “forbidden patterns” tests. - -**Requirements Checklist** - -* [ ] Changing an opcode immediate size requires updating only the spec and tests. - -**Completion Tests** - -* [ ] All new regression tests pass. - ---- - -## Summary of Estimated Cost (Points) - -* Phase 1: PR-01 (3) + PR-02 (5) + PR-03 (3) = **11** -* Phase 2: PR-04 (5) + PR-05 (3) + PR-06 (3) = **11** -* Phase 3: PR-07 (5) + PR-08 (5) + PR-09 (3) = **13** -* Phase 4: PR-10 (5) + PR-11 (5) = **10** -* Phase 5: PR-12 (3) + PR-13 (3) = **6** - -**Total: 51 points** - -> Note: If any PR starts to exceed 5 points in practice, it must be split into smaller PRs. - ---- - -## Non-Negotiables - -* No compatibility with legacy encodings. -* No heuristics. -* No string hacks. -* One canonical decoder/spec/layout. -* Everything in English (including review comments). diff --git a/files/Prometeu Scripting - Language Tour.md b/files/Prometeu Scripting - Language Tour.md deleted file mode 100644 index d3066571..00000000 --- a/files/Prometeu Scripting - Language Tour.md +++ /dev/null @@ -1,341 +0,0 @@ -# Prometeu Base Script (PBS) - -> **A didactic scripting language for game development with explicit cost, explicit memory, and predictable runtime.** - ---- - -## 0. What PBS Is (and What It Is Not) - -PBS (Prometeu Base Script) is a **small, explicit scripting language designed for game engines and real‑time runtimes**. - -Its core goals are: - -* **Didactic clarity** — the language teaches how memory, data, and APIs really work. -* **Game‑friendly execution** — predictable runtime, no hidden allocation, no tracing GC. -* **Explicit cost model** — you always know *when* you allocate, *where* data lives, and *who* can mutate it. - -PBS is **not**: - -* a general‑purpose application language -* a productivity scripting language like Python or Lua -* a language that hides runtime cost - -PBS is intentionally opinionated. It is built for developers who want **control, predictability, and understanding**, especially in **game and engine contexts**. - ---- - -## 1. The Core Philosophy - -PBS is built around one rule: - -> **If you do not allocate, you are safe. If you allocate, you are responsible.** - -From this rule, everything else follows. - -### 1.1 Two Worlds - -PBS has **two explicit memory worlds**: - -| World | Purpose | Properties | -| ----- | -------------- | ---------------------------------------------- | -| SAFE | Stack / values | No aliasing, no leaks, no shared mutation | -| HIP | Storage / heap | Explicit aliasing, shared mutation, refcounted | - -You never cross between these worlds implicitly. - ---- - -## 2. First Contact: SAFE‑Only PBS - -A PBS program that never allocates lives entirely in the SAFE world. - -SAFE code: - -* uses value semantics -* copies data on assignment (conceptually) -* cannot leak memory -* cannot accidentally share mutable state - -This makes SAFE PBS ideal for: - -* gameplay logic -* math and simulation -* AI logic -* scripting without fear - -Example: - -```pbs -let a = Vector(1, 2); -let b = a; // conceptual copy - -b.scale(2); -// a is unchanged -``` - -No pointers. No references. No surprises. - ---- - -## 3. Values, Not Objects - -PBS does not have objects with identity by default. - -### 3.1 Value Types - -All basic types are **values**: - -* numbers -* bool -* string (immutable) -* tuples -* user‑defined `struct` - -A value: - -* has no identity -* has no lifetime beyond its scope -* is safe to copy - -This matches how most gameplay data *should* behave. - ---- - -## 4. Structs (Data First) - -Structs are **pure data models**. - -```pbs -declare struct Vector(x: float, y: float) -{ - pub fn len(self: this): float { ... } - pub fn scale(self: mut this, s: float): void { ... } -} -``` - -Rules: - -* fields are private -* mutation is explicit (`mut this`) -* no inheritance -* no identity - -Structs are designed to be: - -* cache‑friendly -* predictable -* easy to reason about - ---- - -## 5. Mutability Is a Property of Bindings - -In PBS, **types are never mutable**. Bindings are. - -```pbs -let v = Vector.ZERO; -v.scale(); // ERROR - -let w = mut Vector.ZERO; -w.scale(); // OK -``` - -This single rule eliminates entire classes of bugs. - ---- - -## 6. When You Need Power: Allocation (HIP World) - -Allocation is **explicit**. - -```pbs -let enemies = alloc list(); -``` - -Once you allocate: - -* data lives in Storage (heap) -* access happens through **gates** (handles) -* aliasing is real and visible - -This is intentional and explicit. - ---- - -## 7. Gates (Handles, Not Pointers) - -A gate is a small value that refers to heap storage. - -Properties: - -* cheap to copy -* may alias shared data -* managed by reference counting - -### 7.1 Strong vs Weak Gates - -* **Strong gate** — keeps data alive -* **Weak gate** — observes without ownership - -```pbs -let a = alloc Node; -let w: weak = a as weak; -let maybeA = w as strong; -``` - -This model mirrors real engine constraints without hiding them. - ---- - -## 8. Controlled Access to Storage - -Heap data is never accessed directly. - -You must choose *how*: - -* `peek` — copy to SAFE -* `borrow` — temporary read‑only access -* `mutate` — temporary mutable access - -```pbs -mutate enemies as e -{ - e.push(newEnemy); -} -``` - -This keeps mutation visible and scoped. - ---- - -## 9. Errors Are Values - -PBS has no exceptions. - -### 9.1 `optional` - -Used when absence is normal. - -```pbs -let x = maybeValue else 0; -``` - -### 9.2 `result` - -Used when failure matters. - -```pbs -let v = loadTexture(path)?; -``` - -Error propagation is explicit and typed. - ---- - -## 10. Control Flow Without Surprises - -PBS favors explicit flow: - -* `if` — control only -* `when` — expression -* `for` — bounded loops only - -```pbs -for i in [0b..count] { - update(i); -} -``` - -No unbounded iteration by accident. - ---- - -## 11. Services: Explicit API Boundaries - -A `service` is how behavior crosses module boundaries. - -```pbs -pub service Audio -{ - fn play(sound: Sound): void; -} -``` - -Services are: - -* explicit -* statically checked -* singleton‑like - -They map naturally to engine subsystems. - ---- - -## 12. Contracts: Static Guarantees - -Contracts define **what exists**, not how. - -```pbs -pub declare contract Gfx host -{ - fn drawText(x: int, y: int, msg: string): void; -} -``` - -Contracts: - -* have no runtime cost -* are validated at compile time -* define engine ↔ script boundaries - ---- - -## 13. Modules (Simple and Predictable) - -* one directory = one module -* only `pub` symbols cross modules -* no side effects on import - -This keeps build and runtime deterministic. - ---- - -## 14. Why PBS Works for Games - -PBS is designed around real engine needs: - -* frame‑based execution -* explicit allocation -* deterministic cleanup -* predictable performance - -It teaches developers **why engines are written the way they are**, instead of hiding reality. - ---- - -## 15. Who PBS Is For - -PBS is for: - -* game developers who want control -* engine developers -* students learning systems concepts -* educators teaching memory and runtime models - -PBS is *not* for: - -* rapid prototyping without constraints -* general app scripting -* hiding complexity - ---- - -## 16. Design Promise - -PBS makes one promise: - -> **Nothing happens behind your back.** - -If you understand PBS, you understand your runtime. - -That is the product. diff --git a/files/TODO.md b/files/TODO.md deleted file mode 100644 index 9200f3ea..00000000 --- a/files/TODO.md +++ /dev/null @@ -1,340 +0,0 @@ -# Prometeu VM/Compiler/Bytecode — Atomic PR Plan (Junie-ready) - -> **Entry point contract (confirmed)** -> -> * The PBS entry point is **`/src/main/modules/main.pbs::frame(): void`**. -> * The compiler must inject **`FRAME_SYNC` immediately before `RET`** at the end of this function. -> * `FRAME_SYNC` is a **signal only** (no GC opcodes). The VM uses it as a safe point. -> * Missing entry point is a **fatal compile error**. - -> **Goal**: Deliver a sequence of small, incremental PRs that bring the implementation closer to the published PBS/VM specs. -> -> **Rules for Junie (strict)** -> -> * **Do not make product decisions.** Only implement what is specified in the PR. -> * **If anything is unclear, stop and ask.** Do not improvise. -> * **All new/modified code comments must be in English.** -> * Each PR must be **atomic** and **mergeable**. -> * Each PR must include: **Briefing**, **Target**, **Non-goals**, **Implementation notes**, **Tests**, **Acceptance criteria**. - ---- - -## PR-02 — VM: Introduce GateId vs heap index and a minimal GatePool (no RC yet) - -### Briefing - -Current runtime treats `Value::Gate(x)` as a **direct heap base index**. The spec requires a **Gate Pool** where `GateId` resolves to `{alive, base, slots, type_id, rc...}` and heap access happens only through gate validation + resolution. - -This PR introduces the **data model** without changing ownership/RC yet, enabling later RC work. - -### Target - -* Add `GateId` type (e.g., `u32`) and a `GateEntry` struct with fields: - - * `alive: bool` - * `base: u32` - * `slots: u32` - * `type_id: u32` (store it, even if VM doesn’t use it yet) -* Add `GatePool` container to the VM state. -* Update `ALLOC(type_id, slots)` to: - - * bump-alloc `slots` in heap - * insert new `GateEntry { alive: true, base, slots, type_id }` - * push `Value::Gate(GateId)` (GateId is index into gate_pool) -* Update `GATE_LOAD/GATE_STORE` to: - - * validate GateId (in-range + alive) - * bounds-check offset against `slots` - * translate to heap index: `base + offset` - -### Non-goals - -* No reference counting yet. -* No reclaim/free yet. -* No enforcement of borrow/mutate rules yet. - -### Implementation notes - -* Keep heap as `Vec` (or existing representation). In this PR, do not change heap layout. -* Add a helper: `resolve_gate(gate_id) -> Result<&GateEntry, Trap>` and `resolve_gate_mut(...)`. -* Define two new trap codes (or map onto existing ones if already defined): - - * `TRAP_INVALID_GATE` (gate_id out of range) - * `TRAP_DEAD_GATE` (entry exists but `alive == false`) -* Make sure the VM never reads/writes heap directly for gate operations without resolution. - -### Tests - -1. VM unit tests: - - * Allocate 2 gates; ensure they get distinct GateIds. - * Store to gate offset 0, load back, assert equal. - * Store to offset == slots (OOB) triggers OOB trap. - * Use an invalid GateId triggers INVALID_GATE trap. -2. If trap codes are new, test for the exact trap code. - -### Acceptance criteria - -* `ALLOC` returns GateId (not heap base). -* `GATE_LOAD/STORE` uses gate_pool resolution. -* Invalid/dead gate attempts trap deterministically. - ---- - -## PR-03 — VM: Add strong reference counting (RC) with deterministic retain/release semantics - -### Briefing - -The spec requires strong RC tracking for gates and deterministic behavior for invalid/dead gates. Today `GATE_RETAIN`/`GATE_RELEASE` are no-ops (or effectively pop-only). - -This PR implements **strong_rc** and updates runtime to adjust RC in well-defined places. - -### Target - -* Extend `GateEntry` with: - - * `strong_rc: u32` -* Define semantics: - - * New allocation starts with `strong_rc = 1` (gate value returned on stack owns 1 reference) - * `GATE_RETAIN`: increment strong_rc - * `GATE_RELEASE`: decrement strong_rc; if reaches 0 then mark gate `alive=false` and schedule reclaim - -### Non-goals - -* No compacting heap. -* No weak refs. -* Reclaim can be minimal (safe-point only) and may not actually reuse memory yet. - -### Implementation notes - -* Implement a `reclaim_queue: Vec` in VM state. -* On `strong_rc` reaching 0: - - * set `alive = false` - * push gate_id into `reclaim_queue` -* **Safe point**: drain reclaim queue on `FRAME_SYNC`. - - * Compiler guarantees `FRAME_SYNC` at the end of the PBS entry point `main.pbs::frame(): void` (PR-00). -* In this PR, reclaim may simply: - - * overwrite the heap range `[base, base+slots)` with `Value::Nil` (or a safe default) - * keep gate_id non-reusable - -### Tests - -1. RC lifecycle test: - - * alloc gate (rc=1) - * retain (rc=2) - * release (rc=1) - * release (rc=0) => gate becomes dead - * subsequent load/store traps DEAD_GATE -2. Reclaim effect test (if you overwrite heap slots): - - * store a value, release to 0, run safe-point - * confirm heap region is cleared (only if heap is inspectable in tests) - -### Acceptance criteria - -* `GATE_RETAIN/RELEASE` changes RC. -* Gate transitions to `dead` at rc==0. -* Dead gate access traps. -* Reclaim happens at the chosen safe point. - ---- - -## PR-04 — VM: Automatic RC adjustments on stack/local/global/heap moves (no more “manual RC correctness”) - -### Briefing - -Relying on explicit `GATE_RETAIN/RELEASE` everywhere is error-prone. The spec indicates RC must be adjusted on assignments/pops/stores. This PR makes RC correctness **a VM invariant**: when a gate value is copied into a slot, RC increments; when replaced/dropped, RC decrements. - -### Target - -* Implement centralized helpers: - - * `inc_rc_if_gate(Value)` - * `dec_rc_if_gate(Value)` -* Apply them in all places where values are moved or overwritten: - - * Stack `PUSH`/`POP` (when dropping values) - * Local set/get if they clone values - * Global set/get - * `GATE_STORE` (heap cell overwrite) - * Any instruction that overwrites an existing slot (e.g., `STORE_LOCAL`, `STORE_GLOBAL`, etc.) - -### Non-goals - -* No changes to compiler output. -* No borrow/mutate enforcement. - -### Implementation notes - -* When writing into a slot: - - 1. `dec_rc_if_gate(old_value)` - 2. write new value - 3. `inc_rc_if_gate(new_value)` **only if the semantics is “copy into slot”** -* When moving (not copying) is possible, avoid double inc/dec. -* If the VM uses `clone()` widely, be explicit about when RC should increase. - -> ⚠️ If it’s unclear whether an opcode is “move” or “copy”, **stop and ask** (do not guess). - -### Tests - -1. Stack drop test: - - * alloc gate, push into local, pop stack, ensure rc doesn’t underflow. -2. Overwrite test: - - * local = gateA, then local = gateB - * rc of gateA decremented - * gateA becomes dead if no other refs -3. Heap store overwrite test: - - * gateX stores gateA into offset 0 - * then stores gateB into same offset - * rc adjusts accordingly - -### Acceptance criteria - -* No RC leaks on overwrites. -* No premature dead gates when references still exist. -* Tests cover overwrite in at least 2 storage kinds (local + heap). - ---- - -## PR-05 — VM: Implement (debug-mode) Borrow/Mutate/Peek scopes as observable state - -### Briefing - -Currently `GATE_BEGIN_PEEK/BORROW/MUTATE` and `GATE_END_*` are no-ops. Even if v0 is permissive, the VM should at least track scope state to enable future enforcement and better diagnostics. - -### Target - -* Add per-gate “scope counters” (or a small state machine) in `GateEntry`: - - * `peek_count: u32` - * `borrow_count: u32` - * `mutate_count: u32` (should be 0/1 if exclusive) -* Implement opcodes to increment/decrement and validate balanced usage: - - * End without begin => trap (or panic if considered VM bug) - * Negative underflow => trap -* In **debug builds**, optionally enforce: - - * cannot `begin_mutate` when `borrow_count>0` - * cannot `begin_borrow` when `mutate_count>0` - -### Non-goals - -* No compiler changes. -* No runtime copy-back scratch buffers yet. - -### Implementation notes - -* Keep enforcement behind a feature flag or debug-only cfg. -* Always keep counters balanced; mismatches should be deterministic. - -### Tests - -* Begin/End balance test per scope. -* Debug-only conflict test (if enabled). - -### Acceptance criteria - -* Scopes are no longer no-ops. -* Misbalanced begin/end produces deterministic error. - ---- - -## PR-06 — Bytecode/VM: Represent strings as ConstId (dedup + stable value size) - -### Briefing - -`Value::String(String)` stores dynamic payload in runtime values. The spec direction prefers string refs into constant pools for stability and dedup. This PR migrates runtime string values to `ConstId` references. - -### Target - -* Add `Value::StringRef(ConstId)` (or `Value::String(ConstId)`) -* Ensure program image contains a string pool. -* Update `PUSH_CONST` behavior for string constants: - - * push `StringRef(id)` instead of allocating a runtime `String` - -### Non-goals - -* No interning beyond the existing constant pool. -* No changes to the source language. - -### Implementation notes - -* Decide where the string pool lives (ProgramImage / ConstPool). -* Update debug printing and trap formatting if needed. - -### Tests - -* Constant string pushed twice references same ConstId. -* Equality/comparison behavior remains unchanged (if supported). - -### Acceptance criteria - -* Strings in runtime values are pool references. -* Existing programs using constants still run. - ---- - -## PR-07 — Compiler: Enforce import placement rules (top-of-file) - -### Briefing - -The PBS module model specifies that imports must be at the top-level (and typically before other declarations). This PR makes the compiler reject invalid import placement to align with the module/linking spec. - -### Target - -* In parser/collector phase, detect imports appearing after non-import declarations. -* Emit a diagnostic with a clear message and span. - -### Non-goals - -* No changes to how linking works. -* No auto-fix. - -### Implementation notes - -* Track a boolean `seen_non_import_decl` during file scan. -* When an import is encountered and the flag is set, produce an error diagnostic. - -### Tests - -* One file with valid imports at top => ok. -* One file with import after a function/type => error. - -### Acceptance criteria - -* Compiler rejects invalid import placement with stable diagnostic. - ---- - -# Suggested merge order - -1. PR-00 (entry point validation + FRAME_SYNC injection) -2. PR-01 (linker JMP relocation) -3. PR-02 (GatePool + GateId) -4. PR-03 (RC strong + retain/release + reclaim at FRAME_SYNC) -5. PR-04 (automatic RC on moves/overwrites) -6. PR-05 (scope tracking) -7. PR-06 (StringRef ConstId) -8. PR-07 (import placement enforcement) - ---- - -# Open questions (must be answered before PR-04) - -> Junie must stop and ask if any of these cannot be determined from the codebase. - -1. Which exact opcodes correspond to storage overwrites (locals/globals) in the current VM implementation (names may differ). -2. Which trap codes are already defined for gate errors vs whether we introduce new ones. - -> ✅ Note: the safe point for reclaim is **FRAME_SYNC** (compiler inserts it at the end of `fn frame(): void`).