Conversation
parno
left a comment
There was a problem hiding this comment.
Thanks for the contribution! I added some comments/suggestions.
| // Item `k` of what's left to yield is the inner iterator's item `k`, tagged with | ||
| // the index it will be reached at: `count` items have been consumed already. | ||
| #[verifier::prophetic] | ||
| open spec fn remaining(&self) -> Seq<(usize, I::Item)> { |
There was a problem hiding this comment.
In our other iterators, we have marked remaining, will_return_none, and decrease as closed, uninterp functions, and provided the necessary knowledge via the *_postcondition conditions. See, for example, the way Map is handled. For consistency, can we update this iterator accordingly?
| { | ||
| // `w.len() == iter.index()` bounds the loop index by `usize::MAX`, | ||
| // which is what relating it to `Enumerate`'s `usize` index needs. | ||
| assert(i == iter.index()); |
There was a problem hiding this comment.
Is this assert necessary for the proof or is it illustrative? Either way, the comment is a bit unclear.
| } | ||
|
|
||
| // The `(i, x)` pattern is usable in the invariant (via `peek`). | ||
| // `Enumerate`'s index is a `usize`, so relating it to the (unbounded) loop |
There was a problem hiding this comment.
I'm not sure this line or the next is needed; it seems more confusing than clarifying.
| } | ||
|
|
||
| test_verify_one_file! { | ||
| #[test] enumerate_spec_level verus_code! { |
There was a problem hiding this comment.
This test seems tautological, since it's just repeating the relevant definitions in remaining_of_fresh_enumerate, and the second one seems more about collect's functionality. Let's cut the test.
| } => Ok(()) | ||
| } | ||
|
|
||
| test_verify_one_file! { |
There was a problem hiding this comment.
I'm not convinced this one is useful either.
|
I also notice that the Rust docs say:
|
vstd/std_specs/iter.rsprovidesIteratorSpecimplementations for iterator adapters such asRev, but not forcore::iter::Enumerate. A function that consumes anenumeratetherefore fails to verify:Fix
This PR adds an
IteratorSpecforcore::iter::Enumerate. It contributes:remaining(): the items the iterator will yield. It is the inner iterator's remaining items, each paired with the index it will be reached at.enumerate_postcondition: a broadcast axiom givingenumerate_iter(r) == iandenumerate_count(r) == 0, the inner iterator and the counter the result starts from.peek(): the inner iterator's guess, paired with the same index, so that afor-loop's(i, x)pattern is usable in its invariant.opendecrease(),will_return_none()andobeys_prophetic_iter_laws(), each forwarding to the inner iterator.After introducing
Enumeratein this PR, we can reason theEnumerateiterator in Verus. And the problem code above can be proved as below:fn tag_indices(v: Vec<u32>) -> (out: Vec<(usize, u32)>) ensures out@ == v@.map(|k: int, x: u32| ((k as usize), x)), { let mut out: Vec<(usize, u32)> = Vec::new(); let it = v.into_iter(); let en = it.enumerate(); - for (i, x) in en { + for (i, x) in iter: en + invariant out@ == iter.seq().take(iter.index()), + { out.push((i, x)); } out }A
for-loop-over-enumerateregression test is included.By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.