This commit is contained in:
Nilton Constantino 2026-01-29 17:16:01 +00:00
parent fe00eda925
commit 603c8e7862
No known key found for this signature in database
2 changed files with 104 additions and 55 deletions

View File

@ -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 = "

View File

@ -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