From 6620e2b219e29ee36cc29991293def2579278eae Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 5 Oct 2024 11:07:01 +0200 Subject: [PATCH 01/18] std: detect stack overflows in TLS destructors on UNIX Fixes #111272. With #127912 merged, we now have all the infrastructure in place to support stack overflow detection in TLS destructors. This was not possible before because the signal stack was freed in the thread main function, thus a SIGSEGV afterwards would immediately crash. And on platforms without native TLS, the guard page address was stored in an allocation freed in a TLS destructor, so would not be available. #127912 introduced the `local_pointer` macro which allows storing a pointer-sized TLS variable without allocation and the `thread_cleanup` runtime function which is called after all other code managed by the Rust runtime. This PR simply moves the signal stack cleanup to the end of `thread_cleanup` and uses `local_pointer` to store every necessary variable. And so, everything run under the Rust runtime is now properly protected against stack overflows. --- library/std/src/rt.rs | 1 + library/std/src/sys/pal/hermit/mod.rs | 2 + library/std/src/sys/pal/sgx/mod.rs | 2 + library/std/src/sys/pal/solid/mod.rs | 2 + library/std/src/sys/pal/teeos/mod.rs | 2 + library/std/src/sys/pal/uefi/mod.rs | 2 + library/std/src/sys/pal/unix/mod.rs | 10 +- .../std/src/sys/pal/unix/stack_overflow.rs | 246 ++++++++---------- library/std/src/sys/pal/unix/thread.rs | 7 +- library/std/src/sys/pal/unsupported/common.rs | 2 + library/std/src/sys/pal/windows/mod.rs | 2 + library/std/src/sys/pal/zkvm/mod.rs | 2 + tests/ui/runtime/out-of-stack.rs | 85 ++++-- 13 files changed, 192 insertions(+), 173 deletions(-) diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 384369b0012b5..50dd2cd6ba7c4 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -117,6 +117,7 @@ pub(crate) fn thread_cleanup() { // print a nice message. panic::catch_unwind(|| { crate::thread::drop_current(); + crate::sys::thread_cleanup(); }) .unwrap_or_else(handle_rt_panic); } diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index d833c9d632c6d..163ff85786682 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -69,6 +69,8 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { } } +pub fn thread_cleanup() {} + // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() {} diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index ce8a2fed4bc6b..d0d928936abd2 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -37,6 +37,8 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { } } +pub fn thread_cleanup() {} + // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() {} diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index d41042be51844..70099cfaa862e 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -38,6 +38,8 @@ pub mod time; // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} +pub fn thread_cleanup() {} + // SAFETY: must be called only once during runtime cleanup. pub unsafe fn cleanup() {} diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index a9900f55b1926..a4ccc3f236a82 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -45,6 +45,8 @@ pub fn abort_internal() -> ! { // so this should never be called. pub fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {} +pub fn thread_cleanup() {} + // SAFETY: must be called only once during runtime cleanup. // this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() { diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 111bed7a7eb64..66939d1046247 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -76,6 +76,8 @@ pub(crate) unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) { } } +pub fn thread_cleanup() {} + /// # SAFETY /// this is not guaranteed to run, for example when the program aborts. /// - must be called only once during runtime cleanup. diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 3cc1cae8d000e..0894e8370edf4 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -55,7 +55,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { // behavior. reset_sigpipe(sigpipe); - stack_overflow::init(); + stack_overflow::protect(true); args::init(argc, argv); // Normally, `thread::spawn` will call `Thread::set_name` but since this thread @@ -229,12 +229,14 @@ pub(crate) fn on_broken_pipe_flag_used() -> bool { ON_BROKEN_PIPE_FLAG_USED.load(crate::sync::atomic::Ordering::Relaxed) } -// SAFETY: must be called only once during runtime cleanup. -// NOTE: this is not guaranteed to run, for example when the program aborts. -pub unsafe fn cleanup() { +pub fn thread_cleanup() { stack_overflow::cleanup(); } +// SAFETY: must be called only once during runtime cleanup. +// NOTE: this is not guaranteed to run, for example when the program aborts. +pub unsafe fn cleanup() {} + #[allow(unused_imports)] pub use libc::signal; diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index db5c6bd3a1c32..4c9fffaff0d74 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -1,29 +1,6 @@ #![cfg_attr(test, allow(dead_code))] -pub use self::imp::{cleanup, init}; -use self::imp::{drop_handler, make_handler}; - -pub struct Handler { - data: *mut libc::c_void, -} - -impl Handler { - pub unsafe fn new() -> Handler { - make_handler(false) - } - - fn null() -> Handler { - Handler { data: crate::ptr::null_mut() } - } -} - -impl Drop for Handler { - fn drop(&mut self) { - unsafe { - drop_handler(self.data); - } - } -} +pub use self::imp::{cleanup, protect}; #[cfg(any( target_os = "linux", @@ -45,12 +22,11 @@ mod imp { #[cfg(all(target_os = "linux", target_env = "gnu"))] use libc::{mmap64, mprotect, munmap}; - use super::Handler; - use crate::cell::Cell; use crate::ops::Range; use crate::sync::OnceLock; - use crate::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; + use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use crate::sys::pal::unix::os; + use crate::sys::thread_local::{guard, local_pointer}; use crate::{io, mem, ptr, thread}; // We use a TLS variable to store the address of the guard page. While TLS @@ -58,9 +34,11 @@ mod imp { // since we make sure to write to the variable before the signal stack is // installed, thereby ensuring that the variable is always allocated when // the signal handler is called. - thread_local! { - // FIXME: use `Range` once that implements `Copy`. - static GUARD: Cell<(usize, usize)> = const { Cell::new((0, 0)) }; + local_pointer! { + static GUARD_START; + static GUARD_END; + + static SIGALTSTACK; } // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages @@ -93,7 +71,9 @@ mod imp { info: *mut libc::siginfo_t, _data: *mut libc::c_void, ) { - let (start, end) = GUARD.get(); + let start = GUARD_START.get().addr(); + let end = GUARD_END.get().addr(); + // SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`. let addr = unsafe { (*info).si_addr().addr() }; @@ -119,51 +99,72 @@ mod imp { } static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); - static MAIN_ALTSTACK: AtomicPtr = AtomicPtr::new(ptr::null_mut()); static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false); + /// Set up stack overflow protection for the current thread + /// /// # Safety - /// Must be called only once + /// May only be called once per thread. #[forbid(unsafe_op_in_unsafe_fn)] - pub unsafe fn init() { - PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); - - // Always write to GUARD to ensure the TLS variable is allocated. - let guard = unsafe { install_main_guard().unwrap_or(0..0) }; - GUARD.set((guard.start, guard.end)); - - // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" - let mut action: sigaction = unsafe { mem::zeroed() }; - for &signal in &[SIGSEGV, SIGBUS] { - // SAFETY: just fetches the current signal handler into action - unsafe { sigaction(signal, ptr::null_mut(), &mut action) }; - // Configure our signal handler if one is not already set. - if action.sa_sigaction == SIG_DFL { - if !NEED_ALTSTACK.load(Ordering::Relaxed) { - // haven't set up our sigaltstack yet - NEED_ALTSTACK.store(true, Ordering::Release); - let handler = unsafe { make_handler(true) }; - MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); - mem::forget(handler); + pub unsafe fn protect(main_thread: bool) { + if main_thread { + PAGE_SIZE.store(os::page_size(), Ordering::Relaxed); + // Use acquire ordering to observe the page size store above, + // which is propagated by a release store to NEED_ALTSTACK. + } else if !NEED_ALTSTACK.load(Ordering::Acquire) { + return; + } + + let guard = if main_thread { + unsafe { install_main_guard().unwrap_or(0..0) } + } else { + unsafe { current_guard().unwrap_or(0..0) } + }; + + // Always store the guard range to ensure the TLS variables are allocated. + GUARD_START.set(ptr::without_provenance_mut(guard.start)); + GUARD_END.set(ptr::without_provenance_mut(guard.end)); + + if main_thread { + // SAFETY: assuming all platforms define struct sigaction as "zero-initializable" + let mut action: sigaction = unsafe { mem::zeroed() }; + for &signal in &[SIGSEGV, SIGBUS] { + // SAFETY: just fetches the current signal handler into action + unsafe { sigaction(signal, ptr::null_mut(), &mut action) }; + // Configure our signal handler if one is not already set. + if action.sa_sigaction == SIG_DFL { + if !NEED_ALTSTACK.load(Ordering::Relaxed) { + // Set up the signal stack and tell other threads to set + // up their own. This uses a release store to propagate + // the store to PAGE_SIZE above. + NEED_ALTSTACK.store(true, Ordering::Release); + unsafe { setup_sigaltstack() }; + } + + action.sa_flags = SA_SIGINFO | SA_ONSTACK; + action.sa_sigaction = signal_handler as sighandler_t; + // SAFETY: only overriding signals if the default is set + unsafe { sigaction(signal, &action, ptr::null_mut()) }; } - action.sa_flags = SA_SIGINFO | SA_ONSTACK; - action.sa_sigaction = signal_handler as sighandler_t; - // SAFETY: only overriding signals if the default is set - unsafe { sigaction(signal, &action, ptr::null_mut()) }; } + } else { + unsafe { setup_sigaltstack() }; } } /// # Safety - /// Must be called only once + /// Mutates the alternate signal stack #[forbid(unsafe_op_in_unsafe_fn)] - pub unsafe fn cleanup() { - // FIXME: I probably cause more bugs than I'm worth! - // see https://github.com/rust-lang/rust/issues/111272 - unsafe { drop_handler(MAIN_ALTSTACK.load(Ordering::Relaxed)) }; - } + unsafe fn setup_sigaltstack() { + // SAFETY: assuming stack_t is zero-initializable + let mut stack = unsafe { mem::zeroed() }; + // SAFETY: reads current stack_t into stack + unsafe { sigaltstack(ptr::null(), &mut stack) }; + // Do not overwrite the stack if one is already set. + if stack.ss_flags & SS_DISABLE == 0 { + return; + } - unsafe fn get_stack() -> libc::stack_t { // OpenBSD requires this flag for stack mapping // otherwise the said mapping will fail as a no-op on most systems // and has a different meaning on FreeBSD @@ -185,82 +186,60 @@ mod imp { let sigstack_size = sigstack_size(); let page_size = PAGE_SIZE.load(Ordering::Relaxed); - let stackp = mmap64( - ptr::null_mut(), - sigstack_size + page_size, - PROT_READ | PROT_WRITE, - flags, - -1, - 0, - ); - if stackp == MAP_FAILED { + let allocation = unsafe { + mmap64(ptr::null_mut(), sigstack_size + page_size, PROT_READ | PROT_WRITE, flags, -1, 0) + }; + if allocation == MAP_FAILED { panic!("failed to allocate an alternative stack: {}", io::Error::last_os_error()); } - let guard_result = libc::mprotect(stackp, page_size, PROT_NONE); + + let guard_result = unsafe { libc::mprotect(allocation, page_size, PROT_NONE) }; if guard_result != 0 { panic!("failed to set up alternative stack guard page: {}", io::Error::last_os_error()); } - let stackp = stackp.add(page_size); - libc::stack_t { ss_sp: stackp, ss_flags: 0, ss_size: sigstack_size } + let stack = libc::stack_t { + // Reserve a guard page at the bottom of the allocation. + ss_sp: unsafe { allocation.add(page_size) }, + ss_flags: 0, + ss_size: sigstack_size, + }; + // SAFETY: We warned our caller this would happen! + unsafe { + sigaltstack(&stack, ptr::null_mut()); + } + + // Ensure that `rt::thread_cleanup` gets called, which will in turn call + // cleanup, where this signal stack will be freed. + guard::enable(); + SIGALTSTACK.set(allocation.cast()); } - /// # Safety - /// Mutates the alternate signal stack - #[forbid(unsafe_op_in_unsafe_fn)] - pub unsafe fn make_handler(main_thread: bool) -> Handler { - if !NEED_ALTSTACK.load(Ordering::Acquire) { - return Handler::null(); + pub fn cleanup() { + let allocation = SIGALTSTACK.get(); + if allocation.is_null() { + return; } - if !main_thread { - // Always write to GUARD to ensure the TLS variable is allocated. - let guard = unsafe { current_guard() }.unwrap_or(0..0); - GUARD.set((guard.start, guard.end)); - } + SIGALTSTACK.set(ptr::null_mut()); - // SAFETY: assuming stack_t is zero-initializable - let mut stack = unsafe { mem::zeroed() }; - // SAFETY: reads current stack_t into stack - unsafe { sigaltstack(ptr::null(), &mut stack) }; - // Configure alternate signal stack, if one is not already set. - if stack.ss_flags & SS_DISABLE != 0 { - // SAFETY: We warned our caller this would happen! - unsafe { - stack = get_stack(); - sigaltstack(&stack, ptr::null_mut()); - } - Handler { data: stack.ss_sp as *mut libc::c_void } - } else { - Handler::null() - } - } + let sigstack_size = sigstack_size(); + let page_size = PAGE_SIZE.load(Ordering::Relaxed); - /// # Safety - /// Must be called - /// - only with our handler or nullptr - /// - only when done with our altstack - /// This disables the alternate signal stack! - #[forbid(unsafe_op_in_unsafe_fn)] - pub unsafe fn drop_handler(data: *mut libc::c_void) { - if !data.is_null() { - let sigstack_size = sigstack_size(); - let page_size = PAGE_SIZE.load(Ordering::Relaxed); - let disabling_stack = libc::stack_t { - ss_sp: ptr::null_mut(), - ss_flags: SS_DISABLE, - // Workaround for bug in macOS implementation of sigaltstack - // UNIX2003 which returns ENOMEM when disabling a stack while - // passing ss_size smaller than MINSIGSTKSZ. According to POSIX - // both ss_sp and ss_size should be ignored in this case. - ss_size: sigstack_size, - }; - // SAFETY: we warned the caller this disables the alternate signal stack! - unsafe { sigaltstack(&disabling_stack, ptr::null_mut()) }; - // SAFETY: We know from `get_stackp` that the alternate stack we installed is part of - // a mapping that started one page earlier, so walk back a page and unmap from there. - unsafe { munmap(data.sub(page_size), sigstack_size + page_size) }; - } + let disabling_stack = libc::stack_t { + ss_sp: ptr::null_mut(), + ss_flags: SS_DISABLE, + // Workaround for bug in macOS implementation of sigaltstack + // UNIX2003 which returns ENOMEM when disabling a stack while + // passing ss_size smaller than MINSIGSTKSZ. According to POSIX + // both ss_sp and ss_size should be ignored in this case. + ss_size: sigstack_size, + }; + unsafe { sigaltstack(&disabling_stack, ptr::null_mut()) }; + + // SAFETY: we created this mapping in `setup_sigaltstack` above with + // this exact size. + unsafe { munmap(allocation.cast(), sigstack_size + page_size) }; } /// Modern kernels on modern hardware can have dynamic signal stack sizes. @@ -577,13 +556,6 @@ mod imp { target_os = "illumos", )))] mod imp { - pub unsafe fn init() {} - - pub unsafe fn cleanup() {} - - pub unsafe fn make_handler(_main_thread: bool) -> super::Handler { - super::Handler::null() - } - - pub unsafe fn drop_handler(_data: *mut libc::c_void) {} + pub unsafe fn protect(_main_thread: bool) {} + pub fn cleanup() {} } diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index f657f82e6e368..1f7d2f1ac94ad 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -99,10 +99,9 @@ impl Thread { extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { unsafe { - // Next, set up our stack overflow handler which may get triggered if we run - // out of stack. - let _handler = stack_overflow::Handler::new(); - // Finally, let's run some code. + // Protect this thread against stack overflows + stack_overflow::protect(false); + // and run its main function. Box::from_raw(main as *mut Box)(); } ptr::null_mut() diff --git a/library/std/src/sys/pal/unsupported/common.rs b/library/std/src/sys/pal/unsupported/common.rs index 34a766683830d..ec046e226659a 100644 --- a/library/std/src/sys/pal/unsupported/common.rs +++ b/library/std/src/sys/pal/unsupported/common.rs @@ -4,6 +4,8 @@ use crate::io as std_io; // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} +pub fn thread_cleanup() {} + // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() {} diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 4282dbb54934f..c58f3e868773a 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -60,6 +60,8 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { } } +pub fn thread_cleanup() {} + // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() { diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 6ea057720296d..618fb2e3a4120 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -37,6 +37,8 @@ use crate::io as std_io; // NOTE: this is not guaranteed to run, for example when Rust code is called externally. pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} +pub fn thread_cleanup() {} + // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() {} diff --git a/tests/ui/runtime/out-of-stack.rs b/tests/ui/runtime/out-of-stack.rs index c5300635ad924..05f2aa44fb709 100644 --- a/tests/ui/runtime/out-of-stack.rs +++ b/tests/ui/runtime/out-of-stack.rs @@ -21,6 +21,7 @@ use std::env; use std::hint::black_box; use std::process::Command; use std::thread; +use std::cell::Cell; fn silent_recurse() { let buf = [0u8; 1000]; @@ -34,13 +35,34 @@ fn loud_recurse() { black_box(()); // don't optimize this into a tail call. please. } +fn in_tls_destructor(f: impl FnOnce() + 'static) { + struct RunOnDrop(Cell>>); + impl Drop for RunOnDrop { + fn drop(&mut self) { + self.0.take().unwrap()() + } + } + + thread_local! { + static RUN: RunOnDrop = RunOnDrop(Cell::new(None)); + } + + RUN.with(|run| run.0.set(Some(Box::new(f)))); +} + #[cfg(unix)] fn check_status(status: std::process::ExitStatus) { use std::os::unix::process::ExitStatusExt; assert!(!status.success()); + #[cfg(not(target_vendor = "apple"))] assert_eq!(status.signal(), Some(libc::SIGABRT)); + + // Apple's libc has a bug where calling abort in a TLS destructor on a thread + // other than the main thread results in a SIGTRAP instead of a SIGABRT. + #[cfg(target_vendor = "apple")] + assert!(matches!(status.signal(), Some(libc::SIGABRT | libc::SIGTRAP))); } #[cfg(not(unix))] @@ -49,40 +71,47 @@ fn check_status(status: std::process::ExitStatus) assert!(!status.success()); } - fn main() { let args: Vec = env::args().collect(); - if args.len() > 1 && args[1] == "silent" { - silent_recurse(); - } else if args.len() > 1 && args[1] == "loud" { - loud_recurse(); - } else if args.len() > 1 && args[1] == "silent-thread" { - thread::spawn(silent_recurse).join(); - } else if args.len() > 1 && args[1] == "loud-thread" { - thread::spawn(loud_recurse).join(); - } else { - let mut modes = vec![ - "silent-thread", - "loud-thread", - ]; - - // On linux it looks like the main thread can sometimes grow its stack - // basically without bounds, so we only test the child thread cases - // there. - if !cfg!(target_os = "linux") { - modes.push("silent"); - modes.push("loud"); + match args.get(1).map(String::as_str) { + Some("silent") => silent_recurse(), + Some("loud") => loud_recurse(), + Some("silent-thread") => thread::spawn(silent_recurse).join().unwrap(), + Some("loud-thread") => thread::spawn(loud_recurse).join().unwrap(), + Some("silent-tls") => in_tls_destructor(silent_recurse), + Some("loud-tls") => in_tls_destructor(loud_recurse), + Some("silent-thread-tls") => { + thread::spawn(|| in_tls_destructor(silent_recurse)).join().unwrap(); } - for mode in modes { - println!("testing: {}", mode); + Some("loud-thread-tls") => { + thread::spawn(|| in_tls_destructor(loud_recurse)).join().unwrap(); + } + _ => { + let mut modes = vec![ + "silent-thread", + "loud-thread", + "silent-thread-tls", + "loud-thread-tls", + ]; + + // On linux it looks like the main thread can sometimes grow its stack + // basically without bounds, so we only test the child thread cases + // there. + if !cfg!(target_os = "linux") { + modes.extend(["silent", "loud", "silent-tls", "loud-tls"]); + } + + for mode in modes { + println!("testing: {}", mode); - let silent = Command::new(&args[0]).arg(mode).output().unwrap(); + let silent = Command::new(&args[0]).arg(mode).output().unwrap(); - check_status(silent.status); + let error = String::from_utf8_lossy(&silent.stderr); + assert!(error.contains("has overflowed its stack"), + "missing overflow message: {}", error); - let error = String::from_utf8_lossy(&silent.stderr); - assert!(error.contains("has overflowed its stack"), - "missing overflow message: {}", error); + check_status(silent.status); + } } } } From 098235c8b2fe073ba231658195b02758d1ad4ac2 Mon Sep 17 00:00:00 2001 From: joboet Date: Sun, 2 Feb 2025 19:31:46 +0100 Subject: [PATCH 02/18] skip result check for TLS stack overflows in stack overflow test on Windows --- tests/ui/runtime/out-of-stack.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/ui/runtime/out-of-stack.rs b/tests/ui/runtime/out-of-stack.rs index 05f2aa44fb709..f1678b625617e 100644 --- a/tests/ui/runtime/out-of-stack.rs +++ b/tests/ui/runtime/out-of-stack.rs @@ -110,7 +110,11 @@ fn main() { assert!(error.contains("has overflowed its stack"), "missing overflow message: {}", error); - check_status(silent.status); + // Stack overflows in TLS destructors do not result in the error + // code being changed on Windows. + if !(cfg!(target_os = "windows") && mode.contains("tls")) { + check_status(silent.status); + } } } } From c6492d955dc8f60ab7c960648f29eed45f2cb199 Mon Sep 17 00:00:00 2001 From: joboet Date: Sun, 9 Feb 2025 11:17:56 +0100 Subject: [PATCH 03/18] don't check the signal code of the stack overflow handler on macOS The specific signal sent by the handler is unreliable when the stack overflow occurs in a TLS destructor. So just don't check the signal code, the only relevant part is that the program doesn't return a success. --- tests/ui/runtime/out-of-stack.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/ui/runtime/out-of-stack.rs b/tests/ui/runtime/out-of-stack.rs index f1678b625617e..5d716c0bbf976 100644 --- a/tests/ui/runtime/out-of-stack.rs +++ b/tests/ui/runtime/out-of-stack.rs @@ -56,13 +56,10 @@ fn check_status(status: std::process::ExitStatus) use std::os::unix::process::ExitStatusExt; assert!(!status.success()); + // Apple's libc has a bug where calling abort in a TLS destructor on a thread + // other than the main thread doesn't always result in SIGTRAP. #[cfg(not(target_vendor = "apple"))] assert_eq!(status.signal(), Some(libc::SIGABRT)); - - // Apple's libc has a bug where calling abort in a TLS destructor on a thread - // other than the main thread results in a SIGTRAP instead of a SIGABRT. - #[cfg(target_vendor = "apple")] - assert!(matches!(status.signal(), Some(libc::SIGABRT | libc::SIGTRAP))); } #[cfg(not(unix))] From 599f88d7717aca793e81c68e66f91b5b9be2d732 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 13 Feb 2025 17:39:05 +0100 Subject: [PATCH 04/18] Add new setting to wrap source code lines when too long --- src/librustdoc/html/static/css/rustdoc.css | 107 +++++++++++++-------- src/librustdoc/html/static/js/main.js | 37 ++++--- src/librustdoc/html/static/js/settings.js | 13 +++ src/librustdoc/html/static/js/storage.js | 3 + 4 files changed, 99 insertions(+), 61 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 4f5f8f92264c1..8e904c6200e6f 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -41,6 +41,7 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\ --font-family: "Source Serif 4", NanumBarunGothic, serif; --font-family-code: "Source Code Pro", monospace; --line-number-padding: 4px; + --line-number-right-margin: 20px; /* scraped examples icons (34x33px) */ --prev-arrow-image: url('data:image/svg+xml, .example-line-numbers, -.rustdoc .scraped-example .src-line-numbers, -.rustdoc .scraped-example .src-line-numbers > pre { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.rustdoc .example-wrap > .example-line-numbers + pre, -.rustdoc .scraped-example .rust { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} - .rustdoc .scraped-example { position: relative; } @@ -908,45 +893,31 @@ both the code example and the line numbers, so we need to remove the radius in t overflow: auto; } -.rustdoc .example-wrap pre.example-line-numbers, -.rustdoc .example-wrap .src-line-numbers { - min-width: fit-content; /* prevent collapsing into nothing in truncated scraped examples */ - flex-grow: 0; - text-align: right; - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; - padding: 14px 8px; - padding-right: 2px; - color: var(--src-line-numbers-span-color); -} - -.example-wrap.digits-1 [data-nosnippet] { +.example-wrap.digits-1:not(.hide-lines) [data-nosnippet] { width: calc(1ch + var(--line-number-padding) * 2); } -.example-wrap.digits-2 [data-nosnippet] { +.example-wrap.digits-2:not(.hide-lines) [data-nosnippet] { width: calc(2ch + var(--line-number-padding) * 2); } -.example-wrap.digits-3 [data-nosnippet] { +.example-wrap.digits-3:not(.hide-lines) [data-nosnippet] { width: calc(3ch + var(--line-number-padding) * 2); } -.example-wrap.digits-4 [data-nosnippet] { +.example-wrap.digits-4:not(.hide-lines) [data-nosnippet] { width: calc(4ch + var(--line-number-padding) * 2); } -.example-wrap.digits-5 [data-nosnippet] { +.example-wrap.digits-5:not(.hide-lines) [data-nosnippet] { width: calc(5ch + var(--line-number-padding) * 2); } -.example-wrap.digits-6 [data-nosnippet] { +.example-wrap.digits-6:not(.hide-lines) [data-nosnippet] { width: calc(6ch + var(--line-number-padding) * 2); } -.example-wrap.digits-7 [data-nosnippet] { +.example-wrap.digits-7:not(.hide-lines) [data-nosnippet] { width: calc(7ch + var(--line-number-padding) * 2); } -.example-wrap.digits-8 [data-nosnippet] { +.example-wrap.digits-8:not(.hide-lines) [data-nosnippet] { width: calc(8ch + var(--line-number-padding) * 2); } -.example-wrap.digits-9 [data-nosnippet] { +.example-wrap.digits-9:not(.hide-lines) [data-nosnippet] { width: calc(9ch + var(--line-number-padding) * 2); } @@ -954,12 +925,12 @@ both the code example and the line numbers, so we need to remove the radius in t color: var(--src-line-numbers-span-color); text-align: right; display: inline-block; - margin-right: 20px; + margin-right: var(--line-number-right-margin); -moz-user-select: none; -webkit-user-select: none; -ms-user-select: none; user-select: none; - padding: 0 4px; + padding: 0 var(--line-number-padding); } .example-wrap [data-nosnippet]:target { border-right: none; @@ -967,6 +938,60 @@ both the code example and the line numbers, so we need to remove the radius in t .example-wrap .line-highlighted[data-nosnippet] { background-color: var(--src-line-number-highlighted-background-color); } +:root.wrap-source-code .example-wrap [data-nosnippet] { + position: absolute; + left: 0; +} +.wrap-source-code .example-wrap pre > code { + position: relative; + word-break: break-all; +} +:root.wrap-source-code .example-wrap pre > code { + display: block; + white-space: pre-wrap; +} +:root.wrap-source-code .example-wrap pre > code * { + word-break: break-all; +} +:root.wrap-source-code .example-wrap.digits-1 pre > code { + padding-left: calc( + 1ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); +} +:root.wrap-source-code .example-wrap.digits-2 pre > code { + padding-left: calc( + 2ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); +} +:root.wrap-source-code .example-wrap.digits-3 pre > code { + padding-left: calc( + 3ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); +} +:root.wrap-source-code .example-wrap.digits-4 pre > code { + padding-left: calc( + 4ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); +} +:root.wrap-source-code .example-wrap.digits-5 pre > code { + padding-left: calc( + 5ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); +} +:root.wrap-source-code .example-wrap.digits-6 pre > code { + padding-left: calc( + 6ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); +} +:root.wrap-source-code .example-wrap.digits-7 pre > code { + padding-left: calc( + 7ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); +} +:root.wrap-source-code .example-wrap.digits-8 pre > code { + padding-left: calc( + 8ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); +} +:root.wrap-source-code .example-wrap.digits-9 pre > code { + padding-left: calc( + 9ch + var(--line-number-padding) * 2 + var(--line-number-right-margin)); +} +.example-wrap.hide-lines [data-nosnippet] { + display: none; +} .search-loading { text-align: center; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index a348c6c5678b9..67cc48f4cb26e 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1112,35 +1112,32 @@ function preLoadCss(cssUrl) { // @ts-expect-error window.rustdoc_add_line_numbers_to_examples = () => { - if (document.querySelector(".rustdoc.src")) { - // We are in the source code page, nothing to be done here! - return; + // @ts-expect-error + function generateLine(nb) { + return `${nb}`; } + onEachLazy(document.querySelectorAll( - ":not(.scraped-example) > .example-wrap > pre:not(.example-line-numbers)", - ), x => { - const parent = x.parentNode; - const line_numbers = parent.querySelectorAll(".example-line-numbers"); - if (line_numbers.length > 0) { + ".rustdoc:not(.src) :not(.scraped-example) > .example-wrap > pre > code", + ), code => { + if (hasClass(code.parentElement.parentElement, "hide-lines")) { + removeClass(code.parentElement.parentElement, "hide-lines"); return; } - const count = x.textContent.split("\n").length; - const elems = []; - for (let i = 0; i < count; ++i) { - elems.push(i + 1); - } - const node = document.createElement("pre"); - addClass(node, "example-line-numbers"); - node.innerHTML = elems.join("\n"); - parent.insertBefore(node, x); + const lines = code.innerHTML.split("\n"); + const digits = (lines.length + "").length; + // @ts-expect-error + code.innerHTML = lines.map((line, index) => generateLine(index + 1) + line).join("\n"); + addClass(code.parentElement.parentElement, `digits-${digits}`); }); }; // @ts-expect-error window.rustdoc_remove_line_numbers_from_examples = () => { - onEachLazy(document.querySelectorAll(".example-wrap > .example-line-numbers"), x => { - x.parentNode.removeChild(x); - }); + onEachLazy( + document.querySelectorAll(".rustdoc:not(.src) :not(.scraped-example) > .example-wrap"), + x => addClass(x, "hide-lines"), + ); }; if (getSettingValue("line-numbers") === "true") { diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index bf33e0f17e5fe..2cf6d6fde8f1a 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -59,6 +59,14 @@ } else { removeClass(document.documentElement, "sans-serif"); } + break; + case "wrap-source-code": + if (value === true) { + addClass(document.documentElement, "wrap-source-code"); + } else { + removeClass(document.documentElement, "wrap-source-code"); + } + break; } } @@ -246,6 +254,11 @@ "js_name": "sans-serif-fonts", "default": false, }, + { + "name": "Wrap source codes", + "js_name": "wrap-source-code", + "default": false, + }, ]; // Then we build the DOM. diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 3042373fb096f..4f70166848c34 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -286,6 +286,9 @@ if (getSettingValue("hide-modnav") === "true") { if (getSettingValue("sans-serif-fonts") === "true") { addClass(document.documentElement, "sans-serif"); } +if (getSettingValue("wrap-source-code") === "true") { + addClass(document.documentElement, "wrap-source-code"); +} function updateSidebarWidth() { const desktopSidebarWidth = getSettingValue("desktop-sidebar-width"); if (desktopSidebarWidth && desktopSidebarWidth !== "null") { From 10d666f3d6d42e5a65e295551ccff51ac9b0d91a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 13 Feb 2025 22:44:30 +0100 Subject: [PATCH 05/18] Update rustdoc-gui test --- .../docblock-code-block-line-number.goml | 68 +++++-------------- 1 file changed, 16 insertions(+), 52 deletions(-) diff --git a/tests/rustdoc-gui/docblock-code-block-line-number.goml b/tests/rustdoc-gui/docblock-code-block-line-number.goml index 032746a6bdf44..97273ceb195fa 100644 --- a/tests/rustdoc-gui/docblock-code-block-line-number.goml +++ b/tests/rustdoc-gui/docblock-code-block-line-number.goml @@ -21,7 +21,7 @@ assert-css: ( set-local-storage: {"rustdoc-line-numbers": "true"} reload: // We wait for the line numbers to be added into the DOM by the JS... -wait-for: "pre.example-line-numbers" +wait-for: ".digits-1 pre" // Otherwise, we can't check text color show-text: true @@ -35,30 +35,21 @@ define-function: ( call-function: ("switch-theme", {"theme": |theme|}) // If the test didn't fail, it means that it was found! assert-css: ( - "pre.example-line-numbers", + ".digits-1 pre [data-nosnippet]", { "color": |color|, - "margin": "0px", - "padding-top": "14px", - "padding-bottom": "14px", - "padding-left": "8px", - "padding-right": "2px", + "margin-top": "0px", + "margin-bottom": "0px", + "margin-left": "0px", + "margin-right": "20px", + "padding-top": "0px", + "padding-bottom": "0px", + "padding-left": "4px", + "padding-right": "4px", "text-align": "right", - // There should not be a radius on the right of the line numbers. - "border-top-left-radius": "6px", - "border-bottom-left-radius": "6px", - "border-top-right-radius": "0px", - "border-bottom-right-radius": "0px", }, ALL, ) - // There should not be a radius on the left of the line numbers. - assert-css: ("pre.example-line-numbers + .rust", { - "border-top-left-radius": "0px", - "border-bottom-left-radius": "0px", - "border-top-right-radius": "6px", - "border-bottom-right-radius": "6px", - }) }, ) call-function: ("check-colors", { @@ -74,9 +65,6 @@ call-function: ("check-colors", { "color": "#c67e2d", }) -// The first code block has two lines so let's check its `
` elements lists both of them.
-assert-text: ("pre.example-line-numbers", "1\n2")
-
 // Now, try changing the setting dynamically. We'll turn it off, using the settings menu,
 // and make sure it goes away.
 
@@ -87,42 +75,17 @@ assert-css: ("#settings", {"display": "block"})
 
 // Then, click the toggle button.
 click: "input#line-numbers"
-wait-for-false: "pre.example-line-numbers"
+wait-for: ".digits-1.hide-lines"
 assert-local-storage: {"rustdoc-line-numbers": "false" }
 
-// Check that the rounded corners are back.
-assert-css: (
-    ".example-wrap .rust",
-    {
-        "border-top-left-radius": "6px",
-        "border-bottom-left-radius": "6px",
-        "border-top-right-radius": "6px",
-        "border-bottom-right-radius": "6px",
-    },
-    ALL,
-)
-
 // Finally, turn it on again.
 click: "input#line-numbers"
-wait-for: "pre.example-line-numbers"
+wait-for: ".digits-1:not(.hide-lines)"
 assert-local-storage: {"rustdoc-line-numbers": "true" }
-wait-for: "pre.example-line-numbers"
 
 // Same check with scraped examples line numbers.
 go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
 
-assert-css: (
-    ".scraped-example .rust",
-    {
-        // There should not be a radius on the left of the code.
-        "border-top-left-radius": "0px",
-        "border-bottom-left-radius": "0px",
-        "border-top-right-radius": "6px",
-        "border-bottom-right-radius": "6px",
-    },
-    ALL,
-)
-
 define-function: (
     "check-padding",
     [path, padding_bottom],
@@ -203,17 +166,18 @@ assert-local-storage: {"rustdoc-line-numbers": "true" }
 assert: ".example-wrap > pre.language-txt"
 assert: ".example-wrap > pre.rust"
 assert-count: (".example-wrap", 2)
-assert-count: (".example-wrap > pre.example-line-numbers", 2)
+assert-count: (".example-wrap.digits-1", 2)
 
 click: "#settings-menu"
 wait-for: "#settings"
 
 // Then, click the toggle button.
 click: "input#line-numbers"
-wait-for-count: (".example-wrap > pre.example-line-numbers", 0)
+wait-for-count: (".example-wrap.digits-1.hide-lines", 2)
 assert-local-storage-false: {"rustdoc-line-numbers": "true" }
 
 // Now turning off the setting.
 click: "input#line-numbers"
-wait-for-count: (".example-wrap > pre.example-line-numbers", 2)
+wait-for-count: (".example-wrap.digits-1", 2)
+wait-for-count: (".example-wrap.digits-1.hide-lines", 0)
 assert-local-storage: {"rustdoc-line-numbers": "true" }

From 38b5bfce24834457cb3f2f1ccb8ed7cd5c29144b Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Thu, 13 Feb 2025 23:03:34 +0100
Subject: [PATCH 06/18] Add GUI test for code wrapping

---
 tests/rustdoc-gui/source-code-wrapping.goml | 44 +++++++++++++++++++++
 tests/rustdoc-gui/src/test_docs/lib.rs      |  9 +++++
 2 files changed, 53 insertions(+)
 create mode 100644 tests/rustdoc-gui/source-code-wrapping.goml

diff --git a/tests/rustdoc-gui/source-code-wrapping.goml b/tests/rustdoc-gui/source-code-wrapping.goml
new file mode 100644
index 0000000000000..6b70c4c3447c7
--- /dev/null
+++ b/tests/rustdoc-gui/source-code-wrapping.goml
@@ -0,0 +1,44 @@
+// Checks that the interactions with the source code pages are working as expected.
+go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
+show-text: true
+
+define-function: (
+    "click-code-wrapping",
+    [expected],
+    block {
+        click: "#wrap-source-code"
+        wait-for-local-storage: {"rustdoc-wrap-source-code": |expected|}
+    },
+)
+
+store-size: (".rust code", {"width": width, "height": height})
+click: "#settings-menu"
+wait-for: "#settings"
+call-function: ("click-code-wrapping", {"expected": "true"})
+store-size: (".rust code", {"width": new_width, "height": new_height})
+// The width should now be smaller than the window's and the height
+// should have increased.
+assert: |width| > |new_width| && |height| < |new_height|
+
+// Switching back to the original setting.
+call-function: ("click-code-wrapping", {"expected": "false"})
+assert-size: (".rust code", {"width": |width|, "height": |height|})
+
+// Now let's check in docs code examples.
+go-to: "file://" + |DOC_PATH| + "/test_docs/trait_bounds/index.html"
+click: "#settings-menu"
+wait-for: "#settings"
+
+store-size: (".example-wrap .rust code", {"width": rust_width, "height": rust_height})
+store-size: (".example-wrap .language-text code", {"width": txt_width, "height": txt_height})
+call-function: ("click-code-wrapping", {"expected": "true"})
+
+store-size: (".example-wrap .rust code", {"width": new_rust_width, "height": new_rust_height})
+store-size: (".example-wrap .language-text code", {"width": new_txt_width, "height": new_txt_height})
+
+assert: |rust_width| > |new_rust_width| && |rust_height| < |new_rust_height|
+assert: |txt_width| > |new_txt_width| && |txt_height| < |new_txt_height|
+
+call-function: ("click-code-wrapping", {"expected": "false"})
+assert-size: (".example-wrap .rust code", {"width": |rust_width|, "height": |rust_height|})
+assert-size: (".example-wrap .language-text code", {"width": |txt_width|, "height": |txt_height|})
diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs
index 1a9ffbe889851..9cd28c1b4ffbe 100644
--- a/tests/rustdoc-gui/src/test_docs/lib.rs
+++ b/tests/rustdoc-gui/src/test_docs/lib.rs
@@ -615,6 +615,15 @@ pub mod private {
     }
 }
 
+/// ```
+/// fn super_long_function_name_because_i_need_to_hit_the_limit_and_break_beyond_it() {
+/// }
+/// ```
+///
+/// ```text
+/// fn super_long_function_name_because_i_need_to_hit_the_limit_and_break_beyond_it_v2() {
+/// }
+/// ```
 pub mod trait_bounds {
     pub trait OneBound: Sized {}
     pub trait TwoBounds: Sized + Copy {}

From a3f389745e1b07a7fe652a6f23ea5a99a1e6a0ca Mon Sep 17 00:00:00 2001
From: Connor Horman 
Date: Fri, 21 Feb 2025 16:58:37 +0000
Subject: [PATCH 07/18] Stabilize `unbounded_shifts`

---
 library/core/src/num/int_macros.rs         |   8 +-
 library/core/src/num/uint_macros.rs        |   8 +-
 library/coretests/tests/num/int_macros.rs  | 165 +++++++++++++++++++++
 library/coretests/tests/num/uint_macros.rs | 154 +++++++++++++++++++
 4 files changed, 327 insertions(+), 8 deletions(-)

diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index 296b5ebdfafcc..5c921c2e845a0 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -1306,11 +1306,11 @@ macro_rules! int_impl {
         ///
         /// Basic usage:
         /// ```
-        /// #![feature(unbounded_shifts)]
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(4), 0x10);")]
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(129), 0);")]
         /// ```
