Skip to content

Commit 6a1ad55

Browse files
committed
mark vec::IntoIter pointers as !nonnull
1 parent 0c4d5f3 commit 6a1ad55

File tree

6 files changed

+121
-49
lines changed

6 files changed

+121
-49
lines changed

library/alloc/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
#![feature(maybe_uninit_slice)]
140140
#![feature(maybe_uninit_uninit_array)]
141141
#![feature(maybe_uninit_uninit_array_transpose)]
142+
#![feature(non_null_convenience)]
142143
#![feature(pattern)]
143144
#![feature(ptr_internals)]
144145
#![feature(ptr_metadata)]
@@ -180,6 +181,7 @@
180181
#![feature(const_ptr_write)]
181182
#![feature(const_trait_impl)]
182183
#![feature(const_try)]
184+
#![feature(decl_macro)]
183185
#![feature(dropck_eyepatch)]
184186
#![feature(exclusive_range_pattern)]
185187
#![feature(fundamental)]

library/alloc/src/vec/in_place_collect.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ where
257257
// then the source pointer will stay in its initial position and we can't use it as reference
258258
if src.ptr != src_ptr {
259259
debug_assert!(
260-
unsafe { dst_buf.add(len) as *const _ } <= src.ptr,
260+
unsafe { dst_buf.add(len) as *const _ } <= src.ptr.as_ptr(),
261261
"InPlaceIterable contract violation, write pointer advanced beyond read pointer"
262262
);
263263
}

library/alloc/src/vec/into_iter.rs

+68-38
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ use core::ops::Deref;
1818
use core::ptr::{self, NonNull};
1919
use core::slice::{self};
2020

21+
macro non_null {
22+
(mut $place:expr, $t:ident) => {{
23+
#![allow(unused_unsafe)] // we're sometimes used within an unsafe block
24+
unsafe { &mut *(ptr::addr_of_mut!($place) as *mut NonNull<$t>) }
25+
}},
26+
($place:expr, $t:ident) => {{
27+
#![allow(unused_unsafe)] // we're sometimes used within an unsafe block
28+
unsafe { *(ptr::addr_of!($place) as *const NonNull<$t>) }
29+
}},
30+
}
31+
2132
/// An iterator that moves out of a vector.
2233
///
2334
/// This `struct` is created by the `into_iter` method on [`Vec`](super::Vec)
@@ -41,10 +52,12 @@ pub struct IntoIter<
4152
// the drop impl reconstructs a RawVec from buf, cap and alloc
4253
// to avoid dropping the allocator twice we need to wrap it into ManuallyDrop
4354
pub(super) alloc: ManuallyDrop<A>,
44-
pub(super) ptr: *const T,
45-
pub(super) end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
46-
// ptr == end is a quick test for the Iterator being empty, that works
47-
// for both ZST and non-ZST.
55+
pub(super) ptr: NonNull<T>,
56+
/// If T is a ZST, this is actually ptr+len. This encoding is picked so that
57+
/// ptr == end is a quick test for the Iterator being empty, that works
58+
/// for both ZST and non-ZST.
59+
/// For non-ZSTs the pointer is treated as `NonNull<T>`
60+
pub(super) end: *const T,
4861
}
4962

5063
#[stable(feature = "vec_intoiter_debug", since = "1.13.0")]
@@ -68,7 +81,7 @@ impl<T, A: Allocator> IntoIter<T, A> {
6881
/// ```
6982
#[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")]
7083
pub fn as_slice(&self) -> &[T] {
71-
unsafe { slice::from_raw_parts(self.ptr, self.len()) }
84+
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len()) }
7285
}
7386

7487
/// Returns the remaining items of this iterator as a mutable slice.
@@ -97,7 +110,7 @@ impl<T, A: Allocator> IntoIter<T, A> {
97110
}
98111

99112
fn as_raw_mut_slice(&mut self) -> *mut [T] {
100-
ptr::slice_from_raw_parts_mut(self.ptr as *mut T, self.len())
113+
ptr::slice_from_raw_parts_mut(self.ptr.as_ptr(), self.len())
101114
}
102115

