Skip to content

Commit c33f021

Browse files
committed
Auto merge of rust-lang#136771 - scottmcm:poke-slice-iter-next, r=joboet
Simplify `slice::Iter::next` enough that it inlines Inspired by this zulip conversation: <https://rust-lang.zulipchat.com/#narrow/channel/189540-t-compiler.2Fwg-mir-opt/topic/Feedback.20on.20a.20MIR.20optimization.20idea/near/498579990> ~~Draft for now because it needs rust-lang#136735 to get the codegen tests to pass.~~
2 parents f9d795e + 4558031 commit c33f021

File tree

1 file changed

+29
-6
lines changed

1 file changed

+29
-6
lines changed

core/src/slice/iter/macros.rs

+29-6
Original file line numberDiff line numberDiff line change
@@ -154,16 +154,39 @@ macro_rules! iterator {
154154

155155
#[inline]
156156
fn next(&mut self) -> Option<$elem> {
157-
// could be implemented with slices, but this avoids bounds checks
157+
// intentionally not using the helpers because this is
158+
// one of the most mono'd things in the library.
158159

159-
// SAFETY: The call to `next_unchecked` is
160-
// safe since we check if the iterator is empty first.
160+
let ptr = self.ptr;
161+
let end_or_len = self.end_or_len;
162+
// SAFETY: See inner comments. (For some reason having multiple
163+
// block breaks inlining this -- if you can fix that please do!)
161164
unsafe {
162-
if is_empty!(self) {
163-
None
165+
if T::IS_ZST {
166+
let len = end_or_len.addr();
167+
if len == 0 {
168+
return None;
169+
}
170+
// SAFETY: just checked that it's not zero, so subtracting one
171+
// cannot wrap. (Ideally this would be `checked_sub`, which
172+
// does the same thing internally, but as of 2025-02 that
173+
// doesn't optimize quite as small in MIR.)
174+
self.end_or_len = without_provenance_mut(len.unchecked_sub(1));
164175
} else {
165-
Some(self.next_unchecked())
176+
// SAFETY: by type invariant, the `end_or_len` field is always
177+
// non-null for a non-ZST pointee. (This transmute ensures we
178+
// get `!nonnull` metadata on the load of the field.)
179+
if ptr == crate::intrinsics::transmute::<$ptr, NonNull<T>>(end_or_len) {
180+
return None;
181+
}
182+
// SAFETY: since it's not empty, per the check above, moving
183+
// forward one keeps us inside the slice, and this is valid.
184+
self.ptr = ptr.add(1);
166185
}
186+
// SAFETY: Now that we know it wasn't empty and we've moved past
187+
// the first one (to avoid giving a duplicate `&mut` next time),
188+
// we can give out a reference to it.
189+
Some({ptr}.$into_ref())
167190
}
168191
}
169192

0 commit comments

Comments
 (0)