use super::*;
pub(super) fn refinement_lift_for_given_ir(
given_name: &str,
lhs: &Spanned<crate::ast::Expr>,
rhs: &Spanned<crate::ast::Expr>,
refined_types: &std::collections::HashMap<crate::ir::TypeId, crate::ir::RefinedTypeDecl>,
symbols: &crate::ir::SymbolTable,
dep_modules: &[crate::codegen::ModuleInfo],
) -> Option<String> {
let mut result: Option<String> = None;
walk_for_refinement_carrier(
lhs,
given_name,
refined_types,
symbols,
dep_modules,
&mut result,
);
walk_for_refinement_carrier(
rhs,
given_name,
refined_types,
symbols,
dep_modules,
&mut result,
);
result
}
pub(super) fn walk_for_refinement_carrier(
expr: &Spanned<crate::ast::Expr>,
given_name: &str,
refined_types: &std::collections::HashMap<crate::ir::TypeId, crate::ir::RefinedTypeDecl>,
symbols: &crate::ir::SymbolTable,
dep_modules: &[crate::codegen::ModuleInfo],
result: &mut Option<String>,
) {
use crate::ast::Expr;
if result.is_some() {
return;
}
match &expr.node {
Expr::RecordCreate { type_name, fields } if fields.len() == 1 => {
let (_, fvalue) = &fields[0];
let matches_var = matches!(
&fvalue.node,
Expr::Ident(n) | Expr::Resolved { name: n, .. } if n == given_name
);
if matches_var
&& let Some((type_id, _decl)) =
crate::codegen::common::resolve_refined_type_in_with_key(
refined_types,
symbols,
dep_modules,
type_name,
)
{
*result = Some(symbols.type_entry(type_id).key.canonical());
return;
}
for (_, v) in fields {
walk_for_refinement_carrier(
v,
given_name,
refined_types,
symbols,
dep_modules,
result,
);
}
}
Expr::FnCall(callee, args) => {
walk_for_refinement_carrier(
callee,
given_name,
refined_types,
symbols,
dep_modules,
result,
);
for a in args {
walk_for_refinement_carrier(
a,
given_name,
refined_types,
symbols,
dep_modules,
result,
);
}
}
Expr::BinOp(_, l, r) => {
walk_for_refinement_carrier(l, given_name, refined_types, symbols, dep_modules, result);
walk_for_refinement_carrier(r, given_name, refined_types, symbols, dep_modules, result);
}
Expr::Match { subject, arms, .. } => {
walk_for_refinement_carrier(
subject,
given_name,
refined_types,
symbols,
dep_modules,
result,
);
for arm in arms {
walk_for_refinement_carrier(
&arm.body,
given_name,
refined_types,
symbols,
dep_modules,
result,
);
}
}
Expr::Attr(obj, _) => {
walk_for_refinement_carrier(
obj,
given_name,
refined_types,
symbols,
dep_modules,
result,
);
}
_ => {}
}
}
pub(super) fn extract_smart_constructor_guard(
fn_names: &std::collections::BTreeSet<String>,
inputs: &ProofLowerInputs,
) -> Option<crate::ir::SmartGuard> {
use crate::ast::{Expr, MatchArm, Pattern, Stmt};
for fd in iter_all_fn_defs(inputs) {
if !fn_names.contains(&fd.name) {
continue;
}
if !fd.return_type.starts_with("Result<") {
continue;
}
if fd.params.len() != 1 {
continue;
}
let (param_name, param_type) = &fd.params[0];
if param_type != "Int" {
continue;
}
let stmts = fd.body.stmts();
if stmts.len() != 1 {
continue;
}
let Stmt::Expr(body_expr) = &stmts[0] else {
continue;
};
let Expr::Match { subject, arms } = &body_expr.node else {
continue;
};
if !arms_match_bool_ok_err(arms) {
continue;
}
let scope = inputs.fn_owning_scope(fd);
return Some(crate::ir::SmartGuard {
param: param_name.clone(),
predicate: inputs.resolve_expr(subject, scope),
});
#[allow(unreachable_code)]
{
let _: Option<&MatchArm> = None;
let _: Option<&Pattern> = None;
}
}
None
}
pub(super) fn arms_match_bool_ok_err(arms: &[crate::ast::MatchArm]) -> bool {
use crate::ast::{Expr, Literal, Pattern};
if arms.len() != 2 {
return false;
}
let starts_with_ctor = |expr: &Spanned<Expr>, name: &str| -> bool {
match &expr.node {
Expr::Constructor(n, _) => n == name,
Expr::FnCall(callee, _) => {
if let Expr::Attr(obj, field) = &callee.node
&& let Expr::Ident(ns) = &obj.node
{
format!("{ns}.{field}") == name
} else {
false
}
}
_ => false,
}
};
let mut saw_true_ok = false;
let mut saw_false_err = false;
for arm in arms {
match &arm.pattern {
Pattern::Literal(Literal::Bool(true)) => {
if starts_with_ctor(&arm.body, "Result.Ok") {
saw_true_ok = true;
}
}
Pattern::Literal(Literal::Bool(false)) => {
if starts_with_ctor(&arm.body, "Result.Err") {
saw_false_err = true;
}
}
_ => return false,
}
}
saw_true_ok && saw_false_err
}
pub(super) fn pick_witness(
type_name: &str,
type_id: crate::ir::TypeId,
inputs: &ProofLowerInputs,
predicate: &Spanned<Expr>,
param_name: &str,
scope: Option<&str>,
) -> Option<String> {
let smart_ctor_name: Option<String> = match scope {
None => inputs.entry_items.iter().find_map(|item| match item {
TopLevel::FnDef(fd)
if smart_ctor_matches(fd, type_id, type_name, inputs.symbol_table, scope) =>
{
Some(fd.name.clone())
}
_ => None,
}),
Some(prefix) => inputs
.dep_modules
.iter()
.find(|m| m.prefix == prefix)
.and_then(|m| {
m.fn_defs
.iter()
.find(|fd| {
smart_ctor_matches(fd, type_id, type_name, inputs.symbol_table, scope)
})
.map(|fd| fd.name.clone())
}),
};
if let Some(smart_ctor_name) = smart_ctor_name {
if scope.is_none() {
for item in inputs.entry_items {
let TopLevel::Verify(vb) = item else {
continue;
};
if vb.fn_name != smart_ctor_name {
continue;
}
for (lhs, rhs) in &vb.cases {
if !is_result_ok(&rhs.node) {
continue;
}
let Expr::FnCall(_, args) = &lhs.node else {
continue;
};
if args.len() != 1 {
continue;
}
if let Some(lit) = literal_int_value(&args[0]) {
return Some(lit);
}
}
}
}
}
let mut tried = std::collections::HashSet::<i64>::new();
let mut candidates: Vec<i64> = Vec::new();
let mut from_ast: Vec<i64> = Vec::new();
collect_int_literals(predicate, &mut from_ast);
for k in from_ast {
for delta in &[0_i64, 1, -1] {
if let Some(c) = k.checked_add(*delta) {
candidates.push(c);
}
}
}
candidates.extend_from_slice(&[
0, 1, -1, 2, -2, 10, -10, 100, 1_000, 10_000, 100_000, 1_000_000,
]);
for candidate in candidates {
if !tried.insert(candidate) {
continue;
}
if eval_int_bool_predicate(predicate, param_name, candidate) == Some(true) {
return Some(candidate.to_string());
}
}
None
}
pub(super) fn collect_int_literals(expr: &Spanned<Expr>, out: &mut Vec<i64>) {
match &expr.node {
Expr::Literal(Literal::Int(n)) => out.push(*n),
Expr::Neg(inner) => {
if let Expr::Literal(Literal::Int(n)) = &inner.node {
out.push(-n);
} else {
collect_int_literals(inner, out);
}
}
Expr::BinOp(_, l, r) => {
collect_int_literals(l, out);
collect_int_literals(r, out);
}
Expr::FnCall(callee, args) => {
collect_int_literals(callee, out);
for a in args {
collect_int_literals(a, out);
}
}
Expr::Match { subject, arms } => {
collect_int_literals(subject, out);
for arm in arms {
collect_int_literals(&arm.body, out);
}
}
Expr::Attr(o, _) | Expr::ErrorProp(o) => collect_int_literals(o, out),
_ => {}
}
}
pub(super) fn smart_ctor_matches(
fd: &FnDef,
type_id: crate::ir::TypeId,
type_name: &str,
symbols: &crate::ir::SymbolTable,
scope: Option<&str>,
) -> bool {
if fd.params.len() != 1 {
return false;
}
let parsed = crate::types::parse_type_str(&fd.return_type);
let crate::types::Type::Result(ok, _) = parsed else {
return false;
};
let crate::types::Type::Named { name: n, .. } = &*ok else {
return false;
};
let name_is_qualified = n.contains('.');
let resolved_id = if name_is_qualified {
n.rsplit_once('.').and_then(|(prefix, bare)| {
symbols.type_id_of(&crate::ir::TypeKey::in_module(prefix.to_string(), bare))
})
} else if let Some(prefix) = scope {
symbols
.type_id_of(&crate::ir::TypeKey::in_module(
prefix.to_string(),
n.clone(),
))
.or_else(|| symbols.type_id_of(&crate::ir::TypeKey::entry(n.clone())))
} else {
symbols.type_id_of(&crate::ir::TypeKey::entry(n.clone()))
};
match resolved_id {
Some(id) => id == type_id,
None => n == type_name,
}
}
pub(super) fn is_result_ok(expr: &Expr) -> bool {
match expr {
Expr::Constructor(name, _) => name == "Result.Ok",
Expr::FnCall(callee, _) => matches!(
&callee.node,
Expr::Attr(obj, field)
if field == "Ok" && matches!(&obj.node, Expr::Ident(n) if n == "Result")
),
_ => false,
}
}
pub(super) fn literal_int_value(expr: &Spanned<Expr>) -> Option<String> {
match &expr.node {
Expr::Literal(Literal::Int(n)) => Some(n.to_string()),
Expr::Neg(inner) => {
let inner_str = literal_int_value(inner)?;
Some(format!("-{inner_str}"))
}
_ => None,
}
}
pub(super) fn eval_int_bool_predicate(
expr: &Spanned<Expr>,
param_name: &str,
value: i64,
) -> Option<bool> {
match &expr.node {
Expr::Literal(Literal::Bool(b)) => Some(*b),
Expr::BinOp(op, l, r) => {
use crate::ast::BinOp::*;
let li = eval_int_arith(l, param_name, value)?;
let ri = eval_int_arith(r, param_name, value)?;
Some(match op {
Lt => li < ri,
Gt => li > ri,
Lte => li <= ri,
Gte => li >= ri,
Eq => li == ri,
Neq => li != ri,
_ => return None,
})
}
Expr::FnCall(callee, args) if args.len() == 2 => {
let name = expr_to_dotted_name(&callee.node)?;
match name.as_str() {
"Bool.and" => Some(
eval_int_bool_predicate(&args[0], param_name, value)?
&& eval_int_bool_predicate(&args[1], param_name, value)?,
),
"Bool.or" => Some(
eval_int_bool_predicate(&args[0], param_name, value)?
|| eval_int_bool_predicate(&args[1], param_name, value)?,
),
_ => None,
}
}
_ => None,
}
}
pub(super) fn eval_int_arith(expr: &Spanned<Expr>, param_name: &str, value: i64) -> Option<i64> {
match &expr.node {
Expr::Literal(Literal::Int(n)) => Some(*n),
Expr::Ident(name) | Expr::Resolved { name, .. } if name == param_name => Some(value),
Expr::BinOp(op, l, r) => {
use crate::ast::BinOp::*;
let li = eval_int_arith(l, param_name, value)?;
let ri = eval_int_arith(r, param_name, value)?;
match op {
Add => Some(li.checked_add(ri)?),
Sub => Some(li.checked_sub(ri)?),
Mul => Some(li.checked_mul(ri)?),
Div => Some(li.checked_div(ri)?),
_ => None,
}
}
Expr::Neg(inner) => Some(-eval_int_arith(inner, param_name, value)?),
_ => None,
}
}