Add Kani harnesses for Challenge 18: slice iter functions - #651
Conversation
Cover both success-criteria tables of challenge 0018-slice-iter. Part 1: prove the 16 iterator! functions for IterMut (Iter coverage already exists on main) and next/size_hint for SplitN, SplitNMut, RSplitN, and RSplitNMut through the forward_iterator! path. Part 2: add safety contracts for all 9 __iterator_get_unchecked impls that exist in the current snapshot (Windows, Chunks, ChunksMut, ChunksExact, ChunksExactMut, RChunks, RChunksMut, RChunksExact, RChunksExactMut). Each contract lives on a private contracted inherent method that the trait method delegates to, and is proved with proof_for_contract. ArrayChunks and ArrayChunksMut no longer exist in slice/iter.rs. Also prove all listed safe functions with unsafe bodies, plus ArrayWindows and the Split family. Proofs instantiate generic code over representative types ((), u8, char, (char, u8)). Element values are symbolic in every harness. Backing lengths are symbolic up to isize::MAX for () and u32::MAX for u8. Looping harnesses use verified kani::unwind bounds. Verified locally at the pinned Kani commit d4df833c (nightly-2025-11-25): 385 of 385 harnesses successful. Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
feliperodri
left a comment
There was a problem hiding this comment.
Thanks @MavenRain. Reviewed against Challenge 18 with our vacuity tooling. Between the two open Challenge 18 solutions we're prioritizing this one — completeness is strong (Part 1 16/16 for IterMut, Part 2-A 9/9 applicable contracts genuinely proved via inherent-twin + #[kani::proof_for_contract] — which fixes the pre-existing T7 gap where base had orphan #[requires] — and Part 2-B broadly covered) and soundness is clean (no cfg(kani) body swaps, substantive is_safe() invariant, no loop_invariant(true), only benign kani::assume(ptr.is_aligned()) in a dangling branch).
Requesting changes for two reasons:
- Fails unbounded + generic-
T. Every harness is monomorphized over(),u8,char,(char,u8); the chunk-family contracts and next/nth harnesses cap atMAX_LEN=50; looping/Split harnesses atMAX_LEN=4+unwind(5–6). Only Windows get_unchecked, ArrayWindows and IterMut O(1) methods on()/u8are unbounded. The challenge mandates arbitrary length and genericTthroughout. - Runtime-logic edit to reconcile. The new inherent
iterator_get_uncheckedmethods (and theRChunks/RChunksMutchecked_sub(...).unwrap_or(0)rewrite) live in the shipping std source, notcfg(kani)-gated. Behavior-preserving, but per CLAUDE.md this repo mustn't change std runtime logic. Please eithercfg(kani)-gate the refactor or land the extraction upstream first, then verify here.
Nice work on the inherent-twin trick to genuinely prove the trait-impl #[requires]. Blocker is the standing unbounded+generic-T gate (same acceptance question as Ch16/17).
|
Thanks for the review. I have prepared a local fix for the runtime-logic concern: all nine original trait implementations are restored, and the inherent contract wrappers are The arbitrary-length and generic- VeriFast 26.01 verifies a scalar baseline, but even this safe generic slice function fails with unsupported slice-reference ownership: pub fn slice_len<T>(values: &[T]) -> usize
//@ req true;
//@ ens result == ptr_len(values);
//@ on_unwind_ens false;
{
values.len()
}The same restriction remains in the current VeriFast translator. A separate probe also found no specification for Is there an approved proof route or existing generic slice-ownership development you recommend for Challenge 18, or does satisfying this gate first require work on the verifier itself? I have not assumed an exception to the stated criteria. |
|
@btj any advice about the VeriFast route here? |
Solution to Challenge 18: verify the safety of
sliceiter functions.Part 1
iterator!macro are proved forIterMut(make_slice,len,is_empty,next,size_hint,count,nth,advance_by,last,fold,for_each,position,rposition,next_back,nth_back,advance_back_by).Itercoverage already exists on main and stays green.nextandsize_hintare proved forSplitN,SplitNMut,RSplitN, andRSplitNMutthrough theforward_iterator!path.Part 2
slice/iter.rscontains 9__iterator_get_uncheckedimpls:Windows,Chunks,ChunksMut,ChunksExact,ChunksExactMut,RChunks,RChunksMut,RChunksExact,RChunksExactMut.ArrayChunksandArrayChunksMutno longer exist in this file.#[requires]/#[ensures]) on a private contracted inherent method. The__iterator_get_uncheckedtrait method delegates to it. Each contract is proved with#[kani::proof_for_contract]. This pattern sidesteps the known interaction betweenproof_for_contractand generic trait methods.Iter::new,IterMut::new,ChunksExact::new,ChunksExactMut::new,RChunksExact::new,RChunksExactMut::new),IterMut::{into_slice, as_mut_slice}, theSplitfamily,ArrayWindows, and the chunk-familynext/nth/last/next_back/nth_backmethods.Approach and disclosures
()(ZST),u8,char(validity invariant), and(char, u8)(padding). Kani cannot emit one proof for allT; this per-type spread is the same pattern the merged solutions for challenges 16 and 17 use.isize::MAXfor()andu32::MAXforu8;charand(char, u8)use smaller symbolic bounds to keep solver time practical.fold,for_each,position,rposition,last, and the split families) use#[kani::unwind]bounds. Kani verifies the unwinding assertion, so a bound that is too small fails loudly.d4df833c(toolchainnightly-2025-11-25): 385 of 385 harnesses successful, 0 failures.Resolves #282
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.