prusti-dev icon indicating copy to clipboard operation
prusti-dev copied to clipboard

Taking a mutable slice not supported

Open Omar0Tarek opened this issue 3 years ago • 2 comments

In the code below, the function insertion_sort loops over the input array v from the end to the start (v.len() - 2 to 0), inserting the element v[i] every loop in its correct position in the subarray v[i..].

The function insert_head moves the first element in the array to its correct position, as the rest of the array v[1..] is sorted, and returns a completely sorted array.

#[trusted]
#[requires(v.len() > 1)]
#[requires(forall(|x: usize, y: usize| (1 <= x && x <= y && y < v.len()) ==> v[x] <= v[y]))] // this pre-condition might not hold
#[ensures(forall(|x: usize, y: usize| (0 <= x && x <= y && y < v.len()) ==> v[x] <= v[y]))]
#[ensures(v.len() == old(v.len()))]
fn insert_head(v: &mut [i32]) {}

#[requires(v.len() > 1)]
#[ensures(forall(|x: usize, y: usize| ((0 <= x && x <= y && y < v.len()) ==> v[x] <= v[y])))]
fn insertion_sort(v: &mut [i32]) {
    let mut i = v.len() - 2;
    loop {
        body_invariant!(i >= 0 && i < v.len() - 1);
        body_invariant!(forall(|x: usize, y: usize| (i + 1 <= x && x <= y && y < v.len()) ==> v[x] <= v[y]));
        insert_head(&mut v[i..]); // error when calling the function here
        if i == 0 {
            break;
        } else {
            i -= 1;
        }
    }
}

When running Prusti, verification fails with an error that the pre-condition #[requires(forall(|x: usize, y: usize| (1 <= x && x <= y && y < v.len()) ==> v[x] <= v[y]))] of the function insert_head might not hold when the function is called from inside the loop in the function insertion_sort (check comments in above code). The error happens because we are passing an array slice to insert_head.

This was fixed when the array slice was replaced with passing the whole array, along with the slice's starting index, as shown in the code below which was verified successfully.

#[trusted]
#[requires(v.len() > 1)]
#[requires(i < v.len() - 1)]
#[requires(forall(|x: usize, y: usize| (i + 1 <= x && x <= y && y < v.len()) ==> v[x] <= v[y]))]
#[ensures(forall(|x: usize, y: usize| (i <= x && x <= y && y < v.len()) ==> v[x] <= v[y]))]
#[ensures(v.len() == old(v.len()))]
fn insert_head(v: &mut [i32], i: usize) {}

#[requires(v.len() > 1)]
#[ensures(forall(|x: usize, y: usize| ((0 <= x && x <= y && y < v.len()) ==> v[x] <= v[y])))]
fn insertion_sort(v: &mut [i32]) {
    let mut i = v.len() - 2;
    loop {
        body_invariant!(i >= 0 && i < v.len() - 1);
        body_invariant!(forall(|x: usize, y: usize| (i + 1 <= x && x <= y && y < v.len()) ==> v[x] <= v[y]));
        insert_head(v, i);
        if i == 0 {
            break;
        } else {
            i -= 1;
        }
    }
}

Omar0Tarek avatar Apr 30 '22 21:04 Omar0Tarek

Mutably slicing in not supported atm, so Prusti has no idea how the base sequence will be updated from any changes to the slice. I would recommend writing your own wrapper function, e.g.:

#[truested]
#[requires(...)]
#[ensures(...)]
fn take_mut_slice(s: &mut [i32], start: usize) -> &mut [i32] { &mut s[start..] }

with an appropriate contract (e.g. using a pledge in the postcondition)

JonasAlaif avatar May 01 '22 08:05 JonasAlaif

Reopening since we should support this eventually.

vakaras avatar May 01 '22 09:05 vakaras