-
Notifications
You must be signed in to change notification settings - Fork 13.9k
Description
At the moment, many of the core assertions in the standard library make use of the unreachable macro, which is simply an indirection for panic. This is totally fine, but I would like to begin an initiative on upgrading some of these invocations as core::hint::unreachable_unchecked so that we may (at least) give the compiler a chance to optimize the code better.
Below is a non-exhaustive list of possible candidates. I will be editing the issue as more candidates are introduced and as certain examples are marked 100% safe or not.
Merged
None so far.
Definitely Safe
This is the perfect candidate for core::hint::unreachable_unchecked since it is apparent that self is now the Cow::Owned variant.
rust/library/alloc/src/borrow.rs
Lines 275 to 287 in 97f2698
| #[stable(feature = "rust1", since = "1.0.0")] | |
| pub fn to_mut(&mut self) -> &mut <B as ToOwned>::Owned { | |
| match *self { | |
| Borrowed(borrowed) => { | |
| *self = Owned(borrowed.to_owned()); | |
| match *self { | |
| Borrowed(..) => unreachable!(), | |
| Owned(ref mut owned) => owned, | |
| } | |
| } | |
| Owned(ref mut owned) => owned, | |
| } | |
| } |
Possible Candidates
Lines 52 to 64 in 673d0db
| #[unstable(feature = "once_cell", issue = "74465")] | |
| impl<T: Clone> Clone for OnceCell<T> { | |
| fn clone(&self) -> OnceCell<T> { | |
| let res = OnceCell::new(); | |
| if let Some(value) = self.get() { | |
| match res.set(value.clone()) { | |
| Ok(()) => (), | |
| Err(_) => unreachable!(), | |
| } | |
| } | |
| res | |
| } | |
| } |
Lines 121 to 130 in adf1688
| fn clone(&self) -> SyncOnceCell<T> { | |
| let cell = Self::new(); | |
| if let Some(value) = self.get() { | |
| match cell.set(value.clone()) { | |
| Ok(()) => (), | |
| Err(_) => unreachable!(), | |
| } | |
| } | |
| cell | |
| } |
Lines 152 to 158 in adf1688
| fn from(value: T) -> Self { | |
| let cell = Self::new(); | |
| match cell.set(value) { | |
| Ok(()) => cell, | |
| Err(_) => unreachable!(), | |
| } | |
| } |
rust/library/std/src/sys/sgx/abi/mod.rs
Lines 51 to 52 in 17f30e5
| // Initialization is done. | |
| Err(DONE) => {} |
rust/library/std/src/sync/mpsc/oneshot.rs
Lines 105 to 106 in 673d0db
| // Not possible, these are one-use channels | |
| DATA => unreachable!(), |
rust/library/std/src/sync/mpsc/oneshot.rs
Lines 170 to 173 in 673d0db
| match (&mut *self.data.get()).take() { | |
| Some(data) => Ok(data), | |
| None => unreachable!(), | |
| } |
rust/library/std/src/sync/mpsc/oneshot.rs
Lines 188 to 190 in 673d0db
| // We are the sole receiver; there cannot be a blocking | |
| // receiver already. | |
| _ => unreachable!(), |
rust/library/std/src/sync/mpsc/oneshot.rs
Lines 251 to 252 in 673d0db
| // We're the only ones that can block on this port | |
| _ => unreachable!(), |
rust/library/std/src/sync/mpsc/oneshot.rs
Lines 278 to 281 in 673d0db
| // Now that we've got ownership of our state, figure out what to do | |
| // about it. | |
| match state { | |
| EMPTY => unreachable!(), |
rust/library/std/src/sys/sgx/mutex.rs
Lines 113 to 118 in a28109a
| // There was a thread waiting, just pass the lock | |
| if let NotifiedTcs::Single(tcs) = guard.notified_tcs() { | |
| guard.lock_var_mut().owner = Some(tcs) | |
| } else { | |
| unreachable!() // called notify_one | |
| } |
rust/library/core/src/iter/traits/iterator.rs
Lines 3444 to 3449 in 3ed6c1d
| unsafe fn __iterator_get_unchecked(&mut self, _idx: usize) -> Self::Item | |
| where | |
| Self: TrustedRandomAccessNoCoerce, | |
| { | |
| unreachable!("Always specialized"); | |
| } |
rust/library/std/src/sys/sgx/rwlock.rs
Lines 134 to 139 in c5fbcd3
| // One or more readers were waiting, pass the lock to them | |
| if let NotifiedTcs::All { count } = rguard.notified_tcs() { | |
| *rguard.lock_var_mut() = Some(count) | |
| } else { | |
| unreachable!() // called notify_all | |
| } |
rust/library/core/src/iter/adapters/zip.rs
Lines 182 to 186 in a49e38e
| match (self.a.next_back(), self.b.next_back()) { | |
| (Some(x), Some(y)) => Some((x, y)), | |
| (None, None) => None, | |
| _ => unreachable!(), | |
| } |
rust/library/core/src/iter/adapters/zip.rs
Lines 219 to 224 in a49e38e
| default unsafe fn get_unchecked(&mut self, _idx: usize) -> <Self as Iterator>::Item | |
| where | |
| Self: TrustedRandomAccessNoCoerce, | |
| { | |
| unreachable!("Always specialized"); | |
| } |
rust/library/std/src/sync/mpsc/stream.rs
Lines 429 to 444 in fe1c942
| // Now that we've determined that this queue "has data", we peek at the | |
| // queue to see if the data is an upgrade or not. If it's an upgrade, | |
| // then we need to destroy this port and abort selection on the | |
| // upgraded port. | |
| if has_data { | |
| match self.queue.peek() { | |
| Some(&mut GoUp(..)) => match self.queue.pop() { | |
| Some(GoUp(port)) => Err(port), | |
| _ => unreachable!(), | |
| }, | |
| _ => Ok(true), | |
| } | |
| } else { | |
| Ok(false) | |
| } | |
| } |
rust/library/std/src/sync/mpsc/shared.rs
Lines 338 to 343 in 673d0db
| match self.queue.pop() { | |
| mpsc::Data(t) => Ok(t), | |
| mpsc::Empty => Err(Disconnected), | |
| // with no senders, an inconsistency is impossible. | |
| mpsc::Inconsistent => unreachable!(), | |
| } |
rust/library/std/src/sys/unix/kernel_copy.rs
Lines 201 to 207 in dfd7b8d
| match result { | |
| CopyResult::Ended(bytes_copied) => return Ok(bytes_copied + written), | |
| CopyResult::Error(e, _) => return Err(e), | |
| CopyResult::Fallback(0) => { /* use the fallback below */ } | |
| CopyResult::Fallback(_) => { | |
| unreachable!("splice should not return > 0 bytes on the fallback path") | |
| } |
Line 912 in 2ad56d5
| State::Done => unreachable!(), |
rust/library/std/src/net/ip.rs
Lines 1679 to 1684 in a49e38e
| match segments[5] { | |
| // IPv4 Compatible address | |
| 0 => write!(f, "::{}", ipv4), | |
| // IPv4 Mapped address | |
| 0xffff => write!(f, "::ffff:{}", ipv4), | |
| _ => unreachable!(), |
rust/library/alloc/src/collections/btree/split.rs
Lines 49 to 56 in 673d0db
| match (split_edge.force(), right_node.force()) { | |
| (Internal(edge), Internal(node)) => { | |
| left_node = edge.descend(); | |
| right_node = node.first_edge().descend(); | |
| } | |
| (Leaf(_), Leaf(_)) => break, | |
| _ => unreachable!(), | |
| } |
rust/library/alloc/src/collections/btree/map.rs
Lines 184 to 187 in 3ed6c1d
| let mut out_node = match root.borrow_mut().force() { | |
| Leaf(leaf) => leaf, | |
| Internal(_) => unreachable!(), | |
| }; |
rust/library/alloc/src/collections/btree/node.rs
Line 1222 in 23461b2
| Err(_) => unreachable!("empty internal node"), |
rust/library/alloc/src/collections/btree/node.rs
Lines 1424 to 1439 in 23461b2
| match (left_node.reborrow_mut().force(), right_node.reborrow_mut().force()) { | |
| (ForceResult::Internal(mut left), ForceResult::Internal(mut right)) => { | |
| // Make room for stolen edges. | |
| slice_shr(right.edge_area_mut(..new_right_len + 1), count); | |
| // Steal edges. | |
| move_to_slice( | |
| left.edge_area_mut(new_left_len + 1..old_left_len + 1), | |
| right.edge_area_mut(..count), | |
| ); | |
| right.correct_childrens_parent_links(0..new_right_len + 1); | |
| } | |
| (ForceResult::Leaf(_), ForceResult::Leaf(_)) => {} | |
| _ => unreachable!(), | |
| } |
rust/library/alloc/src/collections/btree/node.rs
Lines 1487 to 1503 in 23461b2
| match (left_node.reborrow_mut().force(), right_node.reborrow_mut().force()) { | |
| (ForceResult::Internal(mut left), ForceResult::Internal(mut right)) => { | |
| // Steal edges. | |
| move_to_slice( | |
| right.edge_area_mut(..count), | |
| left.edge_area_mut(old_left_len + 1..new_left_len + 1), | |
| ); | |
| // Fill gap where stolen edges used to be. | |
| slice_shl(right.edge_area_mut(..old_right_len + 1), count); | |
| left.correct_childrens_parent_links(old_left_len + 1..new_left_len + 1); | |
| right.correct_childrens_parent_links(0..new_right_len + 1); | |
| } | |
| (ForceResult::Leaf(_), ForceResult::Leaf(_)) => {} | |
| _ => unreachable!(), | |
| } |
rust/library/alloc/src/collections/btree/node.rs
Lines 1599 to 1609 in 23461b2
| match (left_node.force(), right_node.force()) { | |
| (ForceResult::Internal(mut left), ForceResult::Internal(mut right)) => { | |
| move_to_slice( | |
| left.edge_area_mut(new_left_len + 1..old_left_len + 1), | |
| right.edge_area_mut(1..new_right_len + 1), | |
| ); | |
| right.correct_childrens_parent_links(1..new_right_len + 1); | |
| } | |
| (ForceResult::Leaf(_), ForceResult::Leaf(_)) => {} | |
| _ => unreachable!(), | |
| } |
rust/library/std/src/sync/mpsc/sync.rs
Lines 111 to 114 in 673d0db
| match mem::replace(&mut guard.blocker, f(signal_token)) { | |
| NoneBlocked => {} | |
| _ => unreachable!(), | |
| } |
rust/library/std/src/sync/mpsc/mod.rs
Line 828 in 607d6b0
| Flavor::Sync(..) => unreachable!(), |
rust/library/std/src/sync/mpsc/sync.rs
Line 243 in 673d0db
| BlockedSender(..) => unreachable!(), |
rust/library/std/src/sync/mpsc/sync.rs
Lines 328 to 342 in 673d0db
| // If this is a no-buffer channel (cap == 0), then if we didn't wait we | |
| // need to ACK the sender. If we waited, then the sender waking us up | |
| // was already the ACK. | |
| let pending_sender2 = if guard.cap == 0 && !waited { | |
| match mem::replace(&mut guard.blocker, NoneBlocked) { | |
| NoneBlocked => None, | |
| BlockedReceiver(..) => unreachable!(), | |
| BlockedSender(token) => { | |
| guard.canceled.take(); | |
| Some(token) | |
| } | |
| } | |
| } else { | |
| None | |
| }; |
rust/library/std/src/sync/mpsc/sync.rs
Lines 378 to 382 in 673d0db
| match mem::replace(&mut guard.blocker, NoneBlocked) { | |
| NoneBlocked => {} | |
| BlockedSender(..) => unreachable!(), | |
| BlockedReceiver(token) => wakeup(token, guard), | |
| } |
rust/library/std/src/sync/mpsc/sync.rs
Lines 402 to 409 in 673d0db
| let waiter = match mem::replace(&mut guard.blocker, NoneBlocked) { | |
| NoneBlocked => None, | |
| BlockedSender(token) => { | |
| *guard.canceled.take().unwrap() = true; | |
| Some(token) | |
| } | |
| BlockedReceiver(..) => unreachable!(), | |
| }; |
rust/library/std/src/sync/mpsc/mod.rs
Lines 1157 to 1176 in 607d6b0
| let new_port = match *unsafe { self.inner() } { | |
| Flavor::Oneshot(ref p) => match p.recv(None) { | |
| Ok(t) => return Ok(t), | |
| Err(oneshot::Disconnected) => return Err(RecvError), | |
| Err(oneshot::Upgraded(rx)) => rx, | |
| Err(oneshot::Empty) => unreachable!(), | |
| }, | |
| Flavor::Stream(ref p) => match p.recv(None) { | |
| Ok(t) => return Ok(t), | |
| Err(stream::Disconnected) => return Err(RecvError), | |
| Err(stream::Upgraded(rx)) => rx, | |
| Err(stream::Empty) => unreachable!(), | |
| }, | |
| Flavor::Shared(ref p) => match p.recv(None) { | |
| Ok(t) => return Ok(t), | |
| Err(shared::Disconnected) => return Err(RecvError), | |
| Err(shared::Empty) => unreachable!(), | |
| }, | |
| Flavor::Sync(ref p) => return p.recv(None).map_err(|_| RecvError), | |
| }; |
Must Remain unreachable
None so far.
Action
I would love to send in a PR that resolves this issue! However, I do need some help and guidance from the library maintainers to ensure that undefined behavior is 110% avoided. Feedback on which parts are safe regardless of platform/architecture/hardware is much appreciated.
As mentioned earlier, I will be editing this issue as more people chime in. Thanks!