1use intent_lang_syntax::ast::*;
2use std::collections::HashSet;
3
4#[derive(Debug, Clone)]
6pub struct VerificationCondition {
7 pub name: String,
8 pub kind: VcKind,
9 pub declarations: Vec<SmtDecl>,
11 pub assumes: Vec<Spanned<Expr>>,
13 pub goals: Vec<Spanned<Expr>>,
15 pub safety_rules: Vec<SafetySource>,
17 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
41fn 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
71pub fn generate_vcs(prog: &Program) -> Vec<VerificationCondition> {
73 let mut vcs = Vec::new();
74
75 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 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 Clause::Ensure(e) => assumes.push(e.clone()),
115 Clause::Invariant(e) => {
116 assumes.push(unprime_expr(e));
118 goals.push(e.clone());
120 }
121 }
122 }
123
124 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 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
165fn 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}