-        #[unstable(feature = "unbounded_shifts", issue = "129375")]
+        #[stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")]
+        #[rustc_const_stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
@@ -1428,12 +1428,12 @@ macro_rules! int_impl {
         ///
         /// Basic usage:
         /// ```
-        /// #![feature(unbounded_shifts)]
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(4), 0x1);")]
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(129), 0);")]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.unbounded_shr(129), -1);")]
         /// ```
-        #[unstable(feature = "unbounded_shifts", issue = "129375")]
+        #[stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")]
+        #[rustc_const_stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index 74d3ae699f66f..380e622a38d3b 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -1567,11 +1567,11 @@ macro_rules! uint_impl {
         ///
         /// Basic usage:
         /// ```
-        /// #![feature(unbounded_shifts)]
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(4), 0x10);")]
         #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(129), 0);")]
         /// ```
-        #[unstable(feature = "unbounded_shifts", issue = "129375")]
+        #[stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")]
+        #[rustc_const_stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
@@ -1688,11 +1688,11 @@ macro_rules! uint_impl {
         ///
         /// Basic usage:
         /// ```
-        /// #![feature(unbounded_shifts)]
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(4), 0x1);")]
         #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".unbounded_shr(129), 0);")]
         /// ```
-        #[unstable(feature = "unbounded_shifts", issue = "129375")]
+        #[stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")]
+        #[rustc_const_stable(feature = "unbounded_shifts", since = "CURRENT_RUSTC_VERSION")]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline]
diff --git a/library/coretests/tests/num/int_macros.rs b/library/coretests/tests/num/int_macros.rs
index f13b836378b9e..b4e911ca55c92 100644
--- a/library/coretests/tests/num/int_macros.rs
+++ b/library/coretests/tests/num/int_macros.rs
@@ -478,5 +478,170 @@ macro_rules! int_module {
                 assert_eq_const_safe!(<$T>::midpoint(6, <$T>::MAX), <$T>::MAX / 2 + 3);
             }
         }
