Skip to content

Commit 938fdd1

Browse files
committed
Challenge 12: Verify safety of NonZero
Verify all 38 NonZero functions listed in the challenge specification. 432 Kani proof harnesses across all 12 integer types, 0 failures. Resolves #71 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 2840898 commit 938fdd1

File tree

4 files changed

+891
-2
lines changed

4 files changed

+891
-2
lines changed

doc/src/challenges/0012-nonzero.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,73 @@ code, all proofs must automatically ensure the absence of the following undefine
8585

8686
Note: All solutions to verification challenges need to satisfy the criteria established in the [challenge book](../general-rules.md)
8787
in addition to the ones listed above.
88+
89+
-------------------
90+
91+
## Verification Summary
92+
93+
All 38 functions verified using Kani proof harnesses. 432 total harnesses pass across all 12 integer types.
94+
95+
### Part 1: Core Creation
96+
97+
| Function | Harness | Types | Approach |
98+
|----------|---------|-------|----------|
99+
| `new` | `nonzero_check_new` | all 12 | `#[kani::proof]` — proves iff: `result.is_some() == (n != 0)` and `nz.get() == n` |
100+
| `new_unchecked` | `nonzero_check` (pre-existing) | all 12 | `#[kani::proof_for_contract]` — verifies `#[requires]` + `#[ensures]` |
101+
| `from_mut` | `nonzero_check_from_mut` | all 12 | `#[kani::proof]` — proves iff + dereferences returned reference |
102+
| `from_mut_unchecked` | `nonzero_check_from_mut_unchecked` (pre-existing) | all 12 | `#[kani::proof_for_contract]` — verifies `#[requires]` |
103+
104+
### Part 2: Bitwise & Conversion
105+
106+
| Function | Harness | Types |
107+
|----------|---------|-------|
108+
| `bitor` (NZ\|NZ) | `nonzero_check_bitor_nznz` | all 12 |
109+
| `bitor` (NZ\|T) | `nonzero_check_bitor_nzt` | all 12 |
110+
| `bitor` (T\|NZ) | `nonzero_check_bitor_tnz` | all 12 |
111+
| `count_ones` | `nonzero_check_count_ones` | all 12 |
112+
| `rotate_left` | `nonzero_check_rotate_left` | all 12 |
113+
| `rotate_right` | `nonzero_check_rotate_right` | all 12 |
114+
| `swap_bytes` | `nonzero_check_swap_bytes` | all 12 |
115+
| `reverse_bits` | `nonzero_check_reverse_bits` | all 12 |
116+
| `from_be` | `nonzero_check_from_be` | all 12 |
117+
| `from_le` | `nonzero_check_from_le` | all 12 |
118+
| `to_be` | `nonzero_check_to_be` | all 12 |
119+
| `to_le` | `nonzero_check_to_le` | all 12 |
120+
121+
### Part 2: Arithmetic
122+
123+
| Function | Harness | Types | Notes |
124+
|----------|---------|-------|-------|
125+
| `checked_mul` | `nonzero_check_checked_mul` | all 12 | |
126+
| `saturating_mul` | `nonzero_check_saturating_mul` | all 12 | |
127+
| `unchecked_mul` | `check_mul_unchecked_*` (pre-existing) | all 12 | `proof_for_contract` with interval testing |
128+
| `checked_pow` | `nonzero_check_checked_pow` | all 12 | Required strengthened loop invariant |
129+
| `saturating_pow` | `nonzero_check_saturating_pow` | all 12 | Required strengthened loop invariant |
130+
| `checked_add` | `nonzero_check_checked_add` | 6 unsigned | |
131+
| `saturating_add` | `nonzero_check_saturating_add` | 6 unsigned | |
132+
| `unchecked_add` | `nonzero_check_add` (pre-existing) | 6 unsigned | `proof_for_contract` |
133+
| `checked_next_power_of_two` | `nonzero_check_checked_next_power_of_two` | 6 unsigned | |
134+
| `midpoint` | `nonzero_check_midpoint` | 6 unsigned | |
135+
| `isqrt` | `nonzero_check_isqrt` | 6 unsigned | `#[kani::unwind(65)]` |
136+
137+
### Part 2: Absolute Value & Negation (signed only)
138+
139+
| Function | Harness | Types | Notes |
140+
|----------|---------|-------|-------|
141+
| `abs` | `nonzero_check_abs` | 6 signed | Excludes MIN (documented overflow) |
142+
| `checked_abs` | `nonzero_check_checked_abs` | 6 signed | |
143+
| `overflowing_abs` | `nonzero_check_overflowing_abs` | 6 signed | |
144+
| `saturating_abs` | `nonzero_check_saturating_abs` | 6 signed | |
145+
| `wrapping_abs` | `nonzero_check_wrapping_abs` | 6 signed | Covers MIN |
146+
| `unsigned_abs` | `nonzero_check_unsigned_abs` | 6 signed | |
147+
| `neg` | `nonzero_check_neg` | 6 signed | Excludes MIN (documented overflow) |
148+
| `checked_neg` | `nonzero_check_checked_neg` | 6 signed | |
149+
| `overflowing_neg` | `nonzero_check_overflowing_neg` | 6 signed | Covers MIN |
150+
| `wrapping_neg` | `nonzero_check_wrapping_neg` | 6 signed | Covers MIN |
151+
| `max` | `nonzero_check_max` (pre-existing) | all 12 | |
152+
| `min` | `nonzero_check_min` (pre-existing) | all 12 | |
153+
| `clamp` | `nonzero_check_clamp` (pre-existing) | all 12 | |
154+
155+
### Additional Changes
156+
157+
Strengthened `#[safety::loop_invariant]` on `checked_pow` in `uint_macros.rs` and `int_macros.rs` from `true` to a property that preserves the nonzero invariant through loop iterations. This is a verification annotation fix, not a runtime logic change.

library/core/src/num/int_macros.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1736,7 +1736,7 @@ macro_rules! int_impl {
17361736
let mut base = self;
17371737
let mut acc: Self = 1;
17381738

1739-
#[safety::loop_invariant(true)]
1739+
#[safety::loop_invariant(self == 0 || (acc != 0 && base != 0))]
17401740
loop {
17411741
if (exp & 1) == 1 {
17421742
acc = try_opt!(acc.checked_mul(base));

0 commit comments

Comments
 (0)