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

Implement View and DeepView in slice and array #1078

Merged
merged 4 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
1 change: 1 addition & 0 deletions source/rust_verify_test/tests/arrays.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use common::*;

test_verify_one_file! {
#[test] test1 verus_code! {
use vstd::prelude::*;
use vstd::array::*;

fn test(ar: [u8; 20])
Expand Down
26 changes: 26 additions & 0 deletions source/rust_verify_test/tests/std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,5 +337,31 @@ test_verify_one_file! {
assert(g2);
assert(c2@ == v2@); // FAILS
}

fn test_slice_deep_view(a1: &[Vec<u8>], a2: &[Vec<u8>])
requires
a1.len() == 1,
a2.len() == 1,
a1[0].len() == 1,
a2[0].len() == 1,
a1[0][0] == 10,
a2[0][0] == 10,
ensures
a1.deep_view() == a2.deep_view(),
{
assert(a1.deep_view() =~~= a2.deep_view()); // TODO: get rid of this?
}

fn test_array_deep_view(a1: &[Vec<u8>; 1], a2: &[Vec<u8>; 1])
requires
a1[0].len() == 1,
a2[0].len() == 1,
a1[0][0] == 10,
a2[0][0] == 10,
ensures
a1.deep_view() == a2.deep_view(),
{
assert(a1.deep_view() =~~= a2.deep_view()); // TODO: get rid of this?
}
} => Err(err) => assert_fails(err, 2)
}
18 changes: 15 additions & 3 deletions source/vstd/array.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
#![allow(unused_imports)]
use crate::seq::*;
use crate::slice::SliceAdditionalSpecFns;
use crate::view::*;
use builtin::*;
use builtin_macros::*;

verus! {

pub trait ArrayAdditionalSpecFns<T> {
impl<T, const N: usize> View for [T; N] {
type V = Seq<T>;

spec fn view(&self) -> Seq<T>;
}

impl<T: DeepView, const N: usize> DeepView for [T; N] {
type V = Seq<T::V>;

open spec fn deep_view(&self) -> Seq<T::V> {
let v = self.view();
Seq::new(v.len(), |i: int| v[i].deep_view())
}
}

pub trait ArrayAdditionalSpecFns<T>: View<V = Seq<T>> {
spec fn spec_index(&self, i: int) -> T
recommends
0 <= i < self.view().len(),
Expand All @@ -21,8 +35,6 @@ pub trait ArrayAdditionalExecFns<T> {
}

impl<T, const N: usize> ArrayAdditionalSpecFns<T> for [T; N] {
spec fn view(&self) -> Seq<T>;

#[verifier(inline)]
open spec fn spec_index(&self, i: int) -> T {
self.view().index(i)
Expand Down
47 changes: 35 additions & 12 deletions source/vstd/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,29 @@ pub use super::std_specs::vec::VecAdditionalSpecFns;

verus! {

pub trait SliceAdditionalSpecFns<T> {
impl<T> View for [T] {
type V = Seq<T>;

spec fn view(&self) -> Seq<T>;
}

impl<T: DeepView> DeepView for [T] {
type V = Seq<T::V>;

open spec fn deep_view(&self) -> Seq<T::V> {
let v = self.view();
Seq::new(v.len(), |i: int| v[i].deep_view())
}
Comment on lines +22 to +25
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just making a quick note here that @y1ca1 had some concerns (during a separate discussion) about this adding more triggers than explicitly reimplementing deep_view on each of the vstd data structures individually.

Personally I think that implementing deep_view separately on each of (say) [u8], [u32], [u64] and so on is not really a good idea (because it doesn't scale and would prevent usage on user-defined types without them manually also implementing deepview on [Foo]), and that we should just go with the current approach.

However, @y1ca1, if I've misunderstood what you meant, please do comment here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for mentioning this. What I meant by "explicitly reimplementing" deep_view is to provide separate implementation on each of the methods for the generic slice type[T] whose specs are described in deep_view. This would arguably save a lot of triggers if one uses deep_view ubiquitously, but yeah, definitely less ergonomic and harder to maintain.

}

pub trait SliceAdditionalSpecFns<T>: View<V = Seq<T>> {
spec fn spec_index(&self, i: int) -> T
recommends
0 <= i < self.view().len(),
;
}

impl<T> SliceAdditionalSpecFns<T> for [T] {
spec fn view(&self) -> Seq<T>;

#[verifier(inline)]
open spec fn spec_index(&self, i: int) -> T {
self.view().index(i)
Expand All @@ -39,6 +50,27 @@ pub exec fn slice_index_get<T>(slice: &[T], i: usize) -> (out: &T)
&slice[i]
}

////// Len (with autospec)
pub open spec fn spec_slice_len<T>(slice: &[T]) -> usize;

// This axiom is slightly better than defining spec_slice_len to just be `[email protected]() as usize`
// (the axiom also shows that [email protected]() is in-bounds for usize)
#[verifier(external_body)]
pub broadcast proof fn axiom_spec_len<T>(slice: &[T])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will now need to be added to the vstd_default broadcast group to be visible to users.

ensures
#[trigger] spec_slice_len(slice) == [email protected](),
{
}

#[verifier::external_fn_specification]
#[verifier::when_used_as_spec(spec_slice_len)]
pub fn slice_len<T>(slice: &[T]) -> (len: usize)
ensures
len == spec_slice_len(slice),
{
slice.len()
}

#[cfg(feature = "alloc")]
#[verifier(external_body)]
pub exec fn slice_to_vec<T: Copy>(slice: &[T]) -> (out: alloc::vec::Vec<T>)
Expand All @@ -58,13 +90,4 @@ pub exec fn slice_subrange<T, 'a>(slice: &'a [T], i: usize, j: usize) -> (out: &
&slice[i..j]
}

#[verifier(external_fn_specification)]
pub exec fn slice_len<T>(slice: &[T]) -> (length: usize)
ensures
length >= 0,
length == [email protected](),
{
slice.len()
}

} // verus!
25 changes: 9 additions & 16 deletions source/vstd/std_specs/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,6 @@ pub struct ExVec<T, A: Allocator>(Vec<T, A>);
#[verifier(external_body)]
pub struct ExGlobal(alloc::alloc::Global);

////// Declare 'view' function
pub trait VecAdditionalSpecFns<T> {
spec fn spec_len(&self) -> nat;

spec fn spec_index(&self, i: int) -> T
recommends
0 <= i < self.spec_len(),
;
}

impl<T, A: Allocator> View for Vec<T, A> {
type V = Seq<T>;

Expand All @@ -39,16 +29,19 @@ impl<T: DeepView, A: Allocator> DeepView for Vec<T, A> {
type V = Seq<T::V>;

open spec fn deep_view(&self) -> Seq<T::V> {
Seq::new(self.view().len(), |i: int| self[i].deep_view())
let v = self.view();
Seq::new(v.len(), |i: int| v[i].deep_view())
}
}

impl<T, A: Allocator> VecAdditionalSpecFns<T> for Vec<T, A> {
#[verifier(inline)]
open spec fn spec_len(&self) -> nat {
self.view().len()
}
pub trait VecAdditionalSpecFns<T>: View<V = Seq<T>> {
spec fn spec_index(&self, i: int) -> T
recommends
0 <= i < self.view().len(),
;
}

impl<T, A: Allocator> VecAdditionalSpecFns<T> for Vec<T, A> {
#[verifier(inline)]
open spec fn spec_index(&self, i: int) -> T {
self.view().index(i)
Expand Down
Loading