Skip to content

Commit eb3cc5f

Browse files
committed
Use Option's discriminant as its size hint
1 parent 336e89b commit eb3cc5f

File tree

2 files changed

+51
-8
lines changed

2 files changed

+51
-8
lines changed

library/core/src/option.rs

+17-7
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,13 @@ impl<T> Option<T> {
770770
}
771771
}
772772

773+
#[inline]
774+
const fn len(&self) -> usize {
775+
// Using the intrinsic avoids emitting a branch to get the 0 or 1.
776+
let discriminant: isize = crate::intrinsics::discriminant_value(self);
777+
discriminant as usize
778+
}
779+
773780
/// Returns a slice of the contained value, if any. If this is `None`, an
774781
/// empty slice is returned. This can be useful to have a single type of
775782
/// iterator over an `Option` or slice.
@@ -812,7 +819,7 @@ impl<T> Option<T> {
812819
unsafe {
813820
slice::from_raw_parts(
814821
(self as *const Self).byte_add(core::mem::offset_of!(Self, Some.0)).cast(),
815-
self.is_some() as usize,
822+
self.len(),
816823
)
817824
}
818825
}
@@ -869,7 +876,7 @@ impl<T> Option<T> {
869876
unsafe {
870877
slice::from_raw_parts_mut(
871878
(self as *mut Self).byte_add(core::mem::offset_of!(Self, Some.0)).cast(),
872-
self.is_some() as usize,
879+
self.len(),
873880
)
874881
}
875882
}
@@ -2242,10 +2249,8 @@ impl<A> Iterator for Item<A> {
22422249

22432250
#[inline]
22442251
fn size_hint(&self) -> (usize, Option<usize>) {
2245-
match self.opt {
2246-
Some(_) => (1, Some(1)),
2247-
None => (0, Some(0)),
2248-
}
2252+
let len = self.len();
2253+
(len, Some(len))
22492254
}
22502255
}
22512256

@@ -2256,7 +2261,12 @@ impl<A> DoubleEndedIterator for Item<A> {
22562261
}
22572262
}
22582263

2259-
impl<A> ExactSizeIterator for Item<A> {}
2264+
impl<A> ExactSizeIterator for Item<A> {
2265+
#[inline]
2266+
fn len(&self) -> usize {
2267+
self.opt.len()
2268+
}
2269+
}
22602270
impl<A> FusedIterator for Item<A> {}
22612271
unsafe impl<A> TrustedLen for Item<A> {}
22622272

tests/codegen/option-as-slice.rs

+34-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ pub fn u64_opt_as_slice(o: &Option<u64>) -> &[u64] {
1414
// CHECK-NOT: br
1515
// CHECK-NOT: switch
1616
// CHECK-NOT: icmp
17+
// CHECK: %[[LEN:.+]] = load i64,{{.+}} !range ![[META_U64:.+]], !noundef
18+
// CHECK-NOT: select
19+
// CHECK-NOT: br
20+
// CHECK-NOT: switch
21+
// CHECK-NOT: icmp
22+
// CHECK: %[[T0:.+]] = insertvalue { ptr, i64 } poison, ptr %{{.+}}, 0
23+
// CHECK-NEXT: %[[T1:.+]] = insertvalue { ptr, i64 } %[[T0]], i64 %[[LEN]], 1
24+
// CHECK-NEXT: ret { ptr, i64 } %[[T1]]
1725
o.as_slice()
1826
}
1927

@@ -25,10 +33,35 @@ pub fn nonzero_u64_opt_as_slice(o: &Option<NonZero<u64>>) -> &[NonZero<u64>] {
2533
// CHECK-NOT: switch
2634
// CHECK-NOT: icmp
2735
// CHECK: %[[NZ:.+]] = icmp ne i64 %{{.+}}, 0
28-
// CHECK-NEXT: zext i1 %[[NZ]] to i64
36+
// CHECK-NEXT: %[[LEN:.+]] = zext i1 %[[NZ]] to i64
2937
// CHECK-NOT: select
3038
// CHECK-NOT: br
3139
// CHECK-NOT: switch
3240
// CHECK-NOT: icmp
41+
// CHECK: %[[T0:.+]] = insertvalue { ptr, i64 } poison, ptr %o, 0
42+
// CHECK-NEXT: %[[T1:.+]] = insertvalue { ptr, i64 } %[[T0]], i64 %[[LEN]], 1
43+
// CHECK-NEXT: ret { ptr, i64 } %[[T1]]
3344
o.as_slice()
3445
}
46+
47+
// CHECK-LABEL: @u8_opt_as_slice
48+
#[no_mangle]
49+
pub fn u8_opt_as_slice(o: &Option<u8>) -> &[u8] {
50+
// CHECK-NOT: select
51+
// CHECK-NOT: br
52+
// CHECK-NOT: switch
53+
// CHECK-NOT: icmp
54+
// CHECK: %[[TAG:.+]] = load i8,{{.+}} !range ![[META_U8:.+]], !noundef
55+
// CHECK: %[[LEN:.+]] = zext{{.*}} i8 %[[TAG]] to i64
56+
// CHECK-NOT: select
57+
// CHECK-NOT: br
58+
// CHECK-NOT: switch
59+
// CHECK-NOT: icmp
60+
// CHECK: %[[T0:.+]] = insertvalue { ptr, i64 } poison, ptr %{{.+}}, 0
61+
// CHECK-NEXT: %[[T1:.+]] = insertvalue { ptr, i64 } %[[T0]], i64 %[[LEN]], 1
62+
// CHECK-NEXT: ret { ptr, i64 } %[[T1]]
63+
o.as_slice()
64+
}
65+
66+
// CHECK: ![[META_U64]] = !{i64 0, i64 2}
67+
// CHECK: ![[META_U8]] = !{i8 0, i8 2}

0 commit comments

Comments
 (0)