Skip to content

Commit 76ee6c3

Browse files
authored
Add specifications for NonZero (#2471)
1 parent 49b8806 commit 76ee6c3

5 files changed

Lines changed: 230 additions & 0 deletions

File tree

source/rust_verify_test/tests/std.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,3 +1604,45 @@ test_verify_one_file! {
16041604
}
16051605
} => Err(err) => assert_fails(err, 20)
16061606
}
1607+
1608+
test_verify_one_file! {
1609+
#[test] nonzero verus_code! {
1610+
1611+
use vstd::prelude::*;
1612+
use vstd::std_specs::nonzero::*;
1613+
use std::num::{NonZeroU64, NonZeroI32};
1614+
1615+
fn test() {
1616+
let x = NonZeroI32::new(-20).unwrap();
1617+
assert(x@ == -20);
1618+
1619+
let x1 = x.clone();
1620+
assert(x1@ == -20);
1621+
1622+
let x2 = unsafe { NonZeroI32::new_unchecked(30)};
1623+
assert(x2@ == 30);
1624+
1625+
// assert(x1 < x2); SpecOrd is not supported.
1626+
1627+
let b = x1 < x2;
1628+
assert(b);
1629+
1630+
let x3 = x2.get();
1631+
assert(x3 == 30);
1632+
}
1633+
1634+
fn test_bitor() {
1635+
let x = NonZeroU64::new(0x1011).unwrap();
1636+
let y = NonZeroU64::new(0x100).unwrap();
1637+
let z = x | y;
1638+
assert(0x1011 | 0x100 == 0x1111) by (compute_only);
1639+
assert(z@ == 0x1111);
1640+
1641+
let z1 = x | 0x1000;
1642+
assert(0x1011 == 0x1011 | 0x1000) by (compute_only);
1643+
assert(z1@ == x@);
1644+
assert(z1 == x);
1645+
}
1646+
1647+
} => Ok(())
1648+
}

source/vstd/std_specs/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ pub mod vecdeque;
3636
#[cfg(feature = "alloc")]
3737
pub mod smart_ptrs;
3838

39+
#[cfg(feature = "nonzero_internals")]
40+
pub mod nonzero;
41+
3942
// This struct is a hack that exists purely to create
4043
// a rustdoc page dedicated to 'assume_specification' specs
4144
pub struct VstdSpecsForRustStdLib;

