Skip to content

Commit a531ecc

Browse files
author
Kasim Te
committed
Refine iterator adapter verification harnesses
- Use a subtraction-based, overflow-safe precondition for Skip::__iterator_get_unchecked, matching Zip::get_unchecked. - Add the loop-invariant justification comment to Take::spec_for_each. - Clarify that the large-array harnesses verify slices up to MAX_LEN.
1 parent e80e689 commit a531ecc

3 files changed

Lines changed: 18 additions & 10 deletions

File tree

library/core/src/iter/adapters/skip.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,9 @@ where
162162
}
163163

164164
#[doc(hidden)]
165-
#[requires(idx + self.n < self.iter.size_hint().0)]
165+
// Subtraction-based bound (avoids `idx + self.n` overflow in the contract,
166+
// matching the overflow-safe form used for `Zip::get_unchecked`).
167+
#[requires(self.n <= self.iter.size_hint().0 && idx < self.iter.size_hint().0 - self.n)]
166168
#[cfg_attr(kani, kani::modifies(self))]
167169
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
168170
where

library/core/src/iter/adapters/take.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,10 @@ impl<I: Iterator + TrustedRandomAccess> SpecTake for Take<I> {
317317
#[inline]
318318
fn spec_for_each<F: FnMut(Self::Item)>(mut self, mut f: F) {
319319
let end = self.n.min(self.iter.size());
320+
// __iterator_get_unchecked is a read-only operation for TrustedRandomAccess
321+
// iterators, so iter.size() is preserved across iterations.
322+
// The Kani loop invariant below is intentionally vacuous (`true`) and
323+
// is used only to enable loop-contract mode.
320324
#[cfg_attr(kani, kani::loop_invariant(true))]
321325
for i in 0..end {
322326
// SAFETY: i < end <= self.iter.size() and we discard the iterator at the end
@@ -389,8 +393,8 @@ mod verify {
389393
use super::*;
390394

391395
// spec_fold (TRA specialized — uses __iterator_get_unchecked in a loop)
392-
// Loop invariant on source code enables unbounded verification via
393-
// loop contracts instead of finite unrolling.
396+
// The source-level loop invariant abstracts the get_unchecked loop via loop
397+
// contracts instead of finite unrolling; this harness covers slices up to MAX_LEN.
394398
#[kani::proof]
395399
fn check_take_spec_fold_u8() {
396400
const MAX_LEN: usize = 5000;

library/core/src/iter/adapters/zip.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -748,8 +748,8 @@ mod verify {
748748
// --- Safe abstractions ---
749749

750750
// next (TRA specialized — uses __iterator_get_unchecked internally)
751-
// Single call on large array: next() makes one get_unchecked call per invocation,
752-
// so a single call at arbitrary slice length suffices for unbounded verification.
751+
// next() makes a single get_unchecked call per invocation with no length-dependent
752+
// loop, so this harness covers the access over slices up to MAX_LEN.
753753
#[kani::proof]
754754
fn check_zip_next_u8() {
755755
const MAX_LEN: usize = 5000;
@@ -762,10 +762,11 @@ mod verify {
762762
}
763763

764764
// nth (TRA specialized — uses __iterator_get_unchecked for side effects)
765-
// Loop invariants on nth's while loop and super_nth's while-let loop
766-
// enable unbounded verification. For slice::Iter, MAY_HAVE_SIDE_EFFECT
767-
// is false so nth's loop body is just index increment; super_nth runs
768-
// at most once when called from nth.
765+
// nth's while loop and super_nth's while-let loop carry vacuous Kani loop
766+
// invariants to enable loop-contract mode; this harness covers slices up to
767+
// MAX_LEN. For slice::Iter, MAY_HAVE_SIDE_EFFECT is false, so nth's loop body
768+
// is just an index increment and super_nth runs at most once when called from
769+
// nth (its next()-driven loop does not iterate in this configuration).
769770
#[kani::proof]
770771
fn check_zip_nth_u8() {
771772
const MAX_LEN: usize = 5000;
@@ -792,7 +793,8 @@ mod verify {
792793
}
793794

794795
// fold (TRANC specialized — uses get_unchecked in a loop)
795-
// Loop invariant on source code enables unbounded verification.
796+
// The source-level loop invariant abstracts the get_unchecked loop; this
797+
// harness covers slices up to MAX_LEN.
796798
#[kani::proof]
797799
fn check_zip_fold_u8() {
798800
const MAX_LEN: usize = 5000;

0 commit comments

Comments
 (0)