use logicaffeine_kernel::prelude::StandardLibrary;
use logicaffeine_kernel::{infer_type, surface_elaborate, Context, Term};
fn g(n: &str) -> Term {
Term::Global(n.to_string())
}
fn app(f: Term, x: Term) -> Term {
Term::App(Box::new(f), Box::new(x))
}
fn arrow(a: Term, b: Term) -> Term {
Term::Pi { param: "_".to_string(), param_type: Box::new(a), body_type: Box::new(b) }
}
fn std_ctx() -> Context {
let mut ctx = Context::new();
StandardLibrary::register(&mut ctx);
ctx
}
fn coe_ctx() -> Context {
let mut ctx = std_ctx();
ctx.add_declaration("natToInt", arrow(g("Nat"), g("Int")));
ctx.add_declaration("f", arrow(g("Int"), g("Int")));
ctx.add_coercion(g("Nat"), g("Int"), g("natToInt"));
ctx
}
#[test]
fn coercion_inserted_on_argument_type_mismatch() {
let ctx = coe_ctx();
let elab = surface_elaborate(&ctx, &app(g("f"), g("Zero"))).expect("elaborates with coercion");
assert_eq!(
elab,
app(g("f"), app(g("natToInt"), g("Zero"))),
"the coercion `natToInt` must be inserted around the argument"
);
assert_eq!(infer_type(&ctx, &elab).unwrap(), g("Int"), "the coerced application type-checks to Int");
}
#[test]
fn no_coercion_when_types_already_match() {
let ctx = coe_ctx();
let already_int = app(g("natToInt"), g("Zero"));
let elab = surface_elaborate(&ctx, &app(g("f"), already_int.clone())).expect("elaborates");
assert_eq!(elab, app(g("f"), already_int), "a well-typed argument is left untouched");
}
#[test]
fn elaboration_fails_without_a_matching_coercion() {
let mut ctx = std_ctx();
ctx.add_declaration("f", arrow(g("Int"), g("Int")));
let res = surface_elaborate(&ctx, &app(g("f"), g("Zero")));
let ill = res.map(|t| infer_type(&ctx, &t).is_ok()).unwrap_or(false);
assert!(!ill, "without a coercion, `f Zero` must not elaborate to a well-typed term");
}