Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for inductively verifying contracts of recursive functions #2809

Merged
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
291e6d0
Contract support for inductive verification of recursion
JustusAdam Oct 5, 2023
18d7271
Fix test cases, formatting
JustusAdam Oct 5, 2023
6ea4a1b
Update Documentation
JustusAdam Oct 5, 2023
92c595a
Formatting test cases
JustusAdam Oct 5, 2023
481e7b5
More whitespace
JustusAdam Oct 5, 2023
ad7c2de
This whitespace stuff is getting ridiculous
JustusAdam Oct 5, 2023
65a2a17
AAttach require any
JustusAdam Oct 5, 2023
e20eccf
Merge branch 'main' into inductive-recursion-for-function-contracts
JustusAdam Oct 8, 2023
a31176c
Fix names in test cases
JustusAdam Oct 8, 2023
ba9ea4e
Apply suggestions from code review
JustusAdam Oct 10, 2023
6902193
Suggestions from Zyad
JustusAdam Oct 10, 2023
0817544
Merge branch 'main' into inductive-recursion-for-function-contracts
JustusAdam Oct 10, 2023
832f92c
Better self detector
JustusAdam Oct 11, 2023
081f7d6
Unused name
JustusAdam Oct 11, 2023
5ebfac5
Fix import error, add fail on two test case
JustusAdam Oct 11, 2023
67795ba
fmt
JustusAdam Oct 11, 2023
6c58d37
Added test case for, and fixed self detector
JustusAdam Oct 11, 2023
8ad2ae0
Actually run the new test in regression
JustusAdam Oct 11, 2023
9cd5ed1
Expand test case to also fail on postcondition
JustusAdam Oct 11, 2023
dd45198
Explanation for test case
JustusAdam Oct 11, 2023
d2e3379
fmt
JustusAdam Oct 11, 2023
281e6c1
A few missing bits of documentation.
JustusAdam Oct 17, 2023
e651f6e
Apply suggestions from code review
JustusAdam Oct 17, 2023
3623c16
Merge branch 'main' into inductive-recursion-for-function-contracts
JustusAdam Oct 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 61 additions & 5 deletions library/kani_macros/src/sysroot/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,7 @@ fn requires_ensures_main(attr: TokenStream, item: TokenStream, is_requires: bool
//
// The same care is taken when we emit check and replace functions.
// emit the check function.
let is_impl_fn = is_probably_impl_fn(&item_fn);
let ItemFn { attrs, vis, sig, block } = &item_fn;
handler.output.extend(quote!(
#(#attrs)*
Expand All @@ -874,7 +875,7 @@ fn requires_ensures_main(attr: TokenStream, item: TokenStream, is_requires: bool

let args = pats_to_idents(&mut wrapper_sig.inputs).collect::<Vec<_>>();
let also_args = args.iter();
let (call_check, call_replace) = if is_probably_impl_fn(sig) {
let (call_check, call_replace) = if is_impl_fn {
(quote!(Self::#check_fn_name), quote!(Self::#replace_fn_name))
} else {
(quote!(#check_fn_name), quote!(#replace_fn_name))
Expand Down Expand Up @@ -926,18 +927,18 @@ fn pats_to_idents<P>(
struct SelfDetector(bool);

impl<'ast> Visit<'ast> for SelfDetector {
fn visit_path(&mut self, i: &'ast syn::Path) {
self.0 |= i.is_ident(&Ident::from(syn::Token![Self](Span::call_site())))
fn visit_ident(&mut self, i: &'ast syn::Ident) {
self.0 |= i == &Ident::from(syn::Token![Self](Span::mixed_site()))
}

fn visit_receiver(&mut self, _node: &'ast syn::Receiver) {
self.0 = true;
}
}

fn is_probably_impl_fn(sig: &Signature) -> bool {
fn is_probably_impl_fn(fun: &ItemFn) -> bool {
let mut self_detector = SelfDetector(false);
self_detector.visit_signature(sig);
self_detector.visit_item_fn(fun);
self_detector.0
}

Expand Down Expand Up @@ -966,3 +967,58 @@ macro_rules! passthrough {

passthrough!(stub_verified, false);
passthrough!(proof_for_contract, true);

#[cfg(test)]
mod test {
macro_rules! detect_impl_fn {
($expect_pass:expr, $($tt:tt)*) => {{
let syntax = stringify!($($tt)*);
let ast = syn::parse_str(syntax).unwrap();
assert!($expect_pass == super::is_probably_impl_fn(&ast),
"Incorrect detection.\nExpected is_impl_fun: {}\nInput Expr; {}\nParsed: {:?}",
$expect_pass,
syntax,
ast
);
}}
}

#[test]
fn detect_impl_fn_by_receiver() {
detect_impl_fn!(true, fn self_by_ref(&self, u: usize) -> bool {});

detect_impl_fn!(true, fn self_by_self(self, u: usize) -> bool {});
}

#[test]
fn detect_impl_fn_by_self_ty() {
detect_impl_fn!(true, fn self_by_construct(u: usize) -> Self {});
detect_impl_fn!(true, fn self_by_wrapped_construct(u: usize) -> Arc<Self> {});

detect_impl_fn!(true, fn self_by_other_arg(u: usize, slf: Self) {});

detect_impl_fn!(true, fn self_by_other_wrapped_arg(u: usize, slf: Vec<Self>) {})
}

#[test]
fn detect_impl_fn_by_qself() {
detect_impl_fn!(
true,
fn self_by_mention(u: usize) {
Self::other(u)
JustusAdam marked this conversation as resolved.
Show resolved Hide resolved
}
);
}

#[test]
fn detect_no_impl_fn() {
detect_impl_fn!(
false,
fn self_by_mention(u: usize) {
let self_name = 18;
let self_lit = "self";
let self_lit = "Self";
}
);
}
}