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

Failing (trivial) assertion in presence of a quantifier over a slice #962

Open
Pointerbender opened this issue Apr 17, 2022 · 1 comment

Comments

@Pointerbender
Copy link
Contributor

The following Rust program:

use prusti_contracts::*;

pub fn main() {}

#[requires(forall(|i: usize|
    (0 <= i && i < slice.len()) ==> (slice[i] == i),
    triggers=[(slice[i],)]
))]
pub fn test(slice: &mut [usize]) {
    if slice.len() == 3 {
        debug_assert!(slice[0] == 0);
        debug_assert!(slice[1] == 1);
        debug_assert!(slice[2] == 2);
    }
}

Fails verification with the error:

error: [Prusti: verification error] the asserted expression might not hold
  --> src/lib.rs:30:9
   |
30 |         debug_assert!(slice[2] == 2);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: this error originates in the macro `$crate::assert` (in Nightly builds, run with -Z macro-backtrace for more info)

The weird part is that the first 2 assertions are verified okay, but then Prusti misses out on the third assertion (which should hold).

@Pointerbender
Copy link
Contributor Author

A small addendum, it seems this is because the assertions do not (always?) seem to trigger the quantifier.

The quantifier being (at the Viper level):

  inhale (let _LET_0 == (_1.val_ref) in (
    forall _0_quant_0: Int ::
    { read$Snap$Slice$usize$__$TY$__(snap$__$TY$__Snap$Slice$usize$(_1.val_ref), _0_quant_0) }
    0 <= _0_quant_0 && _0_quant_0 <= 18446744073709551615
      ==> !(0 <= _0_quant_0) || (
        !(_0_quant_0 < len$Snap$Slice$usize$__$TY$__(snap$__$TY$__Snap$Slice$usize$(_LET_0)))
        || _0_quant_0 < len$Snap$Slice$usize$__$TY$__(snap$__$TY$__Snap$Slice$usize$(_LET_0))
        && read$Snap$Slice$usize$__$TY$__(snap$__$TY$__Snap$Slice$usize$(_LET_0), _0_quant_0) == _0_quant_0
      )
  ))

while the array accesses in the Viper method are expressed as:

inhale lookup_pure__$TY$__Slice$usize$usize$(_1.val_ref, _11) == __t26
inhale lookup_pure__$TY$__Slice$usize$usize$(_1.val_ref, _21) == __t30
inhale lookup_pure__$TY$__Slice$usize$usize$(_1.val_ref, _31) == __t34

In order to verify the original example, it needs to properly trigger the quantifier at least once, so that this Viper function is called at least once, after which Silicon knows to connect lookup_pure with read$Snap:

function snap$__$TY$__Snap$Slice$usize$(self: Ref): Snap$Slice$usize
  requires acc(Slice$usize(self), read$())
  ensures [(forall i: Int :: { read$Snap$Slice$usize$__$TY$__(result, i) } { lookup_pure__$TY$__Slice$usize$usize$(self, i) } 0 <= i && i < Slice$len__$TY$__usize$(self) ==> read$Snap$Slice$usize$__$TY$__(result, i) == lookup_pure__$TY$__Slice$usize$usize$(self, i)), true]
  ensures [Slice$len__$TY$__usize$(self) == len$Snap$Slice$usize$__$TY$__(result), true]
{
  cons$Snap$Slice$usize$__$TY$__(seq_collect$Slice$usize$__$TY$__(self, 0))
}

An example of a program that does verify:

use prusti_contracts::*;

fn main() {}

#[requires(forall(|i: usize|
    (0 <= i && i < slice.len()) ==> (slice[i] == i),
    triggers=[(slice[i],)]
))]
#[requires(slice[0] == slice[0])] // dummy statement
pub fn test(slice: &mut [usize]) {
    if slice.len() == 3 {
        debug_assert!(slice[0] == 0);
        debug_assert!(slice[1] == 1);
        debug_assert!(slice[2] == 2);
    }
}

Although it is still interesting why it can verify the first 2 assertions in the original example but not the 3rd, I would expect all three assertions to fail due to the quantifier not triggering :D Also, for the working example there is no pre-condition that describes the length of slice. Should #[requires(slice[0] == slice[0])] result in a possible out-of-bounds error in this case, because the slice could be an empty slice?

Existing Github issues related to this one are #796 and #877.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant