Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
6dbebef
Verify iter adapter unsafe methods + safe abstractions (challenge #16)
MavenRain Jun 17, 2026
17e9794
Verify deferred iter adapter iterating methods (challenge #16)
MavenRain Jun 17, 2026
9f5d372
Format challenge #16 harnesses to satisfy upstream rustfmt
MavenRain Jun 22, 2026
79792d9
Merge branch 'main' into 16-iter-adapters
MavenRain Jul 8, 2026
4459557
Address review on challenge 16: unbounded loop-contract harnesses, di…
MavenRain Aug 17, 2026
561997a
Clarify iterator proof limits and draft a generic VeriFast port
MavenRain Sep 13, 2026
9e95bb7
Enable MaybeUninit pointer conversion in the VeriFast port
MavenRain Sep 13, 2026
199e4b4
Record the hosted VeriFast frontend blocker
MavenRain Sep 13, 2026
7afab54
Merge upstream main and preserve iterator adapter proofs
MavenRain Sep 13, 2026
2f120f6
Support checked verification of unchecked addition in the hosted fron…
MavenRain Sep 13, 2026
d78acda
Translate symbolic usize const parameters in the hosted Rust frontend
MavenRain Sep 13, 2026
4f9e3fc
Use the bundled Dune job-count option
MavenRain Sep 13, 2026
16fbcd8
Fix VeriFast contract clauses and cache the compiled frontend
MavenRain Sep 13, 2026
d192fb8
Translate array borrow predicates with negative regression coverage
MavenRain Sep 13, 2026
3641c2b
Account for reference provenance in array regression contracts
MavenRain Sep 13, 2026
104e654
Enforce mutable reference creation checks in the adapter frontend
MavenRain Sep 13, 2026
0efac51
Align adapter proofs with reference borrowing rules
MavenRain Sep 13, 2026
5de9200
Isolate direct mutable array reference creation in regression tests
MavenRain Sep 13, 2026
c6a460e
Test array reference creation independently of return reborrows
MavenRain Sep 13, 2026
41fbb1e
Preserve original pointer helper bodies for source refinement
MavenRain Sep 13, 2026
b90c981
Match source pointer casts and disambiguate predicate arguments
MavenRain Sep 13, 2026
978390e
Use Rust conditional expression syntax in ghost predicates
MavenRain Sep 13, 2026
0c05bc7
Preserve symbolic const identity during source refinement
MavenRain Sep 13, 2026
504ccdc
Build the declared refinement checker executable target
MavenRain Sep 13, 2026
3d71d23
Decode the constant payload from typed MIR operands
MavenRain Sep 13, 2026
e7df29f
Preserve const parameter constraints in VeriFast refinement
MavenRain Sep 13, 2026
7f30c58
Use VeriFast ownership constructors and preserve Rust declarations
MavenRain Sep 13, 2026
9d32708
Allow arbitrary generic ownership in the live buffer predicate
MavenRain Sep 13, 2026
3707734
Dereference the step receiver in ghost field assertions
MavenRain Sep 13, 2026
462c7b4
Require buffer pointer bounds and record passing source refinement
MavenRain Sep 13, 2026
6fecaa1
Collect bounded per-method diagnostics after a hosted proof failure
MavenRain Sep 13, 2026
30057f0
Express private proof method obligations with unsafe signatures
MavenRain Sep 13, 2026
04094ce
Use raw buffer addresses and explicit borrowing predicates in proofs
MavenRain Sep 13, 2026
bfc7e53
Restore helper source refinement and require shared reference permiss…
MavenRain Sep 13, 2026
b23dbe1
Translate erased slice coercions and test nested array pointers
MavenRain Sep 13, 2026
80878b6
Model pointer casts and the NonZero usize constructor for hosted proofs
MavenRain Sep 13, 2026
f42ac07
Include heap context for a failing hosted array regression
MavenRain Sep 13, 2026
f579051
Preserve pointer and length when unsizing generic arrays
MavenRain Sep 13, 2026
8d7c104
Update refinement cast records and retain bounded proof context
MavenRain Sep 13, 2026
4a9dbb3
Track nested array storage through mutable buffer borrows
MavenRain Sep 13, 2026
12202e0
Load shared layout lemmas through a Rust module
MavenRain Sep 13, 2026
e0f60b1
Keep proof imports in matching crate roots
MavenRain Sep 13, 2026
767c952
Retain the original array view while proving value preservation
MavenRain Sep 13, 2026
5d44d8f
Preserve backing storage across the writable window borrow
MavenRain Sep 13, 2026
06f64c9
Specify MaybeUninit ownership without granting ownership of T
MavenRain Sep 13, 2026
f750710
Connect generic drop glue to array storage ownership
MavenRain Sep 13, 2026
cb41ee4
Use the array alignment expression supported by VeriFast
MavenRain Sep 13, 2026
f225042
Accept symbolic lengths in ghost array conversion helpers
MavenRain Sep 13, 2026
07fd044
Close array ownership through its predicate constructor
MavenRain Sep 13, 2026
6d69913
Prove buffer ownership transfer and covariance obligations
MavenRain Sep 13, 2026
af08c41
Type generic conversion maps at their call sites
MavenRain Sep 13, 2026
b58a188
Retain wrapper values and establish recursive proof progress
MavenRain Sep 13, 2026
96d0983
Repair induction and window borrow proof obligations
MavenRain Sep 13, 2026
9bb5507
Translate MaybeUninit pointer casts without granting storage
MavenRain Sep 13, 2026
1d60d9c
Recognize the std path for MaybeUninit pointer casts
MavenRain Sep 13, 2026
d9fc867
Preserve and restore the window across panicking element drop
MavenRain Sep 13, 2026
a4980f7
Derive shifted window bounds before borrowing the matrix
MavenRain Sep 13, 2026
3ff4cf0
Diagnose compiler cleanup and assertion reachability remotely
MavenRain Sep 13, 2026
1015745
Account for assertion expansion and moved-argument cleanup
MavenRain Sep 13, 2026
2adc934
Preserve conditional assertion control flow in the proof projection
MavenRain Sep 13, 2026
fcec2d1
Preserve reachability directives and test the normal-path guard
MavenRain Sep 13, 2026
4727219
Keep proof directives stable with method-level formatting attributes
MavenRain Sep 13, 2026
6cc9cce
Keep formatting metadata inside equivalent proof bodies
MavenRain Sep 13, 2026
3897f7b
Document passing generic adapter proofs and refinement
MavenRain Sep 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions .github/workflows/verifast-iter-adapters.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: VeriFast iterator adapters

on:
workflow_dispatch:
merge_group:
pull_request:
branches: [main]
paths:
- '.github/workflows/verifast-iter-adapters.yml'
- 'verifast-proofs/core/iter/adapters/**'
- 'verifast-proofs/setup-verifast-home'
- 'verifast-proofs/verifast'
- 'verifast-proofs/refinement-checker'
- 'library/core/src/iter/adapters/map_windows.rs'
- 'library/core/src/iter/adapters/step_by.rs'
push:
branches: [main, 16-iter-adapters]
paths:
- '.github/workflows/verifast-iter-adapters.yml'
- 'verifast-proofs/core/iter/adapters/**'
- 'verifast-proofs/setup-verifast-home'
- 'verifast-proofs/verifast'
- 'verifast-proofs/refinement-checker'
- 'library/core/src/iter/adapters/map_windows.rs'
- 'library/core/src/iter/adapters/step_by.rs'

permissions:
contents: read

concurrency:
group: verifast-iter-adapters-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

defaults:
run:
shell: bash

jobs:
verify-iter-adapters:
name: Verify generic iterator adapter contracts
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Check out proof inputs
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Check sources and contract selection
run: |
bash verifast-proofs/core/iter/adapters/verify.sh --static
python3 -I verifast-proofs/core/iter/adapters/test_sources.py

- name: Install frontend schema compiler
run: |
sudo --non-interactive apt-get update -qq
sudo --non-interactive apt-get install -y --no-install-recommends capnproto

- name: Restore compiled frontend
id: backend-cache
uses: actions/cache/restore@v4
with:
path: ~/.cache/verifast-iter-adapters
key: ${{ runner.os }}-verifast-iter-v1-${{ hashFiles('verifast-proofs/core/iter/adapters/backend/prepare.sh', 'verifast-proofs/core/iter/adapters/backend/*.patch') }}

- name: Verify contracts and source refinement
run: |
# A service cgroup limits the entire compiler/verifier process tree.
# --wait propagates failure, including timeout or memory exhaustion.
sudo --non-interactive systemd-run \
--unit="verifast-iter-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" \
--service-type=exec --wait --pipe --collect \
--uid="$(id -u)" --gid="$(id -g)" \
--working-directory="$PWD" \
--setenv="PATH=$PATH" \
--setenv="CARGO_HOME=${CARGO_HOME:-$HOME/.cargo}" \
--setenv="RUSTUP_HOME=${RUSTUP_HOME:-$HOME/.rustup}" \
--property=MemoryMax=4G \
--property=MemorySwapMax=0 \
--property=TasksMax=256 \
--property=CPUQuota=200% \
--property=OOMPolicy=kill \
--property=KillMode=control-group \
--property=RuntimeMaxSec=1500 \
--property=TimeoutStopSec=15 \
/usr/bin/bash verifast-proofs/core/iter/adapters/verify.sh --remote

- name: Save compiled frontend
if: ${{ always() && !cancelled() && steps.backend-cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v4
with:
path: ~/.cache/verifast-iter-adapters
key: ${{ steps.backend-cache.outputs.cache-primary-key }}
75 changes: 75 additions & 0 deletions library/core/src/iter/adapters/array_chunks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use crate::iter::adapters::SourceIter;
use crate::iter::{
ByRefSized, FusedIterator, InPlaceIterable, TrustedFused, TrustedRandomAccessNoCoerce,
};
#[cfg(kani)]
use crate::kani;
use crate::num::NonZero;
use crate::ops::{ControlFlow, NeverShortCircuit, Try};

Expand Down Expand Up @@ -230,6 +232,14 @@ where
let inner_len = self.iter.size();
let mut i = 0;
// Use a while loop because (0..len).step_by(N) doesn't optimize well.
// Kani: the loop writes only `accum` and `i`, so `inner_len` keeps
// its entry value `self.iter.size()`; the invariant bounds `i`, which
// also guards the `inner_len - i` subtraction in the loop condition
// against underflow. The frame is stated explicitly because the
// `from_fn` closure borrows `self` mutably, and the inferred frame
// would otherwise havoc the whole adapter, `remainder` included.
#[safety::loop_invariant(i <= inner_len)]
#[cfg_attr(kani, kani::loop_modifies(&accum, &i))]
while inner_len - i >= N {
let chunk = crate::array::from_fn(|local| {
// SAFETY: The method consumes the iterator and the loop condition ensures that
Expand Down Expand Up @@ -274,3 +284,68 @@ unsafe impl<I: InPlaceIterable + Iterator, const N: usize> InPlaceIterable for A
}
};
}

#[cfg(kani)]
#[unstable(feature = "kani", issue = "none")]
mod verify {
use super::*;
use crate::kani;

fn any_slice<T>(orig: &[T]) -> &[T] {
if kani::any() {
let last = kani::any_where(|i: &usize| *i <= orig.len());
let first = kani::any_where(|i: &usize| *i <= last);
&orig[first..last]
} else {
let ptr = kani::any_where::<usize, _>(|v| *v != 0) as *const T;
kani::assume(ptr.is_aligned());
unsafe { crate::slice::from_raw_parts(ptr, 0) }
}
}

// `next_back_remainder` pulls the final `len % N` elements off the back via
// `rev().take(rem).next_chunk()`, then reverses them in place, relying on
// `unwrap_err_unchecked` (sound because `rem < N`).
macro_rules! check_next_back_remainder {
($harness:ident, $elem_ty:ty, $n:expr, $max_len:expr) => {
#[kani::proof]
fn $harness() {
const N: usize = $n;
const MAX_LEN: usize = $max_len;
let array: [$elem_ty; MAX_LEN] = kani::any();
let mut it: ArrayChunks<_, N> = ArrayChunks::new(any_slice(&array).iter());
it.next_back_remainder();
let _ = &it.remainder;
}
};
}
check_next_back_remainder!(check_array_chunks_next_back_remainder_unit, (), 2, 8);
check_next_back_remainder!(check_array_chunks_next_back_remainder_u8, u8, 2, 8);
check_next_back_remainder!(check_array_chunks_next_back_remainder_char, char, 3, 9);
check_next_back_remainder!(check_array_chunks_next_back_remainder_tup, (char, u8), 2, 8);

// `fold` on a `TrustedRandomAccessNoCoerce` source builds each chunk with
// `array::from_fn` calling `__iterator_get_unchecked(i + local)` under an
// `inner_len - i >= N` guard; this proves those indexes stay in bounds.
// The chunking loop carries a Kani loop invariant, so no unwind bound is
// necessary and `MAX_LEN` mirrors the accessor harness menu: `u32::MAX`
// for `u8`, `isize::MAX` for the ZST, moderate bounds for the wider
// element types.
macro_rules! check_fold {
($harness:ident, $elem_ty:ty, $n:expr, $max_len:expr) => {
#[kani::proof]
fn $harness() {
const N: usize = $n;
const MAX_LEN: usize = $max_len;
let array: [$elem_ty; MAX_LEN] = kani::any();
let it: ArrayChunks<_, N> = ArrayChunks::new(any_slice(&array).iter());
// Disambiguate from the in-scope internal `SpecFold::fold`.
let _ = crate::iter::Iterator::fold(it, 0usize, |acc, _chunk| acc.wrapping_add(1));
}
};
}
check_fold!(check_array_chunks_fold_unit, (), 2, isize::MAX as usize);
check_fold!(check_array_chunks_fold_u8, u8, 2, u32::MAX as usize);
check_fold!(check_array_chunks_fold_char, char, 3, 10);
check_fold!(check_array_chunks_fold_tup, (char, u8), 2, 10);
}
60 changes: 60 additions & 0 deletions library/core/src/iter/adapters/cloned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ where
I: UncheckedIterator<Item = &'a T>,
T: Clone,
{
#[requires(self.it.size_hint().0 > 0)]
unsafe fn next_unchecked(&mut self) -> T {
// SAFETY: `Cloned` is 1:1 with the inner iterator, so if the caller promised
// that there's an element left, the inner iterator has one too.
Expand Down Expand Up @@ -193,3 +194,62 @@ unsafe impl<I: InPlaceIterable> InPlaceIterable for Cloned<I> {
const EXPAND_BY: Option<NonZero<usize>> = I::EXPAND_BY;
const MERGE_BY: Option<NonZero<usize>> = I::MERGE_BY;
}

#[cfg(kani)]
#[unstable(feature = "kani", issue = "none")]
mod verify {
use super::*;
use crate::kani;

fn any_slice<T>(orig_slice: &[T]) -> &[T] {
if kani::any() {
let last = kani::any_where(|idx: &usize| *idx <= orig_slice.len());
let first = kani::any_where(|idx: &usize| *idx <= last);
&orig_slice[first..last]
} else {
let ptr = kani::any_where::<usize, _>(|val| *val != 0) as *const T;
kani::assume(ptr.is_aligned());
unsafe { crate::slice::from_raw_parts(ptr, 0) }
}
}

fn any_cloned_iter<'a, T: Clone>(orig_slice: &'a [T]) -> Cloned<crate::slice::Iter<'a, T>> {
Cloned::new(any_slice(orig_slice).iter())
}

macro_rules! check_get_unchecked {
($harness:ident, $elem_ty:ty, $max_len:expr) => {
#[kani::proof]
fn $harness() {
const MAX_LEN: usize = $max_len;
let array: [$elem_ty; MAX_LEN] = kani::any();
let mut it = any_cloned_iter::<$elem_ty>(&array);
let idx = kani::any_where(|i: &usize| *i < it.it.size_hint().0);
let _ = unsafe { it.__iterator_get_unchecked(idx) };
}
};
}
check_get_unchecked!(check_cloned_get_unchecked_unit, (), isize::MAX as usize);
check_get_unchecked!(check_cloned_get_unchecked_u8, u8, u32::MAX as usize);
check_get_unchecked!(check_cloned_get_unchecked_char, char, 50);
check_get_unchecked!(check_cloned_get_unchecked_tup, (char, u8), 50);

// `next_unchecked` (UncheckedIterator): the precondition is that the iterator
// is non-empty; establish it by construction.
macro_rules! check_cloned_next_unchecked {
($harness:ident, $elem_ty:ty, $max_len:expr) => {
#[kani::proof]
fn $harness() {
const MAX_LEN: usize = $max_len;
let array: [$elem_ty; MAX_LEN] = kani::any();
let mut it = any_cloned_iter::<$elem_ty>(&array);
kani::assume(it.it.size_hint().0 > 0);
let _ = unsafe { it.next_unchecked() };
}
};
}
check_cloned_next_unchecked!(check_cloned_next_unchecked_unit, (), isize::MAX as usize);
check_cloned_next_unchecked!(check_cloned_next_unchecked_u8, u8, u32::MAX as usize);
check_cloned_next_unchecked!(check_cloned_next_unchecked_char, char, 50);
check_cloned_next_unchecked!(check_cloned_next_unchecked_tup, (char, u8), 50);
}
66 changes: 66 additions & 0 deletions library/core/src/iter/adapters/copied.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,3 +284,69 @@ unsafe impl<I: InPlaceIterable> InPlaceIterable for Copied<I> {
const EXPAND_BY: Option<NonZero<usize>> = I::EXPAND_BY;
const MERGE_BY: Option<NonZero<usize>> = I::MERGE_BY;
}

#[cfg(kani)]
#[unstable(feature = "kani", issue = "none")]
mod verify {
use super::*;
use crate::kani;

fn any_slice<T>(orig_slice: &[T]) -> &[T] {
if kani::any() {
let last = kani::any_where(|idx: &usize| *idx <= orig_slice.len());
let first = kani::any_where(|idx: &usize| *idx <= last);
&orig_slice[first..last]
} else {
let ptr = kani::any_where::<usize, _>(|val| *val != 0) as *const T;
kani::assume(ptr.is_aligned());
unsafe { crate::slice::from_raw_parts(ptr, 0) }
}
}

fn any_adapter_iter<'a, T>(orig_slice: &'a [T]) -> Copied<crate::slice::Iter<'a, T>> {
Copied::new(any_slice(orig_slice).iter())
}

macro_rules! check_get_unchecked {
($harness:ident, $elem_ty:ty, $max_len:expr) => {
#[kani::proof]
fn $harness() {
const MAX_LEN: usize = $max_len;
let array: [$elem_ty; MAX_LEN] = kani::any();
let mut it = any_adapter_iter::<$elem_ty>(&array);
let idx = kani::any_where(|i: &usize| *i < it.it.size_hint().0);
let _ = unsafe { it.__iterator_get_unchecked(idx) };
}
};
}
check_get_unchecked!(check_copied_get_unchecked_unit, (), isize::MAX as usize);
check_get_unchecked!(check_copied_get_unchecked_u8, u8, u32::MAX as usize);
check_get_unchecked!(check_copied_get_unchecked_char, char, 50);
check_get_unchecked!(check_copied_get_unchecked_tup, (char, u8), 50);

// `spec_next_chunk` on the `slice::Iter` specialization bulk-copies `N` (or
// `len`) elements into a `MaybeUninit<[T; N]>` and then either
// `array_assume_init`s the full array or returns a `0..len` `IntoIter`; this
// proves the `copy_nonoverlapping` lengths and the init range stay in bounds.
// `N` is a trait generic, so it is pinned via the result type annotation.
// The `MAX_LEN` menu mirrors `check_get_unchecked` above: the `u8`/ZST
// harnesses raise the bound very high (there is no per-iteration loop, only
// a bulk copy, so the cost stays flat), the wider types use a moderate one.
macro_rules! check_spec_next_chunk {
($harness:ident, $elem_ty:ty, $n:expr, $max_len:expr) => {
#[kani::proof]
fn $harness() {
const N: usize = $n;
const MAX_LEN: usize = $max_len;
let array: [$elem_ty; MAX_LEN] = kani::any();
let mut it = any_slice(&array).iter();
let _result: Result<[$elem_ty; N], crate::array::IntoIter<$elem_ty, N>> =
it.spec_next_chunk();
}
};
}
check_spec_next_chunk!(check_copied_spec_next_chunk_unit, (), 2, isize::MAX as usize);
check_spec_next_chunk!(check_copied_spec_next_chunk_u8, u8, 3, u32::MAX as usize);
check_spec_next_chunk!(check_copied_spec_next_chunk_char, char, 2, 50);
check_spec_next_chunk!(check_copied_spec_next_chunk_tup, (char, u8), 2, 50);
}
67 changes: 67 additions & 0 deletions library/core/src/iter/adapters/enumerate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,3 +321,70 @@ impl<I: Default> Default for Enumerate<I> {
Enumerate::new(Default::default())
}
}

/// Verification harnesses for `Enumerate`'s `unsafe`/contract-bearing methods
/// (verify-rust-std challenge #16).
#[cfg(kani)]
#[unstable(feature = "kani", issue = "none")]
mod verify {
use super::*;
use crate::kani;

/// An arbitrary-length sub-slice of `orig_slice` (mirrors
/// `slice::iter::verify::any_slice`). The slice handed to the iterator
/// has a symbolic length in `0..=MAX_LEN`, so one proof covers every
/// length up to the backing array's size at once. The proof is still
/// bounded by `MAX_LEN`: the `u8`/ZST harnesses raise `MAX_LEN` to
/// `u32::MAX`/`isize::MAX`, far beyond practical slice lengths, while the
/// wider element types use smaller bounds.
fn any_slice<T>(orig_slice: &[T]) -> &[T] {
if kani::any() {
let last = kani::any_where(|idx: &usize| *idx <= orig_slice.len());
let first = kani::any_where(|idx: &usize| *idx <= last);
&orig_slice[first..last]
} else {
let ptr = kani::any_where::<usize, _>(|val| *val != 0) as *const T;
kani::assume(ptr.is_aligned());
// SAFETY: `ptr` is non-null and aligned; length 0 makes the slice trivially valid.
unsafe { crate::slice::from_raw_parts(ptr, 0) }
}
}

/// Wrap an arbitrary sub-slice in `Enumerate<slice::Iter<'_, T>>`. We build
/// the inner `slice::Iter` via `(&[T]).iter()` because `slice::Iter::new` is
/// `pub(super)` to the `slice` module and unreachable from here.
fn any_enumerate_iter<'a, T>(orig_slice: &'a [T]) -> Enumerate<crate::slice::Iter<'a, T>> {
Enumerate::new(any_slice(orig_slice).iter())
}

/// One plain `#[kani::proof]` harness per concrete element type (the NOTE
/// below explains why this is not `proof_for_contract`). `slice::Iter<T>`
/// is `TrustedRandomAccessNoCoerce` for every `T`, satisfying the method's
/// `Self: TrustedRandomAccessNoCoerce` bound.
// NOTE: `__iterator_get_unchecked` is a trait method on the *generic* impl
// `impl<I> Iterator for Enumerate<I>`, and Kani cannot attach a
// `proof_for_contract` to a generic trait method (kani#1997). So instead of
// the contract machinery we use a plain `#[kani::proof]` that establishes the
// method's precondition by construction (`idx < self.iter.size_hint().0`) and
// lets Kani prove the body introduces no UB -- the same safety property the
// contract expresses.
macro_rules! check_enumerate_get_unchecked {
($harness:ident, $elem_ty:ty, $max_len:expr) => {
#[kani::proof]
fn $harness() {
const MAX_LEN: usize = $max_len;
let array: [$elem_ty; MAX_LEN] = kani::any();
let mut enumerate = any_enumerate_iter::<$elem_ty>(&array);
// The method's precondition: `idx < self.iter.size_hint().0`.
let idx = kani::any_where(|i: &usize| *i < enumerate.iter.size_hint().0);
let _ = unsafe { enumerate.__iterator_get_unchecked(idx) };
}
};
}

// Representative element types: ZST, byte, 4-byte-align niche type, composite.
check_enumerate_get_unchecked!(check_enumerate_get_unchecked_unit, (), isize::MAX as usize);
check_enumerate_get_unchecked!(check_enumerate_get_unchecked_u8, u8, u32::MAX as usize);
check_enumerate_get_unchecked!(check_enumerate_get_unchecked_char, char, 50);
check_enumerate_get_unchecked!(check_enumerate_get_unchecked_tup, (char, u8), 50);
}
Loading
Loading