pr 22
This commit is contained in:
parent
603c8e7862
commit
dc3a0268f1
@ -13,8 +13,11 @@ pub struct Lowerer<'a> {
|
|||||||
current_block: Option<Block>,
|
current_block: Option<Block>,
|
||||||
next_block_id: u32,
|
next_block_id: u32,
|
||||||
next_func_id: u32,
|
next_func_id: u32,
|
||||||
|
next_type_id: u32,
|
||||||
local_vars: Vec<HashMap<String, u32>>,
|
local_vars: Vec<HashMap<String, u32>>,
|
||||||
function_ids: HashMap<String, FunctionId>,
|
function_ids: HashMap<String, FunctionId>,
|
||||||
|
type_ids: HashMap<String, TypeId>,
|
||||||
|
struct_slots: HashMap<String, u32>,
|
||||||
contract_registry: ContractRegistry,
|
contract_registry: ContractRegistry,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,8 +33,11 @@ impl<'a> Lowerer<'a> {
|
|||||||
current_block: None,
|
current_block: None,
|
||||||
next_block_id: 0,
|
next_block_id: 0,
|
||||||
next_func_id: 1,
|
next_func_id: 1,
|
||||||
|
next_type_id: 1,
|
||||||
local_vars: Vec::new(),
|
local_vars: Vec::new(),
|
||||||
function_ids: HashMap::new(),
|
function_ids: HashMap::new(),
|
||||||
|
type_ids: HashMap::new(),
|
||||||
|
struct_slots: HashMap::new(),
|
||||||
contract_registry: ContractRegistry::new(),
|
contract_registry: ContractRegistry::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,6 +50,17 @@ impl<'a> Lowerer<'a> {
|
|||||||
self.next_func_id += 1;
|
self.next_func_id += 1;
|
||||||
self.function_ids.insert(n.name.clone(), id);
|
self.function_ids.insert(n.name.clone(), id);
|
||||||
}
|
}
|
||||||
|
if let Node::TypeDecl(n) = decl {
|
||||||
|
let id = TypeId(self.next_type_id);
|
||||||
|
self.next_type_id += 1;
|
||||||
|
self.type_ids.insert(n.name.clone(), id);
|
||||||
|
|
||||||
|
if n.type_kind == "struct" {
|
||||||
|
if let Node::TypeBody(body) = &*n.body {
|
||||||
|
self.struct_slots.insert(n.name.clone(), body.members.len() as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut module = Module {
|
let mut module = Module {
|
||||||
@ -142,11 +159,46 @@ impl<'a> Lowerer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_alloc(&mut self, _n: &AllocNode) {
|
fn lower_alloc(&mut self, n: &AllocNode) {
|
||||||
// Allocation: Now requires explicit TypeId and slots.
|
let (ty_id, slots) = self.get_type_id_and_slots(&n.ty);
|
||||||
// v0 approximation: TypeId(0), slots: 1.
|
self.emit(Instr::Alloc { ty: ty_id, slots });
|
||||||
// Proper derivation will be added in PR-22.
|
}
|
||||||
self.emit(Instr::Alloc { ty: TypeId(0), slots: 1 });
|
|
||||||
|
fn get_type_id_and_slots(&mut self, node: &Node) -> (TypeId, u32) {
|
||||||
|
match node {
|
||||||
|
Node::TypeName(n) => {
|
||||||
|
let slots = self.struct_slots.get(&n.name).cloned().unwrap_or(1);
|
||||||
|
let id = self.get_or_create_type_id(&n.name);
|
||||||
|
(id, slots)
|
||||||
|
}
|
||||||
|
Node::TypeApp(ta) if ta.base == "array" => {
|
||||||
|
let size = if ta.args.len() > 1 {
|
||||||
|
if let Node::IntLit(il) = &ta.args[1] {
|
||||||
|
il.value as u32
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
let elem_ty = self.lower_type_node(&ta.args[0]);
|
||||||
|
let name = format!("array<{}>[{}]", elem_ty, size);
|
||||||
|
let id = self.get_or_create_type_id(&name);
|
||||||
|
(id, size)
|
||||||
|
}
|
||||||
|
_ => (TypeId(0), 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_or_create_type_id(&mut self, name: &str) -> TypeId {
|
||||||
|
if let Some(id) = self.type_ids.get(name) {
|
||||||
|
*id
|
||||||
|
} else {
|
||||||
|
let id = TypeId(self.next_type_id);
|
||||||
|
self.next_type_id += 1;
|
||||||
|
self.type_ids.insert(name.to_string(), id);
|
||||||
|
id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_peek(&mut self, n: &PeekNode) {
|
fn lower_peek(&mut self, n: &PeekNode) {
|
||||||
@ -363,7 +415,7 @@ impl<'a> Lowerer<'a> {
|
|||||||
self.start_block_with_id(merge_id);
|
self.start_block_with_id(merge_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_type_node(&self, node: &Node) -> Type {
|
fn lower_type_node(&mut self, node: &Node) -> Type {
|
||||||
match node {
|
match node {
|
||||||
Node::TypeName(n) => match n.name.as_str() {
|
Node::TypeName(n) => match n.name.as_str() {
|
||||||
"int" => Type::Int,
|
"int" => Type::Int,
|
||||||
@ -373,6 +425,30 @@ impl<'a> Lowerer<'a> {
|
|||||||
"void" => Type::Void,
|
"void" => Type::Void,
|
||||||
_ => Type::Struct(n.name.clone()),
|
_ => Type::Struct(n.name.clone()),
|
||||||
},
|
},
|
||||||
|
Node::TypeApp(ta) => {
|
||||||
|
if ta.base == "array" {
|
||||||
|
let elem_ty = self.lower_type_node(&ta.args[0]);
|
||||||
|
let size = if ta.args.len() > 1 {
|
||||||
|
if let Node::IntLit(il) = &ta.args[1] {
|
||||||
|
il.value as u32
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
Type::Array(Box::new(elem_ty), size)
|
||||||
|
} else if ta.base == "optional" {
|
||||||
|
Type::Optional(Box::new(self.lower_type_node(&ta.args[0])))
|
||||||
|
} else if ta.base == "result" {
|
||||||
|
Type::Result(
|
||||||
|
Box::new(self.lower_type_node(&ta.args[0])),
|
||||||
|
Box::new(self.lower_type_node(&ta.args[1]))
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Type::Struct(format!("{}<{}>", ta.base, ta.args.len()))
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => Type::Void,
|
_ => Type::Void,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -709,4 +785,105 @@ mod tests {
|
|||||||
// Should be a regular call (which might fail later or be a dummy)
|
// Should be a regular call (which might fail later or be a dummy)
|
||||||
assert!(instrs.iter().any(|i| matches!(i, ir_core::Instr::Call(_, _))));
|
assert!(instrs.iter().any(|i| matches!(i, ir_core::Instr::Call(_, _))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_alloc_struct_slots() {
|
||||||
|
let code = "
|
||||||
|
declare struct Vec3 {
|
||||||
|
x: int,
|
||||||
|
y: int,
|
||||||
|
z: int
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let v = alloc Vec3;
|
||||||
|
}
|
||||||
|
";
|
||||||
|
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();
|
||||||
|
|
||||||
|
let alloc = instrs.iter().find_map(|i| {
|
||||||
|
if let Instr::Alloc { ty, slots } = i {
|
||||||
|
Some((ty, slots))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}).expect("Should have Alloc instruction");
|
||||||
|
|
||||||
|
assert_eq!(*alloc.1, 3, "Vec3 should have 3 slots");
|
||||||
|
assert!(alloc.0.0 > 0, "Should have a valid TypeId");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_alloc_array_slots() {
|
||||||
|
let code = "
|
||||||
|
fn main() {
|
||||||
|
let a = alloc array<int>[10b];
|
||||||
|
}
|
||||||
|
";
|
||||||
|
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();
|
||||||
|
|
||||||
|
let alloc = instrs.iter().find_map(|i| {
|
||||||
|
if let Instr::Alloc { ty, slots } = i {
|
||||||
|
Some((ty, slots))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}).expect("Should have Alloc instruction");
|
||||||
|
|
||||||
|
assert_eq!(*alloc.1, 10, "array<int>[10b] should have 10 slots");
|
||||||
|
assert!(alloc.0.0 > 0, "Should have a valid TypeId");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_alloc_primitive_slots() {
|
||||||
|
let code = "
|
||||||
|
fn main() {
|
||||||
|
let x = alloc int;
|
||||||
|
}
|
||||||
|
";
|
||||||
|
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();
|
||||||
|
|
||||||
|
let alloc = instrs.iter().find_map(|i| {
|
||||||
|
if let Instr::Alloc { ty, slots } = i {
|
||||||
|
Some((ty, slots))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}).expect("Should have Alloc instruction");
|
||||||
|
|
||||||
|
assert_eq!(*alloc.1, 1, "Primitive int should have 1 slot");
|
||||||
|
assert!(alloc.0.0 > 0, "Should have a valid TypeId");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -328,7 +328,7 @@ impl Parser {
|
|||||||
fn parse_type_ref(&mut self) -> Result<Node, DiagnosticBundle> {
|
fn parse_type_ref(&mut self) -> Result<Node, DiagnosticBundle> {
|
||||||
let id_tok = self.peek().clone();
|
let id_tok = self.peek().clone();
|
||||||
let name = self.expect_identifier()?;
|
let name = self.expect_identifier()?;
|
||||||
if self.peek().kind == TokenKind::Lt {
|
let mut node = if self.peek().kind == TokenKind::Lt {
|
||||||
self.advance(); // <
|
self.advance(); // <
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
@ -340,17 +340,56 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let end_tok = self.consume(TokenKind::Gt)?;
|
let end_tok = self.consume(TokenKind::Gt)?;
|
||||||
Ok(Node::TypeApp(TypeAppNode {
|
Node::TypeApp(TypeAppNode {
|
||||||
span: Span::new(self.file_id, id_tok.span.start, end_tok.span.end),
|
span: Span::new(self.file_id, id_tok.span.start, end_tok.span.end),
|
||||||
base: name,
|
base: name,
|
||||||
args,
|
args,
|
||||||
}))
|
})
|
||||||
} else {
|
} else {
|
||||||
Ok(Node::TypeName(TypeNameNode {
|
Node::TypeName(TypeNameNode {
|
||||||
span: id_tok.span,
|
span: id_tok.span,
|
||||||
name,
|
name,
|
||||||
}))
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.peek().kind == TokenKind::OpenBracket {
|
||||||
|
self.advance();
|
||||||
|
let size_tok = self.peek().clone();
|
||||||
|
let size = match size_tok.kind {
|
||||||
|
TokenKind::IntLit(v) => {
|
||||||
|
self.advance();
|
||||||
|
v as u32
|
||||||
|
}
|
||||||
|
TokenKind::BoundedLit(v) => {
|
||||||
|
self.advance();
|
||||||
|
v
|
||||||
|
}
|
||||||
|
_ => return Err(self.error_with_code("integer or bounded literal for array size", Some("E_PARSE_EXPECTED_TOKEN"))),
|
||||||
|
};
|
||||||
|
let end_tok = self.consume(TokenKind::CloseBracket)?;
|
||||||
|
let span = Span::new(self.file_id, node.span().start, end_tok.span.end);
|
||||||
|
|
||||||
|
// If it's array<T>[N], we want to represent it cleanly.
|
||||||
|
// Currently TypeAppNode { base: name, args } was created.
|
||||||
|
// If base was "array", it already has T in args.
|
||||||
|
// We can just add N to args.
|
||||||
|
match &mut node {
|
||||||
|
Node::TypeApp(ta) if ta.base == "array" => {
|
||||||
|
ta.args.push(Node::IntLit(IntLitNode { span: size_tok.span, value: size as i64 }));
|
||||||
|
ta.span = span;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Fallback for T[N] if we want to support it, but spec says array<T>[N]
|
||||||
|
node = Node::TypeApp(TypeAppNode {
|
||||||
|
span,
|
||||||
|
base: "array".to_string(),
|
||||||
|
args: vec![node, Node::IntLit(IntLitNode { span: size_tok.span, value: size as i64 })],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_block(&mut self) -> Result<Node, DiagnosticBundle> {
|
fn parse_block(&mut self) -> Result<Node, DiagnosticBundle> {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
@ -13,8 +14,38 @@ pub enum Type {
|
|||||||
Service(String),
|
Service(String),
|
||||||
Contract(String),
|
Contract(String),
|
||||||
ErrorType(String),
|
ErrorType(String),
|
||||||
|
Array(Box<Type>, u32),
|
||||||
Function {
|
Function {
|
||||||
params: Vec<Type>,
|
params: Vec<Type>,
|
||||||
return_type: Box<Type>,
|
return_type: Box<Type>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Type {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Type::Void => write!(f, "void"),
|
||||||
|
Type::Int => write!(f, "int"),
|
||||||
|
Type::Float => write!(f, "float"),
|
||||||
|
Type::Bool => write!(f, "bool"),
|
||||||
|
Type::String => write!(f, "string"),
|
||||||
|
Type::Optional(inner) => write!(f, "optional<{}>", inner),
|
||||||
|
Type::Result(ok, err) => write!(f, "result<{}, {}>", ok, err),
|
||||||
|
Type::Struct(name) => write!(f, "{}", name),
|
||||||
|
Type::Service(name) => write!(f, "{}", name),
|
||||||
|
Type::Contract(name) => write!(f, "{}", name),
|
||||||
|
Type::ErrorType(name) => write!(f, "{}", name),
|
||||||
|
Type::Array(inner, size) => write!(f, "array<{}>[{}]", inner, size),
|
||||||
|
Type::Function { params, return_type } => {
|
||||||
|
write!(f, "fn(")?;
|
||||||
|
for (i, param) in params.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
write!(f, "{}", param)?;
|
||||||
|
}
|
||||||
|
write!(f, ") -> {}", return_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -116,13 +116,14 @@ fn lower_type(ty: &ir_core::Type) -> ir_vm::Type {
|
|||||||
ir_core::Type::Float => ir_vm::Type::Float,
|
ir_core::Type::Float => ir_vm::Type::Float,
|
||||||
ir_core::Type::Bool => ir_vm::Type::Bool,
|
ir_core::Type::Bool => ir_vm::Type::Bool,
|
||||||
ir_core::Type::String => ir_vm::Type::String,
|
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::Optional(inner) => ir_vm::Type::Array(Box::new(lower_type(inner))),
|
||||||
ir_core::Type::Result(ok, _) => lower_type(ok), // Approximation
|
ir_core::Type::Result(ok, _) => lower_type(ok),
|
||||||
ir_core::Type::Struct(_) => ir_vm::Type::Object,
|
ir_core::Type::Struct(_)
|
||||||
ir_core::Type::Service(_) => ir_vm::Type::Object,
|
| ir_core::Type::Service(_)
|
||||||
ir_core::Type::Contract(_) => ir_vm::Type::Object,
|
| ir_core::Type::Contract(_)
|
||||||
ir_core::Type::ErrorType(_) => ir_vm::Type::Object,
|
| ir_core::Type::ErrorType(_) => ir_vm::Type::Object,
|
||||||
ir_core::Type::Function { .. } => ir_vm::Type::Function,
|
ir_core::Type::Function { .. } => ir_vm::Type::Function,
|
||||||
|
ir_core::Type::Array(inner, _) => ir_vm::Type::Array(Box::new(lower_type(inner))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,29 +1,3 @@
|
|||||||
# PR-22 — Make Allocation Shape Explicit in Core IR
|
|
||||||
|
|
||||||
### Goal
|
|
||||||
|
|
||||||
Stop implicit / guessed heap layouts.
|
|
||||||
|
|
||||||
### Required Changes
|
|
||||||
|
|
||||||
* Replace any shape-less `Alloc` with:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
Alloc { ty: TypeId, slots: u32 }
|
|
||||||
```
|
|
||||||
|
|
||||||
Rules:
|
|
||||||
|
|
||||||
* `TypeId` comes from frontend type checking
|
|
||||||
* `slots` is derived deterministically (struct fields / array size)
|
|
||||||
|
|
||||||
### Tests
|
|
||||||
|
|
||||||
* Allocating storage struct emits correct `slots`
|
|
||||||
* Allocating array emits correct `slots`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# PR-23 — Eliminate Invalid Call Fallbacks
|
# PR-23 — Eliminate Invalid Call Fallbacks
|
||||||
|
|
||||||
### Goal
|
### Goal
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user