fix panic-safety in specialized Zip::next_back

This was unsound since a panic in a.next_back() would result in the
length not being updated which would then lead to the same element
being revisited in the side-effect preserving code.
This commit is contained in:
The8472 2021-06-19 02:20:22 +02:00
parent 88ba8ad730
commit 8b518542d0
2 changed files with 27 additions and 1 deletions

View file

@ -295,9 +295,10 @@ where
let sz_a = self.a.size();
if A::MAY_HAVE_SIDE_EFFECT && sz_a > self.len {
for _ in 0..sz_a - self.len {
self.a_len -= 1;
self.a.next_back();
}
self.a_len = self.len;
debug_assert_eq!(self.a_len, self.len);
}
let sz_b = self.b.size();
if B::MAY_HAVE_SIDE_EFFECT && sz_b > self.len {

View file

@ -1,5 +1,7 @@
use super::*;
use core::iter::*;
use std::panic::catch_unwind;
use std::panic::AssertUnwindSafe;
#[test]
fn test_zip_nth() {
@ -232,6 +234,29 @@ fn test_zip_trusted_random_access_composition() {
assert_eq!(z2.next().unwrap(), ((1, 1), 1));
}
#[test]
fn test_zip_trusted_random_access_next_back_drop() {
let mut counter = 0;
let it = [42].iter().map(|e| {
let c = counter;
counter += 1;
if c == 0 {
panic!("bomb");
}
e
});
let it2 = [(); 0].iter();
let mut zip = it.zip(it2);
catch_unwind(AssertUnwindSafe(|| {
zip.next_back();
}))
.unwrap_err();
assert!(zip.next().is_none());
assert_eq!(counter, 1);
}
#[test]
fn test_double_ended_zip() {
let xs = [1, 2, 3, 4, 5, 6];