+
+        const SHIFT_AMOUNT_OVERFLOW: $T = <$T>::BITS;
+        const SHIFT_AMOUNT_OVERFLOW2: $T = <$T>::BITS + 3;
+        const SHIFT_AMOUNT_OVERFLOW3: $T = <$T>::BITS << 2;
+
+
+        const SHIFT_AMOUNT_TEST_ONE: $T = <$T>::BITS >> 1;
+        const SHIFT_AMOUNT_TEST_TWO: $T = <$T>::BITS >> 3;
+        const SHIFT_AMOUNT_TEST_THREE: $T = (<$T>::BITS >> 1) - 1;
+        const SHIFT_AMOUNT_TEST_FOUR: $T = <$T>::BITS - 1;
+
+
+        test_runtime_and_compiletime! {
+            fn test_unbounded_shl() {
+                // <$T>::MIN
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN << SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN << SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN << SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUN_TEST_FOUR), (<$T>::MIN << SHIFT_AMOUN_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 1), (<$T>::MIN << 1));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 3), (<$T>::MIN << 3));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 5), (<$T>::MIN << 5));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // <$T>::MAX
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_ONE), (<$T>::MAX << SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_TWO), (<$T>::MAX << SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_THREE), (<$T>::MAX << SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MAX << SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, 1), (<$T>::MAX << 1));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, 3), (<$T>::MAX << 3));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, 5), (<$T>::MAX << 5));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // 1
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_ONE), (1 << SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_TWO), (1 << SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_THREE), (1 << SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_FOUR), (1 << SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, 1), (1 << 1));
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, 3), (1 << 3));
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, 5), (1 << 5));
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // -1
+                assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_TEST_ONE), (-1 << SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_TEST_TWO), (-1 << SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_TEST_THREE), (-1 << SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_TEST_FOUR), (-1 << SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shl(-1, 1), (-1 << 1));
+                assert_eq_const_safe!(<$T>::unbounded_shl(-1, 3), (-1 << 3));
+                assert_eq_const_safe!(<$T>::unbounded_shl(-1, 5), (-1 << 5));
+                assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(-1, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // 8
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_ONE), (8 << SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_TWO), (8 << SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_THREE), (8 << SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_FOUR), (8 << SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, 1), (8 << 1));
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, 3), (8 << 3));
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, 5), (8 << 5));
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // 17
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_ONE), (17 << SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_TWO), (17 << SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_THREE), (17 << SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_FOUR), (17 << SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, 1), (17 << 1));
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, 3), (17 << 3));
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, 5), (17 << 5));
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW3), 0);
+            }
+
+            fn test_unbounded_shr() {
+                // <$T>::MIN
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN >> SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUN_TEST_FOUR), (<$T>::MIN >> SHIFT_AMOUN_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 1), (<$T>::MIN >> 1));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 3), (<$T>::MIN >> 3));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 5), (<$T>::MIN >> 5));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW), -1);
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW2), -1);
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW3), -1);
+
+                // <$T>::MAX
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_ONE), (<$T>::MAX >> SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_TWO), (<$T>::MAX >> SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_THREE), (<$T>::MAX >> SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MAX >> SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, 1), (<$T>::MAX >> 1));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, 3), (<$T>::MAX >> 3));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, 5), (<$T>::MAX >> 5));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // 1
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_ONE), (1 >> SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_TWO), (1 >> SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_THREE), (1 >> SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_FOUR), (1 >> SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, 1), (1 >> 1));
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, 3), (1 >> 3));
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, 5), (1 >> 5));
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // -1
+                assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_TEST_ONE), (-1 >> SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_TEST_TWO), (-1 >> SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_TEST_THREE), (-1 >> SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_TEST_FOUR), (-1 >> SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shr(-1, 1), (-1 >> 1));
+                assert_eq_const_safe!(<$T>::unbounded_shr(-1, 3), (-1 >> 3));
+                assert_eq_const_safe!(<$T>::unbounded_shr(-1, 5), (-1 >> 5));
+                assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_OVERFLOW), -1);
+                assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_OVERFLOW), -1);
+                assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_OVERFLOW2), -1);
+                assert_eq_const_safe!(<$T>::unbounded_shr(-1, SHIFT_AMOUNT_OVERFLOW3), -1);
+
+                // 8
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_ONE), (8 >> SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_TWO), (8 >> SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_THREE), (8 >> SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_FOUR), (8 >> SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, 1), (8 >> 1));
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, 3), (8 >> 3));
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, 5), (8 >> 5));
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // 17
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_ONE), (17 >> SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_TWO), (17 >> SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_THREE), (17 >> SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_FOUR), (17 >> SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, 1), (17 >> 1));
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, 3), (17 >> 3));
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, 5), (17 >> 5));
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW3), 0);
+            }
+        }
     };
 }
diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs
index 99a2d4cd462b1..fbe44e90e5e74 100644
--- a/library/coretests/tests/num/uint_macros.rs
+++ b/library/coretests/tests/num/uint_macros.rs
@@ -317,5 +317,159 @@ macro_rules! uint_module {
                 assert_eq_const_safe!(<$T>::midpoint(6, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2 + 3);
             }
         }
+
+        test_runtime_and_compiletime! {
+            fn test_unbounded_shl() {
+                // <$T>::MIN
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN << SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN << SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN << SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUN_TEST_FOUR), (<$T>::MIN << SHIFT_AMOUN_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 1), (<$T>::MIN << 1));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 3), (<$T>::MIN << 3));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 5), (<$T>::MIN << 5));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // <$T>::MAX
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_ONE), (<$T>::MAX << SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_TWO), (<$T>::MAX << SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_THREE), (<$T>::MAX << SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MAX << SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, 1), (<$T>::MAX << 1));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, 3), (<$T>::MAX << 3));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, 5), (<$T>::MAX << 5));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // 1
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_ONE), (1 << SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_TWO), (1 << SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_THREE), (1 << SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_TEST_FOUR), (1 << SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, 1), (1 << 1));
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, 3), (1 << 3));
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, 5), (1 << 5));
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(1, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // !0
+                assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_TEST_ONE), (!0 << SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_TEST_TWO), (!0 << SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_TEST_THREE), (!0 << SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_TEST_FOUR), (!0 << SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shl(!0, 1), (!0 << 1));
+                assert_eq_const_safe!(<$T>::unbounded_shl(!0, 3), (!0 << 3));
+                assert_eq_const_safe!(<$T>::unbounded_shl(!0, 5), (!0 << 5));
+                assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(!0, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // 8
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_ONE), (8 << SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_TWO), (8 << SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_THREE), (8 << SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_TEST_FOUR), (8 << SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, 1), (8 << 1));
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, 3), (8 << 3));
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, 5), (8 << 5));
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(8, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // 17
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_ONE), (17 << SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_TWO), (17 << SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_THREE), (17 << SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_TEST_FOUR), (17 << SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, 1), (17 << 1));
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, 3), (17 << 3));
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, 5), (17 << 5));
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shl(17, SHIFT_AMOUNT_OVERFLOW3), 0);
+            }
+
+            fn test_unbounded_shr() {
+                // <$T>::MIN
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN >> SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUN_TEST_FOUR), (<$T>::MIN >> SHIFT_AMOUN_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 1), (<$T>::MIN >> 1));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 3), (<$T>::MIN >> 3));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 5), (<$T>::MIN >> 5));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // <$T>::MAX
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_ONE), (<$T>::MAX >> SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_TWO), (<$T>::MAX >> SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_THREE), (<$T>::MAX >> SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MAX >> SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, 1), (<$T>::MAX >> 1));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, 3), (<$T>::MAX >> 3));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, 5), (<$T>::MAX >> 5));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MAX, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // 1
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_ONE), (1 >> SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_TWO), (1 >> SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_THREE), (1 >> SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_TEST_FOUR), (1 >> SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, 1), (1 >> 1));
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, 3), (1 >> 3));
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, 5), (1 >> 5));
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(1, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // !0
+                assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_TEST_ONE), (!0 >> SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_TEST_TWO), (!0 >> SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_TEST_THREE), (!0 >> SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_TEST_FOUR), (!0 >> SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shr(!0, 1), (!0 >> 1));
+                assert_eq_const_safe!(<$T>::unbounded_shr(!0, 3), (!0 >> 3));
+                assert_eq_const_safe!(<$T>::unbounded_shr(!0, 5), (!0 >> 5));
+                assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(!0, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // 8
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_ONE), (8 >> SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_TWO), (8 >> SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_THREE), (8 >> SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_TEST_FOUR), (8 >> SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, 1), (8 >> 1));
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, 3), (8 >> 3));
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, 5), (8 >> 5));
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(8, SHIFT_AMOUNT_OVERFLOW3), 0);
+
+                // 17
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_ONE), (17 >> SHIFT_AMOUNT_TEST_ONE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_TWO), (17 >> SHIFT_AMOUNT_TEST_TWO));
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_THREE), (17 >> SHIFT_AMOUNT_TEST_THREE));
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_TEST_FOUR), (17 >> SHIFT_AMOUNT_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, 1), (17 >> 1));
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, 3), (17 >> 3));
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, 5), (17 >> 5));
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW2), 0);
+                assert_eq_const_safe!(<$T>::unbounded_shr(17, SHIFT_AMOUNT_OVERFLOW3), 0);
+            }
+        }
     };
 }

From f1c21c9fc6211576c422d5cd6bf7e07182235883 Mon Sep 17 00:00:00 2001
From: Connor Horman 
Date: Fri, 21 Feb 2025 18:13:30 +0000
Subject: [PATCH 08/18] Fix unbounded_shifts tests

---
 library/coretests/tests/num/int_macros.rs  | 21 ++++++++++-----------
 library/coretests/tests/num/uint_macros.rs | 14 ++++++++++++--
 2 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/library/coretests/tests/num/int_macros.rs b/library/coretests/tests/num/int_macros.rs
index b4e911ca55c92..ed0aaa4dc5b23 100644
--- a/library/coretests/tests/num/int_macros.rs
+++ b/library/coretests/tests/num/int_macros.rs
@@ -479,16 +479,15 @@ macro_rules! int_module {
             }
         }
 
-        const SHIFT_AMOUNT_OVERFLOW: $T = <$T>::BITS;
-        const SHIFT_AMOUNT_OVERFLOW2: $T = <$T>::BITS + 3;
-        const SHIFT_AMOUNT_OVERFLOW3: $T = <$T>::BITS << 2;
-
-
-        const SHIFT_AMOUNT_TEST_ONE: $T = <$T>::BITS >> 1;
-        const SHIFT_AMOUNT_TEST_TWO: $T = <$T>::BITS >> 3;
-        const SHIFT_AMOUNT_TEST_THREE: $T = (<$T>::BITS >> 1) - 1;
-        const SHIFT_AMOUNT_TEST_FOUR: $T = <$T>::BITS - 1;
+        // test_unbounded_sh* constants
+        const SHIFT_AMOUNT_OVERFLOW: u32 = <$T>::BITS;
+        const SHIFT_AMOUNT_OVERFLOW2: u32 = <$T>::BITS + 3;
+        const SHIFT_AMOUNT_OVERFLOW3: u32 = <$T>::BITS << 2;
 
+        const SHIFT_AMOUNT_TEST_ONE: u32 = <$T>::BITS >> 1;
+        const SHIFT_AMOUNT_TEST_TWO: u32 = <$T>::BITS >> 3;
+        const SHIFT_AMOUNT_TEST_THREE: u32 = (<$T>::BITS >> 1) - 1;
+        const SHIFT_AMOUNT_TEST_FOUR: u32 = <$T>::BITS - 1;
 
         test_runtime_and_compiletime! {
             fn test_unbounded_shl() {
@@ -496,7 +495,7 @@ macro_rules! int_module {
                 assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN << SHIFT_AMOUNT_TEST_ONE));
                 assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN << SHIFT_AMOUNT_TEST_TWO));
                 assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN << SHIFT_AMOUNT_TEST_THREE));
-                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUN_TEST_FOUR), (<$T>::MIN << SHIFT_AMOUN_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MIN << SHIFT_AMOUNT_TEST_FOUR));
                 assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 1), (<$T>::MIN << 1));
                 assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 3), (<$T>::MIN << 3));
                 assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 5), (<$T>::MIN << 5));
