Skip to content

Commit fd8bd1c

Browse files
committed
fix IntoIter::drop on high-alignment ZST
1 parent d4db1c7 commit fd8bd1c

File tree

1 file changed

+11
-10
lines changed

1 file changed

+11
-10
lines changed

alloc/src/vec/into_iter.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ pub struct IntoIter<
4040
// to avoid dropping the allocator twice we need to wrap it into ManuallyDrop
4141
pub(super) alloc: ManuallyDrop<A>,
4242
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.
4446
}
4547

4648
#[stable(feature = "vec_intoiter_debug", since = "1.13.0")]
@@ -132,7 +134,9 @@ impl<T, A: Allocator> IntoIter<T, A> {
132134

133135
/// Forgets to Drop the remaining elements while still allowing the backing allocation to be freed.
134136
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;
136140
}
137141

138142
#[cfg(not(no_global_oom_handling))]
@@ -184,10 +188,9 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
184188
if self.ptr == self.end {
185189
None
186190
} 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);
191194

192195
// Make up a value of this ZST.
193196
Some(unsafe { mem::zeroed() })
@@ -214,10 +217,8 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
214217
let step_size = self.len().min(n);
215218
let to_drop = ptr::slice_from_raw_parts_mut(self.ptr as *mut T, step_size);
216219
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);
221222
} else {
222223
// SAFETY: the min() above ensures that step_size is in bounds
223224
self.ptr = unsafe { self.ptr.add(step_size) };

0 commit comments

Comments
 (0)