mod linear_int;
mod linear_recurrence2;
mod simp_normalized;
use crate::ast::{Expr, Spanned, VerifyBlock, VerifyLaw};
use crate::codegen::CodegenContext;
use crate::verify_law::canonical_spec_ref;
use super::super::expr::emit_expr;
use super::shared::{body_terminal_expr, callee_matches_name, find_fn_def, law_simp_defs};
use super::{AutoProof, intro_then};
fn emit_effectful_spec_equivalence_law(
vb: &VerifyBlock,
law: &VerifyLaw,
ctx: &CodegenContext,
intro_names: &[String],
) -> Option<AutoProof> {
let impl_fd = find_fn_def(ctx, &vb.fn_name)?;
if impl_fd.effects.is_empty() {
return None;
}
if !impl_fd
.effects
.iter()
.all(|e| crate::types::checker::effect_classification::is_classified(&e.node))
{
return None;
}
if let Some((impl_name, _impl_args, spec_name)) = match_impl_spec_call_shape(vb, law) {
let simp_defs = [
aver_name_to_lean_ident(&impl_name),
aver_name_to_lean_ident(&spec_name),
];
return Some(AutoProof {
support_lines: Vec::new(),
proof_lines: intro_then(
intro_names,
vec![format!("simp [{}]", simp_defs.join(", "))],
),
replaces_theorem: false,
});
}
let _impl_name = match_impl_call_one_side(vb, law)?;
let simp_defs: Vec<String> = law_simp_defs(ctx, vb, law).into_iter().collect();
Some(AutoProof {
support_lines: Vec::new(),
proof_lines: intro_then(
intro_names,
vec![format!("simp [{}]", simp_defs.join(", "))],
),
replaces_theorem: false,
})
}
fn match_impl_call_one_side(vb: &VerifyBlock, law: &VerifyLaw) -> Option<String> {
let side_calls_vb = |e: &Spanned<Expr>| -> bool {
let Expr::FnCall(callee, _) = &e.node else {
return false;
};
expr_fn_name(callee).as_deref() == Some(vb.fn_name.as_str())
};
if side_calls_vb(&law.lhs) || side_calls_vb(&law.rhs) {
Some(vb.fn_name.clone())
} else {
None
}
}
fn match_impl_spec_call_shape(
vb: &VerifyBlock,
law: &VerifyLaw,
) -> Option<(String, Vec<Spanned<Expr>>, String)> {
let try_side = |lhs: &Spanned<Expr>,
rhs: &Spanned<Expr>|
-> Option<(String, Vec<Spanned<Expr>>, String)> {
let Expr::FnCall(l_callee, l_args) = &lhs.node else {
return None;
};
let Expr::FnCall(r_callee, r_args) = &rhs.node else {
return None;
};
if l_args != r_args {
return None;
}
let l_name = expr_fn_name(l_callee)?;
let r_name = expr_fn_name(r_callee)?;
if l_name == r_name {
return None;
}
if l_name != vb.fn_name {
return None;
}
Some((l_name, l_args.clone(), r_name))
};
try_side(&law.lhs, &law.rhs).or_else(|| try_side(&law.rhs, &law.lhs))
}
fn expr_fn_name(expr: &Spanned<Expr>) -> Option<String> {
match &expr.node {
Expr::Ident(name) | Expr::Resolved { name, .. } => Some(name.clone()),
_ => None,
}
}
fn aver_name_to_lean_ident(name: &str) -> String {
super::super::expr::aver_name_to_lean(name)
}
pub(super) fn emit_spec_function_equivalence_law(
vb: &VerifyBlock,
law: &VerifyLaw,
ctx: &CodegenContext,
intro_names: &[String],
) -> Option<AutoProof> {
if let Some(proof) = emit_effectful_spec_equivalence_law(vb, law, ctx, intro_names) {
return Some(proof);
}
let spec_ref = canonical_spec_ref(&vb.fn_name, law, &ctx.fn_sigs)?;
let impl_fd = find_fn_def(ctx, &vb.fn_name)?;
let spec_fd = find_fn_def(ctx, &spec_ref.spec_fn_name)?;
let impl_body = body_terminal_expr(impl_fd.body.as_ref())?;
let spec_body = body_terminal_expr(spec_fd.body.as_ref())?;
let bodies_match_exactly = emit_expr(impl_body, ctx) == emit_expr(spec_body, ctx);
let try_side = |impl_side: &Spanned<Expr>, spec_side: &Spanned<Expr>| -> Option<AutoProof> {
let Expr::FnCall(impl_callee, impl_args) = &impl_side.node else {
return None;
};
let Expr::FnCall(spec_callee, spec_args) = &spec_side.node else {
return None;
};
if !callee_matches_name(impl_callee, &vb.fn_name)
|| !callee_matches_name(spec_callee, &spec_ref.spec_fn_name)
|| impl_args != spec_args
{
return None;
}
let simp_defs = law_simp_defs(ctx, vb, law).into_iter().collect::<Vec<_>>();
Some(AutoProof {
support_lines: Vec::new(),
proof_lines: intro_then(
intro_names,
vec![format!("simpa [{}]", simp_defs.join(", "))],
),
replaces_theorem: false,
})
};
if bodies_match_exactly {
return try_side(&law.lhs, &law.rhs).or_else(|| try_side(&law.rhs, &law.lhs));
}
simp_normalized::emit_simp_normalized_spec_equivalence_law(vb, law, ctx, intro_names)
.or_else(|| {
linear_recurrence2::emit_second_order_linear_recurrence_spec_equivalence_law(
vb,
law,
ctx,
intro_names,
)
})
.or_else(|| {
linear_int::emit_linear_int_omega_spec_equivalence_law(vb, law, ctx, intro_names)
})
}