Skip to content

Commit

Permalink
Use Option's discriminant as its size hint
Browse files Browse the repository at this point in the history
  • Loading branch information
scottmcm committed Jul 15, 2024
1 parent 336e89b commit f7c9396
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 8 deletions.
24 changes: 17 additions & 7 deletions library/core/src/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,13 @@ impl<T> Option<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.
Expand Down Expand Up @@ -812,7 +819,7 @@ impl<T> Option<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(),
)
}
}
Expand Down Expand Up @@ -869,7 +876,7 @@ impl<T> Option<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(),
)
}
}
Expand Down Expand Up @@ -2242,10 +2249,8 @@ impl<A> Iterator for Item<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))
}
}

Expand All @@ -2256,7 +2261,12 @@ impl<A> DoubleEndedIterator for Item<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> {}

Expand Down
35 changes: 34 additions & 1 deletion tests/codegen/option-as-slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

Expand All @@ -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}

0 comments on commit f7c9396

Please sign in to comment.