use std::collections::HashMap;
use crate::ast::Spanned;
pub use crate::ir::identity::{CtorId, FnId, FnKey, LawKey, ModuleId, TypeId, TypeKey};
#[derive(Debug, Clone, Default)]
pub struct ProofIR {
pub refined_types: HashMap<TypeId, RefinedTypeDecl>,
pub fn_contracts: HashMap<FnId, FnContract>,
pub law_theorems: Vec<LawTheorem>,
pub unclassified_fns: Vec<UnclassifiedFn>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct UnclassifiedFn {
pub line: usize,
pub message: String,
}
#[derive(Debug, Clone)]
pub struct SmartGuard {
pub param: String,
pub predicate: Spanned<crate::ir::hir::ResolvedExpr>,
}
#[derive(Debug, Clone)]
pub struct RefinedTypeDecl {
pub name: String,
pub carrier_type: String,
pub carrier_field: String,
pub predicate_param: String,
pub invariant: Predicate,
pub witness: Option<String>,
pub interval: Option<crate::ir::interval::Interval>,
pub op_classes: Vec<(String, crate::ir::interval::OpClass)>,
}
impl RefinedTypeDecl {
pub fn raw_i64_eligible(&self) -> bool {
crate::ir::interval::raw_i64_eligible(
self.interval,
self.op_classes.iter().map(|(_, class)| class),
)
}
}
#[derive(Debug, Clone)]
pub struct FnContract {
pub source_name: String,
pub recursion: Option<RecursionContract>,
}
#[derive(Debug, Clone)]
pub enum RecursionContract {
Fuel {
fuel_metric: FuelMetric,
},
LinearRecurrence2,
Native {
precondition: Vec<Predicate>,
measure: Measure,
preservation: PreservationProof,
decrease: DecreaseProof,
body: NativeIntCountdownBody,
},
WellFoundedToNat {
param: String,
floor_div: Option<FloorDivShrink>,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FloorDivShrink {
pub divisor: i64,
pub helper_fn: Option<String>,
}
#[derive(Debug, Clone)]
pub struct NativeIntCountdownBody {
pub base_arm_literal: i64,
pub base_arm_body: Spanned<crate::ir::hir::ResolvedExpr>,
pub wildcard_arm_body: Spanned<crate::ir::hir::ResolvedExpr>,
}
#[derive(Debug, Clone)]
pub enum FuelMetric {
NatAbsPlusOne { param: String },
BoundMinusParamNatAbsPlusOne {
param: String,
bound: Spanned<crate::ir::hir::ResolvedExpr>,
},
SeqLenPlusOne { param: String },
SizeOfPlusOne,
StringLenMinusPos {
string_param: String,
pos_param: String,
},
Lex { params: Vec<String>, rank: usize },
}
#[derive(Debug, Clone)]
pub enum Measure {
NatAbsInt { param: String },
SeqLen { param: String },
Lex(Vec<Measure>),
}
#[derive(Debug, Clone)]
pub enum PreservationProof {
IntCountdownLiteralZero,
}
#[derive(Debug, Clone)]
pub enum DecreaseProof {
NatAbsCountdown,
}
#[derive(Debug, Clone)]
pub struct LawTheorem {
pub fn_id: FnId,
pub law_name: String,
pub quantifiers: Vec<Quantifier>,
pub premises: Vec<Predicate>,
pub claim_lhs: Spanned<crate::ir::hir::ResolvedExpr>,
pub claim_rhs: Spanned<crate::ir::hir::ResolvedExpr>,
pub strategy: ProofStrategy,
}
#[derive(Debug, Clone)]
pub struct Quantifier {
pub name: String,
pub binder_type: QuantifierType,
}
#[derive(Debug, Clone)]
pub enum QuantifierType {
Plain(String),
RefinedTo { refined_type: String },
OracleSubtype(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum WrapperDriver {
List,
PeanoNat {
type_name: String,
value_first: bool,
},
}
#[derive(Debug, Clone)]
pub enum ProofStrategy {
Reflexive,
SimpOverLemmas(Vec<String>),
Commutative { op: crate::ast::BinOp },
Associative { op: crate::ast::BinOp },
IdentityElement { op: crate::ast::BinOp },
AntiCommutative {
op: crate::ast::BinOp,
neg_on_rhs: bool,
},
UnaryEqualsBinary {
inner_fn: String,
},
LinearArithmetic {
unfold_fns: Vec<String>,
wrapper_return: bool,
smart_guard: Option<SmartGuard>,
lifted: bool,
},
Induction { param: String },
LibraryAxiom {
axiom: String,
args: Vec<Spanned<crate::ir::hir::ResolvedExpr>>,
},
MapUpdatePostcondition {
outer_fn: String,
kind: MapUpdatePostconditionKind,
map_arg: Spanned<crate::ir::hir::ResolvedExpr>,
key_arg: Spanned<crate::ir::hir::ResolvedExpr>,
extra_unfolds: Vec<String>,
},
MapKeyTrackedIncrement {
outer_fn: String,
map_arg: Spanned<crate::ir::hir::ResolvedExpr>,
key_arg: Spanned<crate::ir::hir::ResolvedExpr>,
},
SpecEquivalence {
extra_unfolds: Vec<String>,
},
SpecEquivalenceSimpNormalized {
extra_unfolds: Vec<String>,
},
LinearIntSpecEquivalence {
unfolded_impl: Spanned<crate::ir::hir::ResolvedExpr>,
unfolded_spec: Spanned<crate::ir::hir::ResolvedExpr>,
},
EffectfulSpecEquivalence {
impl_fn: String,
spec_fn: String,
},
LinearRecurrence2SpecEquivalence {
impl_fn: String,
spec_fn: String,
helper_fn: String,
},
BoundedUniversal,
ResultPipelineChain {
chain_qm_fn: String,
chain_manual_fn: String,
step_fns: Vec<String>,
},
WrapperOverRecursion {
wrapper_fn: String,
inner_fn: String,
other_fn: String,
combine_op: crate::ast::BinOp,
driver: WrapperDriver,
combine_fn: Option<String>,
},
TailRecFixedBaseFold {
spec_fn: String,
loop_fn: String,
combine_fn: String,
combine_op: crate::ast::BinOp,
type_name: String,
},
EnumConstantFold {
unfold_fns: Vec<String>,
},
FiniteDomainCases {
givens: Vec<String>,
},
SimpOverPreludeLemmas {
unfold_fns: Vec<String>,
fuel_fns: Vec<String>,
builtins: Vec<String>,
},
IntDecimalRoundtrip {
parse_fn: String,
neg_fn: String,
pos_fn: String,
sign_fn: String,
scanner_fn: String,
predicate_fn: String,
finish_fn: String,
finish_int_fn: String,
serializer_fn: String,
},
StringEscapeRoundtrip(Box<StringEscapeRoundtripPin>),
RingIdentity {
unfold_fns: Vec<String>,
},
FloorDivWindow { figure: FloorWindowFigure },
Sorry,
BackendDispatch,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FloorWindowFigure {
PowPositive { pow_fn: String },
PowSumSplit { pow_fn: String },
SigWindow {
pow_fn: String,
halve_fn: String,
exp_fn: String,
sig_fn: String,
window_fn: String,
},
ProductWindow {
pow_fn: String,
fits_fn: String,
claim_fn: String,
},
}
#[derive(Debug, Clone)]
pub struct StringEscapeRoundtripPin {
pub scan_fn: String,
pub escape_fn: String,
pub validate_fn: String,
pub finish_fn: String,
pub unicode_fn: String,
pub codepoint_fn: String,
pub apply_fn: String,
pub read_hex_fn: String,
pub hex_val_fn: String,
pub high_surrogate_fn: String,
pub low_surrogate_fn: String,
pub producer_fn: String,
pub fold_fn: String,
pub classifier_fn: String,
pub control_fn: String,
pub control_escape_fn: String,
pub ok_ctor: String,
pub str_ctor: String,
pub terminator: char,
pub escape_char: char,
pub unicode_letter: char,
pub pairs: Vec<EscapePairSpec>,
pub control_threshold: i64,
pub high_surrogate_min: i64,
pub low_surrogate_min: i64,
}
#[derive(Debug, Clone)]
pub struct EscapePairSpec {
pub decoded: char,
pub letter: char,
pub from_control_ladder: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MapUpdatePostconditionKind {
HasAfter,
GetAfter,
}
#[derive(Debug, Clone)]
pub struct Predicate {
pub free_vars: Vec<(String, QuantifierType)>,
pub expr: Spanned<crate::ir::hir::ResolvedExpr>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ir::hir::ResolvedExpr;
use crate::ir::interval::{Interval, OpClass};
fn stub_predicate() -> Predicate {
Predicate {
free_vars: vec![("n".to_string(), QuantifierType::Plain("Int".to_string()))],
expr: Spanned::new(ResolvedExpr::Ident("n".to_string()), 0),
}
}
fn decl(interval: Option<Interval>, ops: Vec<(&str, OpClass)>) -> RefinedTypeDecl {
RefinedTypeDecl {
name: "T".to_string(),
carrier_type: "Int".to_string(),
carrier_field: "value".to_string(),
predicate_param: "n".to_string(),
invariant: stub_predicate(),
witness: Some("0".to_string()),
interval,
op_classes: ops.into_iter().map(|(n, c)| (n.to_string(), c)).collect(),
}
}
#[test]
fn two_sided_fits_i64_all_overflow_free_is_eligible() {
let d = decl(
Some(Interval::between(0, 100)),
vec![("add", OpClass::OverflowFree)],
);
assert!(d.raw_i64_eligible());
}
#[test]
fn one_overflow_free_one_unbounded_is_not_eligible() {
let d = decl(
Some(Interval::between(0, 100)),
vec![
("add", OpClass::OverflowFree),
("scaledAdd", OpClass::Unbounded),
],
);
assert!(!d.raw_i64_eligible());
}
#[test]
fn one_needs_wider_scratch_is_not_eligible() {
let d = decl(
Some(Interval::between(0, 100)),
vec![("widePath", OpClass::NeedsWiderScratch)],
);
assert!(!d.raw_i64_eligible());
}
#[test]
fn two_sided_interval_not_fitting_i64_is_not_eligible() {
let d = decl(
Some(Interval::between(0, i64::MAX as i128 + 1)),
vec![("add", OpClass::OverflowFree)],
);
assert!(!d.raw_i64_eligible());
}
#[test]
fn declined_interval_none_is_not_eligible() {
let d = decl(None, vec![("add", OpClass::OverflowFree)]);
assert!(!d.raw_i64_eligible());
}
#[test]
fn one_sided_interval_is_not_eligible() {
let d = decl(Some(Interval::ge(0)), vec![]);
assert!(!d.raw_i64_eligible());
}
#[test]
fn two_sided_fits_i64_empty_ops_is_eligible() {
let d = decl(Some(Interval::between(0, 100)), vec![]);
assert!(d.raw_i64_eligible());
}
}