Skip to main content

intent_lang_core/
vcgen.rs

1use intent_lang_syntax::ast::*;
2use std::collections::HashSet;
3
4/// A verification condition to be checked by SMT.
5#[derive(Debug, Clone)]
6pub struct VerificationCondition {
7    pub name: String,
8    pub kind: VcKind,
9    /// Declarations needed (types, uninterpreted functions).
10    pub declarations: Vec<SmtDecl>,
11    /// Assertions: requires + invariants(unprimed).
12    pub assumes: Vec<Spanned<Expr>>,
13    /// Goal: ensures + invariants(as written, may contain primes).
14    pub goals: Vec<Spanned<Expr>>,
15    /// Safety rules merged from `safety` declarations.
16    pub safety_rules: Vec<SafetySource>,
17    /// If set, this VC cannot be encoded yet — reason string.
18    pub unsupported: Option<String>,
19}
20
21#[derive(Debug, Clone)]
22pub struct SafetySource {
23    pub safety_name: String,
24    pub index: usize,
25    pub expr: Spanned<Expr>,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum VcKind {
30    Intent,
31    Theorem,
32}
33
34#[derive(Debug, Clone)]
35pub enum SmtDecl {
36    DeclareSort(String),
37    DeclareConst(String, TypeExpr),
38    DeclareFun(String, Vec<TypeExpr>, TypeExpr),
39}
40
41/// Remove all Prime nodes from an expression (replace `x'` with `x`).
42fn unprime_expr(expr: &Spanned<Expr>) -> Spanned<Expr> {
43    let node = match &expr.node {
44        Expr::Prime(inner) => return unprime_expr(inner),
45        Expr::IntLit(_) | Expr::BoolLit(_) | Expr::StringLit(_) | Expr::Ident(_) => {
46            return expr.clone()
47        }
48        Expr::FieldAccess(base, field) => {
49            Expr::FieldAccess(Box::new(unprime_expr(base)), field.clone())
50        }
51        Expr::Index(base, idx) => {
52            Expr::Index(Box::new(unprime_expr(base)), Box::new(unprime_expr(idx)))
53        }
54        Expr::BinOp(l, op, r) => {
55            Expr::BinOp(Box::new(unprime_expr(l)), *op, Box::new(unprime_expr(r)))
56        }
57        Expr::UnaryOp(op, o) => Expr::UnaryOp(*op, Box::new(unprime_expr(o))),
58        Expr::IfThenElse(c, t, e) => Expr::IfThenElse(
59            Box::new(unprime_expr(c)),
60            Box::new(unprime_expr(t)),
61            Box::new(unprime_expr(e)),
62        ),
63        Expr::Forall(vars, body) => Expr::Forall(vars.clone(), Box::new(unprime_expr(body))),
64        Expr::Exists(vars, body) => Expr::Exists(vars.clone(), Box::new(unprime_expr(body))),
65        Expr::Call(name, args) => Expr::Call(name.clone(), args.iter().map(unprime_expr).collect()),
66        Expr::Paren(inner) => Expr::Paren(Box::new(unprime_expr(inner))),
67    };
68    Spanned::new(node, expr.span.clone())
69}
70
71/// Generate verification conditions from a program.
72pub fn generate_vcs(prog: &Program) -> Vec<VerificationCondition> {
73    let mut vcs = Vec::new();
74
75    // Collect struct type names for theorem analysis
76    let struct_names: HashSet<String> = prog
77        .declarations
78        .iter()
79        .filter_map(|d| match &d.node {
80            Declaration::Type(t) => Some(t.name.clone()),
81            _ => None,
82        })
83        .collect();
84
85    // Collect safety rules
86    let mut safety_rules: Vec<SafetySource> = Vec::new();
87    for decl in &prog.declarations {
88        if let Declaration::Safety(s) = &decl.node {
89            for (i, inv) in s.invariants.iter().enumerate() {
90                safety_rules.push(SafetySource {
91                    safety_name: s.name.clone(),
92                    index: i + 1,
93                    expr: inv.clone(),
94                });
95            }
96        }
97    }
98
99    for decl in &prog.declarations {
100        match &decl.node {
101            Declaration::Intent(intent) => {
102                let mut assumes = Vec::new();
103                let mut goals = Vec::new();
104                let mut declarations = Vec::new();
105
106                for p in &intent.params {
107                    declarations.push(SmtDecl::DeclareConst(p.name.clone(), p.ty.clone()));
108                }
109
110                for clause in &intent.clauses {
111                    match &clause.node {
112                        Clause::Require(e) => assumes.push(e.clone()),
113                        // Ensures DEFINE post-state — they are assumed
114                        Clause::Ensure(e) => assumes.push(e.clone()),
115                        Clause::Invariant(e) => {
116                            // Pre-state invariant: assumed (unprimed)
117                            assumes.push(unprime_expr(e));
118                            // Post-state invariant: must be proved
119                            goals.push(e.clone());
120                        }
121                    }
122                }
123
124                // Safety rules: assume unprimed, prove primed
125                let sr = safety_rules.clone();
126                for rule in &sr {
127                    assumes.push(unprime_expr(&rule.expr));
128                    goals.push(rule.expr.clone());
129                }
130
131                vcs.push(VerificationCondition {
132                    name: intent.name.clone(),
133                    kind: VcKind::Intent,
134                    declarations,
135                    assumes,
136                    goals,
137                    safety_rules: sr,
138                    unsupported: None,
139                });
140            }
141            Declaration::Theorem(thm) => {
142                // Check if theorem references struct-typed quantifier variables
143                let has_struct_quantifiers = uses_struct_quantifiers(&thm.body, &struct_names);
144                vcs.push(VerificationCondition {
145                    name: thm.name.clone(),
146                    kind: VcKind::Theorem,
147                    declarations: Vec::new(),
148                    assumes: Vec::new(),
149                    goals: vec![thm.body.clone()],
150                    safety_rules: Vec::new(),
151                    unsupported: if has_struct_quantifiers {
152                        Some("theorem uses struct-typed quantifiers (requires intent expansion, not yet implemented)".to_string())
153                    } else {
154                        None
155                    },
156                });
157            }
158            _ => {}
159        }
160    }
161
162    vcs
163}
164
165/// Check if an expression contains forall/exists with struct-typed variables.
166fn uses_struct_quantifiers(expr: &Spanned<Expr>, struct_names: &HashSet<String>) -> bool {
167    match &expr.node {
168        Expr::Forall(vars, body) | Expr::Exists(vars, body) => {
169            let has_struct = vars.iter().any(|v| match &v.ty {
170                TypeExpr::Named(name) => struct_names.contains(name),
171                _ => false,
172            });
173            has_struct || uses_struct_quantifiers(body, struct_names)
174        }
175        Expr::BinOp(l, _, r) => {
176            uses_struct_quantifiers(l, struct_names) || uses_struct_quantifiers(r, struct_names)
177        }
178        Expr::UnaryOp(_, o) => uses_struct_quantifiers(o, struct_names),
179        Expr::Paren(inner) | Expr::Prime(inner) => uses_struct_quantifiers(inner, struct_names),
180        Expr::IfThenElse(c, t, e) => {
181            uses_struct_quantifiers(c, struct_names)
182                || uses_struct_quantifiers(t, struct_names)
183                || uses_struct_quantifiers(e, struct_names)
184        }
185        Expr::Call(_, args) => args
186            .iter()
187            .any(|a| uses_struct_quantifiers(a, struct_names)),
188        Expr::FieldAccess(base, _) => uses_struct_quantifiers(base, struct_names),
189        Expr::Index(base, idx) => {
190            uses_struct_quantifiers(base, struct_names)
191                || uses_struct_quantifiers(idx, struct_names)
192        }
193        _ => false,
194    }
195}