Skip to content

Commit 05a526d

Browse files
zhassan-awstedinski
authored andcommitted
Added new APIs to the RMC crate for creating non-deterministic slices (rust-lang#663)
Added new APIs to the RMC crate for creating non-deterministic slices
1 parent d9e84ef commit 05a526d

File tree

4 files changed

+127
-0
lines changed

4 files changed

+127
-0
lines changed

library/rmc/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33
#![feature(rustc_attrs)] // Used for rustc_diagnostic_item.
44

5+
pub mod slice;
6+
57
/// Creates an assumption that will be valid after this statement run. Note that the assumption
68
/// will only be applied for paths that follow the assumption. If the assumption doesn't hold, the
79
/// program will exit successfully.

library/rmc/src/slice.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
use crate::{assume, nondet};
4+
use core::ops::{Deref, DerefMut};
5+
6+
/// Given an array `arr` of length `LENGTH`, this function returns a **valid**
7+
/// slice of `arr` with non-deterministic start and end points. This is useful
8+
/// in situations where one wants to verify that all possible slices of a given
9+
/// array satisfy some property.
10+
///
11+
/// # Example:
12+
///
13+
/// ```rust
14+
/// let arr = [1, 2, 3];
15+
/// let slice = rmc::slice::nondet_slice_of_array(&arr);
16+
/// foo(slice); // where foo is a function that takes a slice and verifies a property about it
17+
/// ```
18+
pub fn nondet_slice_of_array<T, const LENGTH: usize>(arr: &[T; LENGTH]) -> &[T] {
19+
let (from, to) = nondet_range::<LENGTH>();
20+
&arr[from..to]
21+
}
22+
23+
/// A mutable version of the previous function
24+
pub fn nondet_slice_of_array_mut<T, const LENGTH: usize>(arr: &mut [T; LENGTH]) -> &mut [T] {
25+
let (from, to) = nondet_range::<LENGTH>();
26+
&mut arr[from..to]
27+
}
28+
29+
fn nondet_range<const LENGTH: usize>() -> (usize, usize) {
30+
let from: usize = nondet();
31+
let to: usize = nondet();
32+
assume(to <= LENGTH);
33+
assume(from <= to);
34+
(from, to)
35+
}
36+
37+
/// A struct that stores a slice of type `T` with a non-deterministic length
38+
/// between `0..=MAX_SLICE_LENGTH` and with non-deterministic content. This is
39+
/// useful in situations where one wants to verify that all slices with any
40+
/// content and with a length up to `MAX_SLICE_LENGTH` satisfy a certain
41+
/// property. Use `nondet_slice` to create an instance of this struct.
42+
///
43+
/// # Example:
44+
///
45+
/// ```rust
46+
/// let slice: rmc::slice::NonDetSlice<u8, 5> = rmc::slice::nondet_slice();
47+
/// foo(&slice); // where foo is a function that takes a slice and verifies a property about it
48+
/// ```
49+
pub struct NonDetSlice<T, const MAX_SLICE_LENGTH: usize> {
50+
arr: [T; MAX_SLICE_LENGTH],
51+
slice_len: usize,
52+
}
53+
54+
impl<T, const MAX_SLICE_LENGTH: usize> NonDetSlice<T, MAX_SLICE_LENGTH> {
55+
fn new() -> Self {
56+
let arr: [T; MAX_SLICE_LENGTH] = nondet();
57+
let slice_len: usize = nondet();
58+
assume(slice_len <= MAX_SLICE_LENGTH);
59+
Self { arr, slice_len }
60+
}
61+
62+
pub fn get_slice(&self) -> &[T] {
63+
&self.arr[..self.slice_len]
64+
}
65+
66+
pub fn get_slice_mut(&mut self) -> &mut [T] {
67+
&mut self.arr[..self.slice_len]
68+
}
69+
}
70+
71+
impl<T, const MAX_SLICE_LENGTH: usize> Deref for NonDetSlice<T, MAX_SLICE_LENGTH> {
72+
type Target = [T];
73+
74+
fn deref(&self) -> &Self::Target {
75+
self.get_slice()
76+
}
77+
}
78+
79+
impl<T, const MAX_SLICE_LENGTH: usize> DerefMut for NonDetSlice<T, MAX_SLICE_LENGTH> {
80+
fn deref_mut(&mut self) -> &mut Self::Target {
81+
self.get_slice_mut()
82+
}
83+
}
84+
85+
pub fn nondet_slice<T, const MAX_SLICE_LENGTH: usize>() -> NonDetSlice<T, MAX_SLICE_LENGTH> {
86+
NonDetSlice::<T, MAX_SLICE_LENGTH>::new()
87+
}

src/test/rmc/NondetSlices/test1.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// Test the RMC library's API for creating a non-det slice of a given array
4+
5+
fn check(slice: &[u8]) {
6+
let len = slice.len();
7+
assert!(len >= 0 && len <= 3, "Expected slice length to be between 0 and 3. Got {}.", len);
8+
if len > 0 {
9+
let elem = slice[0];
10+
assert!(
11+
elem == 1 || elem == 2 || elem == 3,
12+
"Expected a value of 1, 2, or 3 for the first element. Got {}.",
13+
elem
14+
);
15+
}
16+
}
17+
18+
fn main() {
19+
let arr = [1, 2, 3];
20+
// The slice returned can be any of the following:
21+
// {[], [1], [2], [3], [1, 2], [2, 3], [1, 2, 3]}
22+
let slice = rmc::slice::nondet_slice_of_array(&arr);
23+
check(slice);
24+
}

src/test/rmc/NondetSlices/test2.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
// Test the RMC library's API for creating a non-det slice
4+
5+
fn check(s: &[u8]) {
6+
let len = s.len();
7+
assert!(len >= 0 && len < 6, "Expected slice length to be between 0 and 5. Got {}.", len);
8+
}
9+
10+
fn main() {
11+
// returns a slice of length between 0 and 5 with non-det content
12+
let slice: rmc::slice::NonDetSlice<u8, 5> = rmc::slice::nondet_slice();
13+
check(&slice);
14+
}

0 commit comments

Comments
 (0)