@@ -107,9 +107,19 @@ fn test_zip_next_back_side_effects_exhausted() {
107107    iter. next ( ) ; 
108108    iter. next ( ) ; 
109109    iter. next ( ) ; 
110-     iter. next ( ) ; 
110+     assert_eq ! ( iter. next( ) ,   None ) ; 
111111    assert_eq ! ( iter. next_back( ) ,  None ) ; 
112-     assert_eq ! ( a,  vec![ 1 ,  2 ,  3 ,  4 ,  6 ,  5 ] ) ; 
112+ 
113+     assert ! ( a. starts_with( & [ 1 ,  2 ,  3 ] ) ) ; 
114+     let  a_len = a. len ( ) ; 
115+     // Tail-side-effects of forward-iteration are "at most one" per next(). 
116+     // And for reverse iteration we don't guarantee much either. 
117+     // But we can put some bounds on the possible behaviors. 
118+     assert ! ( a_len <= 6 ) ; 
119+     assert ! ( a_len >= 3 ) ; 
120+     a. sort ( ) ; 
121+     assert_eq ! ( a,  & [ 1 ,  2 ,  3 ,  4 ,  5 ,  6 ] [ ..a. len( ) ] ) ; 
122+ 
113123    assert_eq ! ( b,  vec![ 200 ,  300 ,  400 ] ) ; 
114124} 
115125
@@ -120,7 +130,8 @@ fn test_zip_cloned_sideffectful() {
120130
121131    for  _ in  xs. iter ( ) . cloned ( ) . zip ( ys. iter ( ) . cloned ( ) )  { } 
122132
123-     assert_eq ! ( & xs,  & [ 1 ,  1 ,  1 ,  0 ] [ ..] ) ; 
133+     // Zip documentation permits either case. 
134+     assert ! ( [ & [ 1 ,  1 ,  1 ,  0 ] ,  & [ 1 ,  1 ,  0 ,  0 ] ] . iter( ) . any( |v| & xs == * v) ) ; 
124135    assert_eq ! ( & ys,  & [ 1 ,  1 ] [ ..] ) ; 
125136
126137    let  xs = [ CountClone :: new ( ) ,  CountClone :: new ( ) ] ; 
@@ -139,7 +150,8 @@ fn test_zip_map_sideffectful() {
139150
140151    for  _ in  xs. iter_mut ( ) . map ( |x| * x += 1 ) . zip ( ys. iter_mut ( ) . map ( |y| * y += 1 ) )  { } 
141152
142-     assert_eq ! ( & xs,  & [ 1 ,  1 ,  1 ,  1 ,  1 ,  0 ] ) ; 
153+     // Zip documentation permits either case. 
154+     assert ! ( [ & [ 1 ,  1 ,  1 ,  1 ,  1 ,  0 ] ,  & [ 1 ,  1 ,  1 ,  1 ,  0 ,  0 ] ] . iter( ) . any( |v| & xs == * v) ) ; 
143155    assert_eq ! ( & ys,  & [ 1 ,  1 ,  1 ,  1 ] ) ; 
144156
145157    let  mut  xs = [ 0 ;  4 ] ; 
@@ -168,7 +180,8 @@ fn test_zip_map_rev_sideffectful() {
168180
169181    { 
170182        let  mut  it = xs. iter_mut ( ) . map ( |x| * x += 1 ) . zip ( ys. iter_mut ( ) . map ( |y| * y += 1 ) ) ; 
171-         ( & mut  it) . take ( 5 ) . count ( ) ; 
183+         // the current impl only trims the tails if the iterator isn't exhausted 
184+         ( & mut  it) . take ( 3 ) . count ( ) ; 
172185        it. next_back ( ) ; 
173186    } 
174187    assert_eq ! ( & xs,  & [ 1 ,  1 ,  1 ,  1 ,  1 ,  1 ] ) ; 
@@ -211,9 +224,18 @@ fn test_zip_nth_back_side_effects_exhausted() {
211224    iter. next ( ) ; 
212225    iter. next ( ) ; 
213226    iter. next ( ) ; 
214-     iter. next ( ) ; 
227+     assert_eq ! ( iter. next( ) ,   None ) ; 
215228    assert_eq ! ( iter. nth_back( 0 ) ,  None ) ; 
216-     assert_eq ! ( a,  vec![ 1 ,  2 ,  3 ,  4 ,  6 ,  5 ] ) ; 
229+     assert ! ( a. starts_with( & [ 1 ,  2 ,  3 ] ) ) ; 
230+     let  a_len = a. len ( ) ; 
231+     // Tail-side-effects of forward-iteration are "at most one" per next(). 
232+     // And for reverse iteration we don't guarantee much either. 
233+     // But we can put some bounds on the possible behaviors. 
234+     assert ! ( a_len <= 6 ) ; 
235+     assert ! ( a_len >= 3 ) ; 
236+     a. sort ( ) ; 
237+     assert_eq ! ( a,  & [ 1 ,  2 ,  3 ,  4 ,  5 ,  6 ] [ ..a. len( ) ] ) ; 
238+ 
217239    assert_eq ! ( b,  vec![ 200 ,  300 ,  400 ] ) ; 
218240} 
219241
@@ -237,32 +259,6 @@ fn test_zip_trusted_random_access_composition() {
237259    assert_eq ! ( z2. next( ) . unwrap( ) ,  ( ( 1 ,  1 ) ,  1 ) ) ; 
238260} 
239261
240- #[ test]  
241- #[ cfg( panic = "unwind" ) ]  
242- fn  test_zip_trusted_random_access_next_back_drop ( )  { 
243-     use  std:: panic:: { AssertUnwindSafe ,  catch_unwind} ; 
244- 
245-     let  mut  counter = 0 ; 
246- 
247-     let  it = [ 42 ] . iter ( ) . map ( |e| { 
248-         let  c = counter; 
249-         counter += 1 ; 
250-         if  c == 0  { 
251-             panic ! ( "bomb" ) ; 
252-         } 
253- 
254-         e
255-     } ) ; 
256-     let  it2 = [ ( ) ;  0 ] . iter ( ) ; 
257-     let  mut  zip = it. zip ( it2) ; 
258-     catch_unwind ( AssertUnwindSafe ( || { 
259-         zip. next_back ( ) ; 
260-     } ) ) 
261-     . unwrap_err ( ) ; 
262-     assert ! ( zip. next( ) . is_none( ) ) ; 
263-     assert_eq ! ( counter,  1 ) ; 
264- } 
265- 
266262#[ test]  
267263fn  test_double_ended_zip ( )  { 
268264    let  xs = [ 1 ,  2 ,  3 ,  4 ,  5 ,  6 ] ; 
@@ -275,6 +271,63 @@ fn test_double_ended_zip() {
275271    assert_eq ! ( it. next( ) ,  None ) ; 
276272} 
277273
274+ #[ test]  
275+ #[ cfg( panic = "unwind" ) ]  
276+ /// Regresion test for #137255 
277+ /// A previous implementation of Zip TrustedRandomAccess specializations tried to do a lot of work 
278+ /// to preserve side-effects of equalizing the iterator lengths during backwards iteration. 
279+ /// This lead to several cases of unsoundness, twice due to being left in an inconsistent state 
280+ /// after panics. 
281+ /// The new implementation does not try as hard, but we still need panic-safety. 
282+ fn  test_nested_zip_panic_safety ( )  { 
283+     use  std:: panic:: { AssertUnwindSafe ,  catch_unwind,  resume_unwind} ; 
284+     use  std:: sync:: atomic:: { AtomicUsize ,  Ordering } ; 
285+ 
286+     let  mut  panic = true ; 
287+     // keeps track of how often element get visited, must be at most once each 
288+     let  witness = [ 8 ,  9 ,  10 ,  11 ,  12 ] . map ( |i| ( i,  AtomicUsize :: new ( 0 ) ) ) ; 
289+     let  a = witness. as_slice ( ) . iter ( ) . map ( |e| { 
290+         e. 1 . fetch_add ( 1 ,  Ordering :: Relaxed ) ; 
291+         if  panic { 
292+             panic = false ; 
293+             resume_unwind ( Box :: new ( ( ) ) ) 
294+         } 
295+         e. 0 
296+     } ) ; 
297+     // shorter than `a`, so `a` will get trimmed 
298+     let  b = [ 1 ,  2 ,  3 ,  4 ] . as_slice ( ) . iter ( ) . copied ( ) ; 
299+     // shorter still, so `ab` will get trimmed.` 
300+     let  c = [ 5 ,  6 ,  7 ] . as_slice ( ) . iter ( ) . copied ( ) ; 
301+ 
302+     // This will panic during backwards trimming. 
303+     let  ab = zip ( a,  b) ; 
304+     // This being Zip + TrustedRandomAccess means it will only call `next_back`` 
305+     // during trimming and otherwise do calls `__iterator_get_unchecked` on `ab`. 
306+     let  mut  abc = zip ( ab,  c) ; 
307+ 
308+     assert_eq ! ( abc. len( ) ,  3 ) ; 
309+     // This will first trigger backwards trimming before it would normally obtain the 
310+     // actual element if it weren't for the panic. 
311+     // This used to corrupt the internal state of `abc`, which then lead to 
312+     // TrustedRandomAccess safety contract violations in calls to  `ab`, 
313+     // which ultimately lead to UB. 
314+     catch_unwind ( AssertUnwindSafe ( || abc. next_back ( ) ) ) . ok ( ) ; 
315+     // check for sane outward behavior after the panic, which indicates a sane internal state. 
316+     // Technically these outcomes are not required because a panic frees us from correctness obligations. 
317+     assert_eq ! ( abc. len( ) ,  2 ) ; 
318+     assert_eq ! ( abc. next( ) ,  Some ( ( ( 8 ,  1 ) ,  5 ) ) ) ; 
319+     assert_eq ! ( abc. next_back( ) ,  Some ( ( ( 9 ,  2 ) ,  6 ) ) ) ; 
320+     for  ( i,  ( _,  w) )  in  witness. iter ( ) . enumerate ( )  { 
321+         let  v = w. load ( Ordering :: Relaxed ) ; 
322+         // required by TRA contract 
323+         assert ! ( v <= 1 ,  "expected idx {i} to be visited at most once, actual: {v}" ) ; 
324+     } 
325+     // Trimming panicked and should only run once, so this one won't be visited. 
326+     // Implementation detail, but not trying to run it again is what keeps 
327+     // things simple. 
328+     assert_eq ! ( witness[ 3 ] . 1 . load( Ordering :: Relaxed ) ,  0 ) ; 
329+ } 
330+ 
278331#[ test]  
279332fn  test_issue_82282 ( )  { 
280333    fn  overflowed_zip ( arr :  & [ i32 ] )  -> impl  Iterator < Item  = ( i32 ,  & ( ) ) >  { 
0 commit comments