@@ -40,7 +40,9 @@ pub struct IntoIter<
40
40
// to avoid dropping the allocator twice we need to wrap it into ManuallyDrop
41
41
pub ( super ) alloc : ManuallyDrop < A > ,
42
42
pub ( super ) ptr : * const T ,
43
- pub ( super ) end : * const T ,
43
+ pub ( super ) end : * const T , // If T is a ZST, this is actually ptr+len. This encoding is picked so that
44
+ // ptr == end is a quick test for the Iterator being empty, that works
45
+ // for both ZST and non-ZST.
44
46
}
45
47
46
48
#[ stable( feature = "vec_intoiter_debug" , since = "1.13.0" ) ]
@@ -132,7 +134,9 @@ impl<T, A: Allocator> IntoIter<T, A> {
132
134
133
135
/// Forgets to Drop the remaining elements while still allowing the backing allocation to be freed.
134
136
pub ( crate ) fn forget_remaining_elements ( & mut self ) {
135
- self . ptr = self . end ;
137
+ // For th ZST case, it is crucial that we mutate `end` here, not `ptr`.
138
+ // `ptr` must stay aligned, while `end` may be unaligned.
139
+ self . end = self . ptr ;
136
140
}
137
141
138
142
#[ cfg( not( no_global_oom_handling) ) ]
@@ -184,10 +188,9 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
184
188
if self . ptr == self . end {
185
189
None
186
190
} else if T :: IS_ZST {
187
- // purposefully don't use 'ptr.offset' because for
188
- // vectors with 0-size elements this would return the
189
- // same pointer.
190
- self . ptr = self . ptr . wrapping_byte_add ( 1 ) ;
191
+ // `ptr` has to stay where it is to remain aligned, so we reduce the length by 1 by
192
+ // reducing the `end`.
193
+ self . end = self . end . wrapping_byte_sub ( 1 ) ;
191
194
192
195
// Make up a value of this ZST.
193
196
Some ( unsafe { mem:: zeroed ( ) } )
@@ -214,10 +217,8 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
214
217
let step_size = self . len ( ) . min ( n) ;
215
218
let to_drop = ptr:: slice_from_raw_parts_mut ( self . ptr as * mut T , step_size) ;
216
219
if T :: IS_ZST {
217
- // SAFETY: due to unchecked casts of unsigned amounts to signed offsets the wraparound
218
- // effectively results in unsigned pointers representing positions 0..usize::MAX,
219
- // which is valid for ZSTs.
220
- self . ptr = self . ptr . wrapping_byte_add ( step_size) ;
220
+ // See `next` for why we sub `end` here.
221
+ self . end = self . end . wrapping_byte_sub ( step_size) ;
221
222
} else {
222
223
// SAFETY: the min() above ensures that step_size is in bounds
223
224
self . ptr = unsafe { self . ptr . add ( step_size) } ;
@@ -250,7 +251,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
250
251
return Err ( unsafe { array:: IntoIter :: new_unchecked ( raw_ary, 0 ..len) } ) ;
251
252
}
252
253
253
- self . ptr = self . ptr . wrapping_byte_add ( N ) ;
254
+ self . end = self . end . wrapping_byte_sub ( N ) ;
254
255
// Safety: ditto
255
256
return Ok ( unsafe { raw_ary. transpose ( ) . assume_init ( ) } ) ;
256
257
}
0 commit comments