diff --git a/Cargo.lock b/Cargo.lock index 90bcc2e4be42a..4e0e72d34153f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1656,12 +1656,13 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.0" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab7905ea95c6d9af62940f9d7dd9596d54c334ae2c15300c482051292d5637f" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "compiler_builtins", "libc", + "rustc-std-workspace-alloc", "rustc-std-workspace-core", ] @@ -4608,7 +4609,7 @@ dependencies = [ "dlmalloc", "fortanix-sgx-abi", "hashbrown", - "hermit-abi 0.2.0", + "hermit-abi 0.2.6", "libc", "miniz_oxide 0.4.0", "object 0.26.2", diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index a57fdc3bfb125..0de99f7a3dbbd 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2401,6 +2401,14 @@ impl<'hir> Ty<'hir> { _ => None, } } + + pub fn peel_refs(&self) -> &Self { + let mut final_ty = self; + while let TyKind::Rptr(_, MutTy { ty, .. }) = &final_ty.kind { + final_ty = &ty; + } + final_ty + } } /// Not represented directly in the AST; referred to by name through a `ty_path`. diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 0e6a8ef8265b2..2139200136413 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1305,31 +1305,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn suggest_array_len(&self, expr: &'tcx hir::Expr<'tcx>, array_len: u64) { - if let Some(parent_hir_id) = self.tcx.hir().find_parent_node(expr.hir_id) { - let ty = match self.tcx.hir().find(parent_hir_id) { - Some( - hir::Node::Local(hir::Local { ty: Some(ty), .. }) - | hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. }), - ) => Some(ty), - _ => None, - }; - if let Some(ty) = ty - && let hir::TyKind::Array(_, length) = ty.kind - && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length - && let Some(span) = self.tcx.hir().opt_span(hir_id) - { - match self.tcx.sess.diagnostic().steal_diagnostic(span, StashKey::UnderscoreForArrayLengths) { - Some(mut err) => { - err.span_suggestion( - span, - "consider specifying the array length", - array_len, - Applicability::MaybeIncorrect, - ); - err.emit(); - } - None => () + let parent_node = self.tcx.hir().parent_iter(expr.hir_id).find(|(_, node)| { + !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::AddrOf(..), .. })) + }); + let Some((_, + hir::Node::Local(hir::Local { ty: Some(ty), .. }) + | hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. })) + ) = parent_node else { + return + }; + if let hir::TyKind::Array(_, length) = ty.peel_refs().kind + && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length + && let Some(span) = self.tcx.hir().opt_span(hir_id) + { + match self.tcx.sess.diagnostic().steal_diagnostic(span, StashKey::UnderscoreForArrayLengths) { + Some(mut err) => { + err.span_suggestion( + span, + "consider specifying the array length", + array_len, + Applicability::MaybeIncorrect, + ); + err.emit(); } + None => () } } } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 9d3e9abf55779..905212eb372b1 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -1819,7 +1819,7 @@ impl<'a> Formatter<'a> { /// write!(formatter, /// "Foo({}{})", /// if self.0 < 0 { '-' } else { '+' }, - /// self.0) + /// self.0.abs()) /// } else { /// write!(formatter, "Foo({})", self.0) /// } @@ -1827,6 +1827,7 @@ impl<'a> Formatter<'a> { /// } /// /// assert_eq!(&format!("{:+}", Foo(23)), "Foo(+23)"); + /// assert_eq!(&format!("{:+}", Foo(-23)), "Foo(-23)"); /// assert_eq!(&format!("{}", Foo(23)), "Foo(23)"); /// ``` #[must_use] diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 20340d42962ef..764e2796202c9 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -160,19 +160,16 @@ pub const unsafe fn unreachable_unchecked() -> ! { #[inline] #[stable(feature = "renamed_spin_loop", since = "1.49.0")] pub fn spin_loop() { - #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "sse2"))] + #[cfg(target_arch = "x86")] { - #[cfg(target_arch = "x86")] - { - // SAFETY: the `cfg` attr ensures that we only execute this on x86 targets. - unsafe { crate::arch::x86::_mm_pause() }; - } + // SAFETY: the `cfg` attr ensures that we only execute this on x86 targets. + unsafe { crate::arch::x86::_mm_pause() }; + } - #[cfg(target_arch = "x86_64")] - { - // SAFETY: the `cfg` attr ensures that we only execute this on x86_64 targets. - unsafe { crate::arch::x86_64::_mm_pause() }; - } + #[cfg(target_arch = "x86_64")] + { + // SAFETY: the `cfg` attr ensures that we only execute this on x86_64 targets. + unsafe { crate::arch::x86_64::_mm_pause() }; } // RISC-V platform spin loop hint implementation diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 3da9565a86d78..324ecc8047730 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -42,7 +42,7 @@ dlmalloc = { version = "0.2.3", features = ['rustc-dep-of-std'] } fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'] } [target.'cfg(target_os = "hermit")'.dependencies] -hermit-abi = { version = "0.2.0", features = ['rustc-dep-of-std'] } +hermit-abi = { version = "0.2.6", features = ['rustc-dep-of-std'] } [target.wasm32-wasi.dependencies] wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false } diff --git a/library/std/src/sys/hermit/condvar.rs b/library/std/src/sys/hermit/condvar.rs deleted file mode 100644 index 22059ca0dbe10..0000000000000 --- a/library/std/src/sys/hermit/condvar.rs +++ /dev/null @@ -1,90 +0,0 @@ -use crate::ffi::c_void; -use crate::ptr; -use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; -use crate::sys::hermit::abi; -use crate::sys::locks::Mutex; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; -use crate::time::Duration; - -// The implementation is inspired by Andrew D. Birrell's paper -// "Implementing Condition Variables with Semaphores" - -pub struct Condvar { - counter: AtomicUsize, - sem1: *const c_void, - sem2: *const c_void, -} - -pub(crate) type MovableCondvar = LazyBox; - -impl LazyInit for Condvar { - fn init() -> Box { - Box::new(Self::new()) - } -} - -unsafe impl Send for Condvar {} -unsafe impl Sync for Condvar {} - -impl Condvar { - pub fn new() -> Self { - let mut condvar = - Self { counter: AtomicUsize::new(0), sem1: ptr::null(), sem2: ptr::null() }; - unsafe { - let _ = abi::sem_init(&mut condvar.sem1, 0); - let _ = abi::sem_init(&mut condvar.sem2, 0); - } - condvar - } - - pub unsafe fn notify_one(&self) { - if self.counter.load(SeqCst) > 0 { - self.counter.fetch_sub(1, SeqCst); - abi::sem_post(self.sem1); - abi::sem_timedwait(self.sem2, 0); - } - } - - pub unsafe fn notify_all(&self) { - let counter = self.counter.swap(0, SeqCst); - for _ in 0..counter { - abi::sem_post(self.sem1); - } - for _ in 0..counter { - abi::sem_timedwait(self.sem2, 0); - } - } - - pub unsafe fn wait(&self, mutex: &Mutex) { - self.counter.fetch_add(1, SeqCst); - mutex.unlock(); - abi::sem_timedwait(self.sem1, 0); - abi::sem_post(self.sem2); - mutex.lock(); - } - - pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - self.counter.fetch_add(1, SeqCst); - mutex.unlock(); - let millis = dur.as_millis().min(u32::MAX as u128) as u32; - - let res = if millis > 0 { - abi::sem_timedwait(self.sem1, millis) - } else { - abi::sem_trywait(self.sem1) - }; - - abi::sem_post(self.sem2); - mutex.lock(); - res == 0 - } -} - -impl Drop for Condvar { - fn drop(&mut self) { - unsafe { - let _ = abi::sem_destroy(self.sem1); - let _ = abi::sem_destroy(self.sem2); - } - } -} diff --git a/library/std/src/sys/hermit/futex.rs b/library/std/src/sys/hermit/futex.rs new file mode 100644 index 0000000000000..b64c174b06c6d --- /dev/null +++ b/library/std/src/sys/hermit/futex.rs @@ -0,0 +1,39 @@ +use super::abi; +use crate::ptr::null; +use crate::sync::atomic::AtomicU32; +use crate::time::Duration; + +pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option) -> bool { + // Calculate the timeout as a relative timespec. + // + // Overflows are rounded up to an infinite timeout (None). + let timespec = timeout.and_then(|dur| { + Some(abi::timespec { + tv_sec: dur.as_secs().try_into().ok()?, + tv_nsec: dur.subsec_nanos().into(), + }) + }); + + let r = unsafe { + abi::futex_wait( + futex.as_mut_ptr(), + expected, + timespec.as_ref().map_or(null(), |t| t as *const abi::timespec), + abi::FUTEX_RELATIVE_TIMEOUT, + ) + }; + + r != -abi::errno::ETIMEDOUT +} + +#[inline] +pub fn futex_wake(futex: &AtomicU32) -> bool { + unsafe { abi::futex_wake(futex.as_mut_ptr(), 1) > 0 } +} + +#[inline] +pub fn futex_wake_all(futex: &AtomicU32) { + unsafe { + abi::futex_wake(futex.as_mut_ptr(), i32::MAX); + } +} diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index 61da096ae1638..827d82900eae4 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -25,6 +25,7 @@ pub mod cmath; pub mod env; pub mod fd; pub mod fs; +pub mod futex; #[path = "../unsupported/io.rs"] pub mod io; pub mod memchr; @@ -45,14 +46,14 @@ pub mod thread_local_dtor; pub mod thread_local_key; pub mod time; -mod condvar; -mod mutex; -mod rwlock; - +#[path = "../unix/locks"] pub mod locks { - pub use super::condvar::*; - pub use super::mutex::*; - pub use super::rwlock::*; + mod futex_condvar; + mod futex_mutex; + mod futex_rwlock; + pub(crate) use futex_condvar::MovableCondvar; + pub(crate) use futex_mutex::{MovableMutex, Mutex}; + pub(crate) use futex_rwlock::{MovableRwLock, RwLock}; } use crate::io::ErrorKind; diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs deleted file mode 100644 index 30c8fc4562d52..0000000000000 --- a/library/std/src/sys/hermit/mutex.rs +++ /dev/null @@ -1,212 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::collections::VecDeque; -use crate::hint; -use crate::ops::{Deref, DerefMut, Drop}; -use crate::sync::atomic::{AtomicUsize, Ordering}; -use crate::sys::hermit::abi; - -/// This type provides a lock based on busy waiting to realize mutual exclusion -/// -/// # Description -/// -/// This structure behaves a lot like a common mutex. There are some differences: -/// -/// - By using busy waiting, it can be used outside the runtime. -/// - It is a so called ticket lock and is completely fair. -#[cfg_attr(target_arch = "x86_64", repr(align(128)))] -#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))] -struct Spinlock { - queue: AtomicUsize, - dequeue: AtomicUsize, - data: UnsafeCell, -} - -unsafe impl Sync for Spinlock {} -unsafe impl Send for Spinlock {} - -/// A guard to which the protected data can be accessed -/// -/// When the guard falls out of scope it will release the lock. -struct SpinlockGuard<'a, T: ?Sized + 'a> { - dequeue: &'a AtomicUsize, - data: &'a mut T, -} - -impl Spinlock { - pub const fn new(user_data: T) -> Spinlock { - Spinlock { - queue: AtomicUsize::new(0), - dequeue: AtomicUsize::new(1), - data: UnsafeCell::new(user_data), - } - } - - #[inline] - fn obtain_lock(&self) { - let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1; - let mut counter: u16 = 0; - while self.dequeue.load(Ordering::SeqCst) != ticket { - counter += 1; - if counter < 100 { - hint::spin_loop(); - } else { - counter = 0; - unsafe { - abi::yield_now(); - } - } - } - } - - #[inline] - pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> { - self.obtain_lock(); - SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() } - } -} - -impl Default for Spinlock { - fn default() -> Spinlock { - Spinlock::new(Default::default()) - } -} - -impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> { - type Target = T; - fn deref(&self) -> &T { - &*self.data - } -} - -impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> { - fn deref_mut(&mut self) -> &mut T { - &mut *self.data - } -} - -impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> { - /// The dropping of the SpinlockGuard will release the lock it was created from. - fn drop(&mut self) { - self.dequeue.fetch_add(1, Ordering::SeqCst); - } -} - -/// Realize a priority queue for tasks -struct PriorityQueue { - queues: [Option>; abi::NO_PRIORITIES], - prio_bitmap: u64, -} - -impl PriorityQueue { - pub const fn new() -> PriorityQueue { - PriorityQueue { - queues: [ - None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, - ], - prio_bitmap: 0, - } - } - - /// Add a task id by its priority to the queue - pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) { - let i: usize = prio.into().into(); - self.prio_bitmap |= (1 << i) as u64; - if let Some(queue) = &mut self.queues[i] { - queue.push_back(id); - } else { - let mut queue = VecDeque::new(); - queue.push_back(id); - self.queues[i] = Some(queue); - } - } - - fn pop_from_queue(&mut self, queue_index: usize) -> Option { - if let Some(queue) = &mut self.queues[queue_index] { - let id = queue.pop_front(); - - if queue.is_empty() { - self.prio_bitmap &= !(1 << queue_index as u64); - } - - id - } else { - None - } - } - - /// Pop the task handle with the highest priority from the queue - pub fn pop(&mut self) -> Option { - for i in 0..abi::NO_PRIORITIES { - if self.prio_bitmap & (1 << i) != 0 { - return self.pop_from_queue(i); - } - } - - None - } -} - -struct MutexInner { - locked: bool, - blocked_task: PriorityQueue, -} - -impl MutexInner { - pub const fn new() -> MutexInner { - MutexInner { locked: false, blocked_task: PriorityQueue::new() } - } -} - -pub struct Mutex { - inner: Spinlock, -} - -pub type MovableMutex = Mutex; - -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} - -impl Mutex { - pub const fn new() -> Mutex { - Mutex { inner: Spinlock::new(MutexInner::new()) } - } - - #[inline] - pub unsafe fn lock(&self) { - loop { - let mut guard = self.inner.lock(); - if guard.locked == false { - guard.locked = true; - return; - } else { - let prio = abi::get_priority(); - let id = abi::getpid(); - - guard.blocked_task.push(prio, id); - abi::block_current_task(); - drop(guard); - abi::yield_now(); - } - } - } - - #[inline] - pub unsafe fn unlock(&self) { - let mut guard = self.inner.lock(); - guard.locked = false; - if let Some(tid) = guard.blocked_task.pop() { - abi::wakeup_task(tid); - } - } - - #[inline] - pub unsafe fn try_lock(&self) -> bool { - let mut guard = self.inner.lock(); - if guard.locked == false { - guard.locked = true; - } - guard.locked - } -} diff --git a/library/std/src/sys/hermit/rwlock.rs b/library/std/src/sys/hermit/rwlock.rs deleted file mode 100644 index 1adf0b2be6b73..0000000000000 --- a/library/std/src/sys/hermit/rwlock.rs +++ /dev/null @@ -1,143 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::sys::locks::{MovableCondvar, Mutex}; - -pub struct RwLock { - lock: Mutex, - cond: MovableCondvar, - state: UnsafeCell, -} - -pub type MovableRwLock = RwLock; - -enum State { - Unlocked, - Reading(usize), - Writing, -} - -unsafe impl Send for RwLock {} -unsafe impl Sync for RwLock {} - -// This rwlock implementation is a relatively simple implementation which has a -// condition variable for readers/writers as well as a mutex protecting the -// internal state of the lock. A current downside of the implementation is that -// unlocking the lock will notify *all* waiters rather than just readers or just -// writers. This can cause lots of "thundering stampede" problems. While -// hopefully correct this implementation is very likely to want to be changed in -// the future. - -impl RwLock { - pub const fn new() -> RwLock { - RwLock { - lock: Mutex::new(), - cond: MovableCondvar::new(), - state: UnsafeCell::new(State::Unlocked), - } - } - - #[inline] - pub unsafe fn read(&self) { - self.lock.lock(); - while !(*self.state.get()).inc_readers() { - self.cond.wait(&self.lock); - } - self.lock.unlock(); - } - - #[inline] - pub unsafe fn try_read(&self) -> bool { - self.lock.lock(); - let ok = (*self.state.get()).inc_readers(); - self.lock.unlock(); - return ok; - } - - #[inline] - pub unsafe fn write(&self) { - self.lock.lock(); - while !(*self.state.get()).inc_writers() { - self.cond.wait(&self.lock); - } - self.lock.unlock(); - } - - #[inline] - pub unsafe fn try_write(&self) -> bool { - self.lock.lock(); - let ok = (*self.state.get()).inc_writers(); - self.lock.unlock(); - return ok; - } - - #[inline] - pub unsafe fn read_unlock(&self) { - self.lock.lock(); - let notify = (*self.state.get()).dec_readers(); - self.lock.unlock(); - if notify { - // FIXME: should only wake up one of these some of the time - self.cond.notify_all(); - } - } - - #[inline] - pub unsafe fn write_unlock(&self) { - self.lock.lock(); - (*self.state.get()).dec_writers(); - self.lock.unlock(); - // FIXME: should only wake up one of these some of the time - self.cond.notify_all(); - } -} - -impl State { - fn inc_readers(&mut self) -> bool { - match *self { - State::Unlocked => { - *self = State::Reading(1); - true - } - State::Reading(ref mut cnt) => { - *cnt += 1; - true - } - State::Writing => false, - } - } - - fn inc_writers(&mut self) -> bool { - match *self { - State::Unlocked => { - *self = State::Writing; - true - } - State::Reading(_) | State::Writing => false, - } - } - - fn dec_readers(&mut self) -> bool { - let zero = match *self { - State::Reading(ref mut cnt) => { - *cnt -= 1; - *cnt == 0 - } - State::Unlocked | State::Writing => invalid(), - }; - if zero { - *self = State::Unlocked; - } - zero - } - - fn dec_writers(&mut self) { - match *self { - State::Writing => {} - State::Unlocked | State::Reading(_) => invalid(), - } - *self = State::Unlocked; - } -} - -fn invalid() -> ! { - panic!("inconsistent rwlock"); -} diff --git a/library/std/src/sys_common/thread_parker/mod.rs b/library/std/src/sys_common/thread_parker/mod.rs index cbd7832eb7a4c..f86a9a555d322 100644 --- a/library/std/src/sys_common/thread_parker/mod.rs +++ b/library/std/src/sys_common/thread_parker/mod.rs @@ -7,6 +7,7 @@ cfg_if::cfg_if! { target_os = "openbsd", target_os = "dragonfly", target_os = "fuchsia", + target_os = "hermit", ))] { mod futex; pub use futex::Parker; diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index bb8e46af76286..84781d8983859 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -52,35 +52,14 @@ pub(crate) fn render_example_with_highlighting( tooltip: Tooltip, playground_button: Option<&str>, ) { - let class = match tooltip { - Tooltip::Ignore => " ignore", - Tooltip::CompileFail => " compile_fail", - Tooltip::ShouldPanic => " should_panic", - Tooltip::Edition(_) => " edition", - Tooltip::None => "", - }; - - if tooltip != Tooltip::None { - write!( - out, - "
", - class, - if let Tooltip::Edition(edition_info) = tooltip { - format!(" data-edition=\"{}\"", edition_info) - } else { - String::new() - }, - ); - } - - write_header(out, &format!("rust-example-rendered{}", class), None); + write_header(out, "rust-example-rendered", None, tooltip); write_code(out, src, None, None); write_footer(out, playground_button); } /// Highlights `src` as a macro, returning the HTML output. pub(crate) fn render_macro_with_highlighting(src: &str, out: &mut Buffer) { - write_header(out, "macro", None); + write_header(out, "macro", None, Tooltip::None); write_code(out, src, None, None); write_footer(out, None); } @@ -93,20 +72,42 @@ pub(crate) fn render_source_with_highlighting( href_context: HrefContext<'_, '_, '_>, decoration_info: DecorationInfo, ) { - write_header(out, "", Some(line_numbers)); + write_header(out, "", Some(line_numbers), Tooltip::None); write_code(out, src, Some(href_context), Some(decoration_info)); write_footer(out, None); } -fn write_header(out: &mut Buffer, class: &str, extra_content: Option) { +fn write_header(out: &mut Buffer, class: &str, extra_content: Option, tooltip: Tooltip) { write!(out, "
"); + + let tooltip_class = match tooltip { + Tooltip::Ignore => " ignore", + Tooltip::CompileFail => " compile_fail", + Tooltip::ShouldPanic => " should_panic", + Tooltip::Edition(_) => " edition", + Tooltip::None => "", + }; + + if tooltip != Tooltip::None { + write!( + out, + "
", + tooltip_class, + if let Tooltip::Edition(edition_info) = tooltip { + format!(" data-edition=\"{}\"", edition_info) + } else { + String::new() + }, + ); + } + if let Some(extra) = extra_content { out.push_buffer(extra); } - if class.is_empty() { + if class.is_empty() && tooltip_class.is_empty() { write!(out, "
");
     } else {
-        write!(out, "
", class);
+        write!(out, "
");
     }
     write!(out, "");
 }
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 22217a3901246..ec9e3b1ecd17b 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -347,10 +347,6 @@ img {
 	max-width: 100%;
 }
 
-li {
-	position: relative;
-}
-
 .source .content {
 	max-width: none;
 	overflow: visible;
@@ -652,7 +648,7 @@ h2.location a {
 	position: relative;
 }
 
-.docblock > :not(.information):not(.more-examples-toggle) {
+.docblock > :not(.more-examples-toggle):not(.example-wrap) {
 	max-width: 100%;
 	overflow-x: auto;
 }
@@ -1169,12 +1165,12 @@ pre.ignore {
 	border-left: 2px solid var(--codeblock-ignore-color);
 }
 
-pre.compile_fail:hover, .information:hover + .example-wrap pre.compile_fail,
-pre.should_panic:hover, .information:hover + .example-wrap pre.should_panic {
+.example-wrap:hover pre.compile_fail,
+.example-wrap:hover pre.should_panic {
 	border-left: 2px solid var(--codeblock-error-hover-color);
 }
 
-pre.ignore:hover, .information:hover + .example-wrap pre.ignore {
+.example-wrap:hover pre.ignore {
 	border-left: 2px solid var(--codeblock-ignore-hover-color);
 }
 
@@ -1187,12 +1183,12 @@ pre.ignore:hover, .information:hover + .example-wrap pre.ignore {
 	color:  var(--codeblock-ignore-color);
 }
 
-.information > .compile_fail:hover,
-.information > .should_panic:hover {
+.example-wrap:hover .tooltip.compile_fail,
+.example-wrap:hover .tooltip.should_panic {
 	color: var(--codeblock-error-hover-color);
 }
 
-.information > .ignore:hover {
+.example-wrap:hover .tooltip.ignore {
 	color: var(--codeblock-ignore-hover-color);
 }
 
@@ -1727,7 +1723,7 @@ in storage.js plus the media query with (max-width: 700px)
 	to prevent an overlay between the "collapse toggle" and the information tooltip.
 	However, it's not needed with smaller screen width because the doc/code block is always put
 	"one line" below. */
-	.docblock > .information:first-child > .tooltip {
+	.docblock > .example-wrap:first-child > .information > .tooltip {
 		margin-top: 16px;
 	}
 
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 7f61c95e794b0..6e9660ddcc96a 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -699,9 +699,8 @@ function loadCss(cssFileName) {
 
     (function() {
         // To avoid checking on "rustdoc-line-numbers" value on every loop...
-        let lineNumbersFunc = () => {};
         if (getSettingValue("line-numbers") === "true") {
-            lineNumbersFunc = x => {
+            onEachLazy(document.getElementsByClassName("rust-example-rendered"), x => {
                 const count = x.textContent.split("\n").length;
                 const elems = [];
                 for (let i = 0; i < count; ++i) {
@@ -711,26 +710,8 @@ function loadCss(cssFileName) {
                 addClass(node, "line-number");
                 node.innerHTML = elems.join("\n");
                 x.parentNode.insertBefore(node, x);
-            };
+            });
         }
-        onEachLazy(document.getElementsByClassName("rust-example-rendered"), e => {
-            if (hasClass(e, "compile_fail")) {
-                e.addEventListener("mouseover", function() {
-                    this.parentElement.previousElementSibling.childNodes[0].style.color = "#f00";
-                });
-                e.addEventListener("mouseout", function() {
-                    this.parentElement.previousElementSibling.childNodes[0].style.color = "";
-                });
-            } else if (hasClass(e, "ignore")) {
-                e.addEventListener("mouseover", function() {
-                    this.parentElement.previousElementSibling.childNodes[0].style.color = "#ff9200";
-                });
-                e.addEventListener("mouseout", function() {
-                    this.parentElement.previousElementSibling.childNodes[0].style.color = "";
-                });
-            }
-            lineNumbersFunc(e);
-        });
     }());
 
     let oldSidebarScrollPosition = null;
diff --git a/src/test/rustdoc-gui/check_info_sign_position.goml b/src/test/rustdoc-gui/check_info_sign_position.goml
index 3bed7a0a03ea5..c249895503a91 100644
--- a/src/test/rustdoc-gui/check_info_sign_position.goml
+++ b/src/test/rustdoc-gui/check_info_sign_position.goml
@@ -4,8 +4,8 @@ goto: file://|DOC_PATH|/test_docs/index.html
 goto: ./fn.check_list_code_block.html
 // If the codeblock is the first element of the docblock, the information tooltip must have
 // have some top margin to avoid going over the toggle (the "[+]").
-assert-css: (".docblock > .information > .compile_fail", { "margin-top": "16px" })
+assert-css: (".docblock > .example-wrap > .information > .compile_fail", { "margin-top": "16px" })
 // Checks that the other codeblocks don't have this top margin.
-assert-css: ("ol > li > .information > .compile_fail", { "margin-top": "0px" })
-assert-css: ("ol > li > .information > .ignore", { "margin-top": "0px" })
-assert-css: (".docblock > .information > .ignore", { "margin-top": "0px" })
+assert-css: ("ol > li > .example-wrap > .information > .compile_fail", { "margin-top": "0px" })
+assert-css: ("ol > li > .example-wrap > .information > .ignore", { "margin-top": "0px" })
+assert-css: (".docblock > .example-wrap > .information > .ignore", { "margin-top": "0px" })
diff --git a/src/test/rustdoc-gui/codeblock-tooltip.goml b/src/test/rustdoc-gui/codeblock-tooltip.goml
index a0bb40fce8e72..4e85c33c8944e 100644
--- a/src/test/rustdoc-gui/codeblock-tooltip.goml
+++ b/src/test/rustdoc-gui/codeblock-tooltip.goml
@@ -8,30 +8,30 @@ reload:
 
 // compile_fail block
 assert-css: (".docblock .information .compile_fail", {"color": "rgba(255, 0, 0, 0.5)"})
-assert-css: (".docblock .example-wrap .compile_fail", {"border-left": "2px solid rgba(255, 0, 0, 0.5)"})
+assert-css: (".docblock .example-wrap pre.compile_fail", {"border-left": "2px solid rgba(255, 0, 0, 0.5)"})
 
 move-cursor-to: ".docblock .information .compile_fail"
 
 assert-css: (".docblock .information .compile_fail", {"color": "rgb(255, 0, 0)"})
-assert-css: (".docblock .example-wrap .compile_fail", {"border-left": "2px solid rgb(255, 0, 0)"})
+assert-css: (".docblock .example-wrap pre.compile_fail", {"border-left": "2px solid rgb(255, 0, 0)"})
 
 // should_panic block
 assert-css: (".docblock .information .should_panic", {"color": "rgba(255, 0, 0, 0.5)"})
-assert-css: (".docblock .example-wrap .should_panic", {"border-left": "2px solid rgba(255, 0, 0, 0.5)"})
+assert-css: (".docblock .example-wrap pre.should_panic", {"border-left": "2px solid rgba(255, 0, 0, 0.5)"})
 
 move-cursor-to: ".docblock .information .should_panic"
 
 assert-css: (".docblock .information .should_panic", {"color": "rgb(255, 0, 0)"})
-assert-css: (".docblock .example-wrap .should_panic", {"border-left": "2px solid rgb(255, 0, 0)"})
+assert-css: (".docblock .example-wrap pre.should_panic", {"border-left": "2px solid rgb(255, 0, 0)"})
 
 // ignore block
 assert-css: (".docblock .information .ignore", {"color": "rgba(255, 142, 0, 0.6)"})
-assert-css: (".docblock .example-wrap .ignore", {"border-left": "2px solid rgba(255, 142, 0, 0.6)"})
+assert-css: (".docblock .example-wrap pre.ignore", {"border-left": "2px solid rgba(255, 142, 0, 0.6)"})
 
 move-cursor-to: ".docblock .information .ignore"
 
 assert-css: (".docblock .information .ignore", {"color": "rgb(255, 142, 0)"})
-assert-css: (".docblock .example-wrap .ignore", {"border-left": "2px solid rgb(255, 142, 0)"})
+assert-css: (".docblock .example-wrap pre.ignore", {"border-left": "2px solid rgb(255, 142, 0)"})
 
 
 // Light theme.
@@ -39,30 +39,30 @@ local-storage: {"rustdoc-theme": "light"}
 reload:
 
 assert-css: (".docblock .information .compile_fail", {"color": "rgba(255, 0, 0, 0.5)"})
-assert-css: (".docblock .example-wrap .compile_fail", {"border-left": "2px solid rgba(255, 0, 0, 0.5)"})
+assert-css: (".docblock .example-wrap pre.compile_fail", {"border-left": "2px solid rgba(255, 0, 0, 0.5)"})
 
 move-cursor-to: ".docblock .information .compile_fail"
 
 assert-css: (".docblock .information .compile_fail", {"color": "rgb(255, 0, 0)"})
-assert-css: (".docblock .example-wrap .compile_fail", {"border-left": "2px solid rgb(255, 0, 0)"})
+assert-css: (".docblock .example-wrap pre.compile_fail", {"border-left": "2px solid rgb(255, 0, 0)"})
 
 // should_panic block
 assert-css: (".docblock .information .should_panic", {"color": "rgba(255, 0, 0, 0.5)"})
-assert-css: (".docblock .example-wrap .should_panic", {"border-left": "2px solid rgba(255, 0, 0, 0.5)"})
+assert-css: (".docblock .example-wrap pre.should_panic", {"border-left": "2px solid rgba(255, 0, 0, 0.5)"})
 
 move-cursor-to: ".docblock .information .should_panic"
 
 assert-css: (".docblock .information .should_panic", {"color": "rgb(255, 0, 0)"})
-assert-css: (".docblock .example-wrap .should_panic", {"border-left": "2px solid rgb(255, 0, 0)"})
+assert-css: (".docblock .example-wrap pre.should_panic", {"border-left": "2px solid rgb(255, 0, 0)"})
 
 // ignore block
 assert-css: (".docblock .information .ignore", {"color": "rgba(255, 142, 0, 0.6)"})
-assert-css: (".docblock .example-wrap .ignore", {"border-left": "2px solid rgba(255, 142, 0, 0.6)"})
+assert-css: (".docblock .example-wrap pre.ignore", {"border-left": "2px solid rgba(255, 142, 0, 0.6)"})
 
 move-cursor-to: ".docblock .information .ignore"
 
 assert-css: (".docblock .information .ignore", {"color": "rgb(255, 142, 0)"})
-assert-css: (".docblock .example-wrap .ignore", {"border-left": "2px solid rgb(255, 142, 0)"})
+assert-css: (".docblock .example-wrap pre.ignore", {"border-left": "2px solid rgb(255, 142, 0)"})
 
 
 // Ayu theme.
@@ -70,27 +70,27 @@ local-storage: {"rustdoc-theme": "ayu"}
 reload:
 
 assert-css: (".docblock .information .compile_fail", {"color": "rgba(255, 0, 0, 0.5)"})
-assert-css: (".docblock .example-wrap .compile_fail", {"border-left": "2px solid rgba(255, 0, 0, 0.5)"})
+assert-css: (".docblock .example-wrap pre.compile_fail", {"border-left": "2px solid rgba(255, 0, 0, 0.5)"})
 
 move-cursor-to: ".docblock .information .compile_fail"
 
 assert-css: (".docblock .information .compile_fail", {"color": "rgb(255, 0, 0)"})
-assert-css: (".docblock .example-wrap .compile_fail", {"border-left": "2px solid rgb(255, 0, 0)"})
+assert-css: (".docblock .example-wrap pre.compile_fail", {"border-left": "2px solid rgb(255, 0, 0)"})
 
 // should_panic block
 assert-css: (".docblock .information .should_panic", {"color": "rgba(255, 0, 0, 0.5)"})
-assert-css: (".docblock .example-wrap .should_panic", {"border-left": "2px solid rgba(255, 0, 0, 0.5)"})
+assert-css: (".docblock .example-wrap pre.should_panic", {"border-left": "2px solid rgba(255, 0, 0, 0.5)"})
 
 move-cursor-to: ".docblock .information .should_panic"
 
 assert-css: (".docblock .information .should_panic", {"color": "rgb(255, 0, 0)"})
-assert-css: (".docblock .example-wrap .should_panic", {"border-left": "2px solid rgb(255, 0, 0)"})
+assert-css: (".docblock .example-wrap pre.should_panic", {"border-left": "2px solid rgb(255, 0, 0)"})
 
 // ignore block
 assert-css: (".docblock .information .ignore", {"color": "rgba(255, 142, 0, 0.6)"})
-assert-css: (".docblock .example-wrap .ignore", {"border-left": "2px solid rgba(255, 142, 0, 0.6)"})
+assert-css: (".docblock .example-wrap pre.ignore", {"border-left": "2px solid rgba(255, 142, 0, 0.6)"})
 
 move-cursor-to: ".docblock .information .ignore"
 
 assert-css: (".docblock .information .ignore", {"color": "rgb(255, 142, 0)"})
-assert-css: (".docblock .example-wrap .ignore", {"border-left": "2px solid rgb(255, 142, 0)"})
+assert-css: (".docblock .example-wrap pre.ignore", {"border-left": "2px solid rgb(255, 142, 0)"})
diff --git a/src/test/rustdoc-gui/overflow-tooltip-information.goml b/src/test/rustdoc-gui/overflow-tooltip-information.goml
index 7ef85a4c44565..5be1aff8d3bce 100644
--- a/src/test/rustdoc-gui/overflow-tooltip-information.goml
+++ b/src/test/rustdoc-gui/overflow-tooltip-information.goml
@@ -2,7 +2,7 @@
 // have overflow and max-width CSS rules set because they create a bug in firefox on
 // mac. For more information: https://github.com/rust-lang/rust/issues/89185
 goto: file://|DOC_PATH|/test_docs/fn.foo.html
-assert-css: (".docblock > .information", {
+assert-css: (".docblock > .example-wrap > .information", {
     "overflow-x": "visible",
     "max-width": "none"
 }, ALL)
diff --git a/src/test/ui/array-slice-vec/suggest-array-length.fixed b/src/test/ui/array-slice-vec/suggest-array-length.fixed
index bae3ab74af676..867c18a7d5e6b 100644
--- a/src/test/ui/array-slice-vec/suggest-array-length.fixed
+++ b/src/test/ui/array-slice-vec/suggest-array-length.fixed
@@ -5,10 +5,22 @@ fn main() {
     const Foo: [i32; 3] = [1, 2, 3];
     //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
     //~| ERROR using `_` for array lengths is unstable
+    const REF_FOO: &[u8; 1] = &[1];
+    //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
+    //~| ERROR using `_` for array lengths is unstable
     let foo: [i32; 3] = [1, 2, 3];
     //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
     //~| ERROR using `_` for array lengths is unstable
     let bar: [i32; 3] = [0; 3];
     //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
     //~| ERROR using `_` for array lengths is unstable
+    let ref_foo: &[i32; 3] = &[1, 2, 3];
+    //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
+    //~| ERROR using `_` for array lengths is unstable
+    let ref_bar: &[i32; 3] = &[0; 3];
+    //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
+    //~| ERROR using `_` for array lengths is unstable
+    let multiple_ref_foo: &&[i32; 3] = &&[1, 2, 3];
+    //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
+    //~| ERROR using `_` for array lengths is unstable
 }
diff --git a/src/test/ui/array-slice-vec/suggest-array-length.rs b/src/test/ui/array-slice-vec/suggest-array-length.rs
index b0867f4e39676..f66b3d4a89991 100644
--- a/src/test/ui/array-slice-vec/suggest-array-length.rs
+++ b/src/test/ui/array-slice-vec/suggest-array-length.rs
@@ -5,10 +5,22 @@ fn main() {
     const Foo: [i32; _] = [1, 2, 3];
     //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
     //~| ERROR using `_` for array lengths is unstable
+    const REF_FOO: &[u8; _] = &[1];
+    //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
+    //~| ERROR using `_` for array lengths is unstable
     let foo: [i32; _] = [1, 2, 3];
     //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
     //~| ERROR using `_` for array lengths is unstable
     let bar: [i32; _] = [0; 3];
     //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
     //~| ERROR using `_` for array lengths is unstable
+    let ref_foo: &[i32; _] = &[1, 2, 3];
+    //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
+    //~| ERROR using `_` for array lengths is unstable
+    let ref_bar: &[i32; _] = &[0; 3];
+    //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
+    //~| ERROR using `_` for array lengths is unstable
+    let multiple_ref_foo: &&[i32; _] = &&[1, 2, 3];
+    //~^ ERROR in expressions, `_` can only be used on the left-hand side of an assignment
+    //~| ERROR using `_` for array lengths is unstable
 }
diff --git a/src/test/ui/array-slice-vec/suggest-array-length.stderr b/src/test/ui/array-slice-vec/suggest-array-length.stderr
index 9000f71602850..16c90a04784d0 100644
--- a/src/test/ui/array-slice-vec/suggest-array-length.stderr
+++ b/src/test/ui/array-slice-vec/suggest-array-length.stderr
@@ -1,21 +1,45 @@
 error: in expressions, `_` can only be used on the left-hand side of an assignment
-  --> $DIR/suggest-array-length.rs:8:20
+  --> $DIR/suggest-array-length.rs:11:20
    |
 LL |     let foo: [i32; _] = [1, 2, 3];
    |                    ^ `_` not allowed here
 
 error: in expressions, `_` can only be used on the left-hand side of an assignment
-  --> $DIR/suggest-array-length.rs:11:20
+  --> $DIR/suggest-array-length.rs:14:20
    |
 LL |     let bar: [i32; _] = [0; 3];
    |                    ^ `_` not allowed here
 
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/suggest-array-length.rs:17:25
+   |
+LL |     let ref_foo: &[i32; _] = &[1, 2, 3];
+   |                         ^ `_` not allowed here
+
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/suggest-array-length.rs:20:25
+   |
+LL |     let ref_bar: &[i32; _] = &[0; 3];
+   |                         ^ `_` not allowed here
+
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/suggest-array-length.rs:23:35
+   |
+LL |     let multiple_ref_foo: &&[i32; _] = &&[1, 2, 3];
+   |                                   ^ `_` not allowed here
+
 error: in expressions, `_` can only be used on the left-hand side of an assignment
   --> $DIR/suggest-array-length.rs:5:22
    |
 LL |     const Foo: [i32; _] = [1, 2, 3];
    |                      ^ `_` not allowed here
 
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/suggest-array-length.rs:8:26
+   |
+LL |     const REF_FOO: &[u8; _] = &[1];
+   |                          ^ `_` not allowed here
+
 error[E0658]: using `_` for array lengths is unstable
   --> $DIR/suggest-array-length.rs:5:22
    |
@@ -26,7 +50,16 @@ LL |     const Foo: [i32; _] = [1, 2, 3];
    = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
 
 error[E0658]: using `_` for array lengths is unstable
-  --> $DIR/suggest-array-length.rs:8:20
+  --> $DIR/suggest-array-length.rs:8:26
+   |
+LL |     const REF_FOO: &[u8; _] = &[1];
+   |                          ^ help: consider specifying the array length: `1`
+   |
+   = note: see issue #85077  for more information
+   = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
+
+error[E0658]: using `_` for array lengths is unstable
+  --> $DIR/suggest-array-length.rs:11:20
    |
 LL |     let foo: [i32; _] = [1, 2, 3];
    |                    ^ help: consider specifying the array length: `3`
@@ -35,7 +68,7 @@ LL |     let foo: [i32; _] = [1, 2, 3];
    = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
 
 error[E0658]: using `_` for array lengths is unstable
-  --> $DIR/suggest-array-length.rs:11:20
+  --> $DIR/suggest-array-length.rs:14:20
    |
 LL |     let bar: [i32; _] = [0; 3];
    |                    ^ help: consider specifying the array length: `3`
@@ -43,6 +76,33 @@ LL |     let bar: [i32; _] = [0; 3];
    = note: see issue #85077  for more information
    = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
 
-error: aborting due to 6 previous errors
+error[E0658]: using `_` for array lengths is unstable
+  --> $DIR/suggest-array-length.rs:17:25
+   |
+LL |     let ref_foo: &[i32; _] = &[1, 2, 3];
+   |                         ^ help: consider specifying the array length: `3`
+   |
+   = note: see issue #85077  for more information
+   = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
+
+error[E0658]: using `_` for array lengths is unstable
+  --> $DIR/suggest-array-length.rs:20:25
+   |
+LL |     let ref_bar: &[i32; _] = &[0; 3];
+   |                         ^ help: consider specifying the array length: `3`
+   |
+   = note: see issue #85077  for more information
+   = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
+
+error[E0658]: using `_` for array lengths is unstable
+  --> $DIR/suggest-array-length.rs:23:35
+   |
+LL |     let multiple_ref_foo: &&[i32; _] = &&[1, 2, 3];
+   |                                   ^ help: consider specifying the array length: `3`
+   |
+   = note: see issue #85077  for more information
+   = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable
+
+error: aborting due to 14 previous errors
 
 For more information about this error, try `rustc --explain E0658`.