diff --git a/prusti-tests/tests/verify/fail/closures/using-type-dep.rs b/prusti-tests/tests/verify/fail/closures/using-type-dep.rs new file mode 100644 index 00000000000..769b411aeca --- /dev/null +++ b/prusti-tests/tests/verify/fail/closures/using-type-dep.rs @@ -0,0 +1,65 @@ +// compiler-flags: -Penable_ghost_constraints=true + +#![feature(unboxed_closures, fn_traits)] + +use prusti_contracts::*; + +pub trait ClosureSpecExt : FnMut { + 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 , [ + requires(>::hist_inv(&self, &self)), + requires(>::call_pre(&self, &args)), + ensures(>::hist_inv(old(&self), &self)), + ensures(>::call_post(old(&self), &self, &args, &result)), + ])] + fn call_mut(&mut self, args: 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 +} diff --git a/prusti-tests/tests/verify/pass/closures/using-type-dep.rs b/prusti-tests/tests/verify/pass/closures/using-type-dep.rs new file mode 100644 index 00000000000..b58e6a72519 --- /dev/null +++ b/prusti-tests/tests/verify/pass/closures/using-type-dep.rs @@ -0,0 +1,66 @@ +// compiler-flags: -Penable_ghost_constraints=true + +#![feature(unboxed_closures, fn_traits)] + +use prusti_contracts::*; + +pub trait ClosureSpecExt : FnMut { + 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 , [ + requires(>::hist_inv(&self, &self)), + requires(>::call_pre(&self, &args)), + ensures(>::hist_inv(old(&self), &self)), + ensures(>::call_post(old(&self), &self, &args, &result)), + ])] + fn call_mut(&mut self, args: 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); +}