Skip to content

Commit 57c4725

Browse files
committed
Add SolverType and CommandError enums, update execute_swine and check_proof_assuming functions
1 parent 2e50b5a commit 57c4725

File tree

2 files changed

+87
-59
lines changed

2 files changed

+87
-59
lines changed

src/slicing/solver.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ impl<'ctx> SliceSolver<'ctx> {
227227

228228
self.prover.add_assumption(&self.slice_stmts.constraints);
229229
self.prover.add_assumption(&inactive_formula);
230-
let res = self.prover.check_proof_assuming(&active_toggle_values);
230+
let res = self.prover.check_proof_assuming(&active_toggle_values, z3rro::prover::SolverType::SWINE);
231231

232232
let mut slice_searcher = SliceModelSearch::new(active_toggle_values.clone());
233233
if let ProveResult::Proof = res {
@@ -605,7 +605,7 @@ fn check_proof_seed<'ctx>(
605605
prover.set_timeout(timeout);
606606

607607
let seed: Vec<_> = seed.iter().cloned().collect();
608-
prover.check_proof_assuming(&seed)
608+
prover.check_proof_assuming(&seed, z3rro::prover::SolverType::SWINE)
609609
}
610610

611611
fn unsat_core_to_seed<'ctx>(

z3rro/src/prover.rs

Lines changed: 85 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
//! Not a SAT solver, but a prover. There's a difference.
2+
use thiserror::Error;
23

3-
//use std::{fmt::Display, time::Duration};
4-
5-
use std::{collections::VecDeque, fmt::Display, io::Write, path::Path, process::Command, time::Duration};
4+
use std::{collections::VecDeque, env, fmt::Display, io::{self, Write}, path::Path, process::{self, Command}, time::Duration};
65

76
use tempfile::NamedTempFile;
87

@@ -17,6 +16,18 @@ use crate::{
1716
util::{set_solver_timeout, ReasonUnknown},
1817
};
1918

19+
#[derive(Debug, Error)]
20+
pub enum CommandError {
21+
#[error("Environment variable error: {0}")]
22+
EnvVarError(#[from] env::VarError),
23+
#[error("Process execution failed: {0}")]
24+
ProcessError(#[from] io::Error),
25+
}
26+
pub enum SolverType {
27+
Z3,
28+
SWINE,
29+
}
30+
2031
/// The result of a prove query.
2132
#[derive(Debug)]
2233
pub enum ProveResult<'ctx> {
@@ -25,40 +36,40 @@ pub enum ProveResult<'ctx> {
2536
Unknown(ReasonUnknown),
2637
}
2738

28-
/// Find the swine-z3 file located under the dir directory, and execute swine-z3 on the file located at file_path
29-
fn execute_swine(dir: &Path, file_path: &Path) {
30-
let swine = "swine-z3";
31-
32-
let find_output = Command::new("find")
33-
.arg(dir)
34-
.arg("-name")
35-
.arg(swine)
36-
.output().unwrap();
37-
38-
if find_output.status.success() {
39-
let stdout = String::from_utf8_lossy(&find_output.stdout);
40-
41-
for line in stdout.lines().rev() {
42-
let path = Path::new(line);
43-
44-
if path.exists() && path.is_file() {
45-
let cmd_output = Command::new(path)
46-
.arg(file_path)
47-
.output().unwrap();
48-
49-
if cmd_output.status.success() {
50-
println!("{}", String::from_utf8_lossy(&cmd_output.stdout));
51-
break;
52-
} else {
53-
eprintln!("Failed to execute swine({}) command with status: {}", line, cmd_output.status);
39+
/// Execute swine-z3 on the file located at file_path
40+
fn execute_swine(file_path: &Path) -> Result<SatResult, CommandError>{
41+
match env::var("SWINE") {
42+
// Use "export SWINE=<path_for_swine>" to set the path for swine in the SWINE variable.
43+
Ok(swine) => {
44+
let output = Command::new(swine)
45+
.arg(file_path)
46+
.output();
47+
48+
match output {
49+
Ok(output) => {
50+
let stdout = String::from_utf8_lossy(&output.stdout);
51+
52+
if stdout.contains("unsat") {
53+
Ok(SatResult::Unsat)
54+
} else if stdout.contains("sat") {
55+
Ok(SatResult::Sat)
56+
} else {
57+
Ok(SatResult::Unknown)
58+
}
59+
}
60+
Err(e) => {
61+
Err(CommandError::ProcessError(e))
5462
}
5563
}
5664
}
57-
} else {
58-
eprintln!("Find command execution failed");
65+
Err(e) => {
66+
Err(CommandError::EnvVarError(e))
67+
}
5968
}
6069
}
6170

71+
/// In order to execute the program, it is necessary to remove lines that
72+
/// contain a forall quantifier or the declaration of the exponential function (exp).
6273
fn remove_lines_for_swine(input: &str) -> String {
6374
let mut output = String::new();
6475
let mut tmp_buffer: VecDeque<char> = VecDeque::new();
@@ -151,42 +162,59 @@ impl<'ctx> Prover<'ctx> {
151162
}
152163

153164
pub fn check_proof(&mut self) -> ProveResult<'ctx> {
154-
self.check_proof_assuming(&[])
165+
self.check_proof_assuming(&[], SolverType::SWINE)
155166
}
156167

157168
/// Do the SAT check, but consider a check with no provables to be a
158169
/// [`ProveResult::Proof`].
159-
pub fn check_proof_assuming(&mut self, assumptions: &[Bool<'ctx>]) -> ProveResult<'ctx> {
170+
pub fn check_proof_assuming(&mut self, assumptions: &[Bool<'ctx>], solver_type: SolverType) -> ProveResult<'ctx> {
160171
if self.min_level_with_provables.is_none() {
161172
return ProveResult::Proof;
162173
}
163174

164-
let mut smtlib = self.get_smtlib();
165-
166-
smtlib.add_check_sat();
167-
168-
let smtlib = smtlib.into_string();
169-
let mut smt_file: NamedTempFile = NamedTempFile::new().unwrap();
170-
171-
smt_file.write_all(remove_lines_for_swine(&smtlib).as_bytes()).unwrap();
172-
173-
let file_path = smt_file.path();
174-
let start_dir = Path::new("../");
175+
let res;
175176

176-
execute_swine(start_dir, file_path);
177+
match solver_type {
178+
SolverType::SWINE => {
179+
let mut smtlib = self.get_smtlib();
180+
smtlib.add_check_sat();
181+
let smtlib = smtlib.into_string();
182+
let mut smt_file: NamedTempFile = NamedTempFile::new().unwrap();
183+
smt_file.write_all(remove_lines_for_swine(&smtlib).as_bytes()).unwrap();
184+
let file_path = smt_file.path();
177185

178-
let res = if assumptions.is_empty() {
179-
self.solver.check()
180-
} else {
181-
self.solver.check_assumptions(assumptions)
182-
};
183-
match res {
184-
SatResult::Unsat => ProveResult::Proof,
185-
SatResult::Unknown => ProveResult::Unknown(self.get_reason_unknown().unwrap()),
186-
SatResult::Sat => {
187-
let model = self.get_model().unwrap();
188-
let model = InstrumentedModel::new(model);
189-
ProveResult::Counterexample(model)
186+
res = execute_swine(file_path).unwrap_or_else(|e| {
187+
eprintln!("{}", e);
188+
process::exit(1)
189+
});
190+
match res {
191+
SatResult::Unsat => ProveResult::Proof,
192+
SatResult::Unknown => {
193+
// TODO: Determine the correct reason for Unknown
194+
ProveResult::Unknown(ReasonUnknown::Other("unknown".to_string()))
195+
},
196+
SatResult::Sat => {
197+
// TODO: Get the model from the output of SWINE
198+
println!("The Result of SWINE: sat");
199+
process::exit(1)
200+
}
201+
}
202+
}
203+
SolverType::Z3 => {
204+
res = if assumptions.is_empty() {
205+
self.solver.check()
206+
} else {
207+
self.solver.check_assumptions(assumptions)
208+
};
209+
match res {
210+
SatResult::Unsat => ProveResult::Proof,
211+
SatResult::Unknown => ProveResult::Unknown(self.get_reason_unknown().unwrap()),
212+
SatResult::Sat => {
213+
let model = self.get_model().unwrap();
214+
let model = InstrumentedModel::new(model);
215+
ProveResult::Counterexample(model)
216+
}
217+
}
190218
}
191219
}
192220
}

0 commit comments

Comments
 (0)