diff --git a/crates/prometeu-compiler/src/backend/emit_bytecode.rs b/crates/prometeu-compiler/src/backend/emit_bytecode.rs index 03914f9a..c589f2e6 100644 --- a/crates/prometeu-compiler/src/backend/emit_bytecode.rs +++ b/crates/prometeu-compiler/src/backend/emit_bytecode.rs @@ -4,13 +4,13 @@ //! converting the Intermediate Representation (IR) into the binary Prometeu ByteCode (PBC) format. //! //! It performs two main tasks: -//! 1. **Instruction Lowering**: Translates `ir::Instruction` into `prometeu_bytecode::asm::Asm` ops. +//! 1. **Instruction Lowering**: Translates `ir_vm::Instruction` into `prometeu_bytecode::asm::Asm` ops. //! 2. **Symbol Mapping**: Associates bytecode offsets (Program Counter) with source code locations. use crate::common::files::FileManager; use crate::common::symbols::Symbol; -use crate::ir; -use crate::ir::instr::InstrKind; +use crate::ir_vm; +use crate::ir_vm::instr::InstrKind; use crate::ir_core::ConstantValue; use anyhow::{anyhow, Result}; use prometeu_bytecode::asm::{assemble, update_pc_by_operand, Asm, Operand}; @@ -26,7 +26,7 @@ pub struct EmitResult { } /// Entry point for emitting a bytecode module from the IR. -pub fn emit_module(module: &ir::Module, file_manager: &FileManager) -> Result { +pub fn emit_module(module: &ir_vm::Module, file_manager: &FileManager) -> Result { let mut emitter = BytecodeEmitter::new(file_manager); emitter.emit(module) } @@ -69,7 +69,7 @@ impl<'a> BytecodeEmitter<'a> { } /// Transforms an IR module into a binary PBC file. - fn emit(&mut self, module: &ir::Module) -> Result { + fn emit(&mut self, module: &ir_vm::Module) -> Result { let mut asm_instrs = Vec::new(); let mut ir_instr_map = Vec::new(); // Maps Asm index to IR instruction (for symbols) @@ -232,9 +232,9 @@ impl<'a> BytecodeEmitter<'a> { #[cfg(test)] mod tests { use super::*; - use crate::ir::module::{Module, Function}; - use crate::ir::instr::{Instruction, InstrKind}; - use crate::ir::types::Type; + use crate::ir_vm::module::{Module, Function}; + use crate::ir_vm::instr::{Instruction, InstrKind}; + use crate::ir_vm::types::Type; use crate::ir_core::ids::FunctionId; use crate::ir_core::const_pool::ConstantValue; use crate::common::files::FileManager; diff --git a/crates/prometeu-compiler/src/compiler.rs b/crates/prometeu-compiler/src/compiler.rs index 21ca56b4..4b82287a 100644 --- a/crates/prometeu-compiler/src/compiler.rs +++ b/crates/prometeu-compiler/src/compiler.rs @@ -8,7 +8,7 @@ use crate::common::config::ProjectConfig; use crate::common::files::FileManager; use crate::common::symbols::Symbol; use crate::frontends::Frontend; -use crate::ir; +use crate::ir_vm; use anyhow::Result; use std::path::Path; @@ -87,7 +87,7 @@ pub fn compile(project_dir: &Path) -> Result { // 3. IR Validation // Ensures the generated IR is sound and doesn't violate any VM constraints // before we spend time generating bytecode. - ir::validate::validate_module(&ir_module) + ir_vm::validate::validate_module(&ir_module) .map_err(|bundle| anyhow::anyhow!("IR Validation failed: {:?}", bundle))?; // 4. Emit Bytecode diff --git a/crates/prometeu-compiler/src/frontends/mod.rs b/crates/prometeu-compiler/src/frontends/mod.rs index 856dda5c..086d2bd8 100644 --- a/crates/prometeu-compiler/src/frontends/mod.rs +++ b/crates/prometeu-compiler/src/frontends/mod.rs @@ -1,5 +1,5 @@ use crate::common::diagnostics::DiagnosticBundle; -use crate::ir; +use crate::ir_vm; use std::path::Path; use crate::common::files::FileManager; @@ -13,5 +13,5 @@ pub trait Frontend { &self, entry: &Path, file_manager: &mut FileManager, - ) -> Result; + ) -> Result; } diff --git a/crates/prometeu-compiler/src/frontends/pbs/mod.rs b/crates/prometeu-compiler/src/frontends/pbs/mod.rs index 877384c1..92776f47 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/mod.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/mod.rs @@ -21,7 +21,7 @@ pub use lowering::Lowerer; use crate::common::diagnostics::DiagnosticBundle; use crate::common::files::FileManager; use crate::frontends::Frontend; -use crate::ir; +use crate::ir_vm; use crate::lowering::core_to_vm; use std::path::Path; @@ -36,7 +36,7 @@ impl Frontend for PbsFrontend { &self, entry: &Path, file_manager: &mut FileManager, - ) -> Result { + ) -> Result { let source = std::fs::read_to_string(entry).map_err(|e| { DiagnosticBundle::error(format!("Failed to read file: {}", e), None) })?; diff --git a/crates/prometeu-compiler/src/ir/instr.rs b/crates/prometeu-compiler/src/ir_vm/instr.rs similarity index 100% rename from crates/prometeu-compiler/src/ir/instr.rs rename to crates/prometeu-compiler/src/ir_vm/instr.rs diff --git a/crates/prometeu-compiler/src/ir/mod.rs b/crates/prometeu-compiler/src/ir_vm/mod.rs similarity index 100% rename from crates/prometeu-compiler/src/ir/mod.rs rename to crates/prometeu-compiler/src/ir_vm/mod.rs diff --git a/crates/prometeu-compiler/src/ir/module.rs b/crates/prometeu-compiler/src/ir_vm/module.rs similarity index 97% rename from crates/prometeu-compiler/src/ir/module.rs rename to crates/prometeu-compiler/src/ir_vm/module.rs index 737576bb..40e83005 100644 --- a/crates/prometeu-compiler/src/ir/module.rs +++ b/crates/prometeu-compiler/src/ir_vm/module.rs @@ -4,8 +4,8 @@ //! The IR is a higher-level representation of the program than bytecode, but lower //! than the source code AST. It is organized into Modules, Functions, and Globals. -use crate::ir::instr::Instruction; -use crate::ir::types::Type; +use crate::ir_vm::instr::Instruction; +use crate::ir_vm::types::Type; use crate::ir_core::const_pool::ConstPool; use crate::ir_core::ids::FunctionId; use serde::{Deserialize, Serialize}; diff --git a/crates/prometeu-compiler/src/ir/types.rs b/crates/prometeu-compiler/src/ir_vm/types.rs similarity index 100% rename from crates/prometeu-compiler/src/ir/types.rs rename to crates/prometeu-compiler/src/ir_vm/types.rs diff --git a/crates/prometeu-compiler/src/ir/validate.rs b/crates/prometeu-compiler/src/ir_vm/validate.rs similarity index 88% rename from crates/prometeu-compiler/src/ir/validate.rs rename to crates/prometeu-compiler/src/ir_vm/validate.rs index 4a52dfa3..9afe4fc6 100644 --- a/crates/prometeu-compiler/src/ir/validate.rs +++ b/crates/prometeu-compiler/src/ir_vm/validate.rs @@ -1,5 +1,5 @@ use crate::common::diagnostics::DiagnosticBundle; -use crate::ir::module::Module; +use crate::ir_vm::module::Module; pub fn validate_module(_module: &Module) -> Result<(), DiagnosticBundle> { // TODO: Implement common IR validations: diff --git a/crates/prometeu-compiler/src/lib.rs b/crates/prometeu-compiler/src/lib.rs index 6cf89d4b..80cf8222 100644 --- a/crates/prometeu-compiler/src/lib.rs +++ b/crates/prometeu-compiler/src/lib.rs @@ -38,7 +38,7 @@ //! See the [`compiler`] module for the main entry point to trigger a compilation programmatically. pub mod common; -pub mod ir; +pub mod ir_vm; pub mod ir_core; pub mod lowering; pub mod backend; diff --git a/crates/prometeu-compiler/src/lowering/core_to_vm.rs b/crates/prometeu-compiler/src/lowering/core_to_vm.rs index ab831d3b..2db0e70b 100644 --- a/crates/prometeu-compiler/src/lowering/core_to_vm.rs +++ b/crates/prometeu-compiler/src/lowering/core_to_vm.rs @@ -1,9 +1,9 @@ -use crate::ir; +use crate::ir_vm; use crate::ir_core; use anyhow::Result; /// Lowers a Core IR program into a VM IR module. -pub fn lower_program(program: &ir_core::Program) -> Result { +pub fn lower_program(program: &ir_core::Program) -> Result { // For now, we assume a single module program or lower the first one. // In the future, we might want to lower all modules and link them. if let Some(core_module) = program.modules.first() { @@ -14,8 +14,8 @@ pub fn lower_program(program: &ir_core::Program) -> Result { } /// Lowers a single Core IR module into a VM IR module. -pub fn lower_module(core_module: &ir_core::Module, const_pool: &ir_core::ConstPool) -> Result { - let mut vm_module = ir::Module::new(core_module.name.clone()); +pub fn lower_module(core_module: &ir_core::Module, const_pool: &ir_core::ConstPool) -> Result { + let mut vm_module = ir_vm::Module::new(core_module.name.clone()); vm_module.const_pool = const_pool.clone(); for core_func in &core_module.functions { @@ -26,11 +26,11 @@ pub fn lower_module(core_module: &ir_core::Module, const_pool: &ir_core::ConstPo } /// Lowers a Core IR function into a VM IR function. -pub fn lower_function(core_func: &ir_core::Function) -> Result { - let mut vm_func = ir::Function { +pub fn lower_function(core_func: &ir_core::Function) -> Result { + let mut vm_func = ir_vm::Function { id: core_func.id, name: core_func.name.clone(), - params: core_func.params.iter().map(|p| ir::Param { + params: core_func.params.iter().map(|p| ir_vm::Param { name: p.name.clone(), r#type: lower_type(&p.ty), }).collect(), @@ -40,62 +40,62 @@ pub fn lower_function(core_func: &ir_core::Function) -> Result { for block in &core_func.blocks { // Core blocks map to labels in the flat VM IR instruction list. - vm_func.body.push(ir::Instruction::new( - ir::InstrKind::Label(ir::Label(format!("block_{}", block.id))), + vm_func.body.push(ir_vm::Instruction::new( + ir_vm::InstrKind::Label(ir_vm::Label(format!("block_{}", block.id))), None, )); for instr in &block.instrs { let kind = match instr { - ir_core::Instr::PushConst(id) => ir::InstrKind::PushConst(*id), - ir_core::Instr::Call(func_id, arg_count) => ir::InstrKind::Call { + ir_core::Instr::PushConst(id) => ir_vm::InstrKind::PushConst(*id), + ir_core::Instr::Call(func_id, arg_count) => ir_vm::InstrKind::Call { func_id: *func_id, arg_count: *arg_count }, - ir_core::Instr::Syscall(id) => ir::InstrKind::Syscall(*id), - ir_core::Instr::GetLocal(slot) => ir::InstrKind::GetLocal(*slot), - ir_core::Instr::SetLocal(slot) => ir::InstrKind::SetLocal(*slot), - ir_core::Instr::Pop => ir::InstrKind::Pop, - ir_core::Instr::Dup => ir::InstrKind::Dup, - ir_core::Instr::Add => ir::InstrKind::Add, - ir_core::Instr::Sub => ir::InstrKind::Sub, - ir_core::Instr::Mul => ir::InstrKind::Mul, - ir_core::Instr::Div => ir::InstrKind::Div, - ir_core::Instr::Neg => ir::InstrKind::Neg, - ir_core::Instr::Eq => ir::InstrKind::Eq, - ir_core::Instr::Neq => ir::InstrKind::Neq, - ir_core::Instr::Lt => ir::InstrKind::Lt, - ir_core::Instr::Lte => ir::InstrKind::Lte, - ir_core::Instr::Gt => ir::InstrKind::Gt, - ir_core::Instr::Gte => ir::InstrKind::Gte, - ir_core::Instr::And => ir::InstrKind::And, - ir_core::Instr::Or => ir::InstrKind::Or, - ir_core::Instr::Not => ir::InstrKind::Not, - ir_core::Instr::Alloc => ir::InstrKind::Alloc, - ir_core::Instr::ReadGate => ir::InstrKind::LoadRef(0), - ir_core::Instr::WriteGate => ir::InstrKind::StoreRef(0), - ir_core::Instr::Free => ir::InstrKind::Nop, + ir_core::Instr::Syscall(id) => ir_vm::InstrKind::Syscall(*id), + ir_core::Instr::GetLocal(slot) => ir_vm::InstrKind::GetLocal(*slot), + ir_core::Instr::SetLocal(slot) => ir_vm::InstrKind::SetLocal(*slot), + ir_core::Instr::Pop => ir_vm::InstrKind::Pop, + ir_core::Instr::Dup => ir_vm::InstrKind::Dup, + ir_core::Instr::Add => ir_vm::InstrKind::Add, + ir_core::Instr::Sub => ir_vm::InstrKind::Sub, + ir_core::Instr::Mul => ir_vm::InstrKind::Mul, + ir_core::Instr::Div => ir_vm::InstrKind::Div, + ir_core::Instr::Neg => ir_vm::InstrKind::Neg, + ir_core::Instr::Eq => ir_vm::InstrKind::Eq, + ir_core::Instr::Neq => ir_vm::InstrKind::Neq, + ir_core::Instr::Lt => ir_vm::InstrKind::Lt, + ir_core::Instr::Lte => ir_vm::InstrKind::Lte, + ir_core::Instr::Gt => ir_vm::InstrKind::Gt, + ir_core::Instr::Gte => ir_vm::InstrKind::Gte, + ir_core::Instr::And => ir_vm::InstrKind::And, + ir_core::Instr::Or => ir_vm::InstrKind::Or, + ir_core::Instr::Not => ir_vm::InstrKind::Not, + ir_core::Instr::Alloc => ir_vm::InstrKind::Alloc, + ir_core::Instr::ReadGate => ir_vm::InstrKind::LoadRef(0), + ir_core::Instr::WriteGate => ir_vm::InstrKind::StoreRef(0), + ir_core::Instr::Free => ir_vm::InstrKind::Nop, }; - vm_func.body.push(ir::Instruction::new(kind, None)); + vm_func.body.push(ir_vm::Instruction::new(kind, None)); } match &block.terminator { ir_core::Terminator::Return => { - vm_func.body.push(ir::Instruction::new(ir::InstrKind::Ret, None)); + vm_func.body.push(ir_vm::Instruction::new(ir_vm::InstrKind::Ret, None)); } ir_core::Terminator::Jump(target) => { - vm_func.body.push(ir::Instruction::new( - ir::InstrKind::Jmp(ir::Label(format!("block_{}", target))), + vm_func.body.push(ir_vm::Instruction::new( + ir_vm::InstrKind::Jmp(ir_vm::Label(format!("block_{}", target))), None, )); } ir_core::Terminator::JumpIfFalse { target, else_target } => { - vm_func.body.push(ir::Instruction::new( - ir::InstrKind::JmpIfFalse(ir::Label(format!("block_{}", target))), + vm_func.body.push(ir_vm::Instruction::new( + ir_vm::InstrKind::JmpIfFalse(ir_vm::Label(format!("block_{}", target))), None, )); - vm_func.body.push(ir::Instruction::new( - ir::InstrKind::Jmp(ir::Label(format!("block_{}", else_target))), + vm_func.body.push(ir_vm::Instruction::new( + ir_vm::InstrKind::Jmp(ir_vm::Label(format!("block_{}", else_target))), None, )); } @@ -105,20 +105,20 @@ pub fn lower_function(core_func: &ir_core::Function) -> Result { Ok(vm_func) } -fn lower_type(ty: &ir_core::Type) -> ir::Type { +fn lower_type(ty: &ir_core::Type) -> ir_vm::Type { match ty { - ir_core::Type::Void => ir::Type::Void, - ir_core::Type::Int => ir::Type::Int, - ir_core::Type::Float => ir::Type::Float, - ir_core::Type::Bool => ir::Type::Bool, - ir_core::Type::String => ir::Type::String, - ir_core::Type::Optional(inner) => ir::Type::Array(Box::new(lower_type(inner))), // Approximation + ir_core::Type::Void => ir_vm::Type::Void, + ir_core::Type::Int => ir_vm::Type::Int, + ir_core::Type::Float => ir_vm::Type::Float, + ir_core::Type::Bool => ir_vm::Type::Bool, + ir_core::Type::String => ir_vm::Type::String, + ir_core::Type::Optional(inner) => ir_vm::Type::Array(Box::new(lower_type(inner))), // Approximation ir_core::Type::Result(ok, _) => lower_type(ok), // Approximation - ir_core::Type::Struct(_) => ir::Type::Object, - ir_core::Type::Service(_) => ir::Type::Object, - ir_core::Type::Contract(_) => ir::Type::Object, - ir_core::Type::ErrorType(_) => ir::Type::Object, - ir_core::Type::Function { .. } => ir::Type::Function, + ir_core::Type::Struct(_) => ir_vm::Type::Object, + ir_core::Type::Service(_) => ir_vm::Type::Object, + ir_core::Type::Contract(_) => ir_vm::Type::Object, + ir_core::Type::ErrorType(_) => ir_vm::Type::Object, + ir_core::Type::Function { .. } => ir_vm::Type::Function, } } @@ -127,7 +127,7 @@ mod tests { use super::*; use crate::ir_core; use crate::ir_core::*; - use crate::ir::*; + use crate::ir_vm::*; #[test] fn test_full_lowering() { diff --git a/docs/specs/pbs/PRs para Junie.md b/docs/specs/pbs/PRs para Junie.md index a9e40a76..2fa3b707 100644 --- a/docs/specs/pbs/PRs para Junie.md +++ b/docs/specs/pbs/PRs para Junie.md @@ -1,357 +1,223 @@ -# PBS Compiler — Junie PR Plan +# PBS ⇄ VM Alignment — Junie PRs (HIP Semantics Hardening) -> **Purpose:** this document defines a sequence of small, focused Pull Requests to be implemented by *Junie*, one at a time. +> **Purpose:** fix semantic mismatches between the PBS frontend (Core IR) and the VM **before** any VM heap/gate implementation. > -> **Audience:** compiler implementer (AI or human). +> These PRs are **surgical**, **mandatory**, and **non-creative**. +> Junie must follow them **exactly**. + +> **Context:** > -> **Scope:** PBS-first compiler architecture. TS and Lua frontends are assumed **removed**. -> -> **Hard rules:** -> -> * Each PR must compile and pass tests. -> * Each PR must include tests. -> * No speculative features. -> * Follow the `Prometeu Base Script (PBS) - Implementation Spec`. -> * VM IR is frozen: new opcodes are forbidden unless explicitly planned in a PR titled “VM Instruction Set Change” with a full rationale + golden bytecode tests. +> * PBS frontend is implemented and produces Core IR. +> * Bytecode stability is a hard requirement. +> * VM currently has stack + const pool; heap exists but is unused. +> * HIP semantics (gates/storage) are currently **incorrectly lowered**. +> * `ir_vm` is feature-frozen at the moment. we are going to validate only `ir_core` +> * Lowering is the only place `ir_core` and `ir_vm` touch each other. +> - VM IR is never imported from Core IR. +> - Core IR never imports VM IR. --- -## Global Architectural Direction (Non-negotiable) +## Global Rules (Read Before Any PR) -* PBS is the **primary language**. -* Frontend is implemented **before** runtime integration. -* Architecture uses **two IR layers**: - * **Core IR** (PBS-semantic, typed, resolved) - * **VM IR** (stack-based, backend-friendly) -* VM IR remains simple and stable. -* Lowering is explicit and testable. +1. **No new features.** Only semantic correction. +2. **No new VM opcodes yet.** VM changes come later. +3. **No fallback values** (e.g. `FunctionId(0)`). Fail with diagnostics. +4. **Every PR must include tests** (golden or unit). +5. **Core IR is the source of semantic truth.** --- -# PR-01 — ProjectConfig and Frontend Selection +# PR-20 — Core IR: Make HIP Semantics Explicit (No Handle Loss) ### Goal -Introduce a project-level configuration that selects the frontend and entry file explicitly. +Fix the Core IR so HIP operations never lose the destination gate. -### Motivation +### Problem (Current Bug) -The compiler must not hardcode entry points or languages. PBS will be the first frontend, others may return later. +Current lowering evaluates a gate, reads storage, stores the result in a local, and later attempts to write back **without having the gate anymore**. -### Scope +This violates PBS semantics: storage access must always be mediated by the **original gate**. -* Add `ProjectConfig` (serde-deserializable) loaded from `prometeu.json` -* Fields (v0): +### Required Changes - * `script_fe: "pbs"` - * `entry: "main.pbs"` -* Refactor compiler entry point to: +#### 1. Extend Core IR instructions - * load config - * select frontend by `script_fe` - * resolve entry path relative to project root +Add explicit HIP instructions: -### Files Likely Touched +```rust +enum CoreInstr { + // existing … -* `compiler/mod.rs` -* `compiler/driver.rs` -* `common/config.rs` (new) + Alloc { ty: TypeId, slots: u32 }, -### Tests (mandatory) + BeginPeek { gate: ValueId }, + BeginBorrow { gate: ValueId }, + BeginMutate { gate: ValueId }, -* unit test: load valid `prometeu.json` -* unit test: invalid frontend → diagnostic -* integration test: project root + entry resolution + EndPeek, + EndBorrow, + EndMutate, +} +``` -### Notes to Junie +Rules: -Do **not** add PBS parsing yet. This PR is infrastructure only. +* `Begin*` instructions **do not consume** the gate. +* Gate identity must remain available until the matching `End*`. + +#### 2. Remove any lowering that copies HIP storage into locals + +* No `ReadGate → SetLocal` pattern. +* Storage views are **not locals**. + +### Tests (Mandatory) + +* Golden Core IR test showing `BeginMutate(gate)` … `EndMutate` wrapping body +* Test asserting gate is still live at `EndMutate` --- -# PR-02 — Core IR Skeleton (PBS-first) +# PR-21 — Distinguish `peek`, `borrow`, and `mutate` in Core IR ### Goal -Introduce a **Core IR** layer independent from the VM IR. +Restore the semantic distinction mandated by PBS. -### Motivation +### Required Semantics -PBS semantics must be represented before lowering to VM instructions. +| Operation | Effect | +| --------- | ------------------------------- | +| `peek` | Copy storage → stack value | +| `borrow` | Temporary read-only view | +| `mutate` | Temporary mutable view + commit | -### Scope +### Required Changes -* Add new module: `ir_core` -* Define minimal structures: +* Lower PBS `peek` → `BeginPeek` / `EndPeek` +* Lower PBS `borrow` → `BeginBorrow` / `EndBorrow` +* Lower PBS `mutate` → `BeginMutate` / `EndMutate` - * `Program` - * `Module` - * `Function` - * `Block` - * `Instr` - * `Terminator` -* IDs only (no string-based calls): - - * `FunctionId` - * `ConstId` - * `TypeId` - -### Constraints - -* Core IR must NOT reference VM opcodes -* No lowering yet +These **must not** share the same lowering path. ### Tests -* construct Core IR manually in tests -* snapshot test (JSON) for deterministic shape +* PBS snippet with all three operations +* Assert distinct Core IR instruction sequences --- -# PR-03 — Constant Pool and IDs +# PR-22 — Make Allocation Shape Explicit in Core IR ### Goal -Introduce a stable constant pool shared by Core IR and VM IR. +Stop implicit / guessed heap layouts. -### Scope +### Required Changes -* Add `ConstPool`: +* Replace any shape-less `Alloc` with: - * strings - * numbers -* Replace inline literals in VM IR with `ConstId` -* Update existing VM IR to accept `PushConst(ConstId)` +```rust +Alloc { ty: TypeId, slots: u32 } +``` + +Rules: + +* `TypeId` comes from frontend type checking +* `slots` is derived deterministically (struct fields / array size) ### Tests -* const pool deduplication -* deterministic ConstId assignment -* IR snapshot stability +* Allocating storage struct emits correct `slots` +* Allocating array emits correct `slots` --- -# PR-04 — VM IR Cleanup (Stabilization) +# PR-23 — Eliminate Invalid Call Fallbacks ### Goal -Stabilize VM IR as a **lowering target**, not a language IR. +Prevent invalid bytecode generation. -### Scope +### Required Changes -* Replace string-based calls with `FunctionId` -* Ensure locals are accessed via slots -* Remove or internalize `PushScope` / `PopScope` +* Remove **all** fallbacks to `FunctionId(0)` or equivalent +* On unresolved symbols during lowering: + + * Emit canonical diagnostic (`E_RESOLVE_UNDEFINED` or `E_LOWER_UNSUPPORTED`) + * Abort lowering ### Tests -* golden VM IR tests -* lowering smoke test (Core IR → VM IR) +* PBS program calling missing function → compile error +* No Core IR or VM IR emitted --- -# PR-05 — Core IR → VM IR Lowering Pass +# PR-24 — Validate Contract Calls in Frontend (Arity + Types) ### Goal -Implement the lowering pass from Core IR to VM IR. +Move contract validation to compile time. -### Scope +### Required Changes -* New module: `lowering/core_to_vm.rs` -* Lowering rules: +* During PBS type checking: - * Core blocks → labels - * Core calls → VM calls - * Host calls preserved -* No PBS frontend yet + * Validate argument count against contract signature + * Validate argument types + +* Lower only validated calls to `HostCall` ### Tests -* lowering correctness -* instruction ordering -* label resolution +* Wrong arity → `E_TYPE_MISMATCH` +* Correct call lowers to Core IR `HostCall` --- -# PR-06 — PBS Frontend: Lexer +# PR-25 — Core IR Invariants Test Suite ### Goal -Implement PBS lexer according to the spec. +Lock in correct semantics before touching the VM. -### Scope +### Required Invariants -* Token kinds -* Keyword table -* Span tracking +* Every `Begin*` has a matching `End*` +* Gate passed to `Begin*` is available at `End*` +* No storage writes without `BeginMutate` +* No silent fallbacks ### Tests -* tokenization tests -* keyword vs identifier tests -* bounded literals +* Property-style tests or golden IR assertions --- -# PR-07 — PBS Frontend: Parser (Raw AST) +## STOP POINT -### Goal +After PR-25: -Parse PBS source into a raw AST. +* Core IR correctly represents PBS HIP semantics +* Lowering is deterministic and safe +* VM is still unchanged -### Scope +**Only after this point may VM PRs begin.** -* Imports -* Top-level declarations -* Blocks -* Expressions (calls, literals, control flow) - -### Tests - -* valid programs -* syntax error recovery +Any VM work before this is a hard rejection. --- -# PR-08 — PBS Frontend: Symbol Collection and Resolver +## Instruction to Junie -### Goal +If any rule in this document is unclear: -Resolve names, modules, and visibility. +* Stop +* Add a failing test +* Document the ambiguity -### Scope +Do not invent behavior. -* Type namespace vs value namespace -* Visibility rules -* Import resolution - -### Tests - -* duplicate symbols -* invalid imports -* visibility errors - ---- - -# PR-09 — PBS Frontend: Type Checking - -### Goal - -Validate PBS semantics. - -### Scope - -* Primitive types -* Structs -* `optional` and `result` -* Mutability rules -* Return path validation - -### Tests - -* type mismatch -* mutability violations -* implicit `none` behavior - ---- - -# PR-10 — PBS Frontend: Semantic Lowering to Core IR - -### Goal - -Lower typed PBS AST into Core IR. - -### Scope - -* ID-based calls -* ConstPool usage -* Control flow lowering -* SAFE vs HIP effects represented explicitly - -### Tests - -* PBS → Core IR snapshots -* semantic correctness - ---- - -# PR-11 — Host-bound Contracts and Syscall Mapping - -### Goal - -Connect PBS host-bound contracts to runtime syscalls (without executing them). - -### Scope - -* Contract registry -* Mapping: contract.method → syscall id -* Core IR host call nodes - -### Tests - -* invalid contract calls -* correct syscall mapping - ---- - -# PR-12 — Diagnostics Canonicalization - -### Goal - -Standardize diagnostics output. - -### Scope - -* Error codes (`E_*`, `W_*`) -* Stable messages -* Span accuracy - -### Tests - -* golden diagnostics - ---- - -# PR-13 — Backend Integration (VM IR → Bytecode) - -### Goal - -Reconnect the pipeline to the Prometeu runtime backend. - -### Scope - -* VM IR → bytecode emission -* No PBS semantics here - -### Tests - -* bytecode emission smoke test - ---- - -# PR-14 — End-to-End PBS Compile Test - -### Goal - -Prove the full pipeline works. - -### Scope - -* Sample PBS project -* Compile → bytecode -* Diagnostics only (no execution) - -### Tests - -* golden bytecode snapshot - ---- - -## Final Note to Junie - -Do **not** skip PRs. -Do **not** merge multiple PRs together. -If the spec is unclear, create a failing test and document the ambiguity. - -This plan is the authoritative roadmap for PBS frontend implementation. +This document is binding.