source/vstd/std_specs/nonzero.rs

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
use super::super::prelude::*;
2+
use super::cmp::{
3+
OrdSpec, OrdSpecImpl, PartialEqSpec, PartialEqSpecImpl, PartialOrdSpec, PartialOrdSpecImpl,
4+
};
5+
use super::convert::FromSpecImpl;
6+
use super::ops::{BitOrSpec, BitOrSpecImpl};
7+
use core::cmp::Ordering;
8+
use core::num::{NonZero, ZeroablePrimitive};
9+
use core::ops::BitOr;
10+
11+
verus! {
12+
13+
#[verifier::external_trait_specification]
14+
#[verifier::external_trait_extension(ZeroablePrimitiveSpec via ZeroablePrimitiveSpecImpl)]
15+
#[verifier::external_trait_private_bound(core::num::nonzero::private::Sealed)]
16+
pub trait ExZeroablePrimitive: Sized + Copy {
17+
type ExternalTraitSpecificationFor: ZeroablePrimitive;
18+
19+
spec fn is_zero(self) -> bool;
20+
}
21+
22+
macro_rules! impl_zeroable_primitive_spec_impl {
23+
($($t:ty),*) => {
24+
$(
25+
verus! {
26+
impl ZeroablePrimitiveSpecImpl for $t {
27+
open spec fn is_zero(self) -> bool {
28+
self == 0
29+
}
30+
}
31+
}
32+
)*
33+
};
34+
}
35+
36+
// The implementators of `ZeroablePrimitive` coincide with `Integer`.
37+
impl_zeroable_primitive_spec_impl!(char, u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
38+
39+
#[verifier::external_type_specification]
40+
#[verifier::external_body]
41+
#[verifier::reject_recursive_types(T)]
42+
pub struct ExNonZero<T: ZeroablePrimitive>(NonZero<T>);
43+
44+
impl<T: ZeroablePrimitive> View for NonZero<T> {
45+
type V = T;
46+
47+
uninterp spec fn view(&self) -> Self::V;
48+
}
49+
50+
// Need this to define `BitOrSpecImpl`
51+
pub uninterp spec fn nonzero_from_primitive<T: ZeroablePrimitive>(n: T) -> NonZero<T>;
52+
53+
pub broadcast axiom fn axiom_nonzero_from_primitive_view_eq<T: ZeroablePrimitive>(n: T)
54+
requires
55+
!n.is_zero(),
56+
ensures
57+
#[trigger] nonzero_from_primitive(n)@ == n,
58+
;
59+
60+
pub broadcast axiom fn axiom_view_nonzero_from_primitive_eq<T: ZeroablePrimitive>(n: NonZero<T>)
61+
ensures
62+
#[trigger] nonzero_from_primitive(n@) == n,
63+
;
64+
65+
pub broadcast axiom fn axiom_nonzero_is_not_zero<T: ZeroablePrimitive>(n: NonZero<T>)
66+
ensures
67+
!(#[trigger] n@).is_zero(),
68+
;
69+
70+
pub assume_specification<T: ZeroablePrimitive>[ NonZero::<T>::new ](n: T) -> (ret: Option<
71+
NonZero<T>,
72+
>)
73+
ensures
74+
match ret {
75+
Some(nz) => nz@ == n && !n.is_zero(),
76+
None => n.is_zero(),
77+
},
78+
opens_invariants none
79+
no_unwind
80+
;
81+
82+
pub assume_specification<T: ZeroablePrimitive>[ NonZero::<T>::new_unchecked ](n: T) -> (ret:
83+
NonZero<T>)
84+
requires
85+
!n.is_zero(),
86+
ensures
87+
ret@ == n,
88+
opens_invariants none
89+
no_unwind
90+
;
91+
92+
#[verifier::inline]
93+
pub open spec fn nonzero_spec_get<T: ZeroablePrimitive>(n: NonZero<T>) -> T {
94+
n@
95+
}
96+
97+
#[verifier::when_used_as_spec(nonzero_spec_get)]
98+
pub assume_specification<T: ZeroablePrimitive>[ NonZero::<T>::get ](n: NonZero<T>) -> T
99+
returns
100+
n@,
101+
opens_invariants none
102+
no_unwind
103+
;
104+
105+
impl<T: ZeroablePrimitive + PartialEqSpec> PartialEqSpecImpl for NonZero<T> {
106+
open spec fn obeys_eq_spec() -> bool {
107+
true
108+
}
109+
110+
open spec fn eq_spec(&self, other: &Self) -> bool {
111+
self.get().eq_spec(&other.get())
112+
}
113+
}
114+
115+
// Ord is not implemented because of the [`Destruct`](https://doc.rust-lang.org/std/marker/trait.Destruct.html) trait bound.
116+
impl<T: ZeroablePrimitive + PartialOrdSpec> PartialOrdSpecImpl for NonZero<T> {
117+
open spec fn obeys_partial_cmp_spec() -> bool {
118+
true
119+
}
120+
121+
open spec fn partial_cmp_spec(&self, other: &Self) -> Option<Ordering> {
122+
self.get().partial_cmp_spec(&other.get())
123+
}
124+
}
125+
126+
impl<T: ZeroablePrimitive + BitOrSpec<Output = T>> BitOrSpecImpl<T> for NonZero<T> {
127+
open spec fn obeys_bitor_spec() -> bool {
128+
true
129+
}
130+
131+
open spec fn bitor_req(self, rhs: T) -> bool {
132+
self.get().bitor_req(rhs)
133+
}
134+
135+
open spec fn bitor_spec(self, rhs: T) -> Self::Output {
136+
nonzero_from_primitive(self.get().bitor_spec(rhs))
137+
}
138+
}
139+
140+
impl<T: ZeroablePrimitive + BitOrSpec<Output = T>> BitOrSpecImpl<NonZero<T>> for NonZero<T> {
141+
open spec fn obeys_bitor_spec() -> bool {
142+
true
143+
}
144+
145+
open spec fn bitor_req(self, rhs: NonZero<T>) -> bool {
146+
self.get().bitor_req(rhs.get())
147+
}
148+
149+
open spec fn bitor_spec(self, rhs: NonZero<T>) -> Self::Output {
150+
nonzero_from_primitive(self.get().bitor_spec(rhs.get()))
151+
}
152+
}
153+
154+
impl<T: ZeroablePrimitive> FromSpecImpl<NonZero<T>> for T {
155+
open spec fn obeys_from_spec() -> bool {
156+
true
157+
}
158+
159+
open spec fn from_spec(nz: NonZero<T>) -> Self {
160+
nz.get()
161+
}
162+
}
163+
164+
pub assume_specification<T: ZeroablePrimitive>[ <NonZero<T> as Clone>::clone ](
165+
nz: &NonZero<T>,
166+
) -> NonZero<T>
167+
returns
168+
nz,
169+
;
170+
171+
pub broadcast group group_nonzero_axioms {
172+
axiom_nonzero_from_primitive_view_eq,
173+
axiom_view_nonzero_from_primitive_eq,
174+
axiom_nonzero_is_not_zero,
175+
}
176+
177+
} // verus!

source/vstd/vstd.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#![cfg_attr(verus_keep_ghost, feature(derive_eq_internals))]
2222
#![cfg_attr(verus_keep_ghost, feature(slice_index_methods))]
2323
#![cfg_attr(all(feature = "alloc", verus_keep_ghost), feature(liballoc_internals))]
24+
#![cfg_attr(verus_keep_ghost, feature(nonzero_internals))]
2425

2526
#[cfg(feature = "alloc")]
2627
extern crate alloc;
@@ -145,6 +146,11 @@ pub broadcast group group_vstd_default {
145146
std_specs::hash::group_hash_axioms,
146147
#[cfg(feature = "alloc")]
147148
std_specs::btree::group_btree_axioms,
149+
//
150+
// std_specs for nonzero_internals
151+
//
152+
#[cfg(feature = "nonzero_internals")]
153+
std_specs::nonzero::group_nonzero_axioms,
148154
}
149155

150156
} // verus!

source/vstd_build/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ fn main() {
140140
child_args.push("--cfg".to_string());
141141
child_args.push("feature=\"alloc\"".to_string());
142142
}
143+
child_args.push("--cfg".to_string());
144+
child_args.push("feature=\"nonzero_internals\"".to_string());
143145
child_args.push(VSTD_RS_PATH.to_string());
144146

145147
let cmd = verus_target_path.join("rust_verify");

0 commit comments

Comments
 (0)