Rollup merge of #127748 - scottmcm:option_len, r=joboet

Use Option's discriminant as its size hint

I was looking at this in MIR after a question on discord, and noticed that it ends up with a switch in MIR (<https://rust.godbolt.org/z/3q4cYnnb3>), which it doesn't need because (as `Option::as_slice` uses) the discriminant is already the length.
This commit is contained in:
Matthias Krüger 2024-07-18 18:10:16 +02:00 committed by GitHub
commit 6f7fa03a06
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 51 additions and 8 deletions

View file

@ -770,6 +770,13 @@ pub const fn as_pin_mut(self: Pin<&mut Self>) -> Option<Pin<&mut T>> {
}
}
#[inline]
const fn len(&self) -> usize {
// Using the intrinsic avoids emitting a branch to get the 0 or 1.
let discriminant: isize = crate::intrinsics::discriminant_value(self);
discriminant as usize
}
/// Returns a slice of the contained value, if any. If this is `None`, an
/// empty slice is returned. This can be useful to have a single type of
/// iterator over an `Option` or slice.
@ -812,7 +819,7 @@ pub const fn as_slice(&self) -> &[T] {
unsafe {
slice::from_raw_parts(
(self as *const Self).byte_add(core::mem::offset_of!(Self, Some.0)).cast(),
self.is_some() as usize,
self.len(),
)
}
}
@ -869,7 +876,7 @@ pub const fn as_mut_slice(&mut self) -> &mut [T] {
unsafe {
slice::from_raw_parts_mut(
(self as *mut Self).byte_add(core::mem::offset_of!(Self, Some.0)).cast(),
self.is_some() as usize,
self.len(),
)
}
}
@ -2242,10 +2249,8 @@ fn next(&mut self) -> Option<A> {
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
match self.opt {
Some(_) => (1, Some(1)),
None => (0, Some(0)),
}
let len = self.len();
(len, Some(len))
}
}
@ -2256,7 +2261,12 @@ fn next_back(&mut self) -> Option<A> {
}
}
impl<A> ExactSizeIterator for Item<A> {}
impl<A> ExactSizeIterator for Item<A> {
#[inline]
fn len(&self) -> usize {
self.opt.len()
}
}
impl<A> FusedIterator for Item<A> {}
unsafe impl<A> TrustedLen for Item<A> {}

View file

@ -14,6 +14,14 @@ pub fn u64_opt_as_slice(o: &Option<u64>) -> &[u64] {
// CHECK-NOT: br
// CHECK-NOT: switch
// CHECK-NOT: icmp
// CHECK: %[[LEN:.+]] = load i64,{{.+}} !range ![[META_U64:.+]], !noundef
// CHECK-NOT: select
// CHECK-NOT: br
// CHECK-NOT: switch
// CHECK-NOT: icmp
// CHECK: %[[T0:.+]] = insertvalue { ptr, i64 } poison, ptr %{{.+}}, 0
// CHECK-NEXT: %[[T1:.+]] = insertvalue { ptr, i64 } %[[T0]], i64 %[[LEN]], 1
// CHECK-NEXT: ret { ptr, i64 } %[[T1]]
o.as_slice()
}
@ -25,10 +33,35 @@ pub fn nonzero_u64_opt_as_slice(o: &Option<NonZero<u64>>) -> &[NonZero<u64>] {
// CHECK-NOT: switch
// CHECK-NOT: icmp
// CHECK: %[[NZ:.+]] = icmp ne i64 %{{.+}}, 0
// CHECK-NEXT: zext i1 %[[NZ]] to i64
// CHECK-NEXT: %[[LEN:.+]] = zext i1 %[[NZ]] to i64
// CHECK-NOT: select
// CHECK-NOT: br
// CHECK-NOT: switch
// CHECK-NOT: icmp
// CHECK: %[[T0:.+]] = insertvalue { ptr, i64 } poison, ptr %o, 0
// CHECK-NEXT: %[[T1:.+]] = insertvalue { ptr, i64 } %[[T0]], i64 %[[LEN]], 1
// CHECK-NEXT: ret { ptr, i64 } %[[T1]]
o.as_slice()
}
// CHECK-LABEL: @u8_opt_as_slice
#[no_mangle]
pub fn u8_opt_as_slice(o: &Option<u8>) -> &[u8] {
// CHECK-NOT: select
// CHECK-NOT: br
// CHECK-NOT: switch
// CHECK-NOT: icmp
// CHECK: %[[TAG:.+]] = load i8,{{.+}} !range ![[META_U8:.+]], !noundef
// CHECK: %[[LEN:.+]] = zext{{.*}} i8 %[[TAG]] to i64
// CHECK-NOT: select
// CHECK-NOT: br
// CHECK-NOT: switch
// CHECK-NOT: icmp
// CHECK: %[[T0:.+]] = insertvalue { ptr, i64 } poison, ptr %{{.+}}, 0
// CHECK-NEXT: %[[T1:.+]] = insertvalue { ptr, i64 } %[[T0]], i64 %[[LEN]], 1
// CHECK-NEXT: ret { ptr, i64 } %[[T1]]
o.as_slice()
}
// CHECK: ![[META_U64]] = !{i64 0, i64 2}
// CHECK: ![[META_U8]] = !{i8 0, i8 2}