@@ -572,7 +571,7 @@ macro_rules! int_module {
                 assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_ONE));
                 assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN >> SHIFT_AMOUNT_TEST_TWO));
                 assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_THREE));
-                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUN_TEST_FOUR), (<$T>::MIN >> SHIFT_AMOUN_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MIN >> SHIFT_AMOUNT_TEST_FOUR));
                 assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 1), (<$T>::MIN >> 1));
                 assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 3), (<$T>::MIN >> 3));
                 assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 5), (<$T>::MIN >> 5));
diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs
index fbe44e90e5e74..6ef83765d0110 100644
--- a/library/coretests/tests/num/uint_macros.rs
+++ b/library/coretests/tests/num/uint_macros.rs
@@ -318,13 +318,23 @@ macro_rules! uint_module {
             }
         }
 
+        // test_unbounded_sh* constants
+        const SHIFT_AMOUNT_OVERFLOW: u32 = <$T>::BITS;
+        const SHIFT_AMOUNT_OVERFLOW2: u32 = <$T>::BITS + 3;
+        const SHIFT_AMOUNT_OVERFLOW3: u32 = <$T>::BITS << 2;
+
+        const SHIFT_AMOUNT_TEST_ONE: u32 = <$T>::BITS >> 1;
+        const SHIFT_AMOUNT_TEST_TWO: u32 = <$T>::BITS >> 3;
+        const SHIFT_AMOUNT_TEST_THREE: u32 = (<$T>::BITS >> 1) - 1;
+        const SHIFT_AMOUNT_TEST_FOUR: u32 = <$T>::BITS - 1;
+
         test_runtime_and_compiletime! {
             fn test_unbounded_shl() {
                 // <$T>::MIN
                 assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN << SHIFT_AMOUNT_TEST_ONE));
                 assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN << SHIFT_AMOUNT_TEST_TWO));
                 assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN << SHIFT_AMOUNT_TEST_THREE));
-                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUN_TEST_FOUR), (<$T>::MIN << SHIFT_AMOUN_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MIN << SHIFT_AMOUNT_TEST_FOUR));
                 assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 1), (<$T>::MIN << 1));
                 assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 3), (<$T>::MIN << 3));
                 assert_eq_const_safe!(<$T>::unbounded_shl(<$T>::MIN, 5), (<$T>::MIN << 5));
@@ -400,7 +410,7 @@ macro_rules! uint_module {
                 assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_ONE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_ONE));
                 assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_TWO), (<$T>::MIN >> SHIFT_AMOUNT_TEST_TWO));
                 assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_THREE), (<$T>::MIN >> SHIFT_AMOUNT_TEST_THREE));
-                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUN_TEST_FOUR), (<$T>::MIN >> SHIFT_AMOUN_TEST_FOUR));
+                assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, SHIFT_AMOUNT_TEST_FOUR), (<$T>::MIN >> SHIFT_AMOUNT_TEST_FOUR));
                 assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 1), (<$T>::MIN >> 1));
                 assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 3), (<$T>::MIN >> 3));
                 assert_eq_const_safe!(<$T>::unbounded_shr(<$T>::MIN, 5), (<$T>::MIN >> 5));

From 6041d18de5d8f93f8b5907db4552d02d594c17fd Mon Sep 17 00:00:00 2001
From: Jason Newcomb 
Date: Fri, 21 Feb 2025 15:59:47 -0500
Subject: [PATCH 09/18] Some cleanup of index types in `rustc_hir_typeck`

---
 .../src/fn_ctxt/arg_matrix.rs                 |  6 ++++
 .../rustc_hir_typeck/src/fn_ctxt/checks.rs    | 36 +++++++++----------
 2 files changed, 23 insertions(+), 19 deletions(-)

diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
index 358bc389bd138..f6298adf2ebb7 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
@@ -22,6 +22,12 @@ impl ExpectedIdx {
     }
 }
 
