Skip to content

Commit 4015f56

Browse files
Implement opt-in verification (closes viperproject#1187)
1 parent 545c487 commit 4015f56

File tree

10 files changed

+116
-2
lines changed

10 files changed

+116
-2
lines changed

docs/dev-guide/src/config/flags.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
| [`MIN_PRUSTI_VERSION`](#min_prusti_version) | `Option<String>` | `None` | A |
4747
| [`NO_VERIFY`](#no_verify) | `bool` | `false` | A |
4848
| [`NO_VERIFY_DEPS`](#no_verify_deps) | `bool` | `false` | B |
49+
| [`OPT_IN_VERIFICATION`](#opt_in_verification) | `bool` | `false` | A |
4950
| [`OPTIMIZATIONS`](#optimizations) | `Vec<String>` | "all" | A |
5051
| [`PRESERVE_SMT_TRACE_FILES`](#preserve_smt_trace_files) | `bool` | `false` | A |
5152
| [`PRINT_COLLECTED_VERIFICATION_ITEMS`](#print_collected_verification_items) | `bool` | `false` | A |
@@ -293,6 +294,9 @@ When enabled, verification is skipped for dependencies. Equivalent to enabling `
293294

294295
> **Note:** applied to all dependency crates when running with `cargo prusti`.
295296
297+
## `OPT_IN_VERIFICATION`
298+
When enabled, Prusti will only try to verify the functions annotated with `#[verified]`. All other functions are assumed to be `#[trusted]`, by default. Functions annotated with both `#[trusted]` and `#[verified]` will not be verified.
299+
296300
## `ONLY_MEMORY_SAFETY`
297301

298302
When enabled, only the core proof is verified.

prusti-contracts/prusti-contracts-proc-macros/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ pub fn trusted(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
4646
tokens
4747
}
4848

49+
#[cfg(not(feature = "prusti"))]
50+
#[proc_macro_attribute]
51+
pub fn verified(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
52+
tokens
53+
}
54+
4955
#[cfg(not(feature = "prusti"))]
5056
#[proc_macro]
5157
pub fn body_invariant(_tokens: TokenStream) -> TokenStream {
@@ -165,6 +171,12 @@ pub fn trusted(attr: TokenStream, tokens: TokenStream) -> TokenStream {
165171
prusti_specs::trusted(attr.into(), tokens.into()).into()
166172
}
167173

174+
#[cfg(feature = "prusti")]
175+
#[proc_macro_attribute]
176+
pub fn verified(attr: TokenStream, tokens: TokenStream) -> TokenStream {
177+
rewrite_prusti_attributes(SpecAttributeKind::Verified, attr.into(), tokens.into()).into()
178+
}
179+
168180
#[cfg(feature = "prusti")]
169181
#[proc_macro]
170182
pub fn body_invariant(tokens: TokenStream) -> TokenStream {

prusti-contracts/prusti-contracts/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ pub use prusti_contracts_proc_macros::pure;
1818
/// A macro for marking a function as trusted.
1919
pub use prusti_contracts_proc_macros::trusted;
2020

21+
/// A macro for marking a function as opted into verification.
22+
pub use prusti_contracts_proc_macros::verified;
23+
2124
/// A macro for type invariants.
2225
pub use prusti_contracts_proc_macros::invariant;
2326

prusti-contracts/prusti-specs/src/lib.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ fn extract_prusti_attributes(
8686
SpecAttributeKind::Pure
8787
| SpecAttributeKind::Terminates
8888
| SpecAttributeKind::Trusted
89-
| SpecAttributeKind::Predicate => {
89+
| SpecAttributeKind::Predicate
90+
| SpecAttributeKind::Verified => {
9091
assert!(attr.tokens.is_empty(), "Unexpected shape of an attribute.");
9192
attr.tokens
9293
}
@@ -165,6 +166,7 @@ fn generate_spec_and_assertions(
165166
SpecAttributeKind::AfterExpiry => generate_for_after_expiry(attr_tokens, item),
166167
SpecAttributeKind::AssertOnExpiry => generate_for_assert_on_expiry(attr_tokens, item),
167168
SpecAttributeKind::Pure => generate_for_pure(attr_tokens, item),
169+
SpecAttributeKind::Verified => generate_for_verified(attr_tokens, item),
168170
SpecAttributeKind::Terminates => generate_for_terminates(attr_tokens, item),
169171
SpecAttributeKind::Trusted => generate_for_trusted(attr_tokens, item),
170172
// Predicates are handled separately below; the entry in the SpecAttributeKind enum
@@ -295,6 +297,23 @@ fn generate_for_pure(attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedR
295297
))
296298
}
297299

300+
/// Generate spec items and attributes to typecheck and later retrieve "verified" annotations.
301+
fn generate_for_verified(attr: TokenStream, item: &untyped::AnyFnItem) -> GeneratedResult {
302+
if !attr.is_empty() {
303+
return Err(syn::Error::new(
304+
attr.span(),
305+
"the `#[verified]` attribute does not take parameters",
306+
));
307+
}
308+
309+
Ok((
310+
vec![],
311+
vec![parse_quote_spanned! {item.span()=>
312+
#[prusti::verified]
313+
}],
314+
))
315+
}
316+
298317
/// Generate spec items and attributes to typecheck and later retrieve "pure" annotations, but encoded as a referenced separate function that type-conditional spec refinements can apply trait bounds to.
299318
fn generate_for_pure_refinements(item: &untyped::AnyFnItem) -> GeneratedResult {
300319
let mut rewriter = rewriter::AstRewriter::new();
@@ -830,6 +849,7 @@ fn extract_prusti_attributes_for_types(
830849
SpecAttributeKind::AssertOnExpiry => unreachable!("assert_on_expiry on type"),
831850
SpecAttributeKind::RefineSpec => unreachable!("refine_spec on type"),
832851
SpecAttributeKind::Pure => unreachable!("pure on type"),
852+
SpecAttributeKind::Verified => unreachable!("verified on type"),
833853
SpecAttributeKind::Invariant => unreachable!("invariant on type"),
834854
SpecAttributeKind::Predicate => unreachable!("predicate on type"),
835855
SpecAttributeKind::Terminates => unreachable!("terminates on type"),
@@ -873,6 +893,7 @@ fn generate_spec_and_assertions_for_types(
873893
SpecAttributeKind::AfterExpiry => unreachable!(),
874894
SpecAttributeKind::AssertOnExpiry => unreachable!(),
875895
SpecAttributeKind::Pure => unreachable!(),
896+
SpecAttributeKind::Verified => unreachable!(),
876897
SpecAttributeKind::Predicate => unreachable!(),
877898
SpecAttributeKind::Invariant => unreachable!(),
878899
SpecAttributeKind::RefineSpec => unreachable!(),

prusti-contracts/prusti-specs/src/spec_attribute_kind.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub enum SpecAttributeKind {
1717
RefineSpec = 9,
1818
Terminates = 10,
1919
PrintCounterexample = 11,
20+
Verified = 12,
2021
}
2122

2223
impl TryFrom<String> for SpecAttributeKind {
@@ -35,6 +36,7 @@ impl TryFrom<String> for SpecAttributeKind {
3536
"refine_spec" => Ok(SpecAttributeKind::RefineSpec),
3637
"model" => Ok(SpecAttributeKind::Model),
3738
"print_counterexample" => Ok(SpecAttributeKind::PrintCounterexample),
39+
"verified" => Ok(SpecAttributeKind::Verified),
3840
_ => Err(name),
3941
}
4042
}

prusti-interface/src/specs/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::{
77
PrustiError,
88
};
99
use log::debug;
10+
use prusti_common::config;
1011
use prusti_rustc_interface::{
1112
ast::ast,
1213
errors::MultiSpan,
@@ -364,7 +365,8 @@ fn get_procedure_spec_ids(def_id: DefId, attrs: &[ast::Attribute]) -> Option<Pro
364365
);
365366

366367
let pure = has_prusti_attr(attrs, "pure");
367-
let trusted = has_prusti_attr(attrs, "trusted");
368+
let trusted = has_prusti_attr(attrs, "trusted")
369+
|| (config::opt_in_verification() && !has_prusti_attr(attrs, "verified"));
368370
let abstract_predicate = has_abstract_predicate_attr(attrs);
369371

370372
if abstract_predicate || pure || trusted || !spec_id_refs.is_empty() {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// compile-flags: -Popt_in_verification=true
2+
use prusti_contracts::*;
3+
4+
fn main() {}
5+
6+
fn i_am_not_verified() {
7+
unreachable!();
8+
}
9+
10+
#[verified]
11+
fn i_am_verified() {
12+
assert!(1 == 2); //~ ERROR
13+
}
14+
15+
#[verified]
16+
#[trusted]
17+
fn i_am_trusted() {
18+
unreachable!();
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// compile-flags: -Popt_in_verification=false
2+
use prusti_contracts::*;
3+
4+
fn main() {}
5+
6+
fn i_am_not_verified() {
7+
unreachable!(); //~ ERROR
8+
}
9+
10+
#[verified]
11+
fn i_am_verified() {
12+
assert!(1 == 2); //~ ERROR
13+
}
14+
15+
#[verified]
16+
#[trusted]
17+
fn i_am_trusted() {
18+
unreachable!();
19+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// compile-flags: -Popt_in_verification=true
2+
use prusti_contracts::*;
3+
4+
fn main() {}
5+
6+
fn i_am_not_verified() {
7+
unreachable!();
8+
}
9+
10+
#[verified]
11+
fn i_am_verified() {
12+
assert!(1 != 2);
13+
}
14+
15+
#[verified]
16+
#[pure]
17+
fn i_am_pure() {
18+
assert!(true);
19+
}
20+
21+
#[verified]
22+
#[trusted]
23+
fn i_am_trusted() {
24+
unreachable!();
25+
}

prusti-utils/src/config.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ lazy_static::lazy_static! {
107107
settings.set_default("allow_unreachable_unsupported_code", false).unwrap();
108108
settings.set_default("no_verify", false).unwrap();
109109
settings.set_default("no_verify_deps", false).unwrap();
110+
settings.set_default("opt_in_verification", false).unwrap();
110111
settings.set_default("full_compilation", false).unwrap();
111112
settings.set_default("json_communication", false).unwrap();
112113
settings.set_default("optimizations", "all").unwrap();
@@ -981,6 +982,12 @@ pub fn no_verify_deps() -> bool {
981982
read_setting("no_verify_deps")
982983
}
983984

985+
/// When enabled, verification is skipped for functions
986+
/// that do not have the `#[verified]` attribute.
987+
pub fn opt_in_verification() -> bool {
988+
read_setting("opt_in_verification")
989+
}
990+
984991
/// When enabled, compilation will continue and a binary will be generated
985992
/// after Prusti terminates.
986993
pub fn full_compilation() -> bool {

0 commit comments

Comments
 (0)