Make the Vec impl be slightly more careful with ZSTs and alignment.

This also incidentally makes the ZST code and final code have the same formatting for the divide.
pull/346/head
Aria Beingessner 3 years ago
parent e64ea939f0
commit 11f1165e8a

@ -239,21 +239,22 @@ impl<T> Iterator for RawValIter<T> {
None None
} else { } else {
unsafe { unsafe {
let result = ptr::read(self.start); if mem::size_of::<T>() == 0 {
self.start = if mem::size_of::<T>() == 0 { self.start = (self.start as usize + 1) as *const _;
(self.start as usize + 1) as *const _ Some(ptr::read(NonNull::<T>::dangling().as_ptr()))
} else { } else {
self.start.offset(1) let old_ptr = self.start;
}; self.start = self.start.offset(1);
Some(result) Some(ptr::read(old_ptr))
}
} }
} }
} }
fn size_hint(&self) -> (usize, Option<usize>) { fn size_hint(&self) -> (usize, Option<usize>) {
let elem_size = mem::size_of::<T>(); let elem_size = mem::size_of::<T>();
let len = (self.end as usize - self.start as usize) / let len = (self.end as usize - self.start as usize)
if elem_size == 0 { 1 } else { elem_size }; / if elem_size == 0 { 1 } else { elem_size };
(len, Some(len)) (len, Some(len))
} }
} }
@ -264,12 +265,13 @@ impl<T> DoubleEndedIterator for RawValIter<T> {
None None
} else { } else {
unsafe { unsafe {
self.end = if mem::size_of::<T>() == 0 { if mem::size_of::<T>() == 0 {
(self.end as usize - 1) as *const _ self.end = (self.end as usize - 1) as *const _;
Some(ptr::read(NonNull::<T>::dangling().as_ptr()))
} else { } else {
self.end.offset(-1) self.end = self.end.offset(-1);
}; Some(ptr::read(self.end))
Some(ptr::read(self.end)) }
} }
} }
} }

@ -130,7 +130,41 @@ Now we have a different bug. Instead of our iterators not running at all, our
iterators now run *forever*. We need to do the same trick in our iterator impls. iterators now run *forever*. We need to do the same trick in our iterator impls.
Also, our size_hint computation code will divide by 0 for ZSTs. Since we'll Also, our size_hint computation code will divide by 0 for ZSTs. Since we'll
basically be treating the two pointers as if they point to bytes, we'll just basically be treating the two pointers as if they point to bytes, we'll just
map size 0 to divide by 1. map size 0 to divide by 1. Here's what `next` will be:
<!-- ignore: simplified code -->
```rust,ignore
fn next(&mut self) -> Option<T> {
if self.start == self.end {
None
} else {
unsafe {
let result = ptr::read(self.start);
self.start = if mem::size_of::<T>() == 0 {
(self.start as usize + 1) as *const _
} else {
self.start.offset(1)
};
Some(result)
}
}
}
```
Do you see the "bug"? No one else did! The original author only noticed the
problem when linking to this page years later. This code is kind of dubious
because abusing the iterator pointers to be *counters* makes them unaligned!
Our *one job* when using ZSTs is to keep pointers aligned! *forehead slap*
Raw pointers don't need to be aligned at all times, so the basic trick of
using pointers as counters is *fine*, but they *should* definitely be aligned
when passed to `ptr::read`! This is *possibly* needless pedantry
because `ptr::read` is a noop for a ZST, but let's be a *little* more
responsible and read from `NonNull::dangling` on the ZST path.
(Alternatively you could call `read_unaligned` on the ZST path. Either is fine,
because either way we're making up a value from nothing and it all compiles
to doing nothing.)
<!-- ignore: simplified code --> <!-- ignore: simplified code -->
```rust,ignore ```rust,ignore
@ -141,13 +175,14 @@ impl<T> Iterator for RawValIter<T> {
None None
} else { } else {
unsafe { unsafe {
let result = ptr::read(self.start); if mem::size_of::<T>() == 0 {
self.start = if mem::size_of::<T>() == 0 { self.start = (self.start as usize + 1) as *const _;
(self.start as usize + 1) as *const _ Some(ptr::read(NonNull::<T>::dangling().as_ptr()))
} else { } else {
self.start.offset(1) let old_ptr = self.start;
}; self.start = self.start.offset(1);
Some(result) Some(ptr::read(old_ptr))
}
} }
} }
} }
@ -166,12 +201,13 @@ impl<T> DoubleEndedIterator for RawValIter<T> {
None None
} else { } else {
unsafe { unsafe {
self.end = if mem::size_of::<T>() == 0 { if mem::size_of::<T>() == 0 {
(self.end as usize - 1) as *const _ self.end = (self.end as usize - 1) as *const _;
Some(ptr::read(NonNull::<T>::dangling().as_ptr()))
} else { } else {
self.end.offset(-1) self.end = self.end.offset(-1);
}; Some(ptr::read(self.end))
Some(ptr::read(self.end)) }
} }
} }
} }

Loading…
Cancel
Save