use crate::error::ProofResult;
use crate::{DerivationTree, InferenceRule, ProofExpr, ProofGoal, ProofTerm};
use logicaffeine_verify::ir::{VerifyExpr, VerifyOp, VerifyType};
use logicaffeine_verify::solver::VerificationSession;
fn contains_inductive_constructs(expr: &ProofExpr) -> bool {
match expr {
ProofExpr::Ctor { .. } => true,
ProofExpr::TypedVar { .. } => true,
ProofExpr::Match { .. } => true,
ProofExpr::Fixpoint { .. } => true,
ProofExpr::And(l, r)
| ProofExpr::Or(l, r)
| ProofExpr::Implies(l, r)
| ProofExpr::Iff(l, r) => {
contains_inductive_constructs(l) || contains_inductive_constructs(r)
}
ProofExpr::Not(inner) => contains_inductive_constructs(inner),
ProofExpr::ForAll { body, .. } | ProofExpr::Exists { body, .. } => {
contains_inductive_constructs(body)
}
ProofExpr::Identity(l, r) => {
contains_inductive_constructs_term(l) || contains_inductive_constructs_term(r)
}
ProofExpr::Predicate { args, .. } => {
args.iter().any(contains_inductive_constructs_term)
}
_ => false,
}
}
fn contains_inductive_constructs_term(term: &ProofTerm) -> bool {
match term {
ProofTerm::Function(name, args) => {
matches!(name.as_str(), "Zero" | "Succ" | "Nil" | "Cons")
|| args.iter().any(contains_inductive_constructs_term)
}
ProofTerm::Variable(v) | ProofTerm::BoundVarRef(v) => {
v.contains(':')
}
ProofTerm::Group(terms) => terms.iter().any(contains_inductive_constructs_term),
ProofTerm::Constant(_) => false,
}
}
pub fn try_oracle(
goal: &ProofGoal,
knowledge_base: &[ProofExpr],
) -> ProofResult<Option<DerivationTree>> {
if contains_inductive_constructs(&goal.target) {
return Ok(None);
}
for kb_expr in knowledge_base {
if contains_inductive_constructs(kb_expr) {
return Ok(None);
}
}
let mut session = VerificationSession::new();
let mut types = TypeInference::new();
types.infer_from_expr(&goal.target);
for ctx_expr in &goal.context {
types.infer_from_expr(ctx_expr);
}
for kb_expr in knowledge_base {
types.infer_from_expr(kb_expr);
}
for (name, ty) in types.variables.iter() {
session.declare(name, *ty);
}
for ctx_expr in &goal.context {
if let Some(verify_expr) = proof_expr_to_verify_expr(ctx_expr) {
session.assume(&verify_expr);
}
}
for kb_expr in knowledge_base {
if let Some(verify_expr) = proof_expr_to_verify_expr(kb_expr) {
session.assume(&verify_expr);
}
}
let goal_expr = match proof_expr_to_verify_expr(&goal.target) {
Some(e) => e,
None => return Ok(None), };
match session.verify(&goal_expr) {
Ok(()) => {
let tree = DerivationTree::leaf(
goal.target.clone(),
InferenceRule::OracleVerification("Verified by Z3".into()),
);
Ok(Some(tree))
}
Err(_) => {
Ok(None)
}
}
}
struct TypeInference {
variables: std::collections::HashMap<String, VerifyType>,
}
impl TypeInference {
fn new() -> Self {
Self {
variables: std::collections::HashMap::new(),
}
}
fn infer_from_expr(&mut self, expr: &ProofExpr) {
match expr {
ProofExpr::Predicate { args, .. } => {
for arg in args {
self.infer_from_term(arg, VerifyType::Int);
}
}
ProofExpr::Identity(left, right) => {
self.infer_from_term(left, VerifyType::Int);
self.infer_from_term(right, VerifyType::Int);
}
ProofExpr::Atom(name) => {
self.variables.insert(name.clone(), VerifyType::Bool);
}
ProofExpr::And(left, right)
| ProofExpr::Or(left, right)
| ProofExpr::Implies(left, right)
| ProofExpr::Iff(left, right) => {
self.infer_from_expr(left);
self.infer_from_expr(right);
}
ProofExpr::Not(inner) => {
self.infer_from_expr(inner);
}
ProofExpr::ForAll { body, .. } | ProofExpr::Exists { body, .. } => {
self.infer_from_expr(body);
}
_ => {}
}
}
fn infer_from_term(&mut self, term: &ProofTerm, context_type: VerifyType) {
match term {
ProofTerm::Variable(name) | ProofTerm::BoundVarRef(name) => {
if !self.variables.contains_key(name) {
self.variables.insert(name.clone(), context_type);
}
}
ProofTerm::Function(_, args) => {
for arg in args {
self.infer_from_term(arg, VerifyType::Int);
}
}
ProofTerm::Group(terms) => {
for t in terms {
self.infer_from_term(t, VerifyType::Int);
}
}
ProofTerm::Constant(_) => {
}
}
}
}
pub fn proof_expr_to_verify_expr(expr: &ProofExpr) -> Option<VerifyExpr> {
match expr {
ProofExpr::Atom(name) => Some(VerifyExpr::var(name)),
ProofExpr::Predicate { name, args, .. } => {
if args.len() == 2 {
let left = proof_term_to_verify_expr(&args[0])?;
let right = proof_term_to_verify_expr(&args[1])?;
match name.as_str() {
"Gt" => return Some(VerifyExpr::gt(left, right)),
"Lt" => return Some(VerifyExpr::lt(left, right)),
"Gte" => return Some(VerifyExpr::gte(left, right)),
"Lte" => return Some(VerifyExpr::lte(left, right)),
"Eq" => return Some(VerifyExpr::eq(left, right)),
"Neq" => return Some(VerifyExpr::neq(left, right)),
_ => {}
}
}
let verify_args: Vec<VerifyExpr> = args
.iter()
.filter_map(proof_term_to_verify_expr)
.collect();
Some(VerifyExpr::apply(name, verify_args))
}
ProofExpr::Identity(left, right) => {
let l = proof_term_to_verify_expr(left)?;
let r = proof_term_to_verify_expr(right)?;
Some(VerifyExpr::eq(l, r))
}
ProofExpr::And(left, right) => {
let l = proof_expr_to_verify_expr(left)?;
let r = proof_expr_to_verify_expr(right)?;
Some(VerifyExpr::and(l, r))
}
ProofExpr::Or(left, right) => {
let l = proof_expr_to_verify_expr(left)?;
let r = proof_expr_to_verify_expr(right)?;
Some(VerifyExpr::or(l, r))
}
ProofExpr::Implies(left, right) => {
let l = proof_expr_to_verify_expr(left)?;
let r = proof_expr_to_verify_expr(right)?;
Some(VerifyExpr::implies(l, r))
}
ProofExpr::Iff(left, right) => {
let l = proof_expr_to_verify_expr(left)?;
let r = proof_expr_to_verify_expr(right)?;
Some(VerifyExpr::and(
VerifyExpr::implies(l.clone(), r.clone()),
VerifyExpr::implies(r, l),
))
}
ProofExpr::Not(inner) => {
let i = proof_expr_to_verify_expr(inner)?;
Some(VerifyExpr::not(i))
}
ProofExpr::ForAll { variable, body } => {
let b = proof_expr_to_verify_expr(body)?;
Some(VerifyExpr::forall(
vec![(variable.clone(), VerifyType::Int)],
b,
))
}
ProofExpr::Exists { variable, body } => {
let b = proof_expr_to_verify_expr(body)?;
Some(VerifyExpr::exists(
vec![(variable.clone(), VerifyType::Int)],
b,
))
}
ProofExpr::Modal { flavor, body, .. } => {
let b = proof_expr_to_verify_expr(body)?;
Some(VerifyExpr::apply(flavor, vec![b]))
}
ProofExpr::Temporal { operator, body } => {
let b = proof_expr_to_verify_expr(body)?;
Some(VerifyExpr::apply(operator, vec![b]))
}
ProofExpr::TemporalBinary { operator, left, right } => {
let l = proof_expr_to_verify_expr(left)?;
let r = proof_expr_to_verify_expr(right)?;
Some(VerifyExpr::apply(operator, vec![l, r]))
}
ProofExpr::Ctor { .. }
| ProofExpr::Match { .. }
| ProofExpr::Fixpoint { .. }
| ProofExpr::TypedVar { .. } => None,
ProofExpr::Lambda { .. }
| ProofExpr::App(_, _)
| ProofExpr::NeoEvent { .. }
| ProofExpr::Hole(_)
| ProofExpr::Term(_)
| ProofExpr::Unsupported(_) => None,
}
}
pub fn proof_term_to_verify_expr(term: &ProofTerm) -> Option<VerifyExpr> {
match term {
ProofTerm::Constant(s) => {
if let Ok(n) = s.parse::<i64>() {
Some(VerifyExpr::int(n))
} else {
Some(VerifyExpr::var(s))
}
}
ProofTerm::Variable(name) | ProofTerm::BoundVarRef(name) => Some(VerifyExpr::var(name)),
ProofTerm::Function(name, args) => {
if args.len() == 2 {
let left = proof_term_to_verify_expr(&args[0])?;
let right = proof_term_to_verify_expr(&args[1])?;
match name.as_str() {
"Add" => {
return Some(VerifyExpr::binary(VerifyOp::Add, left, right))
}
"Sub" => {
return Some(VerifyExpr::binary(VerifyOp::Sub, left, right))
}
"Mul" => {
return Some(VerifyExpr::binary(VerifyOp::Mul, left, right))
}
"Div" => {
return Some(VerifyExpr::binary(VerifyOp::Div, left, right))
}
_ => {}
}
}
let verify_args: Vec<VerifyExpr> = args
.iter()
.filter_map(proof_term_to_verify_expr)
.collect();
Some(VerifyExpr::apply(name, verify_args))
}
ProofTerm::Group(terms) => {
if terms.len() == 1 {
proof_term_to_verify_expr(&terms[0])
} else {
None
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_convert_atom() {
let expr = ProofExpr::Atom("P".into());
let result = proof_expr_to_verify_expr(&expr);
assert!(matches!(result, Some(VerifyExpr::Var(s)) if s == "P"));
}
#[test]
fn test_convert_gt_predicate() {
let expr = ProofExpr::Predicate {
name: "Gt".into(),
args: vec![
ProofTerm::Variable("x".into()),
ProofTerm::Constant("10".into()),
],
world: None,
};
let result = proof_expr_to_verify_expr(&expr);
assert!(matches!(
result,
Some(VerifyExpr::Binary {
op: VerifyOp::Gt,
..
})
));
}
#[test]
fn test_convert_implication() {
let expr = ProofExpr::Implies(
Box::new(ProofExpr::Atom("P".into())),
Box::new(ProofExpr::Atom("Q".into())),
);
let result = proof_expr_to_verify_expr(&expr);
assert!(matches!(
result,
Some(VerifyExpr::Binary {
op: VerifyOp::Implies,
..
})
));
}
#[test]
fn test_convert_arithmetic_function() {
let term = ProofTerm::Function(
"Add".into(),
vec![
ProofTerm::Variable("x".into()),
ProofTerm::Constant("5".into()),
],
);
let result = proof_term_to_verify_expr(&term);
assert!(matches!(
result,
Some(VerifyExpr::Binary {
op: VerifyOp::Add,
..
})
));
}
}