Skip to content

Commit 219ee90

Browse files
committed
mc: add storm backend
1 parent 07f1692 commit 219ee90

File tree

9 files changed

+500
-86
lines changed

9 files changed

+500
-86
lines changed

Cargo.lock

Lines changed: 37 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ shlex = "1.3.0"
6060
clap = { version = "4.5.23", features = ["derive", "string", "cargo"] }
6161
clap_complete = "4.5.42"
6262
regex = "1.11.1"
63+
tempfile = "3.16.0"
6364

6465
[build-dependencies]
6566
lalrpop = "0.22"

src/driver.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,6 @@ impl SourceUnitName {
111111
};
112112
PathBuf::from(file_name)
113113
}
114-
115-
pub fn to_string(&self) -> String {
116-
format!("{}", self)
117-
}
118114
}
119115

120116
impl fmt::Display for SourceUnitName {
@@ -263,6 +259,14 @@ impl SourceUnit {
263259
}
264260
}
265261

262+
/// The span where to report diagnostics.
263+
pub fn diagnostic_span(&self) -> Span {
264+
match self {
265+
SourceUnit::Decl(decl) => decl.name().span,
266+
SourceUnit::Raw(block) => block.span,
267+
}
268+
}
269+
266270
pub fn parse(file: &StoredFile, raw: bool) -> Result<Vec<Item<Self>>, ParseError> {
267271
if raw {
268272
let name = SourceUnitName::new_raw(&file.path);
@@ -372,7 +376,7 @@ impl SourceUnit {
372376
&self,
373377
options: &crate::JaniOptions,
374378
tcx: &TyCtx,
375-
) -> Result<(), VerifyError> {
379+
) -> Result<Option<PathBuf>, VerifyError> {
376380
if let Some(jani_dir) = &options.jani_dir {
377381
match self {
378382
SourceUnit::Decl(decl) => {
@@ -381,13 +385,17 @@ impl SourceUnit {
381385
.map_err(|err| VerifyError::Diagnostic(err.diagnostic()))?;
382386
let file_path = jani_dir.join(format!("{}.jani", decl.name()));
383387
create_dir_all(file_path.parent().unwrap())?;
384-
std::fs::write(file_path, jani::to_string(&jani_model))?;
388+
std::fs::write(&file_path, jani::to_string(&jani_model))?;
389+
Ok(Some(file_path))
390+
} else {
391+
Ok(None)
385392
}
386393
}
387394
SourceUnit::Raw(_) => panic!("raw code not supported with --jani-dir"),
388395
}
396+
} else {
397+
Ok(None)
389398
}
390-
Ok(())
391399
}
392400

393401
/// Apply encodings from annotations.

src/main.rs

Lines changed: 93 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use ast::{DeclKind, Diagnostic, FileId};
2626
use clap::{crate_description, Args, CommandFactory, Parser, Subcommand, ValueEnum};
2727
use driver::{Item, SourceUnit, VerifyUnit};
2828
use intrinsic::{annotations::init_calculi, distributions::init_distributions, list::init_lists};
29+
use mc::run_storm::{run_storm, storm_result_to_diagnostic};
2930
use proof_rules::init_encodings;
3031
use regex::Regex;
3132
use resource_limits::{await_with_resource_limits, LimitError, LimitsRef};
@@ -185,6 +186,17 @@ pub struct ResourceLimitOptions {
185186
pub mem_limit: u64,
186187
}
187188

189+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, ValueEnum)]
190+
pub enum RunWhichStorm {
191+
/// Look for the Storm binary in the PATH.
192+
#[default]
193+
Path,
194+
/// Run Storm using Docker, with the `movesrwth/storm:stable` image.
195+
DockerStable,
196+
/// Run Storm using Docker, with the `movesrwth/storm:ci` image.
197+
DockerCI,
198+
}
199+
188200
#[derive(Debug, Default, Clone, Args)]
189201
#[command(next_help_heading = "JANI Output Options")]
190202
pub struct JaniOptions {
@@ -204,6 +216,26 @@ pub struct JaniOptions {
204216
/// checking quite a bit. To disable this behavior, use this flag.
205217
#[arg(long)]
206218
pub jani_uninit_outputs: bool,
219+
220+
/// Run Storm, indicating which version to execute.
221+
#[arg(long)]
222+
pub run_storm: Option<RunWhichStorm>,
223+
224+
/// Pass the `--exact` flag to Storm. Otherwise Storm will use floating
225+
/// point numbers, which may be arbitrarily imprecise (but are usually good
226+
/// enough).
227+
#[arg(long)]
228+
pub storm_exact: bool,
229+
230+
/// Pass the `--state-limit [number]` option to Storm. This is useful to
231+
/// approximate infinite-state models.
232+
#[arg(long)]
233+
pub storm_state_limit: Option<usize>,
234+
235+
/// Pass the `--constants [constants]` option to Storm, containing values
236+
/// for constants in the model.
237+
#[arg(long)]
238+
pub storm_constants: Option<String>,
207239
}
208240

209241
#[derive(Debug, Default, Args)]
@@ -717,15 +749,13 @@ fn verify_files_main(
717749
}
718750

719751
// write to JANI if requested
720-
for source_unit in &mut source_units {
721-
let source_unit = source_unit.enter();
722-
let jani_res = source_unit.write_to_jani_if_requested(&options.jani_options, &tcx);
723-
match jani_res {
724-
Err(VerifyError::Diagnostic(diagnostic)) => server.add_diagnostic(diagnostic)?,
725-
Err(err) => Err(err)?,
726-
_ => (),
727-
}
728-
}
752+
to_jani_main(
753+
&options.jani_options,
754+
&mut source_units,
755+
server,
756+
&tcx,
757+
false,
758+
)?;
729759

730760
// Desugar encodings from source units. They might generate new source
731761
// units (for side conditions).
@@ -883,36 +913,78 @@ fn run_to_jani_main(options: ToJaniCommand) -> ExitCode {
883913
Ok(value) => value,
884914
Err(value) => return value,
885915
};
886-
let res = to_jani_main(&options, user_files, &server).map(|_| true);
916+
let res = to_jani_loader(&options, user_files, &server).map(|_| true);
887917
finalize_verify_result(server, &options.rlimit_options, res)
888918
}
889919

