pr 24
This commit is contained in:
parent
d216918c79
commit
f56353ce9b
@ -1,7 +1,14 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use crate::frontends::pbs::types::PbsType;
|
||||||
|
|
||||||
|
pub struct ContractMethod {
|
||||||
|
pub id: u32,
|
||||||
|
pub params: Vec<PbsType>,
|
||||||
|
pub return_type: PbsType,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ContractRegistry {
|
pub struct ContractRegistry {
|
||||||
mappings: HashMap<String, HashMap<String, u32>>,
|
mappings: HashMap<String, HashMap<String, ContractMethod>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContractRegistry {
|
impl ContractRegistry {
|
||||||
@ -10,81 +17,233 @@ impl ContractRegistry {
|
|||||||
|
|
||||||
// GFX mappings
|
// GFX mappings
|
||||||
let mut gfx = HashMap::new();
|
let mut gfx = HashMap::new();
|
||||||
gfx.insert("clear".to_string(), 0x1001);
|
gfx.insert("clear".to_string(), ContractMethod {
|
||||||
gfx.insert("fillRect".to_string(), 0x1002);
|
id: 0x1001,
|
||||||
gfx.insert("drawLine".to_string(), 0x1003);
|
params: vec![PbsType::Int],
|
||||||
gfx.insert("drawCircle".to_string(), 0x1004);
|
return_type: PbsType::Void,
|
||||||
gfx.insert("drawDisc".to_string(), 0x1005);
|
});
|
||||||
gfx.insert("drawSquare".to_string(), 0x1006);
|
gfx.insert("fillRect".to_string(), ContractMethod {
|
||||||
gfx.insert("setSprite".to_string(), 0x1007);
|
id: 0x1002,
|
||||||
gfx.insert("drawText".to_string(), 0x1008);
|
params: vec![PbsType::Int, PbsType::Int, PbsType::Int, PbsType::Int, PbsType::Int],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
|
gfx.insert("drawLine".to_string(), ContractMethod {
|
||||||
|
id: 0x1003,
|
||||||
|
params: vec![PbsType::Int, PbsType::Int, PbsType::Int, PbsType::Int, PbsType::Int],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
|
gfx.insert("drawCircle".to_string(), ContractMethod {
|
||||||
|
id: 0x1004,
|
||||||
|
params: vec![PbsType::Int, PbsType::Int, PbsType::Int, PbsType::Int],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
|
gfx.insert("drawDisc".to_string(), ContractMethod {
|
||||||
|
id: 0x1005,
|
||||||
|
params: vec![PbsType::Int, PbsType::Int, PbsType::Int, PbsType::Int, PbsType::Int],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
|
gfx.insert("drawSquare".to_string(), ContractMethod {
|
||||||
|
id: 0x1006,
|
||||||
|
params: vec![PbsType::Int, PbsType::Int, PbsType::Int, PbsType::Int],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
|
gfx.insert("setSprite".to_string(), ContractMethod {
|
||||||
|
id: 0x1007,
|
||||||
|
params: vec![PbsType::Int, PbsType::Int, PbsType::Int],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
|
gfx.insert("drawText".to_string(), ContractMethod {
|
||||||
|
id: 0x1008,
|
||||||
|
params: vec![PbsType::Int, PbsType::Int, PbsType::String, PbsType::Int],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
mappings.insert("Gfx".to_string(), gfx);
|
mappings.insert("Gfx".to_string(), gfx);
|
||||||
|
|
||||||
// Input mappings
|
// Input mappings
|
||||||
let mut input = HashMap::new();
|
let mut input = HashMap::new();
|
||||||
input.insert("getPad".to_string(), 0x2001);
|
input.insert("getPad".to_string(), ContractMethod {
|
||||||
input.insert("getPadPressed".to_string(), 0x2002);
|
id: 0x2001,
|
||||||
input.insert("getPadReleased".to_string(), 0x2003);
|
params: vec![PbsType::Int],
|
||||||
input.insert("getPadHold".to_string(), 0x2004);
|
return_type: PbsType::Int,
|
||||||
|
});
|
||||||
|
input.insert("getPadPressed".to_string(), ContractMethod {
|
||||||
|
id: 0x2002,
|
||||||
|
params: vec![PbsType::Int],
|
||||||
|
return_type: PbsType::Int,
|
||||||
|
});
|
||||||
|
input.insert("getPadReleased".to_string(), ContractMethod {
|
||||||
|
id: 0x2003,
|
||||||
|
params: vec![PbsType::Int],
|
||||||
|
return_type: PbsType::Int,
|
||||||
|
});
|
||||||
|
input.insert("getPadHold".to_string(), ContractMethod {
|
||||||
|
id: 0x2004,
|
||||||
|
params: vec![PbsType::Int],
|
||||||
|
return_type: PbsType::Int,
|
||||||
|
});
|
||||||
mappings.insert("Input".to_string(), input);
|
mappings.insert("Input".to_string(), input);
|
||||||
|
|
||||||
// Touch mappings
|
// Touch mappings
|
||||||
let mut touch = HashMap::new();
|
let mut touch = HashMap::new();
|
||||||
touch.insert("getX".to_string(), 0x2101);
|
touch.insert("getX".to_string(), ContractMethod {
|
||||||
touch.insert("getY".to_string(), 0x2102);
|
id: 0x2101,
|
||||||
touch.insert("isDown".to_string(), 0x2103);
|
params: vec![],
|
||||||
touch.insert("isPressed".to_string(), 0x2104);
|
return_type: PbsType::Int,
|
||||||
touch.insert("isReleased".to_string(), 0x2105);
|
});
|
||||||
touch.insert("getHold".to_string(), 0x2106);
|
touch.insert("getY".to_string(), ContractMethod {
|
||||||
|
id: 0x2102,
|
||||||
|
params: vec![],
|
||||||
|
return_type: PbsType::Int,
|
||||||
|
});
|
||||||
|
touch.insert("isDown".to_string(), ContractMethod {
|
||||||
|
id: 0x2103,
|
||||||
|
params: vec![],
|
||||||
|
return_type: PbsType::Bool,
|
||||||
|
});
|
||||||
|
touch.insert("isPressed".to_string(), ContractMethod {
|
||||||
|
id: 0x2104,
|
||||||
|
params: vec![],
|
||||||
|
return_type: PbsType::Bool,
|
||||||
|
});
|
||||||
|
touch.insert("isReleased".to_string(), ContractMethod {
|
||||||
|
id: 0x2105,
|
||||||
|
params: vec![],
|
||||||
|
return_type: PbsType::Bool,
|
||||||
|
});
|
||||||
|
touch.insert("getHold".to_string(), ContractMethod {
|
||||||
|
id: 0x2106,
|
||||||
|
params: vec![],
|
||||||
|
return_type: PbsType::Int,
|
||||||
|
});
|
||||||
mappings.insert("Touch".to_string(), touch);
|
mappings.insert("Touch".to_string(), touch);
|
||||||
|
|
||||||
// Audio mappings
|
// Audio mappings
|
||||||
let mut audio = HashMap::new();
|
let mut audio = HashMap::new();
|
||||||
audio.insert("playSample".to_string(), 0x3001);
|
audio.insert("playSample".to_string(), ContractMethod {
|
||||||
audio.insert("play".to_string(), 0x3002);
|
id: 0x3001,
|
||||||
|
params: vec![PbsType::Int],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
|
audio.insert("play".to_string(), ContractMethod {
|
||||||
|
id: 0x3002,
|
||||||
|
params: vec![PbsType::Int],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
mappings.insert("Audio".to_string(), audio);
|
mappings.insert("Audio".to_string(), audio);
|
||||||
|
|
||||||
// FS mappings
|
// FS mappings
|
||||||
let mut fs = HashMap::new();
|
let mut fs = HashMap::new();
|
||||||
fs.insert("open".to_string(), 0x4001);
|
fs.insert("open".to_string(), ContractMethod {
|
||||||
fs.insert("read".to_string(), 0x4002);
|
id: 0x4001,
|
||||||
fs.insert("write".to_string(), 0x4003);
|
params: vec![PbsType::String, PbsType::String],
|
||||||
fs.insert("close".to_string(), 0x4004);
|
return_type: PbsType::Int,
|
||||||
fs.insert("listDir".to_string(), 0x4005);
|
});
|
||||||
fs.insert("exists".to_string(), 0x4006);
|
fs.insert("read".to_string(), ContractMethod {
|
||||||
fs.insert("delete".to_string(), 0x4007);
|
id: 0x4002,
|
||||||
|
params: vec![PbsType::Int],
|
||||||
|
return_type: PbsType::String,
|
||||||
|
});
|
||||||
|
fs.insert("write".to_string(), ContractMethod {
|
||||||
|
id: 0x4003,
|
||||||
|
params: vec![PbsType::Int, PbsType::String],
|
||||||
|
return_type: PbsType::Int,
|
||||||
|
});
|
||||||
|
fs.insert("close".to_string(), ContractMethod {
|
||||||
|
id: 0x4004,
|
||||||
|
params: vec![PbsType::Int],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
|
fs.insert("listDir".to_string(), ContractMethod {
|
||||||
|
id: 0x4005,
|
||||||
|
params: vec![PbsType::String],
|
||||||
|
return_type: PbsType::String,
|
||||||
|
});
|
||||||
|
fs.insert("exists".to_string(), ContractMethod {
|
||||||
|
id: 0x4006,
|
||||||
|
params: vec![PbsType::String],
|
||||||
|
return_type: PbsType::Bool,
|
||||||
|
});
|
||||||
|
fs.insert("delete".to_string(), ContractMethod {
|
||||||
|
id: 0x4007,
|
||||||
|
params: vec![PbsType::String],
|
||||||
|
return_type: PbsType::Bool,
|
||||||
|
});
|
||||||
mappings.insert("Fs".to_string(), fs);
|
mappings.insert("Fs".to_string(), fs);
|
||||||
|
|
||||||
// Log mappings
|
// Log mappings
|
||||||
let mut log = HashMap::new();
|
let mut log = HashMap::new();
|
||||||
log.insert("write".to_string(), 0x5001);
|
log.insert("write".to_string(), ContractMethod {
|
||||||
log.insert("writeTag".to_string(), 0x5002);
|
id: 0x5001,
|
||||||
|
params: vec![PbsType::Int, PbsType::String],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
|
log.insert("writeTag".to_string(), ContractMethod {
|
||||||
|
id: 0x5002,
|
||||||
|
params: vec![PbsType::Int, PbsType::Int, PbsType::String],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
mappings.insert("Log".to_string(), log);
|
mappings.insert("Log".to_string(), log);
|
||||||
|
|
||||||
// System mappings
|
// System mappings
|
||||||
let mut system = HashMap::new();
|
let mut system = HashMap::new();
|
||||||
system.insert("hasCart".to_string(), 0x0001);
|
system.insert("hasCart".to_string(), ContractMethod {
|
||||||
system.insert("runCart".to_string(), 0x0002);
|
id: 0x0001,
|
||||||
|
params: vec![],
|
||||||
|
return_type: PbsType::Bool,
|
||||||
|
});
|
||||||
|
system.insert("runCart".to_string(), ContractMethod {
|
||||||
|
id: 0x0002,
|
||||||
|
params: vec![],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
mappings.insert("System".to_string(), system);
|
mappings.insert("System".to_string(), system);
|
||||||
|
|
||||||
// Asset mappings
|
// Asset mappings
|
||||||
let mut asset = HashMap::new();
|
let mut asset = HashMap::new();
|
||||||
asset.insert("load".to_string(), 0x6001);
|
asset.insert("load".to_string(), ContractMethod {
|
||||||
asset.insert("status".to_string(), 0x6002);
|
id: 0x6001,
|
||||||
asset.insert("commit".to_string(), 0x6003);
|
params: vec![PbsType::String],
|
||||||
asset.insert("cancel".to_string(), 0x6004);
|
return_type: PbsType::Int,
|
||||||
|
});
|
||||||
|
asset.insert("status".to_string(), ContractMethod {
|
||||||
|
id: 0x6002,
|
||||||
|
params: vec![PbsType::Int],
|
||||||
|
return_type: PbsType::Int,
|
||||||
|
});
|
||||||
|
asset.insert("commit".to_string(), ContractMethod {
|
||||||
|
id: 0x6003,
|
||||||
|
params: vec![PbsType::Int],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
|
asset.insert("cancel".to_string(), ContractMethod {
|
||||||
|
id: 0x6004,
|
||||||
|
params: vec![PbsType::Int],
|
||||||
|
return_type: PbsType::Void,
|
||||||
|
});
|
||||||
mappings.insert("Asset".to_string(), asset);
|
mappings.insert("Asset".to_string(), asset);
|
||||||
|
|
||||||
// Bank mappings
|
// Bank mappings
|
||||||
let mut bank = HashMap::new();
|
let mut bank = HashMap::new();
|
||||||
bank.insert("info".to_string(), 0x6101);
|
bank.insert("info".to_string(), ContractMethod {
|
||||||
bank.insert("slotInfo".to_string(), 0x6102);
|
id: 0x6101,
|
||||||
|
params: vec![PbsType::Int],
|
||||||
|
return_type: PbsType::String,
|
||||||
|
});
|
||||||
|
bank.insert("slotInfo".to_string(), ContractMethod {
|
||||||
|
id: 0x6102,
|
||||||
|
params: vec![PbsType::Int, PbsType::Int],
|
||||||
|
return_type: PbsType::String,
|
||||||
|
});
|
||||||
mappings.insert("Bank".to_string(), bank);
|
mappings.insert("Bank".to_string(), bank);
|
||||||
|
|
||||||
Self { mappings }
|
Self { mappings }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve(&self, contract: &str, method: &str) -> Option<u32> {
|
pub fn resolve(&self, contract: &str, method: &str) -> Option<u32> {
|
||||||
self.mappings.get(contract).and_then(|m| m.get(method)).copied()
|
self.mappings.get(contract).and_then(|m| m.get(method)).map(|m| m.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_method(&self, contract: &str, method: &str) -> Option<&ContractMethod> {
|
||||||
|
self.mappings.get(contract).and_then(|m| m.get(method))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -414,7 +414,7 @@ impl<'a> Lowerer<'a> {
|
|||||||
|
|
||||||
if is_host_contract && !is_shadowed {
|
if is_host_contract && !is_shadowed {
|
||||||
if let Some(syscall_id) = self.contract_registry.resolve(&obj_id.name, &ma.member) {
|
if let Some(syscall_id) = self.contract_registry.resolve(&obj_id.name, &ma.member) {
|
||||||
self.emit(Instr::Syscall(syscall_id));
|
self.emit(Instr::HostCall(syscall_id));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
} else {
|
||||||
self.error("E_RESOLVE_UNDEFINED", format!("Undefined contract member '{}.{}'", obj_id.name, ma.member), ma.span);
|
self.error("E_RESOLVE_UNDEFINED", format!("Undefined contract member '{}.{}'", obj_id.name, ma.member), ma.span);
|
||||||
@ -771,7 +771,7 @@ mod tests {
|
|||||||
declare contract Log host {}
|
declare contract Log host {}
|
||||||
fn main() {
|
fn main() {
|
||||||
Gfx.clear(0);
|
Gfx.clear(0);
|
||||||
Log.write(\"Hello\");
|
Log.write(2, \"Hello\");
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
let mut parser = Parser::new(code, 0);
|
let mut parser = Parser::new(code, 0);
|
||||||
@ -788,9 +788,9 @@ mod tests {
|
|||||||
let instrs: Vec<_> = func.blocks.iter().flat_map(|b| b.instrs.iter()).collect();
|
let instrs: Vec<_> = func.blocks.iter().flat_map(|b| b.instrs.iter()).collect();
|
||||||
|
|
||||||
// Gfx.clear -> 0x1001
|
// Gfx.clear -> 0x1001
|
||||||
assert!(instrs.iter().any(|i| matches!(i, ir_core::Instr::Syscall(0x1001))));
|
assert!(instrs.iter().any(|i| matches!(i, ir_core::Instr::HostCall(0x1001))));
|
||||||
// Log.write -> 0x5001
|
// Log.write -> 0x5001
|
||||||
assert!(instrs.iter().any(|i| matches!(i, ir_core::Instr::Syscall(0x5001))));
|
assert!(instrs.iter().any(|i| matches!(i, ir_core::Instr::HostCall(0x5001))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -148,7 +148,12 @@ impl<'a> TypeChecker<'a> {
|
|||||||
if let Some(sym) = self.module_symbols.type_symbols.get(&id.name) {
|
if let Some(sym) = self.module_symbols.type_symbols.get(&id.name) {
|
||||||
if sym.kind == SymbolKind::Contract && sym.is_host {
|
if sym.kind == SymbolKind::Contract && sym.is_host {
|
||||||
// Check if the method exists in registry
|
// Check if the method exists in registry
|
||||||
if self.contract_registry.resolve(&id.name, &n.member).is_none() {
|
if let Some(method) = self.contract_registry.get_method(&id.name, &n.member) {
|
||||||
|
return PbsType::Function {
|
||||||
|
params: method.params.clone(),
|
||||||
|
return_type: Box::new(method.return_type.clone()),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
self.diagnostics.push(Diagnostic {
|
self.diagnostics.push(Diagnostic {
|
||||||
level: DiagnosticLevel::Error,
|
level: DiagnosticLevel::Error,
|
||||||
code: Some("E_RESOLVE_UNDEFINED".to_string()),
|
code: Some("E_RESOLVE_UNDEFINED".to_string()),
|
||||||
@ -733,6 +738,32 @@ mod tests {
|
|||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_host_method_arity_mismatch() {
|
||||||
|
let code = "
|
||||||
|
declare contract Gfx host {}
|
||||||
|
fn main() {
|
||||||
|
Gfx.clear(0, 1);
|
||||||
|
}
|
||||||
|
";
|
||||||
|
let res = check_code(code);
|
||||||
|
assert!(res.is_err());
|
||||||
|
assert!(res.unwrap_err().contains("E_TYPE_MISMATCH"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_host_method_type_mismatch() {
|
||||||
|
let code = "
|
||||||
|
declare contract Gfx host {}
|
||||||
|
fn main() {
|
||||||
|
Gfx.clear(\"red\");
|
||||||
|
}
|
||||||
|
";
|
||||||
|
let res = check_code(code);
|
||||||
|
assert!(res.is_err());
|
||||||
|
assert!(res.unwrap_err().contains("E_TYPE_MISMATCH"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_void_return_ok() {
|
fn test_void_return_ok() {
|
||||||
let code = "fn main() { return; }";
|
let code = "fn main() { return; }";
|
||||||
|
|||||||
@ -9,7 +9,7 @@ pub enum Instr {
|
|||||||
/// Placeholder for function calls.
|
/// Placeholder for function calls.
|
||||||
Call(FunctionId, u32),
|
Call(FunctionId, u32),
|
||||||
/// Host calls (syscalls).
|
/// Host calls (syscalls).
|
||||||
Syscall(u32),
|
HostCall(u32),
|
||||||
/// Variable access.
|
/// Variable access.
|
||||||
GetLocal(u32),
|
GetLocal(u32),
|
||||||
SetLocal(u32),
|
SetLocal(u32),
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use crate::ir_vm::module::Module;
|
|||||||
pub fn validate_module(_module: &Module) -> Result<(), DiagnosticBundle> {
|
pub fn validate_module(_module: &Module) -> Result<(), DiagnosticBundle> {
|
||||||
// TODO: Implement common IR validations:
|
// TODO: Implement common IR validations:
|
||||||
// - Type checking rules
|
// - Type checking rules
|
||||||
// - Syscall signatures
|
// - HostCall signatures
|
||||||
// - VM invariants
|
// - VM invariants
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,7 +52,7 @@ pub fn lower_function(core_func: &ir_core::Function) -> Result<ir_vm::Function>
|
|||||||
func_id: *func_id,
|
func_id: *func_id,
|
||||||
arg_count: *arg_count
|
arg_count: *arg_count
|
||||||
},
|
},
|
||||||
ir_core::Instr::Syscall(id) => ir_vm::InstrKind::Syscall(*id),
|
ir_core::Instr::HostCall(id) => ir_vm::InstrKind::Syscall(*id),
|
||||||
ir_core::Instr::GetLocal(slot) => ir_vm::InstrKind::GetLocal(*slot),
|
ir_core::Instr::GetLocal(slot) => ir_vm::InstrKind::GetLocal(*slot),
|
||||||
ir_core::Instr::SetLocal(slot) => ir_vm::InstrKind::SetLocal(*slot),
|
ir_core::Instr::SetLocal(slot) => ir_vm::InstrKind::SetLocal(*slot),
|
||||||
ir_core::Instr::Pop => ir_vm::InstrKind::Pop,
|
ir_core::Instr::Pop => ir_vm::InstrKind::Pop,
|
||||||
@ -160,7 +160,7 @@ mod tests {
|
|||||||
Block {
|
Block {
|
||||||
id: 1,
|
id: 1,
|
||||||
instrs: vec![
|
instrs: vec![
|
||||||
Instr::Syscall(42),
|
Instr::HostCall(42),
|
||||||
],
|
],
|
||||||
terminator: Terminator::Return,
|
terminator: Terminator::Return,
|
||||||
},
|
},
|
||||||
@ -202,7 +202,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
match &func.body[5].kind {
|
match &func.body[5].kind {
|
||||||
InstrKind::Syscall(id) => assert_eq!(*id, 42),
|
InstrKind::Syscall(id) => assert_eq!(*id, 42),
|
||||||
_ => panic!("Expected Syscall 42"),
|
_ => panic!("Expected HostCall 42"),
|
||||||
}
|
}
|
||||||
match &func.body[6].kind {
|
match &func.body[6].kind {
|
||||||
InstrKind::Ret => (),
|
InstrKind::Ret => (),
|
||||||
|
|||||||
@ -1,25 +1,3 @@
|
|||||||
# PR-24 — Validate Contract Calls in Frontend (Arity + Types)
|
|
||||||
|
|
||||||
### Goal
|
|
||||||
|
|
||||||
Move contract validation to compile time.
|
|
||||||
|
|
||||||
### Required Changes
|
|
||||||
|
|
||||||
* During PBS type checking:
|
|
||||||
|
|
||||||
* Validate argument count against contract signature
|
|
||||||
* Validate argument types
|
|
||||||
|
|
||||||
* Lower only validated calls to `HostCall`
|
|
||||||
|
|
||||||
### Tests
|
|
||||||
|
|
||||||
* Wrong arity → `E_TYPE_MISMATCH`
|
|
||||||
* Correct call lowers to Core IR `HostCall`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# PR-25 — Core IR Invariants Test Suite
|
# PR-25 — Core IR Invariants Test Suite
|
||||||
|
|
||||||
### Goal
|
### Goal
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user