Skip to content

Commit 984884f

Browse files
committed
feat(cli): add warnings for unsupported liveness features
The checker silently ignored liveness properties and fairness constraints, which could mislead users about verification coverage. Introducing explicit warnings informs users that only safety invariants are checked, improving transparency and debugging experience.
1 parent cc85047 commit 984884f

2 files changed

Lines changed: 56 additions & 3 deletions

File tree

specl/crates/specl-cli/src/main.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2581,6 +2581,10 @@ fn cmd_check(
25812581
let module =
25822582
parse(&source).map_err(|e| CliError::from_parse_error(e, source.clone(), &filename))?;
25832583

2584+
if !quiet {
2585+
warn_unsupported_features(&module);
2586+
}
2587+
25842588
info!("type checking...");
25852589
specl_types::check_module(&module)
25862590
.map_err(|e| CliError::from_type_error(e, source.clone(), &filename))?;
@@ -3388,6 +3392,10 @@ fn cmd_check_symbolic(
33883392
let module =
33893393
parse(&source).map_err(|e| CliError::from_parse_error(e, source.clone(), &filename))?;
33903394

3395+
if !json {
3396+
warn_unsupported_features(&module);
3397+
}
3398+
33913399
info!("type checking...");
33923400
specl_types::check_module(&module)
33933401
.map_err(|e| CliError::from_type_error(e, source.clone(), &filename))?;
@@ -3753,6 +3761,42 @@ fn filter_invariants(spec: &mut specl_ir::CompiledSpec, check_only: &[String]) -
37533761
Ok(())
37543762
}
37553763

3764+
/// Warn about unsupported liveness features (property, fairness, temporal operators).
3765+
/// These are parsed but silently ignored by the compiler/checker.
3766+
fn warn_unsupported_features(module: &specl_syntax::Module) {
3767+
let mut has_properties = false;
3768+
let mut has_fairness = false;
3769+
let mut property_names = Vec::new();
3770+
3771+
for decl in &module.decls {
3772+
match decl {
3773+
specl_syntax::Decl::Property(d) => {
3774+
has_properties = true;
3775+
property_names.push(d.name.name.clone());
3776+
}
3777+
specl_syntax::Decl::Fairness(_) => {
3778+
has_fairness = true;
3779+
}
3780+
_ => {}
3781+
}
3782+
}
3783+
3784+
if has_properties {
3785+
eprintln!(
3786+
"Warning: specl does not yet support liveness properties. \
3787+
Only safety invariants are checked. The following property declarations \
3788+
will be ignored: {}",
3789+
property_names.join(", ")
3790+
);
3791+
}
3792+
if has_fairness {
3793+
eprintln!(
3794+
"Warning: specl does not yet support fairness constraints. \
3795+
Fairness declarations will be ignored."
3796+
);
3797+
}
3798+
}
3799+
37563800
fn parse_constants(constants: &[String], spec: &specl_ir::CompiledSpec) -> CliResult<Vec<Value>> {
37573801
let mut values = vec![Value::none(); spec.consts.len()];
37583802

specl/crates/specl-cli/tests/regression_integration.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,10 @@ invariant Subset {
125125
"#;
126126
let outcome = check_spec(source, &[("MaxKey", 2)]).expect("should check");
127127
assert!(
128-
matches!(outcome, CheckOutcome::Ok { .. } | CheckOutcome::StateLimitReached { .. }),
128+
matches!(
129+
outcome,
130+
CheckOutcome::Ok { .. } | CheckOutcome::StateLimitReached { .. }
131+
),
129132
"expected OK, got: {outcome:?}"
130133
);
131134
}
@@ -231,7 +234,10 @@ invariant NotInConsistent {
231234
"#;
232235
let outcome = check_spec(source, &[("N", 2)]).expect("should check");
233236
assert!(
234-
matches!(outcome, CheckOutcome::Ok { .. } | CheckOutcome::StateLimitReached { .. }),
237+
matches!(
238+
outcome,
239+
CheckOutcome::Ok { .. } | CheckOutcome::StateLimitReached { .. }
240+
),
235241
"expected OK, got: {outcome:?}"
236242
);
237243
}
@@ -290,7 +296,10 @@ invariant KeysDomain { getKeys(d) == 0..N }
290296
"#;
291297
let outcome = check_spec(source, &[("N", 2)]).expect("should check");
292298
assert!(
293-
matches!(outcome, CheckOutcome::Ok { .. } | CheckOutcome::StateLimitReached { .. }),
299+
matches!(
300+
outcome,
301+
CheckOutcome::Ok { .. } | CheckOutcome::StateLimitReached { .. }
302+
),
294303
"expected OK, got: {outcome:?}"
295304
);
296305
}

0 commit comments

Comments
 (0)