890-
fn to_jani_main(
920+
fn to_jani_loader(
891921
options: &ToJaniCommand,
892922
user_files: Vec<FileId>,
893-
server: &SharedServer,
923+
server: &Mutex<dyn Server>,
894924
) -> Result<(), VerifyError> {
895-
let mut server = server.lock().unwrap();
925+
let mut server_lock = server.lock().unwrap();
896926
let (mut source_units, tcx) = parse_and_tycheck(
897927
&options.input_options,
898928
&options.debug_options,
899-
&mut *server,
929+
&mut *server_lock,
900930
&user_files,
901931
)?;
902-
if options.jani_options.jani_dir.is_none() {
903-
return Err(VerifyError::UserError(
904-
"--jani-dir is required for the to-jani command.".into(),
905-
));
932+
to_jani_main(
933+
&options.jani_options,
934+
&mut source_units,
935+
server_lock.deref_mut(),
936+
&tcx,
937+
true,
938+
)
939+
}
940+
941+
fn to_jani_main(
942+
options: &JaniOptions,
943+
source_units: &mut Vec<Item<SourceUnit>>,
944+
server: &mut dyn Server,
945+
tcx: &TyCtx,
946+
is_jani_command: bool,
947+
) -> Result<(), VerifyError> {
948+
let mut options = options.clone();
949+
950+
let mut temp_dir = None;
951+
if options.jani_dir.is_none() {
952+
if is_jani_command && options.run_storm.is_none() {
953+
return Err(VerifyError::UserError(
954+
"--jani-dir is required for the to-jani command.".into(),
955+
));
956+
} else {
957+
temp_dir = Some(tempfile::tempdir().map_err(|err| {
958+
VerifyError::UserError(
959+
format!("Could not create temporary directory: {}", err).into(),
960+
)
961+
})?);
962+
options.jani_dir = temp_dir.as_ref().map(|dir| dir.path().to_owned());
963+
}
906964
}
907-
for source_unit in &mut source_units {
965+
for source_unit in source_units {
908966
let source_unit = source_unit.enter();
909-
let jani_res = source_unit.write_to_jani_if_requested(&options.jani_options, &tcx);
967+
let jani_res = source_unit.write_to_jani_if_requested(&options, tcx);
910968
match jani_res {
911969
Err(VerifyError::Diagnostic(diagnostic)) => server.add_diagnostic(diagnostic)?,
912970
Err(err) => Err(err)?,
913-
Ok(()) => (),
971+
Ok(Some(path)) => {
972+
tracing::debug!(file=?path.display(), "wrote JANI file");
973+
if let Some(which_storm) = options.run_storm {
974+
let res = run_storm(&options, which_storm, &path, vec!["reward".to_owned()]);
975+
server.add_diagnostic(storm_result_to_diagnostic(
976+
&res,
977+
source_unit.diagnostic_span(),
978+
))?;
979+
}
980+
}
981+
Ok(None) => (),
914982
}
915983
}
984+
985+
// only drop (and thus remove) the temp dir after we're done using it.
986+
drop(temp_dir);
987+
916988
Ok(())
917989
}
918990

src/mc/mod.rs

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,10 @@
33
// TODO: handle name conflicts
44

55
mod opsem;
6+
pub mod run_storm;
67
mod specs;
78

8-
use std::{
9-
cell::RefCell,
10-
collections::HashSet,
11-
convert::TryInto,
12-
mem,
13-
};
9+
use std::{cell::RefCell, collections::HashSet, convert::TryInto, mem};
1410

1511
use ariadne::ReportKind;
1612
use jani::{
@@ -27,9 +23,8 @@ use crate::{
2723
ast::{
2824
util::{is_bot_lit, is_top_lit},
2925
visit::VisitorMut,
30-
BinOpKind, DeclKind, DeclRef, Diagnostic, Expr, ExprBuilder, ExprData,
31-
ExprKind, Ident, Label, LitKind, ProcDecl, Shared, Span, Spanned, Stmt, TyKind, UnOpKind,
32-
VarDecl,
26+
BinOpKind, DeclKind, DeclRef, Diagnostic, Expr, ExprBuilder, ExprData, ExprKind, Ident,
27+
Label, LitKind, ProcDecl, Shared, Span, Spanned, Stmt, TyKind, UnOpKind, VarDecl,
3328
},
3429
procs::proc_verify::verify_proc,
3530
tyctx::TyCtx,
@@ -139,11 +134,7 @@ pub fn proc_to_model(
139134

140135
// translate the statements
141136
let next = op_automaton.spec_part.end_location();
142-
let start = translate_block(
143-
&mut op_automaton,
144-
&verify_unit.block,
145-
next,
146-
)?;
137+
let start = translate_block(&mut op_automaton, &verify_unit.block, next)?;
147138

148139
// now finish building the automaton
149140
let automaton_name = Identifier(proc.name.to_string());

0 commit comments

Comments
 (0)