+impl ProvidedIdx {
+    pub(crate) fn to_expected_idx(self) -> ExpectedIdx {
+        ExpectedIdx::from_u32(self.as_u32())
+    }
+}
+
 // An issue that might be found in the compatibility matrix
 #[derive(Debug)]
 enum Issue {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index e90474cabb420..29ee120a67518 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -773,7 +773,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // First, check if we just need to wrap some arguments in a tuple.
         if let Some((mismatch_idx, terr)) =
-            compatibility_diagonal.iter().enumerate().find_map(|(i, c)| {
+            compatibility_diagonal.iter_enumerated().find_map(|(i, c)| {
                 if let Compatibility::Incompatible(Some(terr)) = c {
                     Some((i, *terr))
                 } else {
@@ -785,24 +785,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // Do we have as many extra provided arguments as the tuple's length?
             // If so, we might have just forgotten to wrap some args in a tuple.
             if let Some(ty::Tuple(tys)) =
-                formal_and_expected_inputs.get(mismatch_idx.into()).map(|tys| tys.1.kind())
+                formal_and_expected_inputs.get(mismatch_idx.to_expected_idx()).map(|tys| tys.1.kind())
                 // If the tuple is unit, we're not actually wrapping any arguments.
                 && !tys.is_empty()
                 && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len()
             {
                 // Wrap up the N provided arguments starting at this position in a tuple.
-                let provided_as_tuple = Ty::new_tup_from_iter(
-                    tcx,
-                    provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx).take(tys.len()),
-                );
+                let provided_args_to_tuple = &provided_arg_tys.raw[mismatch_idx.idx()..];
+                let (provided_args_to_tuple, provided_args_after_tuple) =
+                    provided_args_to_tuple.split_at(tys.len());
+                let provided_as_tuple =
+                    Ty::new_tup_from_iter(tcx, provided_args_to_tuple.iter().map(|&(ty, _)| ty));
 
                 let mut satisfied = true;
                 // Check if the newly wrapped tuple + rest of the arguments are compatible.
                 for ((_, expected_ty), provided_ty) in std::iter::zip(
-                    formal_and_expected_inputs.iter().skip(mismatch_idx),
-                    [provided_as_tuple].into_iter().chain(
-                        provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx + tys.len()),
-                    ),
+                    formal_and_expected_inputs[mismatch_idx.to_expected_idx()..].iter(),
+                    [provided_as_tuple]
+                        .into_iter()
+                        .chain(provided_args_after_tuple.iter().map(|&(ty, _)| ty)),
                 ) {
                     if !self.may_coerce(provided_ty, *expected_ty) {
                         satisfied = false;
@@ -814,10 +815,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Take some care with spans, so we don't suggest wrapping a macro's
                 // innards in parenthesis, for example.
                 if satisfied
-                    && let Some((_, lo)) =
-                        provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx))
-                    && let Some((_, hi)) =
-                        provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx + tys.len() - 1))
+                    && let &[(_, hi @ lo)] | &[(_, lo), .., (_, hi)] = provided_args_to_tuple
                 {
                     let mut err;
                     if tys.len() == 1 {
@@ -825,9 +823,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // so don't do anything special here.
                         err = self.err_ctxt().report_and_explain_type_error(
                             mk_trace(
-                                *lo,
-                                formal_and_expected_inputs[mismatch_idx.into()],
-                                provided_arg_tys[mismatch_idx.into()].0,
+                                lo,
+                                formal_and_expected_inputs[mismatch_idx.to_expected_idx()],
+                                provided_arg_tys[mismatch_idx].0,
                             ),
                             self.param_env,
                             terr,
@@ -866,7 +864,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         callee_ty,
                         call_expr,
                         None,
-                        Some(mismatch_idx),
+                        Some(mismatch_idx.as_usize()),
                         &matched_inputs,
                         &formal_and_expected_inputs,
                         is_method,
@@ -2648,7 +2646,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
 
                 let expected_display_type = self
-                    .resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1)
+                    .resolve_vars_if_possible(formal_and_expected_inputs[idx].1)
                     .sort_string(self.tcx);
                 let label = if idxs_matched == params_with_generics.len() - 1 {
                     format!(

From a74f3fb5fc955fe3876fa206ce2146cd355e1bb5 Mon Sep 17 00:00:00 2001
From: Jason Newcomb 
Date: Fri, 21 Feb 2025 16:01:07 -0500
Subject: [PATCH 10/18] Iterate directly on block indices in
 `rustc_mir_transform`

---
 compiler/rustc_mir_transform/src/check_pointers.rs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/compiler/rustc_mir_transform/src/check_pointers.rs b/compiler/rustc_mir_transform/src/check_pointers.rs
index 72460542f8745..fffd59746622c 100644
--- a/compiler/rustc_mir_transform/src/check_pointers.rs
+++ b/compiler/rustc_mir_transform/src/check_pointers.rs
@@ -70,8 +70,7 @@ pub(crate) fn check_pointers<'a, 'tcx, F>(
     // statements/blocks after. Iterating or visiting the MIR in order would require updating
     // our current location after every insertion. By iterating backwards, we dodge this issue:
     // The only Locations that an insertion changes have already been handled.
-    for block in (0..basic_blocks.len()).rev() {
-        let block = block.into();
+    for block in basic_blocks.indices().rev() {
         for statement_index in (0..basic_blocks[block].statements.len()).rev() {
             let location = Location { block, statement_index };
             let statement = &basic_blocks[block].statements[statement_index];

From 162fb713ac66fef8f6a1d14bae9d1d4b35f76411 Mon Sep 17 00:00:00 2001
From: Jason Newcomb 
Date: Fri, 21 Feb 2025 16:01:19 -0500
Subject: [PATCH 11/18] Allow SliceIndex to be indexed by ranges.

---
 compiler/rustc_codegen_ssa/src/mir/mod.rs     |  4 +-
 .../src/sorted_map/index_map.rs               |  2 +-
 .../rustc_hir_typeck/src/fn_ctxt/checks.rs    |  2 +-
 compiler/rustc_index/src/idx.rs               | 91 +++++++++++++++++++
 compiler/rustc_index/src/lib.rs               |  3 +-
 compiler/rustc_index/src/slice.rs             | 33 ++++---
 .../src/infer/lexical_region_resolve/mod.rs   |  2 +-
 .../src/coverage/counters.rs                  |  2 +-
 .../rustc_mir_transform/src/coverage/graph.rs |  2 +-
 compiler/rustc_mir_transform/src/gvn.rs       |  2 +-
 10 files changed, 121 insertions(+), 22 deletions(-)

diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index 3a896071bc6b8..3cbec337d6bf7 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -3,7 +3,7 @@ use std::iter;
 use rustc_index::IndexVec;
 use rustc_index::bit_set::DenseBitSet;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
-use rustc_middle::mir::{UnwindTerminateReason, traversal};
+use rustc_middle::mir::{Local, UnwindTerminateReason, traversal};
 use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, TyAndLayout};
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
 use rustc_middle::{bug, mir, span_bug};
@@ -240,7 +240,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     let local_values = {
         let args = arg_local_refs(&mut start_bx, &mut fx, &memory_locals);
 
-        let mut allocate_local = |local| {
+        let mut allocate_local = |local: Local| {
             let decl = &mir.local_decls[local];
             let layout = start_bx.layout_of(fx.monomorphize(decl.ty));
             assert!(!layout.ty.has_erasable_regions());
diff --git a/compiler/rustc_data_structures/src/sorted_map/index_map.rs b/compiler/rustc_data_structures/src/sorted_map/index_map.rs
index e9a5fb5197548..278b3a9270c5c 100644
--- a/compiler/rustc_data_structures/src/sorted_map/index_map.rs
+++ b/compiler/rustc_data_structures/src/sorted_map/index_map.rs
@@ -147,7 +147,7 @@ impl FromIterator<(K, V)> for SortedIndexMultiMap {
     where
         J: IntoIterator,
     {
-        let items = IndexVec::from_iter(iter);
+        let items = IndexVec::::from_iter(iter);
         let mut idx_sorted_by_item_key: Vec<_> = items.indices().collect();
 
         // `sort_by_key` is stable, so insertion order is preserved for duplicate items.
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 29ee120a67518..10185117b9cd8 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -791,7 +791,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len()
             {
                 // Wrap up the N provided arguments starting at this position in a tuple.
-                let provided_args_to_tuple = &provided_arg_tys.raw[mismatch_idx.idx()..];
+                let provided_args_to_tuple = &provided_arg_tys[mismatch_idx..];
                 let (provided_args_to_tuple, provided_args_after_tuple) =
                     provided_args_to_tuple.split_at(tys.len());
                 let provided_as_tuple =
diff --git a/compiler/rustc_index/src/idx.rs b/compiler/rustc_index/src/idx.rs
index b85160540d872..33f406e21137d 100644
--- a/compiler/rustc_index/src/idx.rs
+++ b/compiler/rustc_index/src/idx.rs
@@ -1,5 +1,7 @@
 use std::fmt::Debug;
 use std::hash::Hash;
+use std::ops;
+use std::slice::SliceIndex;
 
 /// Represents some newtyped `usize` wrapper.
 ///
@@ -43,3 +45,92 @@ impl Idx for u32 {
         self as usize
     }
 }
+
+/// Helper trait for indexing operations with a custom index type.
+pub trait IntoSliceIdx {
+    type Output: SliceIndex;
+    fn into_slice_idx(self) -> Self::Output;
+}
+
+impl IntoSliceIdx for I {
+    type Output = usize;
+    #[inline]
+    fn into_slice_idx(self) -> Self::Output {
+        self.index()
+    }
+}
+
+impl IntoSliceIdx for ops::RangeFull {
+    type Output = ops::RangeFull;
+    #[inline]
+    fn into_slice_idx(self) -> Self::Output {
+        self
+    }
+}
+
+impl IntoSliceIdx for ops::Range {
+    type Output = ops::Range;
+    #[inline]
+    fn into_slice_idx(self) -> Self::Output {
+        ops::Range { start: self.start.index(), end: self.end.index() }
+    }
+}
+
+impl IntoSliceIdx for ops::RangeFrom {
+    type Output = ops::RangeFrom;
+    #[inline]
+    fn into_slice_idx(self) -> Self::Output {
+        ops::RangeFrom { start: self.start.index() }
+    }
+}
+
+impl IntoSliceIdx for ops::RangeTo {
+    type Output = ops::RangeTo;
+    #[inline]
+    fn into_slice_idx(self) -> Self::Output {
+        ..self.end.index()
+    }
+}
+
+impl IntoSliceIdx for ops::RangeInclusive {
+    type Output = ops::RangeInclusive;
+    #[inline]
+    fn into_slice_idx(self) -> Self::Output {
+        ops::RangeInclusive::new(self.start().index(), self.end().index())
+    }
+}
+
+impl IntoSliceIdx for ops::RangeToInclusive {
+    type Output = ops::RangeToInclusive;
+    #[inline]
+    fn into_slice_idx(self) -> Self::Output {
+        ..=self.end.index()
+    }
+}
+
+#[cfg(feature = "nightly")]
+impl IntoSliceIdx for core::range::Range {
+    type Output = core::range::Range;
+    #[inline]
+    fn into_slice_idx(self) -> Self::Output {
+        core::range::Range { start: self.start.index(), end: self.end.index() }
+    }
+}
+
+#[cfg(feature = "nightly")]
+impl IntoSliceIdx for core::range::RangeFrom {
+    type Output = core::range::RangeFrom;
+    #[inline]
+    fn into_slice_idx(self) -> Self::Output {
+        core::range::RangeFrom { start: self.start.index() }
+    }
+}
+
+#[cfg(feature = "nightly")]
+impl IntoSliceIdx for core::range::RangeInclusive {
+    type Output = core::range::RangeInclusive;
+    #[inline]
+    fn into_slice_idx(self) -> Self::Output {
+        core::range::RangeInclusive { start: self.start.index(), end: self.end.index() }
+    }
+}
diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs
index cae55230b0679..3441a5f65c785 100644
--- a/compiler/rustc_index/src/lib.rs
+++ b/compiler/rustc_index/src/lib.rs
@@ -2,6 +2,7 @@
 #![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))]
 #![cfg_attr(feature = "nightly", allow(internal_features))]
 #![cfg_attr(feature = "nightly", feature(extend_one, step_trait, test))]
+#![cfg_attr(feature = "nightly", feature(new_range_api))]
 #![cfg_attr(feature = "nightly", feature(new_zeroed_alloc))]
 #![warn(unreachable_pub)]
 // tidy-alphabetical-end
@@ -14,7 +15,7 @@ mod idx;
 mod slice;
 mod vec;
 
-pub use idx::Idx;
+pub use idx::{Idx, IntoSliceIdx};
 pub use rustc_index_macros::newtype_index;
 pub use slice::IndexSlice;
 #[doc(no_inline)]
diff --git a/compiler/rustc_index/src/slice.rs b/compiler/rustc_index/src/slice.rs
index 956d32c9a6943..cc8080ee69753 100644
--- a/compiler/rustc_index/src/slice.rs
+++ b/compiler/rustc_index/src/slice.rs
@@ -1,8 +1,9 @@
+use std::fmt;
 use std::marker::PhantomData;
 use std::ops::{Index, IndexMut};
-use std::{fmt, slice};
+use std::slice::{self, SliceIndex};
 
-use crate::{Idx, IndexVec};
+use crate::{Idx, IndexVec, IntoSliceIdx};
 
 /// A view into contiguous `T`s, indexed by `I` rather than by `usize`.
 ///
@@ -99,13 +100,19 @@ impl IndexSlice {
     }
 
     #[inline]
-    pub fn get(&self, index: I) -> Option<&T> {
-        self.raw.get(index.index())
+    pub fn get>(
+        &self,
+        index: R,
+    ) -> Option<&>::Output> {
+        self.raw.get(index.into_slice_idx())
     }
 
     #[inline]
-    pub fn get_mut(&mut self, index: I) -> Option<&mut T> {
-        self.raw.get_mut(index.index())
+    pub fn get_mut>(
+        &mut self,
+        index: R,
+    ) -> Option<&mut >::Output> {
+        self.raw.get_mut(index.into_slice_idx())
     }
 
     /// Returns mutable references to two distinct elements, `a` and `b`.
@@ -186,19 +193,19 @@ impl fmt::Debug for IndexSlice {
     }
 }
 
-impl Index for IndexSlice {
-    type Output = T;
+impl> Index for IndexSlice {
+    type Output = >::Output;
 
     #[inline]
-    fn index(&self, index: I) -> &T {
-        &self.raw[index.index()]
+    fn index(&self, index: R) -> &Self::Output {
+        &self.raw[index.into_slice_idx()]
     }
 }
 
-impl IndexMut for IndexSlice {
+impl> IndexMut for IndexSlice {
     #[inline]
-    fn index_mut(&mut self, index: I) -> &mut T {
-        &mut self.raw[index.index()]
+    fn index_mut(&mut self, index: R) -> &mut Self::Output {
+        &mut self.raw[index.into_slice_idx()]
     }
 }
 
diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
index e454a88e847fd..03c4614af139b 100644
--- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
+++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
@@ -160,7 +160,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
     /// empty region. The `expansion` phase will grow this larger.
     fn construct_var_data(&self) -> LexicalRegionResolutions<'tcx> {
         LexicalRegionResolutions {
-            values: IndexVec::from_fn_n(
+            values: IndexVec::::from_fn_n(
                 |vid| {
                     let vid_universe = self.var_infos[vid].universe;
                     VarValue::Empty(vid_universe)
diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs
index adb99a75a9e47..fddcd341a9974 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters.rs
@@ -47,7 +47,7 @@ fn make_node_flow_priority_list(
     // A "reloop" node has exactly one out-edge, which jumps back to the top
     // of an enclosing loop. Reloop nodes are typically visited more times
     // than loop-exit nodes, so try to avoid giving them physical counters.
-    let is_reloop_node = IndexVec::from_fn_n(
+    let is_reloop_node = IndexVec::::from_fn_n(
         |node| match graph.successors[node].as_slice() {
             &[succ] => graph.dominates(succ, node),
             _ => false,
diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs
index 392b54c8d819b..d7db0140f3dd8 100644
--- a/compiler/rustc_mir_transform/src/coverage/graph.rs
+++ b/compiler/rustc_mir_transform/src/coverage/graph.rs
@@ -42,7 +42,7 @@ impl CoverageGraph {
         // `SwitchInt` to have multiple targets to the same destination `BasicBlock`, so
         // de-duplication is required. This is done without reordering the successors.
 
-        let successors = IndexVec::from_fn_n(
+        let successors = IndexVec::::from_fn_n(
             |bcb| {
                 let mut seen_bcbs = FxHashSet::default();
                 let terminator = mir_body[bcbs[bcb].last_bb()].terminator();
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index d2ffd26f0a06d..4128e8b09d022 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -1259,7 +1259,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
         let layout = self.ecx.layout_of(lhs_ty).ok()?;
 
-        let as_bits = |value| {
+        let as_bits = |value: VnIndex| {
             let constant = self.evaluated[value].as_ref()?;
             if layout.backend_repr.is_scalar() {
                 let scalar = self.ecx.read_scalar(constant).discard_err()?;

From a1471f3aa68ad97fc809e37c3c2c7b8b9e2ae227 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Sun, 16 Feb 2025 00:34:29 +0100
Subject: [PATCH 12/18] Rename feature into `Word wrap source code`

---
 src/librustdoc/html/static/css/rustdoc.css  | 26 ++++++++++-----------
 src/librustdoc/html/static/js/settings.js   | 10 ++++----
 src/librustdoc/html/static/js/storage.js    |  4 ++--
 tests/rustdoc-gui/source-code-wrapping.goml |  9 ++++---
 4 files changed, 26 insertions(+), 23 deletions(-)

diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 8e904c6200e6f..0ea4d8f1e3914 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -938,54 +938,54 @@ ul.block, .block li, .block ul {
 .example-wrap .line-highlighted[data-nosnippet] {
 	background-color: var(--src-line-number-highlighted-background-color);
 }
-:root.wrap-source-code .example-wrap [data-nosnippet] {
+:root.word-wrap-source-code .example-wrap [data-nosnippet] {
 	position: absolute;
 	left: 0;
 }
-.wrap-source-code .example-wrap pre > code {
+.word-wrap-source-code .example-wrap pre > code {
 	position: relative;
 	word-break: break-all;
 }
-:root.wrap-source-code .example-wrap pre > code {
+:root.word-wrap-source-code .example-wrap pre > code {
 	display: block;
 	white-space: pre-wrap;
 }
-:root.wrap-source-code .example-wrap pre > code * {
+:root.word-wrap-source-code .example-wrap pre > code * {
 	word-break: break-all;
 }
-:root.wrap-source-code .example-wrap.digits-1 pre > code {
+:root.word-wrap-source-code .example-wrap.digits-1 pre > code {
 	padding-left: calc(
 		1ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
 }
-:root.wrap-source-code .example-wrap.digits-2 pre > code {
+:root.word-wrap-source-code .example-wrap.digits-2 pre > code {
 	padding-left: calc(
 		2ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
 }
-:root.wrap-source-code .example-wrap.digits-3 pre > code {
+:root.word-wrap-source-code .example-wrap.digits-3 pre > code {
 	padding-left: calc(
 		3ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
 }
-:root.wrap-source-code .example-wrap.digits-4 pre > code {
+:root.word-wrap-source-code .example-wrap.digits-4 pre > code {
 	padding-left: calc(
 		4ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
 }
-:root.wrap-source-code .example-wrap.digits-5 pre > code {
+:root.word-wrap-source-code .example-wrap.digits-5 pre > code {
 	padding-left: calc(
 		5ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
 }
-:root.wrap-source-code .example-wrap.digits-6 pre > code {
+:root.word-wrap-source-code .example-wrap.digits-6 pre > code {
 	padding-left: calc(
 		6ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
 }
-:root.wrap-source-code .example-wrap.digits-7 pre > code {
+:root.word-wrap-source-code .example-wrap.digits-7 pre > code {
 	padding-left: calc(
 		7ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
 }
-:root.wrap-source-code .example-wrap.digits-8 pre > code {
+:root.word-wrap-source-code .example-wrap.digits-8 pre > code {
 	padding-left: calc(
 		8ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
 }
-:root.wrap-source-code .example-wrap.digits-9 pre > code {
+:root.word-wrap-source-code .example-wrap.digits-9 pre > code {
 	padding-left: calc(
 		9ch + var(--line-number-padding) * 2 + var(--line-number-right-margin));
 }
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index 2cf6d6fde8f1a..5f1bbd27328cb 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -60,11 +60,11 @@
                     removeClass(document.documentElement, "sans-serif");
                 }
                 break;
-            case "wrap-source-code":
+            case "word-wrap-source-code":
                 if (value === true) {
-                    addClass(document.documentElement, "wrap-source-code");
+                    addClass(document.documentElement, "word-wrap-source-code");
                 } else {
-                    removeClass(document.documentElement, "wrap-source-code");
+                    removeClass(document.documentElement, "word-wrap-source-code");
                 }
                 break;
         }
@@ -255,8 +255,8 @@
                 "default": false,
             },
             {
-                "name": "Wrap source codes",
-                "js_name": "wrap-source-code",
+                "name": "Word wrap source code",
+                "js_name": "word-wrap-source-code",
                 "default": false,
             },
         ];
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index 4f70166848c34..425b915b5f94e 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -286,8 +286,8 @@ if (getSettingValue("hide-modnav") === "true") {
 if (getSettingValue("sans-serif-fonts") === "true") {
     addClass(document.documentElement, "sans-serif");
 }
-if (getSettingValue("wrap-source-code") === "true") {
-    addClass(document.documentElement, "wrap-source-code");
+if (getSettingValue("word-wrap-source-code") === "true") {
+    addClass(document.documentElement, "word-wrap-source-code");
 }
 function updateSidebarWidth() {
     const desktopSidebarWidth = getSettingValue("desktop-sidebar-width");
diff --git a/tests/rustdoc-gui/source-code-wrapping.goml b/tests/rustdoc-gui/source-code-wrapping.goml
index 6b70c4c3447c7..cb2fd3052cdac 100644
--- a/tests/rustdoc-gui/source-code-wrapping.goml
+++ b/tests/rustdoc-gui/source-code-wrapping.goml
@@ -1,13 +1,14 @@
 // Checks that the interactions with the source code pages are working as expected.
 go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
 show-text: true
+set-window-size: (1000, 1000)
 
 define-function: (
     "click-code-wrapping",
     [expected],
     block {
-        click: "#wrap-source-code"
-        wait-for-local-storage: {"rustdoc-wrap-source-code": |expected|}
+        click: "#word-wrap-source-code"
+        wait-for-local-storage: {"rustdoc-word-wrap-source-code": |expected|}
     },
 )
 
@@ -15,6 +16,7 @@ store-size: (".rust code", {"width": width, "height": height})
 click: "#settings-menu"
 wait-for: "#settings"
 call-function: ("click-code-wrapping", {"expected": "true"})
+wait-for-size-false: (".rust code", {"width": |width|, "height": |height|})
 store-size: (".rust code", {"width": new_width, "height": new_height})
 // The width should now be smaller than the window's and the height
 // should have increased.
@@ -32,6 +34,7 @@ wait-for: "#settings"
 store-size: (".example-wrap .rust code", {"width": rust_width, "height": rust_height})
 store-size: (".example-wrap .language-text code", {"width": txt_width, "height": txt_height})
 call-function: ("click-code-wrapping", {"expected": "true"})
+wait-for-size-false: (".example-wrap .rust code", {"width": |rust_width|, "height": |rust_height|})
 
 store-size: (".example-wrap .rust code", {"width": new_rust_width, "height": new_rust_height})
 store-size: (".example-wrap .language-text code", {"width": new_txt_width, "height": new_txt_height})
@@ -40,5 +43,5 @@ assert: |rust_width| > |new_rust_width| && |rust_height| < |new_rust_height|
 assert: |txt_width| > |new_txt_width| && |txt_height| < |new_txt_height|
 
 call-function: ("click-code-wrapping", {"expected": "false"})
-assert-size: (".example-wrap .rust code", {"width": |rust_width|, "height": |rust_height|})
+wait-for-size: (".example-wrap .rust code", {"width": |rust_width|, "height": |rust_height|})
 assert-size: (".example-wrap .language-text code", {"width": |txt_width|, "height": |txt_height|})

From 87c03807994b3767b9dc806df730ca2491d4cf94 Mon Sep 17 00:00:00 2001
From: Dennis Duda 
Date: Sun, 23 Feb 2025 14:19:58 +0100
Subject: [PATCH 13/18] Win: use existing wrappers for
 `SetFileInformationByHandle` in `File::open_native`

---
 library/std/src/sys/pal/windows/fs.rs | 36 ++++++++-------------------
 1 file changed, 11 insertions(+), 25 deletions(-)

diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs
index 0ddce30cf8e44..dce5a429cb0d4 100644
--- a/library/std/src/sys/pal/windows/fs.rs
+++ b/library/std/src/sys/pal/windows/fs.rs
@@ -1,4 +1,4 @@
-use super::api::{self, WinError};
+use super::api::{self, WinError, set_file_information_by_handle};
 use super::{IoResult, to_u16s};
 use crate::alloc::{alloc, handle_alloc_error};
 use crate::borrow::Cow;
@@ -319,31 +319,17 @@ impl File {
                 && creation == c::OPEN_ALWAYS
                 && api::get_last_error() == WinError::ALREADY_EXISTS
             {
-                unsafe {
-                    // This first tries `FileAllocationInfo` but falls back to
-                    // `FileEndOfFileInfo` in order to support WINE.
-                    // If WINE gains support for FileAllocationInfo, we should
-                    // remove the fallback.
-                    let alloc = c::FILE_ALLOCATION_INFO { AllocationSize: 0 };
-                    let result = c::SetFileInformationByHandle(
-                        handle.as_raw_handle(),
-                        c::FileAllocationInfo,
-                        (&raw const alloc).cast::(),
-                        mem::size_of::() as u32,
-                    );
-                    if result == 0 {
+                // This first tries `FileAllocationInfo` but falls back to
+                // `FileEndOfFileInfo` in order to support WINE.
+                // If WINE gains support for FileAllocationInfo, we should
+                // remove the fallback.
+                let alloc = c::FILE_ALLOCATION_INFO { AllocationSize: 0 };
+                set_file_information_by_handle(handle.as_raw_handle(), &alloc)
+                    .or_else(|_| {
                         let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 };
-                        let result = c::SetFileInformationByHandle(
-                            handle.as_raw_handle(),
-                            c::FileEndOfFileInfo,
-                            (&raw const eof).cast::(),
-                            mem::size_of::() as u32,
-                        );
-                        if result == 0 {
-                            return Err(io::Error::last_os_error());
-                        }
-                    }
-                }
+                        set_file_information_by_handle(handle.as_raw_handle(), &eof)
+                    })
+                    .io_result()?;
             }
             Ok(File { handle: Handle::from_inner(handle) })
         } else {

From 1a440d56d63458e4491f289c4950396f73304264 Mon Sep 17 00:00:00 2001
From: yukang 
Date: Sun, 23 Feb 2025 22:29:53 +0800
Subject: [PATCH 14/18] Fix documentation for unstable sort

---
 library/core/src/slice/mod.rs | 48 ++++++++++++++++++++++++-----------
 1 file changed, 33 insertions(+), 15 deletions(-)

diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 7b9a76e814a15..c8f723dd0c1e4 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -2928,10 +2928,17 @@ impl [T] {
     /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not
     /// allocate), and *O*(*n* \* log(*n*)) worst-case.
     ///
-    /// If the implementation of [`Ord`] for `T` does not implement a [total order] the resulting
-    /// order of elements in the slice is unspecified. All original elements will remain in the
-    /// slice and any possible modifications via interior mutability are observed in the input. Same
-    /// is true if the implementation of [`Ord`] for `T` panics.
+    /// If the implementation of [`Ord`] for `T` does not implement a [total order], the function
+    /// may panic; even if the function exits normally, the resulting order of elements in the slice
+    /// is unspecified. See also the note on panicking below.
+    ///
+    /// For example `|a, b| (a - b).cmp(a)` is a comparison function that is neither transitive nor
+    /// reflexive nor total, `a < b < c < a` with `a = 1, b = 2, c = 3`. For more information and
+    /// examples see the [`Ord`] documentation.
+    ///
+    ///
+    /// All original elements will remain in the slice and any possible modifications via interior
+    /// mutability are observed in the input. Same is true if the implementation of [`Ord`] for `T` panics.
     ///
     /// Sorting types that only implement [`PartialOrd`] such as [`f32`] and [`f64`] require
     /// additional precautions. For example, `f32::NAN != f32::NAN`, which doesn't fulfill the
@@ -2954,7 +2961,8 @@ impl [T] {
     ///
     /// # Panics
     ///
-    /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order].
+    /// May panic if the implementation of [`Ord`] for `T` does not implement a [total order], or if
+    /// the [`Ord`] implementation panics.
     ///
     /// # Examples
     ///
@@ -2982,15 +2990,17 @@ impl [T] {
     /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not
     /// allocate), and *O*(*n* \* log(*n*)) worst-case.
     ///
-    /// If the comparison function `compare` does not implement a [total order] the resulting order
-    /// of elements in the slice is unspecified. All original elements will remain in the slice and
-    /// any possible modifications via interior mutability are observed in the input. Same is true
-    /// if `compare` panics.
+    /// If the comparison function `compare` does not implement a [total order], the function
+    /// may panic; even if the function exits normally, the resulting order of elements in the slice
+    /// is unspecified. See also the note on panicking below.
     ///
     /// For example `|a, b| (a - b).cmp(a)` is a comparison function that is neither transitive nor
     /// reflexive nor total, `a < b < c < a` with `a = 1, b = 2, c = 3`. For more information and
     /// examples see the [`Ord`] documentation.
     ///
+    /// All original elements will remain in the slice and any possible modifications via interior
+    /// mutability are observed in the input. Same is true if `compare` panics.
+    ///
     /// # Current implementation
     ///
     /// The current implementation is based on [ipnsort] by Lukas Bergdoll and Orson Peters, which
@@ -3003,7 +3013,8 @@ impl [T] {
     ///
     /// # Panics
     ///
-    /// May panic if `compare` does not implement a [total order].
+    /// May panic if the `compare` does not implement a [total order], or if
+    /// the `compare` itself panics.
     ///
     /// # Examples
     ///
@@ -3034,10 +3045,16 @@ impl [T] {
     /// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not
     /// allocate), and *O*(*n* \* log(*n*)) worst-case.
     ///
-    /// If the implementation of [`Ord`] for `K` does not implement a [total order] the resulting
-    /// order of elements in the slice is unspecified. All original elements will remain in the
-    /// slice and any possible modifications via interior mutability are observed in the input. Same
-    /// is true if the implementation of [`Ord`] for `K` panics.
+    /// If the implementation of [`Ord`] for `K` does not implement a [total order], the function
+    /// may panic; even if the function exits normally, the resulting order of elements in the slice
+    /// is unspecified. See also the note on panicking below.
+    ///
+    /// For example `|a, b| (a - b).cmp(a)` is a comparison function that is neither transitive nor
+    /// reflexive nor total, `a < b < c < a` with `a = 1, b = 2, c = 3`. For more information and
+    /// examples see the [`Ord`] documentation.
+    ///
+    /// All original elements will remain in the slice and any possible modifications via interior
+    /// mutability are observed in the input. Same is true if the implementation of [`Ord`] for `K` panics.
     ///
     /// # Current implementation
     ///
@@ -3051,7 +3068,8 @@ impl [T] {
     ///
     /// # Panics
     ///
-    /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order].
+    /// May panic if the implementation of [`Ord`] for `K` does not implement a [total order], or if
+    /// the [`Ord`] implementation panics.
     ///
     /// # Examples
     ///

From e15ce94eae9e6d085b64cb0d9226c3548494aee9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?=
 <39484203+jieyouxu@users.noreply.github.com>
Date: Mon, 24 Feb 2025 01:12:55 +0800
Subject: [PATCH 15/18] tests: tighten `CHECK-NOT`s to make
 `str-to-string-128690.rs` less likely to collide with symbol name mangling

---
 tests/codegen/issues/str-to-string-128690.rs | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/tests/codegen/issues/str-to-string-128690.rs b/tests/codegen/issues/str-to-string-128690.rs
index 8b416306ba66e..d9e69764be28e 100644
--- a/tests/codegen/issues/str-to-string-128690.rs
+++ b/tests/codegen/issues/str-to-string-128690.rs
@@ -2,25 +2,27 @@
 #![crate_type = "lib"]
 
 //! Make sure str::to_string is specialized not to use fmt machinery.
+//!
+//! Note that the `CHECK-NOT`s here try to match on calls to functions under `core::fmt`.
 
 // CHECK-LABEL: define {{(dso_local )?}}void @one_ref
 #[no_mangle]
 pub fn one_ref(input: &str) -> String {
-    // CHECK-NOT: {{(call|invoke).*}}fmt
+    // CHECK-NOT: {{(call|invoke)}}{{.*}}@{{.*}}core{{.*}}fmt{{.*}}
     input.to_string()
 }
 
 // CHECK-LABEL: define {{(dso_local )?}}void @two_ref
 #[no_mangle]
 pub fn two_ref(input: &&str) -> String {
-    // CHECK-NOT: {{(call|invoke).*}}fmt
+    // CHECK-NOT: {{(call|invoke)}}{{.*}}@{{.*}}core{{.*}}fmt{{.*}}
     input.to_string()
 }
 
 // CHECK-LABEL: define {{(dso_local )?}}void @thirteen_ref
 #[no_mangle]
 pub fn thirteen_ref(input: &&&&&&&&&&&&&str) -> String {
-    // CHECK-NOT: {{(call|invoke).*}}fmt
+    // CHECK-NOT: {{(call|invoke)}}{{.*}}@{{.*}}core{{.*}}fmt{{.*}}
     input.to_string()
 }
 
@@ -31,6 +33,6 @@ pub fn thirteen_ref(input: &&&&&&&&&&&&&str) -> String {
 // CHECK-LABEL: define {{(dso_local )?}}void @fourteen_ref
 #[no_mangle]
 pub fn fourteen_ref(input: &&&&&&&&&&&&&&str) -> String {
-    // CHECK: {{(call|invoke).*}}fmt
+    // CHECK: {{(call|invoke)}}{{.*}}@{{.*}}core{{.*}}fmt{{.*}}
     input.to_string()
 }

From 1eddb158f9f804a9ca843d5ccce70c341340c9d2 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Mon, 24 Feb 2025 09:16:10 +1100
Subject: [PATCH 16/18] Move `impl` blocks out of
 `rustc_middle/src/mir/syntax.rs`.

As the comment at the top says, this file is not supposed to contain any
code. But some has crept in. This commit moves it out.
---
 compiler/rustc_middle/src/mir/mod.rs        |  11 ++
 compiler/rustc_middle/src/mir/statement.rs  |  57 ++++++++++
 compiler/rustc_middle/src/mir/syntax.rs     | 112 --------------------
 compiler/rustc_middle/src/mir/terminator.rs |  38 +++++++
 4 files changed, 106 insertions(+), 112 deletions(-)

diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index cf90df1b198b8..ea0bb5feb1220 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -96,6 +96,17 @@ impl<'tcx> HasLocalDecls<'tcx> for Body<'tcx> {
 }
 
 impl MirPhase {
+    pub fn name(&self) -> &'static str {
+        match *self {
+            MirPhase::Built => "built",
+            MirPhase::Analysis(AnalysisPhase::Initial) => "analysis",
+            MirPhase::Analysis(AnalysisPhase::PostCleanup) => "analysis-post-cleanup",
+            MirPhase::Runtime(RuntimePhase::Initial) => "runtime",
+            MirPhase::Runtime(RuntimePhase::PostCleanup) => "runtime-post-cleanup",
+            MirPhase::Runtime(RuntimePhase::Optimized) => "runtime-optimized",
+        }
+    }
+
     /// Gets the (dialect, phase) index of the current `MirPhase`. Both numbers
     /// are 1-indexed.
     pub fn index(&self) -> (usize, usize) {
diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs
index 595a5e548b011..f05a798949b7e 100644
--- a/compiler/rustc_middle/src/mir/statement.rs
+++ b/compiler/rustc_middle/src/mir/statement.rs
@@ -25,6 +25,26 @@ impl Statement<'_> {
 }
 
 impl<'tcx> StatementKind<'tcx> {
+    /// Returns a simple string representation of a `StatementKind` variant, independent of any
+    /// values it might hold (e.g. `StatementKind::Assign` always returns `"Assign"`).
+    pub const fn name(&self) -> &'static str {
+        match self {
+            StatementKind::Assign(..) => "Assign",
+            StatementKind::FakeRead(..) => "FakeRead",
+            StatementKind::SetDiscriminant { .. } => "SetDiscriminant",
+            StatementKind::Deinit(..) => "Deinit",
+            StatementKind::StorageLive(..) => "StorageLive",
+            StatementKind::StorageDead(..) => "StorageDead",
+            StatementKind::Retag(..) => "Retag",
+            StatementKind::PlaceMention(..) => "PlaceMention",
+            StatementKind::AscribeUserType(..) => "AscribeUserType",
+            StatementKind::Coverage(..) => "Coverage",
+            StatementKind::Intrinsic(..) => "Intrinsic",
+            StatementKind::ConstEvalCounter => "ConstEvalCounter",
+            StatementKind::Nop => "Nop",
+            StatementKind::BackwardIncompatibleDropHint { .. } => "BackwardIncompatibleDropHint",
+        }
+    }
     pub fn as_assign_mut(&mut self) -> Option<&mut (Place<'tcx>, Rvalue<'tcx>)> {
         match self {
             StatementKind::Assign(x) => Some(x),
@@ -862,3 +882,40 @@ impl<'tcx> BinOp {
         })
     }
 }
+
+impl From for RawPtrKind {
+    fn from(other: Mutability) -> Self {
+        match other {
+            Mutability::Mut => RawPtrKind::Mut,
+            Mutability::Not => RawPtrKind::Const,
+        }
+    }
+}
+
+impl RawPtrKind {
+    pub fn is_fake(self) -> bool {
+        match self {
+            RawPtrKind::Mut | RawPtrKind::Const => false,
+            RawPtrKind::FakeForPtrMetadata => true,
+        }
+    }
+
+    pub fn to_mutbl_lossy(self) -> Mutability {
+        match self {
+            RawPtrKind::Mut => Mutability::Mut,
+            RawPtrKind::Const => Mutability::Not,
+
+            // We have no type corresponding to a fake borrow, so use
+            // `*const` as an approximation.
+            RawPtrKind::FakeForPtrMetadata => Mutability::Not,
+        }
+    }
+
+    pub fn ptr_str(self) -> &'static str {
+        match self {
+            RawPtrKind::Mut => "mut",
+            RawPtrKind::Const => "const",
+            RawPtrKind::FakeForPtrMetadata => "const (fake)",
+        }
+    }
+}
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index af6f0e4c55183..4f86703e95376 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -97,19 +97,6 @@ pub enum MirPhase {
     Runtime(RuntimePhase),
 }
 
-impl MirPhase {
-    pub fn name(&self) -> &'static str {
-        match *self {
-            MirPhase::Built => "built",
-            MirPhase::Analysis(AnalysisPhase::Initial) => "analysis",
-            MirPhase::Analysis(AnalysisPhase::PostCleanup) => "analysis-post-cleanup",
-            MirPhase::Runtime(RuntimePhase::Initial) => "runtime",
-            MirPhase::Runtime(RuntimePhase::PostCleanup) => "runtime-post-cleanup",
-            MirPhase::Runtime(RuntimePhase::Optimized) => "runtime-optimized",
-        }
-    }
-}
-
 /// See [`MirPhase::Analysis`].
 #[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)]
 #[derive(HashStable)]
@@ -206,43 +193,6 @@ pub enum RawPtrKind {
     FakeForPtrMetadata,
 }
 
-impl From for RawPtrKind {
-    fn from(other: Mutability) -> Self {
-        match other {
-            Mutability::Mut => RawPtrKind::Mut,
-            Mutability::Not => RawPtrKind::Const,
-        }
-    }
-}
-
-impl RawPtrKind {
-    pub fn is_fake(self) -> bool {
-        match self {
-            RawPtrKind::Mut | RawPtrKind::Const => false,
-            RawPtrKind::FakeForPtrMetadata => true,
-        }
-    }
-
-    pub fn to_mutbl_lossy(self) -> Mutability {
-        match self {
-            RawPtrKind::Mut => Mutability::Mut,
-            RawPtrKind::Const => Mutability::Not,
-
-            // We have no type corresponding to a fake borrow, so use
-            // `*const` as an approximation.
-            RawPtrKind::FakeForPtrMetadata => Mutability::Not,
-        }
-    }
-
-    pub fn ptr_str(self) -> &'static str {
-        match self {
-            RawPtrKind::Mut => "mut",
-            RawPtrKind::Const => "const",
-            RawPtrKind::FakeForPtrMetadata => "const (fake)",
-        }
-    }
-}
-
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)]
 #[derive(Hash, HashStable)]
 pub enum MutBorrowKind {
@@ -515,29 +465,6 @@ pub enum StatementKind<'tcx> {
     },
 }
 
-impl StatementKind<'_> {
-    /// Returns a simple string representation of a `StatementKind` variant, independent of any
-    /// values it might hold (e.g. `StatementKind::Assign` always returns `"Assign"`).
-    pub const fn name(&self) -> &'static str {
-        match self {
-            StatementKind::Assign(..) => "Assign",
-            StatementKind::FakeRead(..) => "FakeRead",
-            StatementKind::SetDiscriminant { .. } => "SetDiscriminant",
-            StatementKind::Deinit(..) => "Deinit",
-            StatementKind::StorageLive(..) => "StorageLive",
-            StatementKind::StorageDead(..) => "StorageDead",
-            StatementKind::Retag(..) => "Retag",
-            StatementKind::PlaceMention(..) => "PlaceMention",
-            StatementKind::AscribeUserType(..) => "AscribeUserType",
-            StatementKind::Coverage(..) => "Coverage",
-            StatementKind::Intrinsic(..) => "Intrinsic",
-            StatementKind::ConstEvalCounter => "ConstEvalCounter",
-            StatementKind::Nop => "Nop",
-            StatementKind::BackwardIncompatibleDropHint { .. } => "BackwardIncompatibleDropHint",
-        }
-    }
-}
-
 #[derive(
     Clone,
     TyEncodable,
@@ -673,12 +600,6 @@ pub enum CallSource {
     Normal,
 }
 
-impl CallSource {
-    pub fn from_hir_call(self) -> bool {
-        matches!(self, CallSource::Normal)
-    }
-}
-
 #[derive(Clone, Copy, Debug, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
 #[derive(TypeFoldable, TypeVisitable)]
 /// The macro that an inline assembly block was created by
@@ -689,15 +610,6 @@ pub enum InlineAsmMacro {
     NakedAsm,
 }
 
-impl InlineAsmMacro {
-    pub const fn diverges(self, options: InlineAsmOptions) -> bool {
-        match self {
-            InlineAsmMacro::Asm => options.contains(InlineAsmOptions::NORETURN),
-            InlineAsmMacro::NakedAsm => true,
-        }
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////
 // Terminators
 
@@ -999,30 +911,6 @@ pub enum BackwardIncompatibleDropReason {
     Edition2024,
 }
 
-impl TerminatorKind<'_> {
-    /// Returns a simple string representation of a `TerminatorKind` variant, independent of any
-    /// values it might hold (e.g. `TerminatorKind::Call` always returns `"Call"`).
-    pub const fn name(&self) -> &'static str {
-        match self {
-            TerminatorKind::Goto { .. } => "Goto",
-            TerminatorKind::SwitchInt { .. } => "SwitchInt",
-            TerminatorKind::UnwindResume => "UnwindResume",
-            TerminatorKind::UnwindTerminate(_) => "UnwindTerminate",
-            TerminatorKind::Return => "Return",
-            TerminatorKind::Unreachable => "Unreachable",
-            TerminatorKind::Drop { .. } => "Drop",
-            TerminatorKind::Call { .. } => "Call",
-            TerminatorKind::TailCall { .. } => "TailCall",
-            TerminatorKind::Assert { .. } => "Assert",
-            TerminatorKind::Yield { .. } => "Yield",
-            TerminatorKind::CoroutineDrop => "CoroutineDrop",
-            TerminatorKind::FalseEdge { .. } => "FalseEdge",
-            TerminatorKind::FalseUnwind { .. } => "FalseUnwind",
-            TerminatorKind::InlineAsm { .. } => "InlineAsm",
-        }
-    }
-}
-
 #[derive(Debug, Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)]
 pub struct SwitchTargets {
     /// Possible values. For each value, the location to branch to is found in
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index fdfcb128778a7..7d6795ee678c0 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -2,6 +2,7 @@
 
 use std::slice;
 
+use rustc_ast::InlineAsmOptions;
 use rustc_data_structures::packed::Pu128;
 use rustc_hir::LangItem;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
@@ -414,6 +415,28 @@ impl<'tcx> Terminator<'tcx> {
 }
 
 impl<'tcx> TerminatorKind<'tcx> {
+    /// Returns a simple string representation of a `TerminatorKind` variant, independent of any
+    /// values it might hold (e.g. `TerminatorKind::Call` always returns `"Call"`).
+    pub const fn name(&self) -> &'static str {
+        match self {
+            TerminatorKind::Goto { .. } => "Goto",
+            TerminatorKind::SwitchInt { .. } => "SwitchInt",
+            TerminatorKind::UnwindResume => "UnwindResume",
+            TerminatorKind::UnwindTerminate(_) => "UnwindTerminate",
+            TerminatorKind::Return => "Return",
+            TerminatorKind::Unreachable => "Unreachable",
+            TerminatorKind::Drop { .. } => "Drop",
+            TerminatorKind::Call { .. } => "Call",
+            TerminatorKind::TailCall { .. } => "TailCall",
+            TerminatorKind::Assert { .. } => "Assert",
+            TerminatorKind::Yield { .. } => "Yield",
+            TerminatorKind::CoroutineDrop => "CoroutineDrop",
+            TerminatorKind::FalseEdge { .. } => "FalseEdge",
+            TerminatorKind::FalseUnwind { .. } => "FalseUnwind",
+            TerminatorKind::InlineAsm { .. } => "InlineAsm",
+        }
+    }
+
     #[inline]
     pub fn if_(cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> {
         TerminatorKind::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) }
@@ -698,3 +721,18 @@ impl<'tcx> TerminatorKind<'tcx> {
         }
     }
 }
+
+impl CallSource {
+    pub fn from_hir_call(self) -> bool {
+        matches!(self, CallSource::Normal)
+    }
+}
+
+impl InlineAsmMacro {
+    pub const fn diverges(self, options: InlineAsmOptions) -> bool {
+        match self {
+            InlineAsmMacro::Asm => options.contains(InlineAsmOptions::NORETURN),
+            InlineAsmMacro::NakedAsm => true,
+        }
+    }
+}

From 4183c085116e36a2ee78a5b1f3d94892cefce7ba Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote 
Date: Mon, 24 Feb 2025 09:30:42 +1100
Subject: [PATCH 17/18] Fix some `use` items that import more than necessary.

---
 compiler/rustc_middle/src/mir/terminator.rs                | 2 +-
 compiler/rustc_middle/src/ty/mod.rs                        | 2 +-
 compiler/rustc_mir_build/src/builder/matches/match_pair.rs | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index 7d6795ee678c0..b887370fd699a 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -8,7 +8,7 @@ use rustc_hir::LangItem;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
 use smallvec::{SmallVec, smallvec};
 
-use super::{TerminatorKind, *};
+use super::*;
 
 impl SwitchTargets {
     /// Creates switch targets from an iterator of values and target blocks.
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 8ed5a118093f3..dbbbdc606bbb4 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -47,7 +47,7 @@ pub use rustc_session::lint::RegisteredTools;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::{ExpnId, ExpnKind, Ident, Span, Symbol, kw, sym};
 pub use rustc_type_ir::relate::VarianceDiagInfo;
-pub use rustc_type_ir::{Movability, Mutability, *};
+pub use rustc_type_ir::*;
 use tracing::{debug, instrument};
 pub use vtable::*;
 use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir};
diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs
index ee331713736e7..10b43390eb288 100644
--- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs
+++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs
@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use rustc_middle::mir::*;
-use rustc_middle::thir::{self, *};
+use rustc_middle::thir::*;
 use rustc_middle::ty::{self, Ty, TypeVisitableExt};
 
 use crate::builder::Builder;
@@ -134,7 +134,7 @@ impl<'tcx> MatchPairTree<'tcx> {
             PatKind::Constant { value } => TestCase::Constant { value },
 
             PatKind::AscribeUserType {
-                ascription: thir::Ascription { ref annotation, variance },
+                ascription: Ascription { ref annotation, variance },
                 ref subpattern,
                 ..
             } => {

From f49b6c6cd58aa61c804dcb22cc013c728f8924af Mon Sep 17 00:00:00 2001
From: Madhav Madhusoodanan 
Date: Mon, 24 Feb 2025 00:36:25 +0530
Subject: [PATCH 18/18] Added into_value const function to ControlFlow

Fixed issue with usage of generics and moved feature gate to crate root

Removed const tag

Fixed alphabetical ordering of feature gate, added same to doctest

Removed crate-level declaration of feature gate control_flow_into_value

Used const_precise_live_drops to constify into_value without issue of a drop
---
 library/core/src/ops/control_flow.rs | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs
index c8fcee5c140f5..8993e14fcd379 100644
--- a/library/core/src/ops/control_flow.rs
+++ b/library/core/src/ops/control_flow.rs
@@ -229,6 +229,27 @@ impl ControlFlow {
     }
 }
 
+impl ControlFlow {
+    /// Extracts the value `T` that is wrapped by `ControlFlow`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(control_flow_into_value)]
+    /// use std::ops::ControlFlow;
+    ///
+    /// assert_eq!(ControlFlow::::Break(1024).into_value(), 1024);
+    /// assert_eq!(ControlFlow::::Continue(512).into_value(), 512);
+    /// ```
+    #[unstable(feature = "control_flow_into_value", issue = "137461")]
+    #[rustc_allow_const_fn_unstable(const_precise_live_drops)]
+    pub const fn into_value(self) -> T {
+        match self {
+            ControlFlow::Continue(x) | ControlFlow::Break(x) => x,
+        }
+    }
+}
+
 /// These are used only as part of implementing the iterator adapters.
 /// They have mediocre names and non-obvious semantics, so aren't
 /// currently on a path to potential stabilization.