use std::collections::{HashMap, HashSet};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use relon_codegen_llvm::{LlvmAotEvaluator, WorldMode};
use relon_eval_api::{Evaluator, NativeArgs, RelonFunction, RuntimeError, Value};
const SRC: &str = "#main(Int x) -> Int\nadd_seven(x)";
const HOST_FN: &str = "add_seven";
const HOST_SHIM_SRC: &str = r#"
#[no_mangle]
pub extern "C" fn add_seven(x: i64) -> i64 {
x.wrapping_add(7)
}
"#;
fn host_options() -> relon_analyzer::AnalyzeOptions {
let sig = relon_analyzer::FnSignature {
name: HOST_FN.to_string(),
generics: Vec::new(),
params: vec![relon_analyzer::FnParam {
name: "_".to_string(),
ty: relon_analyzer::type_node_simple("Int"),
optional: false,
}],
return_type: relon_analyzer::type_node_simple("Int"),
variadic_tail: None,
};
let mut signatures = HashMap::new();
signatures.insert(HOST_FN.to_string(), sig);
let mut gates = HashMap::new();
gates.insert(HOST_FN.to_string(), relon_analyzer::NativeFnGate::default());
let mut names = HashSet::new();
names.insert(HOST_FN.to_string());
relon_analyzer::AnalyzeOptions {
host_fn_names: names,
host_fn_signatures: signatures,
host_fn_gates: gates,
caps: relon_analyzer::Capabilities::default(),
strict_mode: false,
..Default::default()
}
}
struct AddSeven {
hits: AtomicU64,
}
impl RelonFunction for AddSeven {
fn call(&self, args: NativeArgs, _r: relon_parser::TokenRange) -> Result<Value, RuntimeError> {
self.hits.fetch_add(1, Ordering::SeqCst);
match args.positional.first() {
Some(Value::Int(x)) => Ok(Value::Int(x.wrapping_add(7))),
other => Err(RuntimeError::Unsupported {
reason: format!("AddSeven expects Int, got {other:?}"),
}),
}
}
}
fn open_world_value(x: i64) -> i64 {
let native = Arc::new(AddSeven {
hits: AtomicU64::new(0),
});
let dynn: Arc<dyn RelonFunction> = native.clone();
let mut host_fns: HashMap<String, Arc<dyn RelonFunction>> = HashMap::new();
host_fns.insert(HOST_FN.to_string(), dynn);
let llvm = LlvmAotEvaluator::from_source_with_options(SRC, &host_options())
.expect("open-world build")
.with_host_fns(&host_fns);
let mut args = HashMap::new();
args.insert("x".to_string(), Value::Int(x));
match llvm.run_main(args).expect("open-world dispatch") {
Value::Int(v) => v,
other => panic!("open-world returned non-Int: {other:?}"),
}
}
fn count_calls_to(ir: &str, name: &str) -> usize {
ir.lines()
.filter(|line| {
let l = line.trim_start();
(l.starts_with("call ") || l.starts_with("tail call ") || l.contains(" call "))
&& (l.contains(&format!("@{name}(")) || l.contains(&format!("@{name} ")))
})
.count()
}
#[test]
fn source_driven_closed_world_buffer_inlines_host_fn_and_matches_open_world() {
let cc = LlvmAotEvaluator::from_source_closed_world(SRC, &host_options(), HOST_SHIM_SRC)
.expect("source-driven closed-world buffer build");
let ir = cc.emit_ir_dump().to_string();
if std::env::var_os("RELON_DUMP_COCOMPILE").is_some() {
eprintln!("--- CLOSED-WORLD BUFFER POST-O3 IR ---\n{ir}");
}
assert!(
ir.contains("add i64") || ir.contains("add nsw i64") || ir.contains(", 7"),
"post-O3 IR must contain the inlined `add ... 7` from the host body; got:\n{ir}"
);
assert_eq!(
count_calls_to(&ir, "relon_llvm_call_native"),
0,
"closed-world IR must have ZERO `call @relon_llvm_call_native`; got:\n{ir}"
);
assert_eq!(
count_calls_to(&ir, HOST_FN),
0,
"closed-world IR must have ZERO `call @{HOST_FN}` (host fn must be inlined); got:\n{ir}"
);
let mut args = HashMap::new();
args.insert("x".to_string(), Value::Int(35));
let closed = match cc.run_main(args).expect("closed-world dispatch") {
Value::Int(v) => v,
other => panic!("closed-world returned non-Int: {other:?}"),
};
let open = open_world_value(35);
assert_eq!(open, 42, "open-world oracle (add_seven(35)) must be 42");
assert_eq!(
closed, open,
"closed-world result ({closed}) must byte-match the open-world path ({open})"
);
}
#[test]
fn emit_object_with_options_closed_world_produces_object() {
let dir =
std::env::temp_dir().join(format!("relon_cocompile_buffer_obj_{}", std::process::id()));
std::fs::create_dir_all(&dir).expect("mkdir tmp");
let out = dir.join("closed_world_main.o");
let info = LlvmAotEvaluator::emit_object_with_options(
SRC,
"relon_closed_world_main",
&out,
&host_options(),
WorldMode::ClosedWorld,
Some(HOST_SHIM_SRC),
)
.expect("closed-world emit_object_with_options");
assert_eq!(info.entry_symbol, "relon_closed_world_main");
let meta = std::fs::metadata(&out).expect("object file written");
assert!(meta.len() > 0, "emitted object must be non-empty");
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn emit_object_default_open_world_still_works() {
let dir =
std::env::temp_dir().join(format!("relon_cocompile_buffer_ow_{}", std::process::id()));
std::fs::create_dir_all(&dir).expect("mkdir tmp");
let out = dir.join("plain_main.o");
let info =
LlvmAotEvaluator::emit_object("#main(Int x) -> Int\nx + 1", "relon_plain_main", &out)
.expect("open-world emit_object");
assert_eq!(info.entry_symbol, "relon_plain_main");
assert!(std::fs::metadata(&out).expect("object written").len() > 0);
let _ = std::fs::remove_dir_all(&dir);
}