Skip to content

Commit

Permalink
test closures using type-dependent contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
Aurel300 committed Jul 18, 2022
1 parent 414e081 commit 2b6d632
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 0 deletions.
65 changes: 65 additions & 0 deletions prusti-tests/tests/verify/fail/closures/using-type-dep.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// compiler-flags: -Penable_ghost_constraints=true

#![feature(unboxed_closures, fn_traits)]

use prusti_contracts::*;

pub trait ClosureSpecExt<A> : FnMut<A> {
predicate! { fn call_pre(&self, args: &A) -> bool; }
predicate! { fn call_post(prev: &Self, curr: &Self, args: &A, res: &Self::Output) -> bool; }
predicate! { fn hist_inv(prev: &Self, curr: &Self) -> bool; }
}

#[extern_spec]
trait FnMut<#[generic] A> {
#[ghost_constraint(Self: ClosureSpecExt<A> , [
requires(<Self as ClosureSpecExt<A>>::hist_inv(&self, &self)),
requires(<Self as ClosureSpecExt<A>>::call_pre(&self, &args)),
ensures(<Self as ClosureSpecExt<A>>::hist_inv(old(&self), &self)),
ensures(<Self as ClosureSpecExt<A>>::call_post(old(&self), &self, &args, &result)),
])]
fn call_mut(&mut self, args: A) -> <Self as FnOnce<A>>::Output;
}

// encoding of:
// #[requires(x >= 5)]
// #[ensures(result == x + val)]
// #[ensures(val == old(val) + 1)]
// #[invariant(val >= old(val))]
// |x: i32| { val += 1; x + val }

struct MyClosure {
val: i32,
}

impl FnOnce<(i32,)> for MyClosure {
type Output = i32;
extern "rust-call" fn call_once(mut self, args: (i32,)) -> i32 {
self.val += 1;
args.0 + self.val
}
}
impl FnMut<(i32,)> for MyClosure {
extern "rust-call" fn call_mut(&mut self, args: (i32,)) -> i32 {
self.val += 1;
args.0 + self.val
}
}
#[refine_trait_spec]
impl ClosureSpecExt<(i32,)> for MyClosure {
predicate! { fn call_pre(&self, args: &(i32,)) -> bool {
args.0 >= 5
} }
predicate! { fn call_post(prev: &Self, curr: &Self, args: &(i32,), res: &i32) -> bool {
*res == args.0 + curr.val
&& curr.val == prev.val + 1
} }
predicate! { fn hist_inv(prev: &Self, curr: &Self) -> bool {
curr.val >= prev.val
} }
}

fn main() {
let mut cl = MyClosure { val: 3 };
cl(3); //~ ERROR precondition might not hold
}
66 changes: 66 additions & 0 deletions prusti-tests/tests/verify/pass/closures/using-type-dep.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// compiler-flags: -Penable_ghost_constraints=true

#![feature(unboxed_closures, fn_traits)]

use prusti_contracts::*;

pub trait ClosureSpecExt<A> : FnMut<A> {
predicate! { fn call_pre(&self, args: &A) -> bool; }
predicate! { fn call_post(prev: &Self, curr: &Self, args: &A, res: &Self::Output) -> bool; }
predicate! { fn hist_inv(prev: &Self, curr: &Self) -> bool; }
}

#[extern_spec]
trait FnMut<#[generic] A> {
#[ghost_constraint(Self: ClosureSpecExt<A> , [
requires(<Self as ClosureSpecExt<A>>::hist_inv(&self, &self)),
requires(<Self as ClosureSpecExt<A>>::call_pre(&self, &args)),
ensures(<Self as ClosureSpecExt<A>>::hist_inv(old(&self), &self)),
ensures(<Self as ClosureSpecExt<A>>::call_post(old(&self), &self, &args, &result)),
])]
fn call_mut(&mut self, args: A) -> <Self as FnOnce<A>>::Output;
}

// encoding of:
// #[requires(x >= 5)]
// #[ensures(result == x + val)]
// #[ensures(val == old(val) + 1)]
// #[invariant(val >= old(val))]
// |x: i32| { val += 1; x + val }

struct MyClosure {
val: i32,
}

impl FnOnce<(i32,)> for MyClosure {
type Output = i32;
extern "rust-call" fn call_once(mut self, args: (i32,)) -> i32 {
self.val += 1;
args.0 + self.val
}
}
impl FnMut<(i32,)> for MyClosure {
extern "rust-call" fn call_mut(&mut self, args: (i32,)) -> i32 {
self.val += 1;
args.0 + self.val
}
}
#[refine_trait_spec]
impl ClosureSpecExt<(i32,)> for MyClosure {
predicate! { fn call_pre(&self, args: &(i32,)) -> bool {
args.0 >= 5
} }
predicate! { fn call_post(prev: &Self, curr: &Self, args: &(i32,), res: &i32) -> bool {
*res == args.0 + curr.val
&& curr.val == prev.val + 1
} }
predicate! { fn hist_inv(prev: &Self, curr: &Self) -> bool {
curr.val >= prev.val
} }
}

fn main() {
let mut cl = MyClosure { val: 3 };
assert!(cl(6) == 10);
assert!(cl(6) == 11);
}

0 comments on commit 2b6d632

Please sign in to comment.