Skip to content

Commit f50838a

Browse files
committed
Achieve verification of very simple programs in Lithium
1 parent c5e4f9c commit f50838a

File tree

5 files changed

+119
-176
lines changed

5 files changed

+119
-176
lines changed

Diff for: viper/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
66

77
#![feature(try_blocks)]
8+
#![feature(let_chains)]
89
#![deny(unused_must_use)]
910

1011
mod ast_factory;

Diff for: viper/src/native_backend/fol.rs

+4-58
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use std::collections::HashMap;
22
use vir::{
3-
common::position::Positioned,
43
low::{
54
ast::{expression::*, statement::*},
65
*,
@@ -11,16 +10,9 @@ pub enum FolStatement {
1110
Comment(String),
1211
Assume(Expression),
1312
Assert(Expression),
14-
Sequence(Box<FolStatement>, Box<FolStatement>),
1513
Choice(Box<FolStatement>, Box<FolStatement>),
1614
}
1715

18-
#[derive(Clone)]
19-
pub enum SMTGenTarget {
20-
Predicate(Expression),
21-
Comment(String),
22-
}
23-
2416
fn vir_statement_to_fol_statements(
2517
statement: &Statement,
2618
known_methods: &HashMap<String, MethodDecl>,
@@ -140,59 +132,13 @@ fn vir_statement_to_fol_statements(
140132
}
141133
}
142134

143-
pub fn vir_statements_to_predicates(
135+
pub fn vir_to_fol(
144136
statements: &Vec<Statement>,
145137
known_methods: &HashMap<String, MethodDecl>,
146-
) -> Vec<SMTGenTarget> {
138+
) -> Vec<FolStatement> {
147139
// reduce so that the order in the vector is now the Sequence operator
148-
let sequence = statements
140+
statements
149141
.iter()
150142
.flat_map(|s| vir_statement_to_fol_statements(s, known_methods))
151-
.reduce(|a, b| FolStatement::Sequence(Box::new(a), Box::new(b)));
152-
153-
match sequence {
154-
None => Vec::new(),
155-
Some(seq) => fold_statement_to_formula_set(&seq, Vec::new()),
156-
}
157-
}
158-
159-
fn fold_statement_to_formula_set(
160-
statement: &FolStatement,
161-
set: Vec<SMTGenTarget>,
162-
) -> Vec<SMTGenTarget> {
163-
match statement {
164-
FolStatement::Comment(comment) => {
165-
let mut set = set;
166-
set.push(SMTGenTarget::Comment(comment.clone()));
167-
set
168-
}
169-
FolStatement::Assert(expr) => {
170-
let mut set = set;
171-
set.push(SMTGenTarget::Predicate(expr.clone()));
172-
set
173-
}
174-
FolStatement::Assume(expr) => set
175-
.into_iter()
176-
.map(|e| match e {
177-
SMTGenTarget::Predicate(e) => {
178-
SMTGenTarget::Predicate(Expression::BinaryOp(BinaryOp {
179-
op_kind: BinaryOpKind::Implies,
180-
left: Box::new(expr.clone()),
181-
right: Box::new(e.clone()),
182-
position: e.position(),
183-
}))
184-
}
185-
x => x,
186-
})
187-
.collect(),
188-
FolStatement::Sequence(fst, snd) => {
189-
fold_statement_to_formula_set(fst, fold_statement_to_formula_set(snd, set))
190-
}
191-
FolStatement::Choice(fst, snd) => {
192-
let mut fst_set = fold_statement_to_formula_set(fst, set.clone());
193-
let snd_set = fold_statement_to_formula_set(snd, set);
194-
fst_set.extend(snd_set);
195-
fst_set
196-
}
197-
}
143+
.collect()
198144
}

Diff for: viper/src/native_backend/native_verifier.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,11 @@ impl<'a> Verifier<'a> for NativeVerifier {
7878

7979
if output.status.success() {
8080
let raw_output = String::from_utf8(output.stdout)?;
81-
raw_output
81+
if raw_output.lines().any(|line| line == "sat" || line == "unknown") {
82+
Err(raw_output)?
83+
} else {
84+
raw_output
85+
}
8286
} else {
8387
let err = String::from_utf8(output.stdout)?;
8488
Err(err)?
@@ -100,11 +104,11 @@ impl<'a> Verifier<'a> for NativeVerifier {
100104
if is_failure {
101105
let mut errors: Vec<VerificationError> = vec![];
102106

103-
let error_full_id = "1".to_string();
107+
let error_full_id = "verification.error".to_string();
104108
let pos_id = None;
105109
let offending_pos_id = None;
106110
let reason_pos_id = None;
107-
let message = "Lithium backend not implemented".to_string();
111+
let message = "At least one assertion was 'sat' or 'unknown'".to_string();
108112
let counterexample = None;
109113

110114
errors.push(VerificationError::new(

Diff for: viper/src/native_backend/smt_lib.rs

+106-52
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
use super::theory_provider::*;
2-
use crate::native_backend::fol::*;
1+
use super::{
2+
fol::{vir_to_fol, FolStatement},
3+
theory_provider::*,
4+
};
35
use lazy_static::lazy_static;
46
use log::warn;
57
use regex::Regex;
6-
use std::collections::HashMap;
8+
use std::collections::{HashMap, HashSet};
79
use vir::{
810
common::position::Positioned,
911
low::{
@@ -24,6 +26,7 @@ pub struct SMTLib {
2426
func_decls: Vec<String>,
2527
code: Vec<String>,
2628
methods: HashMap<String, MethodDecl>,
29+
blocks: HashMap<String, BasicBlock>,
2730
}
2831

2932
impl SMTLib {
@@ -33,6 +36,7 @@ impl SMTLib {
3336
func_decls: Vec::new(),
3437
code: Vec::new(),
3538
methods: HashMap::new(),
39+
blocks: Default::default(),
3640
}
3741
}
3842
fn add_sort_decl(&mut self, sort_decl: String) {
@@ -48,8 +52,66 @@ impl SMTLib {
4852
fn add_code(&mut self, text: String) {
4953
self.code.push(text);
5054
}
51-
fn add_code_lines(&mut self, texts: impl Iterator<Item = String>) {
52-
self.code.extend(texts);
55+
fn follow(&mut self, block_label: &String, precond: Option<&Expression>) {
56+
let block = self
57+
.blocks
58+
.get(block_label)
59+
.expect("Block not found")
60+
.clone();
61+
62+
if block.statements.is_empty() {
63+
return;
64+
}
65+
66+
self.add_code(format!("; Basic block: {}", block.label));
67+
self.add_code("(push)".to_string());
68+
69+
// assume precond if any
70+
if let Some(precond) = precond {
71+
self.add_code("; Branch precond:".to_string());
72+
self.add_code(format!("(assert {})", precond.to_smt()));
73+
}
74+
75+
// verify body
76+
let predicates = vir_to_fol(&block.statements, &self.methods);
77+
78+
for predicate in predicates.into_iter() {
79+
match predicate {
80+
FolStatement::Comment(comment) => self.add_code(format!("; {}", comment)),
81+
FolStatement::Assume(expression) => {
82+
// assert predicate
83+
self.add_code(format!("(assert {})", expression.to_smt()));
84+
}
85+
FolStatement::Assert(expression) => {
86+
// negate predicate
87+
let position = expression.position();
88+
let negated = Expression::UnaryOp(UnaryOp {
89+
op_kind: UnaryOpKind::Not,
90+
argument: Box::new(expression),
91+
position,
92+
});
93+
// assert negated predicate
94+
self.add_code("(push)".to_string());
95+
self.add_code(format!("(assert {})", negated.to_smt()));
96+
self.add_code("(check-sat)".to_string());
97+
self.add_code("(pop)".to_string());
98+
}
99+
FolStatement::Choice(_, _) => unimplemented!("Choice"),
100+
}
101+
}
102+
103+
// process successor
104+
match &block.successor {
105+
Successor::Goto(label) => {
106+
self.follow(&label.name, None);
107+
}
108+
Successor::GotoSwitch(mapping) => mapping.iter().for_each(|(expr, label)| {
109+
self.follow(&label.name, Some(expr));
110+
}),
111+
Successor::Return => {}
112+
}
113+
114+
self.add_code(format!("(pop); End basic block: {}", block.label));
53115
}
54116
}
55117

@@ -117,12 +179,19 @@ impl ToString for SMTLib {
117179
}
118180

119181
result.push_str(&main);
182+
183+
// strip all lines containing "$marker"
184+
// TODO: SSO form for marker variables?
120185
result
186+
.lines()
187+
.filter(|line| !line.contains("$marker"))
188+
.collect::<Vec<_>>()
189+
.join("\n")
121190
}
122191
}
123192

124193
pub trait SMTTranslatable {
125-
fn build_smt(&self, smt: &mut SMTLib) {
194+
fn build_smt(&self, _: &mut SMTLib) {
126195
unimplemented!()
127196
}
128197
fn to_smt(&self) -> String {
@@ -230,7 +299,37 @@ impl SMTTranslatable for ProcedureDecl {
230299
self.locals.iter().for_each(|l| l.build_smt(smt));
231300

232301
// process basic blocks
233-
self.basic_blocks.iter().for_each(|b| b.build_smt(smt));
302+
smt.blocks.clear();
303+
304+
self.basic_blocks.iter().for_each(|b| {
305+
smt.blocks.insert(b.label.name.clone(), b.clone()); // TODO: Inefficient copy
306+
});
307+
308+
// find a starting block
309+
let mut start_blocks = smt.blocks.keys().collect::<HashSet<_>>();
310+
311+
for (_, block) in &smt.blocks {
312+
match &block.successor {
313+
Successor::Goto(label) => {
314+
start_blocks.remove(&label.name);
315+
}
316+
Successor::GotoSwitch(labels) => {
317+
labels.iter().for_each(|(_, l)| {
318+
start_blocks.remove(&l.name);
319+
});
320+
}
321+
Successor::Return => {}
322+
}
323+
}
324+
325+
assert!(
326+
start_blocks.len() == 1,
327+
"Expected exactly one start block, found {}",
328+
start_blocks.len()
329+
);
330+
331+
let start = start_blocks.drain().next().unwrap().clone();
332+
smt.follow(&start, None);
234333

235334
smt.add_code("(pop)".to_string());
236335
}
@@ -246,51 +345,6 @@ impl SMTTranslatable for VariableDecl {
246345
}
247346
}
248347

249-
impl SMTTranslatable for BasicBlock {
250-
fn build_smt(&self, smt: &mut SMTLib) {
251-
if self.statements.is_empty() {
252-
return;
253-
}
254-
255-
let mut code = Vec::new();
256-
257-
code.push(format!("; Basic block: {}", self.label));
258-
code.push("(push)".to_string());
259-
260-
// verify body
261-
let predicates = vir_statements_to_predicates(&self.statements, &smt.methods);
262-
263-
let mut assertions = 0;
264-
for predicate in predicates.into_iter() {
265-
match predicate {
266-
SMTGenTarget::Predicate(expression) => {
267-
// negate predicate
268-
let position = expression.position();
269-
let negated = Expression::UnaryOp(UnaryOp {
270-
op_kind: UnaryOpKind::Not,
271-
argument: Box::new(expression),
272-
position,
273-
});
274-
// assert negated predicate
275-
code.push(format!("(assert {})", negated.to_smt()));
276-
assertions += 1;
277-
}
278-
SMTGenTarget::Comment(comment) => code.push(format!("; {}", comment)),
279-
}
280-
}
281-
282-
// check if the negated predicates are satisfiable
283-
// TODO: each in its own scope?
284-
if assertions > 0 {
285-
code.push("(check-sat)".to_string());
286-
code.push("(pop)".to_string());
287-
smt.add_code_lines(code.into_iter());
288-
}
289-
290-
// TODO: Goto
291-
}
292-
}
293-
294348
/* #endregion */
295349

296350
impl SMTTranslatable for Expression {

0 commit comments

Comments
 (0)