diff --git a/crates/prometeu-compiler/src/frontends/pbs/lowering.rs b/crates/prometeu-compiler/src/frontends/pbs/lowering.rs index 63c21b04..a2d0d070 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/lowering.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/lowering.rs @@ -135,9 +135,9 @@ impl<'a> Lowerer<'a> { Node::Unary(n) => self.lower_unary(n), Node::IfExpr(n) => self.lower_if_expr(n), Node::Alloc(n) => self.lower_alloc(n), - Node::Mutate(n) => self.lower_hip(n.span, &n.target, &n.binding, &n.body, "mutate"), - Node::Borrow(n) => self.lower_hip(n.span, &n.target, &n.binding, &n.body, "borrow"), - Node::Peek(n) => self.lower_hip(n.span, &n.target, &n.binding, &n.body, "peek"), + Node::Mutate(n) => self.lower_mutate(n), + Node::Borrow(n) => self.lower_borrow(n), + Node::Peek(n) => self.lower_peek(n), _ => {} } } @@ -149,44 +149,83 @@ impl<'a> Lowerer<'a> { self.emit(Instr::Alloc { ty: TypeId(0), slots: 1 }); } - fn lower_hip(&mut self, _span: crate::common::spans::Span, target: &Node, binding: &str, body: &Node, op: &str) { - // HIP Access Pattern (Explicit Semantics): + fn lower_peek(&mut self, n: &PeekNode) { // 1. Evaluate target (gate) - self.lower_node(target); + self.lower_node(&n.target); - // 2. Preserve gate identity. - // We MUST NOT lose the gate, as storage access must be mediated by it. + // 2. Preserve gate identity let gate_slot = self.get_next_local_slot(); self.local_vars.last_mut().unwrap().insert(format!("$gate_{}", gate_slot), gate_slot); self.emit(Instr::SetLocal(gate_slot)); - // 3. Begin Operation. - // This instruction reads the gate from the local and pushes a view. - let instr = match op { - "peek" => Instr::BeginPeek { gate: ValueId(gate_slot) }, - "borrow" => Instr::BeginBorrow { gate: ValueId(gate_slot) }, - "mutate" => Instr::BeginMutate { gate: ValueId(gate_slot) }, - _ => unreachable!(), - }; - self.emit(instr); + // 3. Begin Operation + self.emit(Instr::BeginPeek { gate: ValueId(gate_slot) }); // 4. Bind view to local self.local_vars.push(HashMap::new()); let view_slot = self.get_next_local_slot(); - self.local_vars.last_mut().unwrap().insert(binding.to_string(), view_slot); + self.local_vars.last_mut().unwrap().insert(n.binding.to_string(), view_slot); self.emit(Instr::SetLocal(view_slot)); // 5. Body - self.lower_node(body); + self.lower_node(&n.body); // 6. End Operation - let end_instr = match op { - "peek" => Instr::EndPeek, - "borrow" => Instr::EndBorrow, - "mutate" => Instr::EndMutate, - _ => unreachable!(), - }; - self.emit(end_instr); + self.emit(Instr::EndPeek); + + self.local_vars.pop(); + } + + fn lower_borrow(&mut self, n: &BorrowNode) { + // 1. Evaluate target (gate) + self.lower_node(&n.target); + + // 2. Preserve gate identity + let gate_slot = self.get_next_local_slot(); + self.local_vars.last_mut().unwrap().insert(format!("$gate_{}", gate_slot), gate_slot); + self.emit(Instr::SetLocal(gate_slot)); + + // 3. Begin Operation + self.emit(Instr::BeginBorrow { gate: ValueId(gate_slot) }); + + // 4. Bind view to local + self.local_vars.push(HashMap::new()); + let view_slot = self.get_next_local_slot(); + self.local_vars.last_mut().unwrap().insert(n.binding.to_string(), view_slot); + self.emit(Instr::SetLocal(view_slot)); + + // 5. Body + self.lower_node(&n.body); + + // 6. End Operation + self.emit(Instr::EndBorrow); + + self.local_vars.pop(); + } + + fn lower_mutate(&mut self, n: &MutateNode) { + // 1. Evaluate target (gate) + self.lower_node(&n.target); + + // 2. Preserve gate identity + let gate_slot = self.get_next_local_slot(); + self.local_vars.last_mut().unwrap().insert(format!("$gate_{}", gate_slot), gate_slot); + self.emit(Instr::SetLocal(gate_slot)); + + // 3. Begin Operation + self.emit(Instr::BeginMutate { gate: ValueId(gate_slot) }); + + // 4. Bind view to local + self.local_vars.push(HashMap::new()); + let view_slot = self.get_next_local_slot(); + self.local_vars.last_mut().unwrap().insert(n.binding.to_string(), view_slot); + self.emit(Instr::SetLocal(view_slot)); + + // 5. Body + self.lower_node(&n.body); + + // 6. End Operation + self.emit(Instr::EndMutate); self.local_vars.pop(); } @@ -525,6 +564,45 @@ mod tests { assert!(!json.contains("WriteGate"), "WriteGate should be gone"); } + #[test] + fn test_hip_semantics_distinction() { + let code = " + fn test_hip(g: int) { + peek g as p { + let x = p; + } + borrow g as b { + let y = b; + } + mutate g as m { + let z = m; + } + } + "; + let mut parser = Parser::new(code, 0); + let ast = parser.parse_file().expect("Failed to parse"); + + let mut collector = SymbolCollector::new(); + let (type_symbols, value_symbols) = collector.collect(&ast).expect("Failed to collect symbols"); + let module_symbols = ModuleSymbols { type_symbols, value_symbols }; + + let lowerer = Lowerer::new(&module_symbols); + let program = lowerer.lower_file(&ast, "test"); + + let func = &program.modules[0].functions[0]; + let instrs: Vec<_> = func.blocks.iter().flat_map(|b| b.instrs.iter()).collect(); + + // Assert distinct Core IR instruction sequences + assert!(instrs.iter().any(|i| matches!(i, Instr::BeginPeek { .. }))); + assert!(instrs.iter().any(|i| matches!(i, Instr::EndPeek))); + + assert!(instrs.iter().any(|i| matches!(i, Instr::BeginBorrow { .. }))); + assert!(instrs.iter().any(|i| matches!(i, Instr::EndBorrow))); + + assert!(instrs.iter().any(|i| matches!(i, Instr::BeginMutate { .. }))); + assert!(instrs.iter().any(|i| matches!(i, Instr::EndMutate))); + } + #[test] fn test_host_contract_call_lowering() { let code = " diff --git a/docs/specs/pbs/files/PRs para Junie.md b/docs/specs/pbs/files/PRs para Junie.md index 38d1e776..d5f7f2ce 100644 --- a/docs/specs/pbs/files/PRs para Junie.md +++ b/docs/specs/pbs/files/PRs para Junie.md @@ -1,32 +1,3 @@ -# PR-21 — Distinguish `peek`, `borrow`, and `mutate` in Core IR - -### Goal - -Restore the semantic distinction mandated by PBS. - -### Required Semantics - -| Operation | Effect | -| --------- | ------------------------------- | -| `peek` | Copy storage → stack value | -| `borrow` | Temporary read-only view | -| `mutate` | Temporary mutable view + commit | - -### Required Changes - -* Lower PBS `peek` → `BeginPeek` / `EndPeek` -* Lower PBS `borrow` → `BeginBorrow` / `EndBorrow` -* Lower PBS `mutate` → `BeginMutate` / `EndMutate` - -These **must not** share the same lowering path. - -### Tests - -* PBS snippet with all three operations -* Assert distinct Core IR instruction sequences - ---- - # PR-22 — Make Allocation Shape Explicit in Core IR ### Goal