Skip to content

Commit 48bb306

Browse files
authored
Replace struct impl of Invariant by Arbitrary (rust-lang#1401)
We have recently realized that implementing Invariant of Option, Result and array has a limitation. This is not sufficient to generate arbitrary values of types that only implements Arbitrary. Thus, implement Arbitrary instead.
1 parent fed3132 commit 48bb306

File tree

5 files changed

+145
-35
lines changed

5 files changed

+145
-35
lines changed

library/kani/src/arbitrary.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,38 @@ where
2020
value
2121
}
2222
}
23+
24+
impl<T, const N: usize> Arbitrary for [T; N]
25+
where
26+
T: Arbitrary,
27+
{
28+
fn any() -> Self {
29+
// The "correct way" would be to MaybeUninit but there is performance penalty.
30+
let mut data: [T; N] = unsafe { crate::any_raw() };
31+
32+
for elem in &mut data[..] {
33+
*elem = T::any();
34+
}
35+
36+
data
37+
}
38+
}
39+
40+
impl<T> Arbitrary for Option<T>
41+
where
42+
T: Arbitrary,
43+
{
44+
fn any() -> Self {
45+
if bool::any() { Some(T::any()) } else { None }
46+
}
47+
}
48+
49+
impl<T, E> Arbitrary for Result<T, E>
50+
where
51+
T: Arbitrary,
52+
E: Arbitrary,
53+
{
54+
fn any() -> Self {
55+
if bool::any() { Ok(T::any()) } else { Err(E::any()) }
56+
}
57+
}

library/kani/src/invariant.rs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -71,39 +71,6 @@ unsafe impl Invariant for char {
7171
}
7272
}
7373

74-
unsafe impl<T: Invariant, const N: usize> Invariant for [T; N] {
75-
fn is_valid(&self) -> bool {
76-
self.iter().all(|e| e.is_valid())
77-
}
78-
}
79-
80-
unsafe impl<T> Invariant for Option<T>
81-
where
82-
T: Invariant,
83-
{
84-
#[inline(always)]
85-
fn is_valid(&self) -> bool {
86-
if let Some(v) = self { v.is_valid() } else { matches!(*self, None) }
87-
}
88-
}
89-
90-
unsafe impl<T, E> Invariant for Result<T, E>
91-
where
92-
T: Invariant,
93-
E: Invariant,
94-
{
95-
#[inline(always)]
96-
fn is_valid(&self) -> bool {
97-
if let Ok(v) = self {
98-
v.is_valid()
99-
} else if let Err(e) = self {
100-
e.is_valid()
101-
} else {
102-
false
103-
}
104-
}
105-
}
106-
10774
macro_rules! nonzero_invariant {
10875
( $type: ty ) => {
10976
unsafe impl Invariant for $type {
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
//
4+
// Check that users can use any Option, Result and array if they implement Arbitrary or Invariant.
5+
#![cfg_attr(kani, feature(min_specialization))]
6+
7+
extern crate kani;
8+
use kani::Arbitrary;
9+
use kani::Invariant;
10+
11+
trait PercentTrait {
12+
fn val(&self) -> u8;
13+
fn ok(&self) -> bool;
14+
}
15+
16+
macro_rules! percent_type {
17+
( $type: tt ) => {
18+
struct $type {
19+
inner: u8,
20+
}
21+
impl PercentTrait for $type {
22+
fn val(&self) -> u8 {
23+
self.inner
24+
}
25+
26+
fn ok(&self) -> bool {
27+
self.inner <= 100
28+
}
29+
}
30+
};
31+
}
32+
33+
percent_type!(Percent);
34+
percent_type!(PercentInvariant);
35+
percent_type!(PercentArbitrary);
36+
37+
unsafe impl Invariant for PercentInvariant {
38+
fn is_valid(&self) -> bool {
39+
self.ok()
40+
}
41+
}
42+
43+
impl Arbitrary for PercentArbitrary {
44+
fn any() -> Self {
45+
let val = kani::any();
46+
kani::assume(val <= 100);
47+
PercentArbitrary { inner: val }
48+
}
49+
}
50+
51+
unsafe impl Invariant for Percent {
52+
fn is_valid(&self) -> bool {
53+
self.ok()
54+
}
55+
}
56+
57+
impl Arbitrary for Percent {
58+
fn any() -> Self {
59+
let val = kani::any();
60+
kani::assume(val <= 100);
61+
Percent { inner: val }
62+
}
63+
}
64+
65+
fn check<T: PercentTrait + Arbitrary>() {
66+
let var = Option::<T>::any();
67+
match var {
68+
None => assert!(T::any().ok()),
69+
Some(p) => assert!(p.ok()),
70+
}
71+
}
72+
73+
fn check_result<T: PercentTrait + Arbitrary>() {
74+
let var = Result::<T, ()>::any();
75+
match var {
76+
Err(_) => assert!(T::any().ok()),
77+
Ok(p) => assert!(p.ok()),
78+
}
79+
}
80+
81+
fn check_array<T: PercentTrait + Arbitrary>() {
82+
let var: [T; 10] = kani::any();
83+
assert!(var.iter().all(|e| e.ok()));
84+
}
85+
86+
#[kani::proof]
87+
#[kani::unwind(12)]
88+
fn check_invariant() {
89+
check::<PercentInvariant>();
90+
check_result::<PercentInvariant>();
91+
check_array::<PercentInvariant>();
92+
}
93+
94+
#[kani::proof]
95+
#[kani::unwind(12)]
96+
fn check_arbitrary() {
97+
check::<PercentArbitrary>();
98+
check_result::<PercentArbitrary>();
99+
check_array::<PercentArbitrary>();
100+
}
101+
102+
#[kani::proof]
103+
#[kani::unwind(12)]
104+
fn check_both() {
105+
check::<Percent>();
106+
check_result::<Percent>();
107+
check_array::<Percent>();
108+
}

tests/kani/FunctionAbstractions/mem_replace.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ unsafe impl kani::Invariant for Pair {
1717
}
1818
}
1919

20-
fn test<T: kani::Invariant + std::cmp::PartialEq + Clone>() {
20+
fn test<T: kani::Arbitrary + std::cmp::PartialEq + Clone>() {
2121
let mut var1 = kani::any::<T>();
2222
let var2 = kani::any::<T>();
2323
let old_var1 = var1.clone();

tests/kani/FunctionAbstractions/mem_swap.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ unsafe impl kani::Invariant for Pair {
1717
}
1818
}
1919

20-
fn test<T: kani::Invariant + std::cmp::PartialEq + Clone>() {
20+
fn test<T: kani::Arbitrary + std::cmp::PartialEq + Clone>() {
2121
let mut var1 = kani::any::<T>();
2222
let mut var2 = kani::any::<T>();
2323
let old_var1 = var1.clone();

0 commit comments

Comments
 (0)