Skip to content

Commit 3a967e3

Browse files
authored
Contracts & Harnesses for wrapping_shl (#112)
Towards #59 ### Changes * Added contracts for `wrapping_shl` (located in `library/core/src/num/int_macros.rs` and `uint_macros.rs`) * Added a macro for generating wrapping_{shl, shr} harnesses * Added harnesses for `wrapping_shl` of each integer type * `i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `u8`, `u16`, `u32`, `u64`, `u128`, `usize` --- 12 harnesses in total. ### Revalidation 1. Per the discussion in #59, we have to **build and run Kani from `feature/verify-rust-std` branch**. 2. To revalidate the verification results, run the following command. `<harness_to_run>` can be either `num::verify` to run all harnesses or `num::verify::<harness_name>` (e.g. `checked_wrapping_shl_i8`) to run a specific harness. ``` kani verify-std "path/to/library" \ --harness <harness_to_run> \ -Z unstable-options \ -Z function-contracts \ -Z mem-predicates ``` All harnesses should pass the default checks (1251 checks where 1 unreachable). ``` SUMMARY: ** 0 of 1251 failed (1 unreachable) VERIFICATION:- SUCCESSFUL Verification Time: 2.4682913s Complete - 1 successfully verified harnesses, 0 failures, 1 total. ``` Example of the unreachable check: ``` Check 123: num::<impl i8>::wrapping_shl.assertion.1 - Status: UNREACHABLE - Description: "attempt to subtract with overflow" - Location: library/core/src/num/int_macros.rs:2172:42 in function num::<impl i8>::wrapping_shl ``` ### Questions 1. Should we add `requires` (and `ensures`) for `wrapping_shl` given that `unchecked_shl` already has a `requires`? By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.
1 parent dc862c4 commit 3a967e3

File tree

3 files changed

+40
-0
lines changed

3 files changed

+40
-0
lines changed

library/core/src/num/int_macros.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2160,6 +2160,7 @@ macro_rules! int_impl {
21602160
without modifying the original"]
21612161
#[inline(always)]
21622162
#[rustc_allow_const_fn_unstable(unchecked_shifts)]
2163+
#[ensures(|result| *result == self << (rhs & (Self::BITS - 1)))]
21632164
pub const fn wrapping_shl(self, rhs: u32) -> Self {
21642165
// SAFETY: the masking by the bitsize of the type ensures that we do not shift
21652166
// out of bounds

library/core/src/num/mod.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1687,6 +1687,19 @@ mod verify {
16871687
}
16881688
}
16891689

1690+
// Verify `wrapping_{shl, shr}` which internally uses `unchecked_{shl,shr}`
1691+
macro_rules! generate_wrapping_shift_harness {
1692+
($type:ty, $method:ident, $harness_name:ident) => {
1693+
#[kani::proof_for_contract($type::$method)]
1694+
pub fn $harness_name() {
1695+
let num1: $type = kani::any::<$type>();
1696+
let num2: u32 = kani::any::<u32>();
1697+
1698+
let _ = num1.$method(num2);
1699+
}
1700+
}
1701+
}
1702+
16901703
// `unchecked_add` proofs
16911704
//
16921705
// Target types:
@@ -1905,4 +1918,29 @@ mod verify {
19051918
widening_mul_u64_large, u64::MAX - 10u64, u64::MAX,
19061919
widening_mul_u64_mid_edge, (u64::MAX / 2) - 10u64, (u64::MAX / 2) + 10u64
19071920
);
1921+
1922+
// `wrapping_shl` proofs
1923+
//
1924+
// Target types:
1925+
// i{8,16,32,64,128,size} and u{8,16,32,64,128,size} -- 12 types in total
1926+
//
1927+
// Target contracts:
1928+
// #[ensures(|result| *result == self << (rhs & (Self::BITS - 1)))]
1929+
//
1930+
// Target function:
1931+
// pub const fn wrapping_shl(self, rhs: u32) -> Self
1932+
//
1933+
// This function performs an panic-free bitwise left shift operation.
1934+
generate_wrapping_shift_harness!(i8, wrapping_shl, checked_wrapping_shl_i8);
1935+
generate_wrapping_shift_harness!(i16, wrapping_shl, checked_wrapping_shl_i16);
1936+
generate_wrapping_shift_harness!(i32, wrapping_shl, checked_wrapping_shl_i32);
1937+
generate_wrapping_shift_harness!(i64, wrapping_shl, checked_wrapping_shl_i64);
1938+
generate_wrapping_shift_harness!(i128, wrapping_shl, checked_wrapping_shl_i128);
1939+
generate_wrapping_shift_harness!(isize, wrapping_shl, checked_wrapping_shl_isize);
1940+
generate_wrapping_shift_harness!(u8, wrapping_shl, checked_wrapping_shl_u8);
1941+
generate_wrapping_shift_harness!(u16, wrapping_shl, checked_wrapping_shl_u16);
1942+
generate_wrapping_shift_harness!(u32, wrapping_shl, checked_wrapping_shl_u32);
1943+
generate_wrapping_shift_harness!(u64, wrapping_shl, checked_wrapping_shl_u64);
1944+
generate_wrapping_shift_harness!(u128, wrapping_shl, checked_wrapping_shl_u128);
1945+
generate_wrapping_shift_harness!(usize, wrapping_shl, checked_wrapping_shl_usize);
19081946
}

library/core/src/num/uint_macros.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2138,6 +2138,7 @@ macro_rules! uint_impl {
21382138
without modifying the original"]
21392139
#[inline(always)]
21402140
#[rustc_allow_const_fn_unstable(unchecked_shifts)]
2141+
#[ensures(|result| *result == self << (rhs & (Self::BITS - 1)))]
21412142
pub const fn wrapping_shl(self, rhs: u32) -> Self {
21422143
// SAFETY: the masking by the bitsize of the type ensures that we do not shift
21432144
// out of bounds

0 commit comments

Comments
 (0)