103116
/// Drops remaining elements and relinquishes the backing allocation.
@@ -124,7 +137,7 @@ impl<T, A: Allocator> IntoIter<T, A> {
124137
// this creates less assembly
125138
self.cap = 0;
126139
self.buf = unsafe { NonNull::new_unchecked(RawVec::NEW.ptr()) };
127-
self.ptr = self.buf.as_ptr();
140+
self.ptr = self.buf;
128141
self.end = self.buf.as_ptr();
129142

130143
// Dropping the remaining elements can panic, so this needs to be
@@ -138,7 +151,7 @@ impl<T, A: Allocator> IntoIter<T, A> {
138151
pub(crate) fn forget_remaining_elements(&mut self) {
139152
// For the ZST case, it is crucial that we mutate `end` here, not `ptr`.
140153
// `ptr` must stay aligned, while `end` may be unaligned.
141-
self.end = self.ptr;
154+
self.end = self.ptr.as_ptr();
142155
}
143156

144157
#[cfg(not(no_global_oom_handling))]
@@ -160,7 +173,7 @@ impl<T, A: Allocator> IntoIter<T, A> {
160173
// say that they're all at the beginning of the "allocation".
161174
0..this.len()
162175
} else {
163-
this.ptr.sub_ptr(buf)..this.end.sub_ptr(buf)
176+
this.ptr.sub_ptr(this.buf)..this.end.sub_ptr(buf)
164177
};
165178
let cap = this.cap;
166179
let alloc = ManuallyDrop::take(&mut this.alloc);
@@ -187,37 +200,43 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
187200

188201
#[inline]
189202
fn next(&mut self) -> Option<T> {
190-
if self.ptr == self.end {
191-
None
192-
} else if T::IS_ZST {
193-
// `ptr` has to stay where it is to remain aligned, so we reduce the length by 1 by
194-
// reducing the `end`.
195-
self.end = self.end.wrapping_byte_sub(1);
196-
197-
// Make up a value of this ZST.
198-
Some(unsafe { mem::zeroed() })
203+
if T::IS_ZST {
204+
if self.ptr.as_ptr() == self.end as *mut _ {
205+
None
206+
} else {
207+
// `ptr` has to stay where it is to remain aligned, so we reduce the length by 1 by
208+
// reducing the `end`.
209+
self.end = self.end.wrapping_byte_sub(1);
210+
211+
// Make up a value of this ZST.
212+
Some(unsafe { mem::zeroed() })
213+
}
199214
} else {
200-
let old = self.ptr;
201-
self.ptr = unsafe { self.ptr.add(1) };
215+
if self.ptr == non_null!(self.end, T) {
216+
None
217+
} else {
218+
let old = self.ptr;
219+
self.ptr = unsafe { old.add(1) };
202220

203-
Some(unsafe { ptr::read(old) })
221+
Some(unsafe { ptr::read(old.as_ptr()) })
222+
}
204223
}
205224
}
206225

207226
#[inline]
208227
fn size_hint(&self) -> (usize, Option<usize>) {
209228
let exact = if T::IS_ZST {
210-
self.end.addr().wrapping_sub(self.ptr.addr())
229+
self.end.addr().wrapping_sub(self.ptr.as_ptr().addr())
211230
} else {
212-
unsafe { self.end.sub_ptr(self.ptr) }
231+
unsafe { non_null!(self.end, T).sub_ptr(self.ptr) }
213232
};
214233
(exact, Some(exact))
215234
}
216235

217236
#[inline]
218237
fn advance_by(&mut self, n: usize) -> Result<(), NonZeroUsize> {
219238
let step_size = self.len().min(n);
220-
let to_drop = ptr::slice_from_raw_parts_mut(self.ptr as *mut T, step_size);
239+
let to_drop = ptr::slice_from_raw_parts_mut(self.ptr.as_ptr(), step_size);
221240
if T::IS_ZST {
222241
// See `next` for why we sub `end` here.
223242
self.end = self.end.wrapping_byte_sub(step_size);
@@ -259,7 +278,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
259278
// Safety: `len` indicates that this many elements are available and we just checked that
260279
// it fits into the array.
261280
unsafe {
262-
ptr::copy_nonoverlapping(self.ptr, raw_ary.as_mut_ptr() as *mut T, len);
281+
ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, len);
263282
self.forget_remaining_elements();
264283
return Err(array::IntoIter::new_unchecked(raw_ary, 0..len));
265284
}
@@ -268,7 +287,7 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
268287
// Safety: `len` is larger than the array size. Copy a fixed amount here to fully initialize
269288
// the array.
270289
return unsafe {
271-
ptr::copy_nonoverlapping(self.ptr, raw_ary.as_mut_ptr() as *mut T, N);
290+
ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, N);
272291
self.ptr = self.ptr.add(N);
273292
Ok(raw_ary.transpose().assume_init())
274293
};
@@ -286,26 +305,33 @@ impl<T, A: Allocator> Iterator for IntoIter<T, A> {
286305
// Also note the implementation of `Self: TrustedRandomAccess` requires
287306
// that `T: Copy` so reading elements from the buffer doesn't invalidate
288307
// them for `Drop`.
289-
unsafe { if T::IS_ZST { mem::zeroed() } else { ptr::read(self.ptr.add(i)) } }
308+
unsafe { if T::IS_ZST { mem::zeroed() } else { self.ptr.add(i).read() } }
290309
}
291310
}
292311

293312
#[stable(feature = "rust1", since = "1.0.0")]
294313
impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> {
295314
#[inline]
296315
fn next_back(&mut self) -> Option<T> {
297-
if self.end == self.ptr {
298-
None
299-
} else if T::IS_ZST {
300-
// See above for why 'ptr.offset' isn't used
301-
self.end = self.end.wrapping_byte_sub(1);
302-
303-
// Make up a value of this ZST.
304-
Some(unsafe { mem::zeroed() })
316+
if T::IS_ZST {
317+
if self.end as *mut _ == self.ptr.as_ptr() {
318+
None
319+
} else {
320+
// See above for why 'ptr.offset' isn't used
321+
self.end = self.end.wrapping_byte_sub(1);
322+
323+
// Make up a value of this ZST.
324+
Some(unsafe { mem::zeroed() })
325+
}
305326
} else {
306-
self.end = unsafe { self.end.sub(1) };
327+
if non_null!(self.end, T) == self.ptr {
328+
None
329+
} else {
330+
let new_end = unsafe { non_null!(self.end, T).sub(1) };
331+
*non_null!(mut self.end, T) = new_end;
307332

308-
Some(unsafe { ptr::read(self.end) })
333+
Some(unsafe { ptr::read(new_end.as_ptr()) })
334+
}
309335
}
310336
}
311337

@@ -331,7 +357,11 @@ impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> {
331357
#[stable(feature = "rust1", since = "1.0.0")]
332358
impl<T, A: Allocator> ExactSizeIterator for IntoIter<T, A> {
333359
fn is_empty(&self) -> bool {
334-
self.ptr == self.end
360+
if T::IS_ZST {
361+
self.ptr.as_ptr() == self.end as *mut _
362+
} else {
363+
self.ptr == non_null!(self.end, T)
364+
}
335365
}
336366
}
337367

library/alloc/src/vec/mod.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -2825,14 +2825,8 @@ impl<T, A: Allocator> IntoIterator for Vec<T, A> {
28252825
begin.add(me.len()) as *const T
28262826
};
28272827
let cap = me.buf.capacity();
2828-
IntoIter {
2829-
buf: NonNull::new_unchecked(begin),
2830-
phantom: PhantomData,
2831-
cap,
2832-
alloc,
2833-
ptr: begin,
2834-
end,
2835-
}
2828+
let buf = NonNull::new_unchecked(begin);
2829+
IntoIter { buf, phantom: PhantomData, cap, alloc, ptr: buf, end }
28362830
}
28372831
}
28382832
}

library/alloc/src/vec/spec_from_iter.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ impl<T> SpecFromIter<T, IntoIter<T>> for Vec<T> {
4444
// than creating it through the generic FromIterator implementation would. That limitation
4545
// is not strictly necessary as Vec's allocation behavior is intentionally unspecified.
4646
// But it is a conservative choice.
47-
let has_advanced = iterator.buf.as_ptr() as *const _ != iterator.ptr;
47+
let has_advanced = iterator.buf != iterator.ptr;
4848
if !has_advanced || iterator.len() >= iterator.cap / 2 {
4949
unsafe {
5050
let it = ManuallyDrop::new(iterator);
5151
if has_advanced {
52-
ptr::copy(it.ptr, it.buf.as_ptr(), it.len());
52+
ptr::copy(it.ptr.as_ptr(), it.buf.as_ptr(), it.len());
5353
}
5454
return Vec::from_raw_parts(it.buf.as_ptr(), it.len(), it.cap);
5555
}

tests/codegen/vec-iter.rs

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// ignore-debug: the debug assertions get in the way
2+
// compile-flags: -O
3+
#![crate_type = "lib"]
4+
#![feature(exact_size_is_empty)]
5+
6+
use std::vec;
7+
8+
// CHECK-LABEL: @vec_iter_len_nonnull
9+
#[no_mangle]
10+
pub fn vec_iter_len_nonnull(it: &vec::IntoIter<u8>) -> usize {
11+
// CHECK: %subt{{.+}}.i.i = load ptr
12+
// CHECK-SAME: !nonnull
13+
// CHECK-SAME: !noundef
14+
// CHECK: %self2.i.i = load ptr
15+
// CHECK-SAME: !nonnull
16+
// CHECK-SAME: !noundef
17+
// CHECK: sub nuw
18+
// CHECK: ret
19+
it.len()
20+
}
21+
22+
// CHECK-LABEL: @vec_iter_is_empty_nonnull
23+
#[no_mangle]
24+
pub fn vec_iter_is_empty_nonnull(it: &vec::IntoIter<u8>) -> bool {
25+
// CHECK: %other.i = load ptr
26+
// CHECK-SAME: !nonnull
27+
// CHECK-SAME: !noundef
28+
// CHECK: %self2.i = load ptr
29+
// CHECK-SAME: !nonnull
30+
// CHECK-SAME: !noundef
31+
// CHECK: ret
32+
it.is_empty()
33+
}
34+
35+
// CHECK-LABEL: @vec_iter_next
36+
#[no_mangle]
37+
pub fn vec_iter_next(it: &mut vec::IntoIter<u8>) -> Option<u8> {
38+
// CHECK: %other.i = load ptr
39+
// CHECK-SAME: !nonnull
40+
// CHECK-SAME: !noundef
41+
// CHECK: %self2.i = load ptr
42+
// CHECK-SAME: !nonnull
43+
// CHECK-SAME: !noundef
44+
// CHECK: ret
45+
it.next()
46+
}

0 commit comments

Comments
 (0)