diff --git a/.github/workflows/flux.yml b/.github/workflows/flux.yml index 78a59426259c5..9870f2d925232 100644 --- a/.github/workflows/flux.yml +++ b/.github/workflows/flux.yml @@ -8,8 +8,8 @@ on: branches: [main] env: - FLUX_VERSION: "27e3ef52b52253a1d128aff7b80894bed4da0c95" - FIXPOINT_VERSION: "nightly-01-14-2026" + FLUX_VERSION: "9c1b5e278791f57eed1fd109816bbe180423fa9d" + FIXPOINT_VERSION: "nightly-2026-07-20" jobs: check-flux-on-core: diff --git a/.github/workflows/kani.yml b/.github/workflows/kani.yml index 0f3661885946c..56a3a7357c7cf 100644 --- a/.github/workflows/kani.yml +++ b/.github/workflows/kani.yml @@ -35,6 +35,10 @@ jobs: WORKER_INDEX: ${{ matrix.partition }} # Total number of workers running this step WORKER_TOTAL: 4 + # Cap parallel harness verification on ubuntu-latest: its 4-core/16 GB + # runners get OOM-killed when 4 memory-hungry harnesses (up to ~10 GB + # each, e.g. the ffi::c_str ones) run concurrently. + KANI_JOBS: ${{ matrix.os == 'ubuntu-latest' && 2 || '' }} steps: - name: Remove unnecessary software to free up disk space diff --git a/library/Cargo.lock b/library/Cargo.lock index 8f60cea459c7d..4801f92c63e5a 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -146,9 +146,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" dependencies = [ "rustc-std-workspace-core", ] @@ -175,9 +175,9 @@ dependencies = [ [[package]] name = "moto-rt" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058a2807a30527bee4c30df7ababe971cdde94372d4dbd1ff145bb403381436c" +checksum = "29aea9f7dfeb258e030a84e0ec38a9c2ec2063d4f45eb2db31445cfc40b3dba1" dependencies = [ "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -274,21 +274,20 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" dependencies = [ "rustc-std-workspace-core", ] [[package]] name = "rustc-literal-escaper" -version = "0.0.5" +version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ee29da77c5a54f42697493cd4c9b9f31b74df666a6c04dfc4fde77abe0438b" +checksum = "8be87abb9e40db7466e0681dc8ecd9dcfd40360cb10b4c8fe24a7c4c3669b198" dependencies = [ "rustc-std-workspace-core", - "rustc-std-workspace-std", ] [[package]] @@ -347,7 +346,7 @@ dependencies = [ "vex-sdk", "wasi 0.11.1+wasi-snapshot-preview1", "wasi 0.14.4+wasi-0.2.4", - "windows-targets 0.0.0", + "windows-link 0.0.0", ] [[package]] @@ -428,6 +427,10 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "windows-link" +version = "0.0.0" + [[package]] name = "windows-link" version = "0.2.1" @@ -440,20 +443,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] -[[package]] -name = "windows-targets" -version = "0.0.0" - [[package]] name = "windows-targets" version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link", + "windows-link 0.2.1", "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", diff --git a/library/Cargo.toml b/library/Cargo.toml index e30e624094285..87df966bb6b87 100644 --- a/library/Cargo.toml +++ b/library/Cargo.toml @@ -12,7 +12,7 @@ members = [ exclude = [ # stdarch has its own Cargo workspace "stdarch", - "windows_targets" + "windows_link" ] [profile.release.package.compiler_builtins] @@ -54,6 +54,32 @@ rustflags = ["-Cpanic=abort"] [profile.release.package.panic_abort] rustflags = ["-Cpanic=abort"] +# The "dist" profile is used by bootstrap for prebuilt libstd artifacts +# These settings ensure that the prebuilt artifacts support a variety of features +# in the user's profile. +[profile.dist] +inherits = "release" +codegen-units = 1 +debug = 1 # "limited" +rustflags = [ + # `profile.lto=off` implies `-Cembed-bitcode=no`, but unconditionally embedding + # bitcode is necessary for when users enable LTO. + # Required until Cargo can re-build the standard library based on the value + # of `profile.lto` in the user's profile. + "-Cembed-bitcode=yes", + # Enable frame pointers + "-Zunstable-options", + "-Cforce-frame-pointers=non-leaf", +] + +[profile.dist.package.panic_abort] +rustflags = [ + "-Cpanic=abort", + "-Cembed-bitcode=yes", + "-Zunstable-options", + "-Cforce-frame-pointers=non-leaf", +] + [patch.crates-io] # See comments in `library/rustc-std-workspace-core/README.md` for what's going on here rustc-std-workspace-core = { path = 'rustc-std-workspace-core' } diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index af8afe3533d40..bfe96a7b3bc94 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -22,7 +22,6 @@ safety = { path = "../contracts/safety" } [features] compiler-builtins-mem = ['compiler_builtins/mem'] compiler-builtins-c = ["compiler_builtins/c"] -compiler-builtins-no-f16-f128 = ["compiler_builtins/no-f16-f128"] # Choose algorithms that are optimized for binary size instead of runtime performance optimize_for_size = ["core/optimize_for_size"] diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 39450f69ce30a..cd1c2ea8fcd1e 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -5,8 +5,8 @@ #[stable(feature = "alloc_module", since = "1.28.0")] #[doc(inline)] pub use core::alloc::*; -use core::hint; use core::ptr::{self, NonNull}; +use core::{cmp, hint}; unsafe extern "Rust" { // These are the magic symbols to call the global allocator. rustc generates @@ -182,9 +182,9 @@ pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { impl Global { #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result, AllocError> { + fn alloc_impl_runtime(layout: Layout, zeroed: bool) -> Result, AllocError> { match layout.size() { - 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), + 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling_ptr(), 0)), // SAFETY: `layout` is non-zero in size, size => unsafe { let raw_ptr = if zeroed { alloc_zeroed(layout) } else { alloc(layout) }; @@ -194,10 +194,26 @@ impl Global { } } + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + fn deallocate_impl_runtime(ptr: NonNull, layout: Layout) { + if layout.size() != 0 { + // SAFETY: + // * We have checked that `layout` is non-zero in size. + // * The caller is obligated to provide a layout that "fits", and in this case, + // "fit" always means a layout that is equal to the original, because our + // `allocate()`, `grow()`, and `shrink()` implementations never returns a larger + // allocation than requested. + // * Other conditions must be upheld by the caller, as per `Allocator::deallocate()`'s + // safety documentation. + unsafe { dealloc(ptr.as_ptr(), layout) } + } + } + // SAFETY: Same as `Allocator::grow` #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - unsafe fn grow_impl( + fn grow_impl_runtime( &self, ptr: NonNull, old_layout: Layout, @@ -241,10 +257,172 @@ impl Global { }, } } + + // SAFETY: Same as `Allocator::grow` + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + fn shrink_impl_runtime( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + _zeroed: bool, + ) -> Result, AllocError> { + debug_assert!( + new_layout.size() <= old_layout.size(), + "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" + ); + + match new_layout.size() { + // SAFETY: conditions must be upheld by the caller + 0 => unsafe { + self.deallocate(ptr, old_layout); + Ok(NonNull::slice_from_raw_parts(new_layout.dangling_ptr(), 0)) + }, + + // SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller + new_size if old_layout.align() == new_layout.align() => unsafe { + // `realloc` probably checks for `new_size <= old_layout.size()` or something similar. + hint::assert_unchecked(new_size <= old_layout.size()); + + let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; + Ok(NonNull::slice_from_raw_parts(ptr, new_size)) + }, + + // SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`, + // both the old and new memory allocation are valid for reads and writes for `new_size` + // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap + // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract + // for `dealloc` must be upheld by the caller. + new_size => unsafe { + let new_ptr = self.allocate(new_layout)?; + ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size); + self.deallocate(ptr, old_layout); + Ok(new_ptr) + }, + } + } + + // SAFETY: Same as `Allocator::allocate` + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + const fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result, AllocError> { + core::intrinsics::const_eval_select( + (layout, zeroed), + Global::alloc_impl_const, + Global::alloc_impl_runtime, + ) + } + + // SAFETY: Same as `Allocator::deallocate` + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + const unsafe fn deallocate_impl(&self, ptr: NonNull, layout: Layout) { + core::intrinsics::const_eval_select( + (ptr, layout), + Global::deallocate_impl_const, + Global::deallocate_impl_runtime, + ) + } + + // SAFETY: Same as `Allocator::grow` + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + const unsafe fn grow_impl( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + zeroed: bool, + ) -> Result, AllocError> { + core::intrinsics::const_eval_select( + (self, ptr, old_layout, new_layout, zeroed), + Global::grow_shrink_impl_const, + Global::grow_impl_runtime, + ) + } + + // SAFETY: Same as `Allocator::shrink` + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + const unsafe fn shrink_impl( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + core::intrinsics::const_eval_select( + (self, ptr, old_layout, new_layout, false), + Global::grow_shrink_impl_const, + Global::shrink_impl_runtime, + ) + } + + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + const fn alloc_impl_const(layout: Layout, zeroed: bool) -> Result, AllocError> { + match layout.size() { + 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling_ptr(), 0)), + // SAFETY: `layout` is non-zero in size, + size => unsafe { + let raw_ptr = core::intrinsics::const_allocate(layout.size(), layout.align()); + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; + if zeroed { + // SAFETY: the pointer returned by `const_allocate` is valid to write to. + ptr.write_bytes(0, size); + } + Ok(NonNull::slice_from_raw_parts(ptr, size)) + }, + } + } + + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + const fn deallocate_impl_const(ptr: NonNull, layout: Layout) { + if layout.size() != 0 { + // SAFETY: We checked for nonzero size; other preconditions must be upheld by caller. + unsafe { + core::intrinsics::const_deallocate(ptr.as_ptr(), layout.size(), layout.align()); + } + } + } + + #[inline] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + const fn grow_shrink_impl_const( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + zeroed: bool, + ) -> Result, AllocError> { + let new_ptr = self.alloc_impl(new_layout, zeroed)?; + // SAFETY: both pointers are valid and this operations is in bounds. + unsafe { + ptr::copy_nonoverlapping( + ptr.as_ptr(), + new_ptr.as_mut_ptr(), + cmp::min(old_layout.size(), new_layout.size()), + ); + } + unsafe { + self.deallocate_impl(ptr, old_layout); + } + Ok(new_ptr) + } } #[unstable(feature = "allocator_api", issue = "32838")] -unsafe impl Allocator for Global { +#[rustc_const_unstable(feature = "const_heap", issue = "79597")] +unsafe impl const Allocator for Global { #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces fn allocate(&self, layout: Layout) -> Result, AllocError> { @@ -260,17 +438,8 @@ unsafe impl Allocator for Global { #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { - if layout.size() != 0 { - // SAFETY: - // * We have checked that `layout` is non-zero in size. - // * The caller is obligated to provide a layout that "fits", and in this case, - // "fit" always means a layout that is equal to the original, because our - // `allocate()`, `grow()`, and `shrink()` implementations never returns a larger - // allocation than requested. - // * Other conditions must be upheld by the caller, as per `Allocator::deallocate()`'s - // safety documentation. - unsafe { dealloc(ptr.as_ptr(), layout) } - } + // SAFETY: all conditions must be upheld by the caller + unsafe { self.deallocate_impl(ptr, layout) } } #[inline] @@ -305,40 +474,8 @@ unsafe impl Allocator for Global { old_layout: Layout, new_layout: Layout, ) -> Result, AllocError> { - debug_assert!( - new_layout.size() <= old_layout.size(), - "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" - ); - - match new_layout.size() { - // SAFETY: conditions must be upheld by the caller - 0 => unsafe { - self.deallocate(ptr, old_layout); - Ok(NonNull::slice_from_raw_parts(new_layout.dangling(), 0)) - }, - - // SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller - new_size if old_layout.align() == new_layout.align() => unsafe { - // `realloc` probably checks for `new_size <= old_layout.size()` or something similar. - hint::assert_unchecked(new_size <= old_layout.size()); - - let raw_ptr = realloc(ptr.as_ptr(), old_layout, new_size); - let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; - Ok(NonNull::slice_from_raw_parts(ptr, new_size)) - }, - - // SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`, - // both the old and new memory allocation are valid for reads and writes for `new_size` - // bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap - // `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract - // for `dealloc` must be upheld by the caller. - new_size => unsafe { - let new_ptr = self.allocate(new_layout)?; - ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), new_size); - self.deallocate(ptr, old_layout); - Ok(new_ptr) - }, - } + // SAFETY: all conditions must be upheld by the caller + unsafe { self.shrink_impl(ptr, old_layout, new_layout) } } } @@ -426,20 +563,9 @@ pub mod __alloc_error_handler { // `#[alloc_error_handler]`. #[rustc_std_internal_symbol] pub unsafe fn __rdl_alloc_error_handler(size: usize, _align: usize) -> ! { - unsafe extern "Rust" { - // This symbol is emitted by rustc next to __rust_alloc_error_handler. - // Its value depends on the -Zoom={panic,abort} compiler option. - #[rustc_std_internal_symbol] - fn __rust_alloc_error_handler_should_panic_v2() -> u8; - } - - if unsafe { __rust_alloc_error_handler_should_panic_v2() != 0 } { - panic!("memory allocation of {size} bytes failed") - } else { - core::panicking::panic_nounwind_fmt( - format_args!("memory allocation of {size} bytes failed"), - /* force_no_backtrace */ false, - ) - } + core::panicking::panic_nounwind_fmt( + format_args!("memory allocation of {size} bytes failed"), + /* force_no_backtrace */ false, + ) } } diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs index aa973e0bb0240..d1c7cd47da0f0 100644 --- a/library/alloc/src/borrow.rs +++ b/library/alloc/src/borrow.rs @@ -16,19 +16,6 @@ use crate::fmt; #[cfg(not(no_global_oom_handling))] use crate::string::String; -// FIXME(inference): const bounds removed due to inference regressions found by crater; -// see https://github.com/rust-lang/rust/issues/147964 -// #[rustc_const_unstable(feature = "const_convert", issue = "143773")] -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, B: ?Sized + ToOwned> Borrow for Cow<'a, B> -// where -// B::Owned: [const] Borrow, -{ - fn borrow(&self) -> &B { - &**self - } -} - /// A generalization of `Clone` to borrowed data. /// /// Some types make it possible to go from borrowed to owned, usually by @@ -192,6 +179,19 @@ where Owned(#[stable(feature = "rust1", since = "1.0.0")] ::Owned), } +// FIXME(inference): const bounds removed due to inference regressions found by crater; +// see https://github.com/rust-lang/rust/issues/147964 +// #[rustc_const_unstable(feature = "const_convert", issue = "143773")] +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, B: ?Sized + ToOwned> Borrow for Cow<'a, B> +// where +// B::Owned: [const] Borrow, +{ + fn borrow(&self) -> &B { + &**self + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Cow<'_, B> { fn clone(&self) -> Self { diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 2b767ffe02bee..0844239826bf5 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -184,7 +184,6 @@ #![stable(feature = "rust1", since = "1.0.0")] use core::borrow::{Borrow, BorrowMut}; -#[cfg(not(no_global_oom_handling))] use core::clone::CloneToUninit; use core::cmp::Ordering; use core::error::{self, Error}; @@ -733,6 +732,128 @@ impl Box { } } +impl Box { + /// Allocates memory on the heap then clones `src` into it. + /// + /// This doesn't actually allocate if `src` is zero-sized. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// + /// let hello: Box = Box::clone_from_ref("hello"); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "clone_from_ref", issue = "149075")] + #[must_use] + #[inline] + pub fn clone_from_ref(src: &T) -> Box { + Box::clone_from_ref_in(src, Global) + } + + /// Allocates memory on the heap then clones `src` into it, returning an error if allocation fails. + /// + /// This doesn't actually allocate if `src` is zero-sized. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// + /// let hello: Box = Box::try_clone_from_ref("hello")?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + #[must_use] + #[inline] + pub fn try_clone_from_ref(src: &T) -> Result, AllocError> { + Box::try_clone_from_ref_in(src, Global) + } +} + +impl Box { + /// Allocates memory in the given allocator then clones `src` into it. + /// + /// This doesn't actually allocate if `src` is zero-sized. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// + /// let hello: Box = Box::clone_from_ref_in("hello", System); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + #[must_use] + #[inline] + pub fn clone_from_ref_in(src: &T, alloc: A) -> Box { + let layout = Layout::for_value::(src); + match Box::try_clone_from_ref_in(src, alloc) { + Ok(bx) => bx, + Err(_) => handle_alloc_error(layout), + } + } + + /// Allocates memory in the given allocator then clones `src` into it, returning an error if allocation fails. + /// + /// This doesn't actually allocate if `src` is zero-sized. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// + /// let hello: Box = Box::try_clone_from_ref_in("hello", System)?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + #[must_use] + #[inline] + pub fn try_clone_from_ref_in(src: &T, alloc: A) -> Result, AllocError> { + struct DeallocDropGuard<'a, A: Allocator>(Layout, &'a A, NonNull); + impl<'a, A: Allocator> Drop for DeallocDropGuard<'a, A> { + fn drop(&mut self) { + let &mut DeallocDropGuard(layout, alloc, ptr) = self; + // Safety: `ptr` was allocated by `*alloc` with layout `layout` + unsafe { + alloc.deallocate(ptr, layout); + } + } + } + let layout = Layout::for_value::(src); + let (ptr, guard) = if layout.size() == 0 { + (layout.dangling_ptr(), None) + } else { + // Safety: layout is non-zero-sized + let ptr = alloc.allocate(layout)?.cast(); + (ptr, Some(DeallocDropGuard(layout, &alloc, ptr))) + }; + let ptr = ptr.as_ptr(); + // Safety: `*ptr` is newly allocated, correctly aligned to `align_of_val(src)`, + // and is valid for writes for `size_of_val(src)`. + // If this panics, then `guard` will deallocate for us (if allocation occuured) + unsafe { + ::clone_to_uninit(src, ptr); + } + // Defuse the deallocate guard + core::mem::forget(guard); + // Safety: We just initialized `*ptr` as a clone of `src` + Ok(unsafe { Box::from_raw_in(ptr.with_metadata_of(src), alloc) }) + } +} + impl Box<[T]> { /// Constructs a new boxed slice with uninitialized contents. /// @@ -1192,7 +1313,7 @@ impl Box { /// ``` /// /// [memory layout]: self#memory-layout - #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] + #[unstable(feature = "box_vec_non_null", issue = "130364")] #[inline] #[must_use = "call `drop(Box::from_non_null(ptr))` if you intend to drop the `Box`"] pub unsafe fn from_non_null(ptr: NonNull) -> Self { @@ -1310,7 +1431,7 @@ impl Box { /// /// [memory layout]: self#memory-layout #[must_use = "losing the pointer will leak memory"] - #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] + #[unstable(feature = "box_vec_non_null", issue = "130364")] #[inline] pub fn into_non_null(b: Self) -> NonNull { // SAFETY: `Box` is guaranteed to be non-null. @@ -1419,7 +1540,7 @@ impl Box { /// /// [memory layout]: self#memory-layout #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] + // #[unstable(feature = "box_vec_non_null", issue = "130364")] #[inline] pub unsafe fn from_non_null_in(raw: NonNull, alloc: A) -> Self { // SAFETY: guaranteed by the caller. @@ -1534,7 +1655,7 @@ impl Box { /// [memory layout]: self#memory-layout #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] + // #[unstable(feature = "box_vec_non_null", issue = "130364")] #[inline] pub fn into_non_null_with_allocator(b: Self) -> (NonNull, A) { let (ptr, alloc) = Box::into_raw_with_allocator(b); @@ -2132,7 +2253,7 @@ impl + ?Sized, A: Allocator> AsyncFn for Box #[unstable(feature = "coerce_unsized", issue = "18598")] impl, U: ?Sized, A: Allocator> CoerceUnsized> for Box {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for Box {} // It is quite crucial that we only allow the `Global` allocator here. diff --git a/library/alloc/src/boxed/convert.rs b/library/alloc/src/boxed/convert.rs index 73940db5d2f50..d6a8e78991b84 100644 --- a/library/alloc/src/boxed/convert.rs +++ b/library/alloc/src/boxed/convert.rs @@ -1,21 +1,15 @@ use core::any::Any; -#[cfg(not(no_global_oom_handling))] -use core::clone::TrivialClone; use core::error::Error; +#[cfg(not(no_global_oom_handling))] +use core::fmt; use core::mem; use core::pin::Pin; -#[cfg(not(no_global_oom_handling))] -use core::{fmt, ptr}; use crate::alloc::Allocator; #[cfg(not(no_global_oom_handling))] use crate::borrow::Cow; use crate::boxed::Box; #[cfg(not(no_global_oom_handling))] -use crate::raw_vec::RawVec; -#[cfg(not(no_global_oom_handling))] -use crate::str::from_boxed_utf8_unchecked; -#[cfg(not(no_global_oom_handling))] use crate::string::String; #[cfg(not(no_global_oom_handling))] use crate::vec::Vec; @@ -62,35 +56,6 @@ where } } -/// Specialization trait used for `From<&[T]>`. -#[cfg(not(no_global_oom_handling))] -trait BoxFromSlice { - fn from_slice(slice: &[T]) -> Self; -} - -#[cfg(not(no_global_oom_handling))] -impl BoxFromSlice for Box<[T]> { - #[inline] - default fn from_slice(slice: &[T]) -> Self { - slice.to_vec().into_boxed_slice() - } -} - -#[cfg(not(no_global_oom_handling))] -impl BoxFromSlice for Box<[T]> { - #[inline] - fn from_slice(slice: &[T]) -> Self { - let len = slice.len(); - let buf = RawVec::with_capacity(len); - // SAFETY: since `T` implements `TrivialClone`, this is sound and - // equivalent to the above. - unsafe { - ptr::copy_nonoverlapping(slice.as_ptr(), buf.ptr(), len); - buf.into_box(slice.len()).assume_init() - } - } -} - #[cfg(not(no_global_oom_handling))] #[stable(feature = "box_from_slice", since = "1.17.0")] impl From<&[T]> for Box<[T]> { @@ -109,7 +74,7 @@ impl From<&[T]> for Box<[T]> { /// ``` #[inline] fn from(slice: &[T]) -> Box<[T]> { - >::from_slice(slice) + Box::clone_from_ref(slice) } } @@ -170,7 +135,7 @@ impl From<&str> for Box { /// ``` #[inline] fn from(s: &str) -> Box { - unsafe { from_boxed_utf8_unchecked(Box::from(s.as_bytes())) } + Box::clone_from_ref(s) } } diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 1cce36606d2c0..b50810b8d923d 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -245,7 +245,7 @@ impl WithHeader { // Some paranoia checking, mostly so that the ThinBox tests are // more able to catch issues. debug_assert!(value_offset == 0 && T::IS_ZST && H::IS_ZST); - layout.dangling() + layout.dangling_ptr() } else { let ptr = alloc::alloc(layout); if ptr.is_null() { @@ -282,7 +282,7 @@ impl WithHeader { // Some paranoia checking, mostly so that the ThinBox tests are // more able to catch issues. debug_assert!(value_offset == 0 && size_of::() == 0 && size_of::() == 0); - layout.dangling() + layout.dangling_ptr() } else { let ptr = alloc::alloc(layout); if ptr.is_null() { diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 63828b482b9a9..97aafbc7b6994 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -466,7 +466,7 @@ impl Clone for BinaryHeap { } #[stable(feature = "rust1", since = "1.0.0")] -impl Default for BinaryHeap { +impl Default for BinaryHeap { /// Creates an empty `BinaryHeap`. #[inline] fn default() -> BinaryHeap { @@ -496,7 +496,7 @@ impl Drop for RebuildOnDrop<'_, T, A> { } } -impl BinaryHeap { +impl BinaryHeap { /// Creates an empty `BinaryHeap` as a max-heap. /// /// # Examples @@ -537,7 +537,7 @@ impl BinaryHeap { } } -impl BinaryHeap { +impl BinaryHeap { /// Creates an empty `BinaryHeap` as a max-heap, using `A` as allocator. /// /// # Examples @@ -581,7 +581,9 @@ impl BinaryHeap { pub fn with_capacity_in(capacity: usize, alloc: A) -> BinaryHeap { BinaryHeap { data: Vec::with_capacity_in(capacity, alloc) } } +} +impl BinaryHeap { /// Returns a mutable reference to the greatest item in the binary heap, or /// `None` if it is empty. /// @@ -647,6 +649,33 @@ impl BinaryHeap { }) } + /// Removes and returns the greatest item from the binary heap if the predicate + /// returns `true`, or [`None`] if the predicate returns false or the heap + /// is empty (the predicate will not be called in that case). + /// + /// # Examples + /// + /// ``` + /// #![feature(binary_heap_pop_if)] + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::from([1, 2]); + /// let pred = |x: &i32| *x % 2 == 0; + /// + /// assert_eq!(heap.pop_if(pred), Some(2)); + /// assert_eq!(heap.as_slice(), [1]); + /// assert_eq!(heap.pop_if(pred), None); + /// assert_eq!(heap.as_slice(), [1]); + /// ``` + /// + /// # Time complexity + /// + /// The worst case cost of `pop_if` on a heap containing *n* elements is *O*(log(*n*)). + #[unstable(feature = "binary_heap_pop_if", issue = "151828")] + pub fn pop_if(&mut self, predicate: impl FnOnce(&T) -> bool) -> Option { + let first = self.peek()?; + if predicate(first) { self.pop() } else { None } + } + /// Pushes an item onto the binary heap. /// /// # Examples diff --git a/library/alloc/src/collections/btree/append.rs b/library/alloc/src/collections/btree/append.rs index 091376d5d685b..66ea22e75247c 100644 --- a/library/alloc/src/collections/btree/append.rs +++ b/library/alloc/src/collections/btree/append.rs @@ -104,9 +104,14 @@ where { type Item = (K, V); - /// If two keys are equal, returns the key-value pair from the right source. + /// If two keys are equal, returns the key from the left and the value from the right. fn next(&mut self) -> Option<(K, V)> { let (a_next, b_next) = self.0.nexts(|a: &(K, V), b: &(K, V)| K::cmp(&a.0, &b.0)); - b_next.or(a_next) + match (a_next, b_next) { + (Some((a_k, _)), Some((_, b_v))) => Some((a_k, b_v)), + (Some(a), None) => Some(a), + (None, Some(b)) => Some(b), + (None, None) => None, + } } } diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 766f4589177a8..426be364a56b0 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -3,7 +3,7 @@ use core::cmp::Ordering; use core::error::Error; use core::fmt::{self, Debug}; use core::hash::{Hash, Hasher}; -use core::iter::FusedIterator; +use core::iter::{FusedIterator, TrustedLen}; use core::marker::PhantomData; use core::mem::{self, ManuallyDrop}; use core::ops::{Bound, Index, RangeBounds}; @@ -1181,6 +1181,10 @@ impl BTreeMap { /// /// If a key from `other` is already present in `self`, the respective /// value from `self` will be overwritten with the respective value from `other`. + /// Similar to [`insert`], though, the key is not overwritten, + /// which matters for types that can be `==` without being identical. + /// + /// [`insert`]: BTreeMap::insert /// /// # Examples /// @@ -1624,6 +1628,9 @@ impl ExactSizeIterator for Iter<'_, K, V> { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Iter<'_, K, V> {} + #[stable(feature = "rust1", since = "1.0.0")] impl Clone for Iter<'_, K, V> { fn clone(&self) -> Self { @@ -1696,6 +1703,9 @@ impl ExactSizeIterator for IterMut<'_, K, V> { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IterMut<'_, K, V> {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IterMut<'_, K, V> {} @@ -1817,6 +1827,9 @@ impl ExactSizeIterator for IntoIter { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoIter {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} @@ -1865,6 +1878,9 @@ impl ExactSizeIterator for Keys<'_, K, V> { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Keys<'_, K, V> {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Keys<'_, K, V> {} @@ -1920,6 +1936,9 @@ impl ExactSizeIterator for Values<'_, K, V> { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Values<'_, K, V> {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Values<'_, K, V> {} @@ -2160,6 +2179,9 @@ impl ExactSizeIterator for ValuesMut<'_, K, V> { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for ValuesMut<'_, K, V> {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ValuesMut<'_, K, V> {} @@ -2222,6 +2244,9 @@ impl ExactSizeIterator for IntoKeys { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoKeys {} + #[stable(feature = "map_into_keys_values", since = "1.54.0")] impl FusedIterator for IntoKeys {} @@ -2273,6 +2298,9 @@ impl ExactSizeIterator for IntoValues { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoValues {} + #[stable(feature = "map_into_keys_values", since = "1.54.0")] impl FusedIterator for IntoValues {} @@ -2416,7 +2444,7 @@ impl Default for BTreeMap { #[stable(feature = "rust1", since = "1.0.0")] impl PartialEq for BTreeMap { fn eq(&self, other: &BTreeMap) -> bool { - self.iter().eq(other) + self.len() == other.len() && self.iter().zip(other).all(|(a, b)| a == b) } } diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 79879d31d3dfd..938e867b85812 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -1,4 +1,4 @@ -use core::assert_matches::assert_matches; +use core::assert_matches; use std::iter; use std::ops::Bound::{Excluded, Included, Unbounded}; use std::panic::{AssertUnwindSafe, catch_unwind}; @@ -11,7 +11,7 @@ use crate::fmt::Debug; use crate::rc::Rc; use crate::string::{String, ToString}; use crate::testing::crash_test::{CrashTestDummy, Panic}; -use crate::testing::ord_chaos::{Cyclic3, Governed, Governor}; +use crate::testing::ord_chaos::{Cyclic3, Governed, Governor, IdBased}; use crate::testing::rng::DeterministicRng; // Minimum number of elements to insert, to guarantee a tree with 2 levels, @@ -2137,9 +2137,9 @@ fn test_append_drop_leak() { let mut left = BTreeMap::new(); let mut right = BTreeMap::new(); left.insert(a.spawn(Panic::Never), ()); - left.insert(b.spawn(Panic::InDrop), ()); // first duplicate key, dropped during append + left.insert(b.spawn(Panic::Never), ()); left.insert(c.spawn(Panic::Never), ()); - right.insert(b.spawn(Panic::Never), ()); + right.insert(b.spawn(Panic::InDrop), ()); // first duplicate key, dropped during append right.insert(c.spawn(Panic::Never), ()); catch_unwind(move || left.append(&mut right)).unwrap_err(); @@ -2587,3 +2587,31 @@ fn cursor_peek_prev_agrees_with_cursor_mut() { let prev = cursor.peek_prev(); assert_matches!(prev, Some((&3, _))); } + +#[test] +fn test_id_based_insert() { + let mut lhs = BTreeMap::new(); + let mut rhs = BTreeMap::new(); + + lhs.insert(IdBased { id: 0, name: "lhs_k".to_string() }, "lhs_v".to_string()); + rhs.insert(IdBased { id: 0, name: "rhs_k".to_string() }, "rhs_v".to_string()); + + for (k, v) in rhs.into_iter() { + lhs.insert(k, v); + } + + assert_eq!(lhs.pop_first().unwrap().0.name, "lhs_k".to_string()); +} + +#[test] +fn test_id_based_append() { + let mut lhs = BTreeMap::new(); + let mut rhs = BTreeMap::new(); + + lhs.insert(IdBased { id: 0, name: "lhs_k".to_string() }, "lhs_v".to_string()); + rhs.insert(IdBased { id: 0, name: "rhs_k".to_string() }, "rhs_v".to_string()); + + lhs.append(&mut rhs); + + assert_eq!(lhs.pop_first().unwrap().0.name, "lhs_k".to_string()); +} diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 28d26699d7d2c..fd27e87b1f470 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -3,7 +3,7 @@ use core::cmp::Ordering::{self, Equal, Greater, Less}; use core::cmp::{max, min}; use core::fmt::{self, Debug}; use core::hash::{Hash, Hasher}; -use core::iter::{FusedIterator, Peekable}; +use core::iter::{FusedIterator, Peekable, TrustedLen}; use core::mem::ManuallyDrop; use core::ops::{BitAnd, BitOr, BitXor, Bound, RangeBounds, Sub}; @@ -1753,6 +1753,7 @@ impl Clone for Iter<'_, T> { Iter { iter: self.iter.clone() } } } + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> Iterator for Iter<'a, T> { type Item = &'a T; @@ -1783,12 +1784,14 @@ impl<'a, T> Iterator for Iter<'a, T> { self.next_back() } } + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> DoubleEndedIterator for Iter<'a, T> { fn next_back(&mut self) -> Option<&'a T> { self.iter.next_back() } } + #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for Iter<'_, T> { fn len(&self) -> usize { @@ -1796,6 +1799,9 @@ impl ExactSizeIterator for Iter<'_, T> { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for Iter<'_, T> {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Iter<'_, T> {} @@ -1832,6 +1838,7 @@ impl DoubleEndedIterator for IntoIter { self.iter.next_back().map(|(k, _)| k) } } + #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for IntoIter { fn len(&self) -> usize { @@ -1839,6 +1846,9 @@ impl ExactSizeIterator for IntoIter { } } +#[unstable(feature = "trusted_len", issue = "37572")] +unsafe impl TrustedLen for IntoIter {} + #[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 8bc0e08a4b26b..3889fca30bc87 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -167,7 +167,7 @@ impl LinkedList { /// Adds the given node to the front of the list. /// /// # Safety - /// `node` must point to a valid node that was boxed and leaked using the list's allocator. + /// `node` must point to a valid node in the list's allocator. /// This method takes ownership of the node, so the pointer should not be used again. #[inline] unsafe fn push_front_node(&mut self, node: NonNull>) { @@ -212,7 +212,7 @@ impl LinkedList { /// Adds the given node to the back of the list. /// /// # Safety - /// `node` must point to a valid node that was boxed and leaked using the list's allocator. + /// `node` must point to a valid node in the list's allocator. /// This method takes ownership of the node, so the pointer should not be used again. #[inline] unsafe fn push_back_node(&mut self, node: NonNull>) { @@ -597,7 +597,7 @@ impl LinkedList { #[must_use] #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn cursor_back(&self) -> Cursor<'_, T, A> { - Cursor { index: self.len.checked_sub(1).unwrap_or(0), current: self.tail, list: self } + Cursor { index: self.len.saturating_sub(1), current: self.tail, list: self } } /// Provides a cursor with editing operations at the back element. @@ -607,7 +607,7 @@ impl LinkedList { #[must_use] #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn cursor_back_mut(&mut self) -> CursorMut<'_, T, A> { - CursorMut { index: self.len.checked_sub(1).unwrap_or(0), current: self.tail, list: self } + CursorMut { index: self.len.saturating_sub(1), current: self.tail, list: self } } /// Returns `true` if the `LinkedList` is empty. @@ -854,7 +854,6 @@ impl LinkedList { /// # Examples /// /// ``` - /// #![feature(push_mut)] /// use std::collections::LinkedList; /// /// let mut dl = LinkedList::from([1, 2, 3]); @@ -863,15 +862,15 @@ impl LinkedList { /// *ptr += 4; /// assert_eq!(dl.front().unwrap(), &6); /// ``` - #[unstable(feature = "push_mut", issue = "135974")] + #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] #[must_use = "if you don't need a reference to the value, use `LinkedList::push_front` instead"] pub fn push_front_mut(&mut self, elt: T) -> &mut T { - let node = Box::new_in(Node::new(elt), &self.alloc); - let mut node_ptr = NonNull::from(Box::leak(node)); - // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked + let mut node = + Box::into_non_null_with_allocator(Box::new_in(Node::new(elt), &self.alloc)).0; + // SAFETY: node is a unique pointer to a node in self.alloc unsafe { - self.push_front_node(node_ptr); - &mut node_ptr.as_mut().element + self.push_front_node(node); + &mut node.as_mut().element } } @@ -926,7 +925,6 @@ impl LinkedList { /// # Examples /// /// ``` - /// #![feature(push_mut)] /// use std::collections::LinkedList; /// /// let mut dl = LinkedList::from([1, 2, 3]); @@ -935,15 +933,15 @@ impl LinkedList { /// *ptr += 4; /// assert_eq!(dl.back().unwrap(), &6); /// ``` - #[unstable(feature = "push_mut", issue = "135974")] + #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] #[must_use = "if you don't need a reference to the value, use `LinkedList::push_back` instead"] pub fn push_back_mut(&mut self, elt: T) -> &mut T { - let node = Box::new_in(Node::new(elt), &self.alloc); - let mut node_ptr = NonNull::from(Box::leak(node)); - // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked + let mut node = + Box::into_non_null_with_allocator(Box::new_in(Node::new(elt), &self.alloc)).0; + // SAFETY: node is a unique pointer to a node in self.alloc unsafe { - self.push_back_node(node_ptr); - &mut node_ptr.as_mut().element + self.push_back_node(node); + &mut node.as_mut().element } } @@ -1432,7 +1430,7 @@ impl<'a, T, A: Allocator> Cursor<'a, T, A> { // No current. We're at the start of the list. Yield None and jump to the end. None => { self.current = self.list.tail; - self.index = self.list.len().checked_sub(1).unwrap_or(0); + self.index = self.list.len().saturating_sub(1); } // Have a prev. Yield it and go to the previous element. Some(current) => unsafe { @@ -1559,7 +1557,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { // No current. We're at the start of the list. Yield None and jump to the end. None => { self.current = self.list.tail; - self.index = self.list.len().checked_sub(1).unwrap_or(0); + self.index = self.list.len().saturating_sub(1); } // Have a prev. Yield it and go to the previous element. Some(current) => unsafe { @@ -1646,9 +1644,8 @@ impl<'a, T> CursorMut<'a, T> { #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn splice_after(&mut self, list: LinkedList) { unsafe { - let (splice_head, splice_tail, splice_len) = match list.detach_all_nodes() { - Some(parts) => parts, - _ => return, + let Some((splice_head, splice_tail, splice_len)) = list.detach_all_nodes() else { + return; }; let node_next = match self.current { None => self.list.head, @@ -1691,7 +1688,8 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn insert_after(&mut self, item: T) { unsafe { - let spliced_node = Box::leak(Box::new_in(Node::new(item), &self.list.alloc)).into(); + let spliced_node = + Box::into_non_null_with_allocator(Box::new_in(Node::new(item), &self.list.alloc)).0; let node_next = match self.current { None => self.list.head, Some(node) => node.as_ref().next, @@ -1711,7 +1709,8 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn insert_before(&mut self, item: T) { unsafe { - let spliced_node = Box::leak(Box::new_in(Node::new(item), &self.list.alloc)).into(); + let spliced_node = + Box::into_non_null_with_allocator(Box::new_in(Node::new(item), &self.list.alloc)).0; let node_prev = match self.current { None => self.list.tail, Some(node) => node.as_ref().prev, @@ -1856,9 +1855,11 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { // node at index 0, which is expected. if self.list.head == self.current { self.move_next(); - } else { - self.index -= 1; } + // An element was removed before (or at) our current position, so + // the index must be decremented. `saturating_sub` handles the + // ghost node case where index could be 0. + self.index = self.index.saturating_sub(1); self.list.pop_front() } } diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index 3d6c740e80b3b..a51af6345e205 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -748,6 +748,60 @@ fn test_cursor_pop_front_back() { assert_eq!(c.index, 2); } +#[test] +fn test_cursor_pop_front_index() { + // Regression test for issue #147616: `pop_front` was not correctly + // updating the cursor index when the cursor was pointing to the front. + + // Test case 1: pop_front when cursor is not at front, then at front + let mut ll: LinkedList = LinkedList::new(); + ll.extend(&[0, 1, 2]); + let mut c = ll.cursor_front_mut(); + + c.move_next(); + assert_eq!(c.index(), Some(1)); + assert_eq!(c.current(), Some(&mut 1)); + + // Pop front when cursor is not at front - index should decrement + c.pop_front(); + assert_eq!(c.index(), Some(0)); + assert_eq!(c.current(), Some(&mut 1)); + + // Now cursor is at front, pop_front again - index should remain 0 + c.pop_front(); + assert_eq!(c.index(), Some(0)); + assert_eq!(c.current(), Some(&mut 2)); + check_links(&ll); + + // Test case 2: minimal reproduction - cursor at front, pop_front + let mut ll: LinkedList = LinkedList::new(); + ll.extend(&[0, 1]); + let mut c = ll.cursor_front_mut(); + + assert_eq!(c.index(), Some(0)); + assert_eq!(c.current(), Some(&mut 0)); + + // Pop front when cursor is at front - should move to next and index stays 0 + c.pop_front(); + assert_eq!(c.index(), Some(0)); + assert_eq!(c.current(), Some(&mut 1)); + check_links(&ll); + + // Test case 3: single element list + let mut ll: LinkedList = LinkedList::new(); + ll.push_back(42); + let mut c = ll.cursor_front_mut(); + + assert_eq!(c.index(), Some(0)); + assert_eq!(c.current(), Some(&mut 42)); + + // Pop the only element - cursor should be at ghost node with index 0 + c.pop_front(); + assert_eq!(c.index(), None); + assert_eq!(c.current(), None); + check_links(&ll); +} + #[test] fn test_extend_ref() { let mut a = LinkedList::new(); diff --git a/library/alloc/src/collections/mod.rs b/library/alloc/src/collections/mod.rs index 212d7c8465b6e..d306d2016ea28 100644 --- a/library/alloc/src/collections/mod.rs +++ b/library/alloc/src/collections/mod.rs @@ -84,13 +84,14 @@ impl TryReserveError { reason = "Uncertain how much info should be exposed", issue = "48043" )] - pub fn kind(&self) -> TryReserveErrorKind { + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + pub const fn kind(&self) -> TryReserveErrorKind { self.kind.clone() } } /// Details of the allocation that caused a `TryReserveError` -#[derive(Clone, PartialEq, Eq, Debug)] +#[derive(PartialEq, Eq, Debug)] #[unstable( feature = "try_reserve_kind", reason = "Uncertain how much info should be exposed", @@ -120,6 +121,24 @@ pub enum TryReserveErrorKind { }, } +#[unstable( + feature = "try_reserve_kind", + reason = "Uncertain how much info should be exposed", + issue = "48043" +)] +#[rustc_const_unstable(feature = "const_heap", issue = "79597")] +#[cfg(not(test))] +impl const Clone for TryReserveErrorKind { + fn clone(&self) -> Self { + match self { + TryReserveErrorKind::CapacityOverflow => TryReserveErrorKind::CapacityOverflow, + TryReserveErrorKind::AllocError { layout, non_exhaustive: () } => { + TryReserveErrorKind::AllocError { layout: *layout, non_exhaustive: () } + } + } + } +} + #[cfg(test)] pub use realalloc::collections::TryReserveErrorKind; @@ -137,7 +156,7 @@ impl const From for TryReserveError { } } -#[unstable(feature = "try_reserve_kind", reason = "new API", issue = "48043")] +#[unstable(feature = "try_reserve_kind", issue = "48043")] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] #[cfg(not(test))] impl const From for TryReserveErrorKind { diff --git a/library/alloc/src/collections/vec_deque/drain.rs b/library/alloc/src/collections/vec_deque/drain.rs index 321621d18be9f..a43853604a2d2 100644 --- a/library/alloc/src/collections/vec_deque/drain.rs +++ b/library/alloc/src/collections/vec_deque/drain.rs @@ -21,14 +21,14 @@ pub struct Drain< > { // We can't just use a &mut VecDeque, as that would make Drain invariant over T // and we want it to be covariant instead - deque: NonNull>, + pub(super) deque: NonNull>, // drain_start is stored in deque.len - drain_len: usize, + pub(super) drain_len: usize, // index into the logical array, not the physical one (always lies in [0..deque.len)) - idx: usize, - // number of elements remaining after dropping the drain - new_len: usize, - remaining: usize, + pub(super) idx: usize, + // number of elements after the drained range + pub(super) tail_len: usize, + pub(super) remaining: usize, // Needed to make Drain covariant over T _marker: PhantomData<&'a T>, } @@ -40,12 +40,12 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> { drain_len: usize, ) -> Self { let orig_len = mem::replace(&mut deque.len, drain_start); - let new_len = orig_len - drain_len; + let tail_len = orig_len - drain_start - drain_len; Drain { deque: NonNull::from(deque), drain_len, idx: drain_start, - new_len, + tail_len, remaining: drain_len, _marker: PhantomData, } @@ -53,7 +53,7 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> { // Only returns pointers to the slices, as that's all we need // to drop them. May only be called if `self.remaining != 0`. - unsafe fn as_slices(&self) -> (*mut [T], *mut [T]) { + pub(super) unsafe fn as_slices(&self) -> (*mut [T], *mut [T]) { unsafe { let deque = self.deque.as_ref(); @@ -78,7 +78,7 @@ impl fmt::Debug for Drain<'_, T, A> { f.debug_tuple("Drain") .field(&self.drain_len) .field(&self.idx) - .field(&self.new_len) + .field(&self.tail_len) .field(&self.remaining) .finish() } @@ -125,7 +125,9 @@ impl Drop for Drain<'_, T, A> { let source_deque = unsafe { self.0.deque.as_mut() }; let drain_len = self.0.drain_len; - let new_len = self.0.new_len; + let head_len = source_deque.len; // #elements in front of the drain + let tail_len = self.0.tail_len; // #elements behind the drain + let new_len = head_len + tail_len; if T::IS_ZST { // no need to copy around any memory if T is a ZST @@ -133,9 +135,6 @@ impl Drop for Drain<'_, T, A> { return; } - let head_len = source_deque.len; // #elements in front of the drain - let tail_len = new_len - head_len; // #elements behind the drain - // Next, we will fill the hole left by the drain with as few writes as possible. // The code below handles the following control flow and reduces the amount of // branches under the assumption that `head_len == 0 || tail_len == 0`, i.e. @@ -219,7 +218,7 @@ impl Drop for Drain<'_, T, A> { } if new_len == 0 { - // Special case: If the entire dequeue was drained, reset the head back to 0, + // Special case: If the entire deque was drained, reset the head back to 0, // like `.clear()` does. source_deque.head = 0; } else if head_len < tail_len { diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 436a05f12b909..7b58915087294 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -62,6 +62,13 @@ use self::spec_from_iter::SpecFromIter; mod spec_from_iter; +#[cfg(not(no_global_oom_handling))] +#[unstable(feature = "deque_extend_front", issue = "146975")] +pub use self::splice::Splice; + +#[cfg(not(no_global_oom_handling))] +mod splice; + #[cfg(test)] mod tests; @@ -1841,6 +1848,64 @@ impl VecDeque { unsafe { Drain::new(self, drain_start, drain_len) } } + /// Creates a splicing iterator that replaces the specified range in the deque with the given + /// `replace_with` iterator and yields the removed items. `replace_with` does not need to be the + /// same length as `range`. + /// + /// `range` is removed even if the `Splice` iterator is not consumed before it is dropped. + /// + /// It is unspecified how many elements are removed from the deque if the `Splice` value is + /// leaked. + /// + /// The input iterator `replace_with` is only consumed when the `Splice` value is dropped. + /// + /// This is optimal if: + /// + /// * The tail (elements in the deque after `range`) is empty, + /// * or `replace_with` yields fewer or equal elements than `range`'s length + /// * or the lower bound of its `size_hint()` is exact. + /// + /// Otherwise, a temporary vector is allocated and the tail is moved twice. + /// + /// # Panics + /// + /// Panics if the range has `start_bound > end_bound`, or, if the range is + /// bounded on either end and past the length of the deque. + /// + /// # Examples + /// + /// ``` + /// # #![feature(deque_extend_front)] + /// # use std::collections::VecDeque; + /// + /// let mut v = VecDeque::from(vec![1, 2, 3, 4]); + /// let new = [7, 8, 9]; + /// let u: Vec<_> = v.splice(1..3, new).collect(); + /// assert_eq!(v, [1, 7, 8, 9, 4]); + /// assert_eq!(u, [2, 3]); + /// ``` + /// + /// Using `splice` to insert new items into a vector efficiently at a specific position + /// indicated by an empty range: + /// + /// ``` + /// # #![feature(deque_extend_front)] + /// # use std::collections::VecDeque; + /// + /// let mut v = VecDeque::from(vec![1, 5]); + /// let new = [2, 3, 4]; + /// v.splice(1..1, new); + /// assert_eq!(v, [1, 2, 3, 4, 5]); + /// ``` + #[unstable(feature = "deque_extend_front", issue = "146975")] + pub fn splice(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter, A> + where + R: RangeBounds, + I: IntoIterator, + { + Splice { drain: self.drain(range), replace_with: replace_with.into_iter() } + } + /// Clears the deque, removing all values. /// /// # Examples @@ -2056,7 +2121,7 @@ impl VecDeque { /// assert_eq!(deque, [1, 2, 3, 4]); /// assert_eq!(deque.pop_front_if(pred), None); /// ``` - #[stable(feature = "vec_deque_pop_if", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "vec_deque_pop_if", since = "1.93.0")] pub fn pop_front_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { let first = self.front_mut()?; if predicate(first) { self.pop_front() } else { None } @@ -2078,7 +2143,7 @@ impl VecDeque { /// assert_eq!(deque, [0, 1, 2, 3]); /// assert_eq!(deque.pop_back_if(pred), None); /// ``` - #[stable(feature = "vec_deque_pop_if", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "vec_deque_pop_if", since = "1.93.0")] pub fn pop_back_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { let last = self.back_mut()?; if predicate(last) { self.pop_back() } else { None } @@ -2106,7 +2171,6 @@ impl VecDeque { /// # Examples /// /// ``` - /// #![feature(push_mut)] /// use std::collections::VecDeque; /// /// let mut d = VecDeque::from([1, 2, 3]); @@ -2114,7 +2178,7 @@ impl VecDeque { /// *x -= 1; /// assert_eq!(d.front(), Some(&7)); /// ``` - #[unstable(feature = "push_mut", issue = "135974")] + #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] #[must_use = "if you don't need a reference to the value, use `VecDeque::push_front` instead"] pub fn push_front_mut(&mut self, value: T) -> &mut T { if self.is_full() { @@ -2150,7 +2214,6 @@ impl VecDeque { /// # Examples /// /// ``` - /// #![feature(push_mut)] /// use std::collections::VecDeque; /// /// let mut d = VecDeque::from([1, 2, 3]); @@ -2158,7 +2221,7 @@ impl VecDeque { /// *x += 1; /// assert_eq!(d.back(), Some(&10)); /// ``` - #[unstable(feature = "push_mut", issue = "135974")] + #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] #[must_use = "if you don't need a reference to the value, use `VecDeque::push_back` instead"] pub fn push_back_mut(&mut self, value: T) -> &mut T { if self.is_full() { @@ -2357,7 +2420,6 @@ impl VecDeque { /// # Examples /// /// ``` - /// #![feature(push_mut)] /// use std::collections::VecDeque; /// /// let mut vec_deque = VecDeque::from([1, 2, 3]); @@ -2366,7 +2428,7 @@ impl VecDeque { /// *x += 7; /// assert_eq!(vec_deque, &[1, 12, 2, 3]); /// ``` - #[unstable(feature = "push_mut", issue = "135974")] + #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] #[must_use = "if you don't need a reference to the value, use `VecDeque::insert` instead"] pub fn insert_mut(&mut self, index: usize, value: T) -> &mut T { assert!(index <= self.len(), "index out of bounds"); diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs index f73ba795cbea4..184e8b769347d 100644 --- a/library/alloc/src/collections/vec_deque/spec_extend.rs +++ b/library/alloc/src/collections/vec_deque/spec_extend.rs @@ -1,7 +1,7 @@ use core::iter::{Copied, Rev, TrustedLen}; use core::slice; -use super::VecDeque; +use super::{Drain, VecDeque}; use crate::alloc::Allocator; #[cfg(not(test))] use crate::vec; @@ -77,8 +77,8 @@ where } #[cfg(not(test))] -impl SpecExtend> for VecDeque { - fn spec_extend(&mut self, mut iterator: vec::IntoIter) { +impl SpecExtend> for VecDeque { + fn spec_extend(&mut self, mut iterator: vec::IntoIter) { let slice = iterator.as_slice(); self.reserve(slice.len()); @@ -153,23 +153,27 @@ where } #[cfg(not(test))] -impl SpecExtendFront> for VecDeque { +impl SpecExtendFront> for VecDeque { #[track_caller] - fn spec_extend_front(&mut self, mut iterator: vec::IntoIter) { + fn spec_extend_front(&mut self, mut iterator: vec::IntoIter) { let slice = iterator.as_slice(); - // SAFETY: elements in the slice are forgotten after this call + self.reserve(slice.len()); + // SAFETY: `slice.len()` space was just reserved and elements in the slice are forgotten after this call unsafe { prepend_reversed(self, slice) }; iterator.forget_remaining_elements(); } } #[cfg(not(test))] -impl SpecExtendFront>> for VecDeque { +impl SpecExtendFront>> + for VecDeque +{ #[track_caller] - fn spec_extend_front(&mut self, iterator: Rev>) { + fn spec_extend_front(&mut self, iterator: Rev>) { let mut iterator = iterator.into_inner(); let slice = iterator.as_slice(); - // SAFETY: elements in the slice are forgotten after this call + self.reserve(slice.len()); + // SAFETY: `slice.len()` space was just reserved and elements in the slice are forgotten after this call unsafe { prepend(self, slice) }; iterator.forget_remaining_elements(); } @@ -182,7 +186,8 @@ where #[track_caller] fn spec_extend_front(&mut self, iter: Copied>) { let slice = iter.into_inner().as_slice(); - // SAFETY: T is Copy because Copied> is Iterator + self.reserve(slice.len()); + // SAFETY: `slice.len()` space was just reserved and T is Copy because Copied> is Iterator unsafe { prepend_reversed(self, slice) }; } } @@ -194,17 +199,69 @@ where #[track_caller] fn spec_extend_front(&mut self, iter: Rev>>) { let slice = iter.into_inner().into_inner().as_slice(); - // SAFETY: T is Copy because Rev>> is Iterator + self.reserve(slice.len()); + // SAFETY: `slice.len()` space was just reserved and T is Copy because Rev>> is Iterator unsafe { prepend(self, slice) }; } } +impl<'a, T, A1: Allocator, A2: Allocator> SpecExtendFront> for VecDeque { + #[track_caller] + fn spec_extend_front(&mut self, mut iter: Drain<'a, T, A2>) { + if iter.remaining == 0 { + return; + } + + self.reserve(iter.remaining); + unsafe { + // SAFETY: iter.remaining != 0. + let (left, right) = iter.as_slices(); + // SAFETY: + // - `iter.remaining` space was reserved, `iter.remaining == left.len() + right.len()`. + // - The elements in `left` and `right` are forgotten after these calls. + prepend_reversed(self, &*left); + prepend_reversed(self, &*right); + } + + iter.idx += iter.remaining; + iter.remaining = 0; + } +} + +impl<'a, T, A1: Allocator, A2: Allocator> SpecExtendFront>> + for VecDeque +{ + #[track_caller] + fn spec_extend_front(&mut self, iter: Rev>) { + let mut iter = iter.into_inner(); + + if iter.remaining == 0 { + return; + } + + self.reserve(iter.remaining); + unsafe { + // SAFETY: iter.remaining != 0. + let (left, right) = iter.as_slices(); + // SAFETY: + // - `iter.remaining` space was reserved, `iter.remaining == left.len() + right.len()`. + // - The elements in `left` and `right` are forgotten after these calls. + prepend(self, &*right); + prepend(self, &*left); + } + + iter.idx += iter.remaining; + iter.remaining = 0; + } +} + +/// Prepends elements of `slice` to `deque` using a copy. +/// /// # Safety /// -/// Elements of `slice` will be copied into the deque, make sure to forget the items if `T` is not `Copy`. +/// - `deque` must have space for `slice.len()` new elements. +/// - Elements of `slice` will be copied into the deque, make sure to forget the elements if `T` is not `Copy`. unsafe fn prepend(deque: &mut VecDeque, slice: &[T]) { - deque.reserve(slice.len()); - unsafe { deque.head = deque.wrap_sub(deque.head, slice.len()); deque.copy_slice(deque.head, slice); @@ -212,12 +269,13 @@ unsafe fn prepend(deque: &mut VecDeque, slice: &[T]) { } } +/// Prepends elements of `slice` to `deque` in reverse order using a copy. +/// /// # Safety /// -/// Elements of `slice` will be copied into the deque, make sure to forget the items if `T` is not `Copy`. +/// - `deque` must have space for `slice.len()` new elements. +/// - Elements of `slice` will be copied into the deque, make sure to forget the elements if `T` is not `Copy`. unsafe fn prepend_reversed(deque: &mut VecDeque, slice: &[T]) { - deque.reserve(slice.len()); - unsafe { deque.head = deque.wrap_sub(deque.head, slice.len()); deque.copy_slice_reversed(deque.head, slice); diff --git a/library/alloc/src/collections/vec_deque/splice.rs b/library/alloc/src/collections/vec_deque/splice.rs new file mode 100644 index 0000000000000..d7b9a96291c39 --- /dev/null +++ b/library/alloc/src/collections/vec_deque/splice.rs @@ -0,0 +1,154 @@ +use core::alloc::Allocator; + +use crate::alloc::Global; +use crate::collections::vec_deque::Drain; +use crate::vec::Vec; + +/// A splicing iterator for `VecDeque`. +/// +/// This struct is created by [`VecDeque::splice()`][super::VecDeque::splice]. +/// See its documentation for more. +/// +/// # Example +/// +/// ``` +/// # #![feature(deque_extend_front)] +/// # use std::collections::VecDeque; +/// +/// let mut v = VecDeque::from(vec![0, 1, 2]); +/// let new = [7, 8]; +/// let iter: std::collections::vec_deque::Splice<'_, _> = v.splice(1.., new); +/// ``` +#[unstable(feature = "deque_extend_front", issue = "146975")] +#[derive(Debug)] +pub struct Splice< + 'a, + I: Iterator + 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + 'a = Global, +> { + pub(super) drain: Drain<'a, I::Item, A>, + pub(super) replace_with: I, +} + +#[unstable(feature = "deque_extend_front", issue = "146975")] +impl Iterator for Splice<'_, I, A> { + type Item = I::Item; + + fn next(&mut self) -> Option { + self.drain.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.drain.size_hint() + } +} + +#[unstable(feature = "deque_extend_front", issue = "146975")] +impl DoubleEndedIterator for Splice<'_, I, A> { + fn next_back(&mut self) -> Option { + self.drain.next_back() + } +} + +#[unstable(feature = "deque_extend_front", issue = "146975")] +impl ExactSizeIterator for Splice<'_, I, A> {} + +// See also: [`crate::vec::Splice`]. +#[unstable(feature = "deque_extend_front", issue = "146975")] +impl Drop for Splice<'_, I, A> { + fn drop(&mut self) { + // This will set drain.remaining to 0, so its drop won't try to read deallocated memory on + // drop. + self.drain.by_ref().for_each(drop); + + // At this point draining is done and the only remaining tasks are splicing + // and moving things into the final place. + + unsafe { + let tail_len = self.drain.tail_len; // #elements behind the drain + + if tail_len == 0 { + self.drain.deque.as_mut().extend(self.replace_with.by_ref()); + return; + } + + // First fill the range left by drain(). + if !self.drain.fill(&mut self.replace_with) { + return; + } + + // There may be more elements. Use the lower bound as an estimate. + // FIXME: Is the upper bound a better guess? Or something else? + let (lower_bound, _upper_bound) = self.replace_with.size_hint(); + if lower_bound > 0 { + self.drain.move_tail(lower_bound); + if !self.drain.fill(&mut self.replace_with) { + return; + } + } + + // Collect any remaining elements. + // This is a zero-length vector which does not allocate if `lower_bound` was exact. + let mut collected = self.replace_with.by_ref().collect::>().into_iter(); + // Now we have an exact count. + if collected.len() > 0 { + self.drain.move_tail(collected.len()); + let filled = self.drain.fill(&mut collected); + debug_assert!(filled); + debug_assert_eq!(collected.len(), 0); + } + } + // Let `Drain::drop` move the tail back if necessary and restore `deque.len`. + } +} + +/// Private helper methods for `Splice::drop` +impl Drain<'_, T, A> { + /// The range from `self.deque.len` to `self.deque.len + self.drain_len` contains elements that + /// have been moved out. + /// Fill that range as much as possible with new elements from the `replace_with` iterator. + /// Returns `true` if we filled the entire range. (`replace_with.next()` didn’t return `None`.) + /// + /// # Safety + /// + /// self.deque must be valid. self.deque.len and self.deque.len + self.drain_len must be less + /// than twice the deque's capacity. + unsafe fn fill>(&mut self, replace_with: &mut I) -> bool { + let deque = unsafe { self.deque.as_mut() }; + let range_start = deque.len; + let range_end = range_start + self.drain_len; + + for idx in range_start..range_end { + if let Some(new_item) = replace_with.next() { + let index = deque.to_physical_idx(idx); + unsafe { deque.buffer_write(index, new_item) }; + deque.len += 1; + self.drain_len -= 1; + } else { + return false; + } + } + true + } + + /// Makes room for inserting more elements before the tail. + /// + /// # Safety + /// + /// self.deque must be valid. + unsafe fn move_tail(&mut self, additional: usize) { + let deque = unsafe { self.deque.as_mut() }; + let tail_start = deque.len + self.drain_len; + deque.buf.reserve(tail_start + self.tail_len, additional); + + let new_tail_start = tail_start + additional; + unsafe { + deque.wrap_copy( + deque.to_physical_idx(tail_start), + deque.to_physical_idx(new_tail_start), + self.tail_len, + ); + } + self.drain_len += additional; + } +} diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index 3e78d680ea68a..d6dcba7107a9c 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -636,7 +636,7 @@ impl CString { Self { inner: v.into_boxed_slice() } } - /// Attempts to converts a [Vec]<[u8]> to a [`CString`]. + /// Attempts to convert a [Vec]<[u8]> to a [`CString`]. /// /// Runtime checks are present to ensure there is only one nul byte in the /// [`Vec`], its last element. @@ -766,8 +766,7 @@ impl From<&CStr> for Box { /// Converts a `&CStr` into a `Box`, /// by copying the contents into a newly allocated [`Box`]. fn from(s: &CStr) -> Box { - let boxed: Box<[u8]> = Box::from(s.to_bytes_with_nul()); - unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) } + Box::clone_from_ref(s) } } diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index 4d6fe220a09ad..e3ff2ba51aba0 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -136,9 +136,10 @@ //! padding specified by fill/alignment will be used to take up the required //! space (see below). //! -//! The value for the width can also be provided as a [`usize`] in the list of -//! parameters by adding a postfix `$`, indicating that the second argument is -//! a [`usize`] specifying the width. +//! The width can also be provided dynamically by referencing another argument +//! with a `$` suffix. Use `{:N$}` to reference the Nth positional argument +//! (where N is an integer), or `{:name$}` to reference a named argument. The +//! referenced argument must be of type [`usize`]. //! //! Referring to an argument with the dollar syntax does not affect the "next //! argument" counter, so it's usually a good idea to refer to arguments by @@ -236,7 +237,8 @@ //! //! 2. An integer or name followed by dollar sign `.N$`: //! -//! use format *argument* `N` (which must be a `usize`) as the precision. +//! use the value of format *argument* `N` (which must be a `usize`) as the precision. +//! An integer refers to a positional argument, and a name refers to a named argument. //! //! 3. An asterisk `.*`: //! @@ -363,7 +365,10 @@ //! - `ws` is any character for which [`char::is_whitespace`] returns `true`, has no semantic //! meaning and is completely optional, //! - `integer` is a decimal integer that may contain leading zeroes and must fit into an `usize` and -//! - `identifier` is an `IDENTIFIER_OR_KEYWORD` (not an `IDENTIFIER`) as defined by the [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html). +//! - `identifier` is an `IDENTIFIER_OR_KEYWORD` (not an `IDENTIFIER`) as +//! defined by the [Rust language +//! reference](https://doc.rust-lang.org/reference/identifiers.html), except +//! for a bare `_`. //! //! # Formatting traits //! @@ -602,7 +607,7 @@ pub use core::fmt::{DebugAsHex, FormattingOptions, Sign}; pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::{Formatter, Result, Write}; -#[stable(feature = "fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "fmt_from_fn", since = "1.93.0")] pub use core::fmt::{FromFn, from_fn}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::{LowerExp, UpperExp}; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 9a714e42c14b1..2503faa0f923c 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -87,14 +87,13 @@ // tidy-alphabetical-start #![cfg_attr(kani, feature(kani))] #![cfg_attr(not(no_global_oom_handling), feature(string_replace_in_place))] -#![feature(alloc_layout_extra)] #![feature(allocator_api)] #![feature(array_into_iter_constructors)] -#![feature(array_windows)] #![feature(ascii_char)] #![feature(assert_matches)] #![feature(async_fn_traits)] #![feature(async_iterator)] +#![feature(box_vec_non_null)] #![feature(bstr)] #![feature(bstr_internals)] #![feature(cast_maybe_uninit)] @@ -102,10 +101,15 @@ #![feature(char_internals)] #![feature(clone_to_uninit)] #![feature(coerce_unsized)] +#![feature(const_clone)] +#![feature(const_cmp)] #![feature(const_convert)] #![feature(const_default)] +#![feature(const_destruct)] #![feature(const_eval_select)] #![feature(const_heap)] +#![feature(const_option_ops)] +#![feature(const_try)] #![feature(copied_into_inner)] #![feature(core_intrinsics)] #![feature(deprecated_suggestion)] @@ -120,6 +124,7 @@ #![feature(fmt_internals)] #![feature(fn_traits)] #![feature(formatting_options)] +#![feature(freeze)] #![feature(generic_atomic)] #![feature(hasher_prefixfree_extras)] #![feature(inplace_iteration)] @@ -127,8 +132,8 @@ #![feature(iter_next_chunk)] #![feature(layout_for_ptr)] #![feature(legacy_receiver_trait)] +#![feature(likely_unlikely)] #![feature(local_waker)] -#![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array_transpose)] #![feature(panic_internals)] #![feature(pattern)] @@ -173,6 +178,7 @@ #![feature(const_trait_impl)] #![feature(coroutine_trait)] #![feature(decl_macro)] +#![feature(derive_const)] #![feature(dropck_eyepatch)] #![feature(fundamental)] #![feature(hashmap_internals)] diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index 236e33e2f450e..ff996ba93cd7f 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -4,7 +4,7 @@ // Note: This module is also included in the alloctests crate using #[path] to // run the tests. See the comment there for an explanation why this is the case. -use core::marker::PhantomData; +use core::marker::{Destruct, PhantomData}; use core::mem::{ManuallyDrop, MaybeUninit, SizedTypeProperties}; use core::ptr::{self, Alignment, NonNull, Unique}; use core::{cmp, hint}; @@ -24,7 +24,7 @@ mod tests; // only one location which panics rather than a bunch throughout the module. #[cfg(not(no_global_oom_handling))] #[cfg_attr(not(panic = "immediate-abort"), inline(never))] -fn capacity_overflow() -> ! { +const fn capacity_overflow() -> ! { panic!("capacity overflow"); } @@ -165,6 +165,30 @@ const fn min_non_zero_cap(size: usize) -> usize { } } +#[rustc_const_unstable(feature = "const_heap", issue = "79597")] +#[rustfmt::skip] // FIXME(fee1-dead): temporary measure before rustfmt is bumped +const impl RawVec { + /// Like `with_capacity`, but parameterized over the choice of + /// allocator for the returned `RawVec`. + #[cfg(not(no_global_oom_handling))] + #[inline] + pub(crate) fn with_capacity_in(capacity: usize, alloc: A) -> Self { + Self { + inner: RawVecInner::with_capacity_in(capacity, alloc, T::LAYOUT), + _marker: PhantomData, + } + } + + /// A specialized version of `self.reserve(len, 1)` which requires the + /// caller to ensure `len == self.capacity()`. + #[cfg(not(no_global_oom_handling))] + #[inline(never)] + pub(crate) fn grow_one(&mut self) { + // SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout + unsafe { self.inner.grow_one(T::LAYOUT) } + } +} + impl RawVec { #[cfg(not(no_global_oom_handling))] pub(crate) const MIN_NON_ZERO_CAP: usize = min_non_zero_cap(size_of::()); @@ -178,17 +202,6 @@ impl RawVec { Self { inner: RawVecInner::new_in(alloc, Alignment::of::()), _marker: PhantomData } } - /// Like `with_capacity`, but parameterized over the choice of - /// allocator for the returned `RawVec`. - #[cfg(not(no_global_oom_handling))] - #[inline] - pub(crate) fn with_capacity_in(capacity: usize, alloc: A) -> Self { - Self { - inner: RawVecInner::with_capacity_in(capacity, alloc, T::LAYOUT), - _marker: PhantomData, - } - } - /// Like `try_with_capacity`, but parameterized over the choice of /// allocator for the returned `RawVec`. #[inline] @@ -327,15 +340,6 @@ impl RawVec { unsafe { self.inner.reserve(len, additional, T::LAYOUT) } } - /// A specialized version of `self.reserve(len, 1)` which requires the - /// caller to ensure `len == self.capacity()`. - #[cfg(not(no_global_oom_handling))] - #[inline(never)] - pub(crate) fn grow_one(&mut self) { - // SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout - unsafe { self.inner.grow_one(T::LAYOUT) } - } - /// The same as `reserve`, but returns on errors instead of panicking or aborting. pub(crate) fn try_reserve( &mut self, @@ -405,14 +409,9 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec { } } -impl RawVecInner { - #[inline] - const fn new_in(alloc: A, align: Alignment) -> Self { - let ptr = Unique::from_non_null(NonNull::without_provenance(align.as_nonzero())); - // `cap: 0` means "unallocated". zero-sized types are ignored. - Self { ptr, cap: ZERO_CAP, alloc } - } - +#[rustc_const_unstable(feature = "const_heap", issue = "79597")] +#[rustfmt::skip] // FIXME(fee1-dead): temporary measure before rustfmt is bumped +const impl RawVecInner { #[cfg(not(no_global_oom_handling))] #[inline] fn with_capacity_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { @@ -428,24 +427,6 @@ impl RawVecInner { } } - #[inline] - fn try_with_capacity_in( - capacity: usize, - alloc: A, - elem_layout: Layout, - ) -> Result { - Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) - } - - #[cfg(not(no_global_oom_handling))] - #[inline] - fn with_capacity_zeroed_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { - match Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc, elem_layout) { - Ok(res) => res, - Err(err) => handle_error(err), - } - } - fn try_allocate_in( capacity: usize, init: AllocInit, @@ -484,6 +465,118 @@ impl RawVecInner { }) } + /// # Safety + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment + #[cfg(not(no_global_oom_handling))] + #[inline] + unsafe fn grow_one(&mut self, elem_layout: Layout) { + // SAFETY: Precondition passed to caller + if let Err(err) = unsafe { self.grow_amortized(self.cap.as_inner(), 1, elem_layout) } { + handle_error(err); + } + } + + /// # Safety + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment + /// - The sum of `len` and `additional` must be greater than the current capacity + unsafe fn grow_amortized( + &mut self, + len: usize, + additional: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + // This is ensured by the calling contexts. + debug_assert!(additional > 0); + + if elem_layout.size() == 0 { + // Since we return a capacity of `usize::MAX` when `elem_size` is + // 0, getting to here necessarily means the `RawVec` is overfull. + return Err(CapacityOverflow.into()); + } + + // Nothing we can really do about these checks, sadly. + let required_cap = len.checked_add(additional).ok_or(CapacityOverflow)?; + + // This guarantees exponential growth. The doubling cannot overflow + // because `cap <= isize::MAX` and the type of `cap` is `usize`. + let cap = cmp::max(self.cap.as_inner() * 2, required_cap); + let cap = cmp::max(min_non_zero_cap(elem_layout.size()), cap); + + // SAFETY: + // - cap >= len + additional + // - other preconditions passed to caller + let ptr = unsafe { self.finish_grow(cap, elem_layout)? }; + + // SAFETY: `finish_grow` would have failed if `cap > isize::MAX` + unsafe { self.set_ptr_and_cap(ptr, cap) }; + Ok(()) + } + + /// # Safety + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment + /// - `cap` must be greater than the current capacity + // not marked inline(never) since we want optimizers to be able to observe the specifics of this + // function, see tests/codegen-llvm/vec-reserve-extend.rs. + #[cold] + unsafe fn finish_grow( + &self, + cap: usize, + elem_layout: Layout, + ) -> Result, TryReserveError> { + let new_layout = layout_array(cap, elem_layout)?; + + let memory = if let Some((ptr, old_layout)) = unsafe { self.current_memory(elem_layout) } { + // FIXME(const-hack): switch to `debug_assert_eq` + debug_assert!(old_layout.align() == new_layout.align()); + unsafe { + // The allocator checks for alignment equality + hint::assert_unchecked(old_layout.align() == new_layout.align()); + self.alloc.grow(ptr, old_layout, new_layout) + } + } else { + self.alloc.allocate(new_layout) + }; + + // FIXME(const-hack): switch back to `map_err` + match memory { + Ok(memory) => Ok(memory), + Err(_) => Err(AllocError { layout: new_layout, non_exhaustive: () }.into()), + } + } +} + +impl RawVecInner { + #[inline] + const fn new_in(alloc: A, align: Alignment) -> Self { + let ptr = Unique::from_non_null(NonNull::without_provenance(align.as_nonzero())); + // `cap: 0` means "unallocated". zero-sized types are ignored. + Self { ptr, cap: ZERO_CAP, alloc } + } + + #[inline] + fn try_with_capacity_in( + capacity: usize, + alloc: A, + elem_layout: Layout, + ) -> Result { + Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) + } + + #[cfg(not(no_global_oom_handling))] + #[inline] + fn with_capacity_zeroed_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { + match Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc, elem_layout) { + Ok(res) => res, + Err(err) => handle_error(err), + } + } + #[inline] unsafe fn from_raw_parts_in(ptr: *mut u8, cap: Cap, alloc: A) -> Self { Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap, alloc } @@ -519,7 +612,8 @@ impl RawVecInner { /// initially construct `self` /// - `elem_layout`'s size must be a multiple of its alignment #[inline] - unsafe fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull, Layout)> { + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + const unsafe fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull, Layout)> { if elem_layout.size() == 0 || self.cap.as_inner() == 0 { None } else { @@ -566,19 +660,6 @@ impl RawVecInner { } } - /// # Safety - /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to - /// initially construct `self` - /// - `elem_layout`'s size must be a multiple of its alignment - #[cfg(not(no_global_oom_handling))] - #[inline] - unsafe fn grow_one(&mut self, elem_layout: Layout) { - // SAFETY: Precondition passed to caller - if let Err(err) = unsafe { self.grow_amortized(self.cap.as_inner(), 1, elem_layout) } { - handle_error(err); - } - } - /// # Safety /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to /// initially construct `self` @@ -651,12 +732,13 @@ impl RawVecInner { } #[inline] - fn needs_to_grow(&self, len: usize, additional: usize, elem_layout: Layout) -> bool { + const fn needs_to_grow(&self, len: usize, additional: usize, elem_layout: Layout) -> bool { additional > self.capacity(elem_layout.size()).wrapping_sub(len) } #[inline] - unsafe fn set_ptr_and_cap(&mut self, ptr: NonNull<[u8]>, cap: usize) { + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + const unsafe fn set_ptr_and_cap(&mut self, ptr: NonNull<[u8]>, cap: usize) { // Allocators currently return a `NonNull<[u8]>` whose length matches // the size requested. If that ever changes, the capacity here should // change to `ptr.len() / size_of::()`. @@ -664,44 +746,6 @@ impl RawVecInner { self.cap = unsafe { Cap::new_unchecked(cap) }; } - /// # Safety - /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to - /// initially construct `self` - /// - `elem_layout`'s size must be a multiple of its alignment - /// - The sum of `len` and `additional` must be greater than the current capacity - unsafe fn grow_amortized( - &mut self, - len: usize, - additional: usize, - elem_layout: Layout, - ) -> Result<(), TryReserveError> { - // This is ensured by the calling contexts. - debug_assert!(additional > 0); - - if elem_layout.size() == 0 { - // Since we return a capacity of `usize::MAX` when `elem_size` is - // 0, getting to here necessarily means the `RawVec` is overfull. - return Err(CapacityOverflow.into()); - } - - // Nothing we can really do about these checks, sadly. - let required_cap = len.checked_add(additional).ok_or(CapacityOverflow)?; - - // This guarantees exponential growth. The doubling cannot overflow - // because `cap <= isize::MAX` and the type of `cap` is `usize`. - let cap = cmp::max(self.cap.as_inner() * 2, required_cap); - let cap = cmp::max(min_non_zero_cap(elem_layout.size()), cap); - - // SAFETY: - // - cap >= len + additional - // - other preconditions passed to caller - let ptr = unsafe { self.finish_grow(cap, elem_layout)? }; - - // SAFETY: `finish_grow` would have failed if `cap > isize::MAX` - unsafe { self.set_ptr_and_cap(ptr, cap) }; - Ok(()) - } - /// # Safety /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to /// initially construct `self` @@ -729,35 +773,6 @@ impl RawVecInner { Ok(()) } - /// # Safety - /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to - /// initially construct `self` - /// - `elem_layout`'s size must be a multiple of its alignment - /// - `cap` must be greater than the current capacity - // not marked inline(never) since we want optimizers to be able to observe the specifics of this - // function, see tests/codegen-llvm/vec-reserve-extend.rs. - #[cold] - unsafe fn finish_grow( - &self, - cap: usize, - elem_layout: Layout, - ) -> Result, TryReserveError> { - let new_layout = layout_array(cap, elem_layout)?; - - let memory = if let Some((ptr, old_layout)) = unsafe { self.current_memory(elem_layout) } { - debug_assert_eq!(old_layout.align(), new_layout.align()); - unsafe { - // The allocator checks for alignment equality - hint::assert_unchecked(old_layout.align() == new_layout.align()); - self.alloc.grow(ptr, old_layout, new_layout) - } - } else { - self.alloc.allocate(new_layout) - }; - - memory.map_err(|_| AllocError { layout: new_layout, non_exhaustive: () }.into()) - } - /// # Safety /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to /// initially construct `self` @@ -788,9 +803,7 @@ impl RawVecInner { elem_layout: Layout, ) -> Result<(), TryReserveError> { // SAFETY: Precondition passed to caller - let (ptr, layout) = if let Some(mem) = unsafe { self.current_memory(elem_layout) } { - mem - } else { + let Some((ptr, layout)) = (unsafe { self.current_memory(elem_layout) }) else { return Ok(()); }; @@ -841,7 +854,8 @@ impl RawVecInner { #[cfg(not(no_global_oom_handling))] #[cold] #[optimize(size)] -fn handle_error(e: TryReserveError) -> ! { +#[rustc_const_unstable(feature = "const_heap", issue = "79597")] +const fn handle_error(e: TryReserveError) -> ! { match e.kind() { CapacityOverflow => capacity_overflow(), AllocError { layout, .. } => handle_alloc_error(layout), @@ -849,6 +863,15 @@ fn handle_error(e: TryReserveError) -> ! { } #[inline] -fn layout_array(cap: usize, elem_layout: Layout) -> Result { - elem_layout.repeat(cap).map(|(layout, _pad)| layout).map_err(|_| CapacityOverflow.into()) +#[rustc_const_unstable(feature = "const_heap", issue = "79597")] +const fn layout_array(cap: usize, elem_layout: Layout) -> Result { + // This is only used with `elem_layout`s which are those of real rust types, + // which lets us use the much-simpler `repeat_packed`. + debug_assert!(elem_layout.size() == elem_layout.pad_to_align().size()); + + // FIXME(const-hack) return to using `map` and `map_err` once `const_closures` is implemented + match elem_layout.repeat_packed(cap) { + Ok(layout) => Ok(layout), + Err(_) => Err(CapacityOverflow.into()), + } } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 0ab019a68ea05..cec41524325e0 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -243,16 +243,16 @@ use core::any::Any; use core::cell::{Cell, CloneFromCell}; -use core::clone::UseCloned; #[cfg(not(no_global_oom_handling))] -use core::clone::{CloneToUninit, TrivialClone}; +use core::clone::TrivialClone; +use core::clone::{CloneToUninit, UseCloned}; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize}; -use core::mem::{self, ManuallyDrop, align_of_val_raw}; +use core::mem::{self, ManuallyDrop}; use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver}; #[cfg(not(no_global_oom_handling))] @@ -261,7 +261,7 @@ use core::panic::{RefUnwindSafe, UnwindSafe}; #[cfg(not(no_global_oom_handling))] use core::pin::Pin; use core::pin::PinCoerceUnsized; -use core::ptr::{self, NonNull, drop_in_place}; +use core::ptr::{self, Alignment, NonNull, drop_in_place}; #[cfg(not(no_global_oom_handling))] use core::slice::from_raw_parts_mut; use core::{borrow, fmt, hint}; @@ -1290,6 +1290,104 @@ impl Rc, A> { } } +impl Rc { + /// Constructs a new `Rc` with a clone of `value`. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// use std::rc::Rc; + /// + /// let hello: Rc = Rc::clone_from_ref("hello"); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "clone_from_ref", issue = "149075")] + pub fn clone_from_ref(value: &T) -> Rc { + Rc::clone_from_ref_in(value, Global) + } + + /// Constructs a new `Rc` with a clone of `value`, returning an error if allocation fails + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// use std::rc::Rc; + /// + /// let hello: Rc = Rc::try_clone_from_ref("hello")?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + pub fn try_clone_from_ref(value: &T) -> Result, AllocError> { + Rc::try_clone_from_ref_in(value, Global) + } +} + +impl Rc { + /// Constructs a new `Rc` with a clone of `value` in the provided allocator. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// use std::rc::Rc; + /// use std::alloc::System; + /// + /// let hello: Rc = Rc::clone_from_ref_in("hello", System); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + pub fn clone_from_ref_in(value: &T, alloc: A) -> Rc { + // `in_progress` drops the allocation if we panic before finishing initializing it. + let mut in_progress: UniqueRcUninit = UniqueRcUninit::new(value, alloc); + + // Initialize with clone of value. + let initialized_clone = unsafe { + // Clone. If the clone panics, `in_progress` will be dropped and clean up. + value.clone_to_uninit(in_progress.data_ptr().cast()); + // Cast type of pointer, now that it is initialized. + in_progress.into_rc() + }; + + initialized_clone + } + + /// Constructs a new `Rc` with a clone of `value` in the provided allocator, returning an error if allocation fails + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// use std::rc::Rc; + /// use std::alloc::System; + /// + /// let hello: Rc = Rc::try_clone_from_ref_in("hello", System)?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + pub fn try_clone_from_ref_in(value: &T, alloc: A) -> Result, AllocError> { + // `in_progress` drops the allocation if we panic before finishing initializing it. + let mut in_progress: UniqueRcUninit = UniqueRcUninit::try_new(value, alloc)?; + + // Initialize with clone of value. + let initialized_clone = unsafe { + // Clone. If the clone panics, `in_progress` will be dropped and clean up. + value.clone_to_uninit(in_progress.data_ptr().cast()); + // Cast type of pointer, now that it is initialized. + in_progress.into_rc() + }; + + Ok(initialized_clone) + } +} + impl Rc<[mem::MaybeUninit], A> { /// Converts to `Rc<[T]>`. /// @@ -1969,22 +2067,7 @@ impl Rc { if Rc::strong_count(this) != 1 { // Gotta clone the data, there are other Rcs. - - let this_data_ref: &T = &**this; - // `in_progress` drops the allocation if we panic before finishing initializing it. - let mut in_progress: UniqueRcUninit = - UniqueRcUninit::new(this_data_ref, this.alloc.clone()); - - // Initialize with clone of this. - let initialized_clone = unsafe { - // Clone. If the clone panics, `in_progress` will be dropped and clean up. - this_data_ref.clone_to_uninit(in_progress.data_ptr().cast()); - // Cast type of pointer, now that it is initialized. - in_progress.into_rc() - }; - - // Replace `this` with newly constructed Rc. - *this = initialized_clone; + *this = Rc::clone_from_ref_in(&**this, this.alloc.clone()); } else if Rc::weak_count(this) != 0 { // Can just steal the data, all that's left is Weaks @@ -2334,14 +2417,14 @@ impl Deref for Rc { } } -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for Rc {} //#[unstable(feature = "unique_rc_arc", issue = "112566")] -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for UniqueRc {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for Weak {} #[unstable(feature = "deref_pure_trait", issue = "87121")] @@ -3762,15 +3845,15 @@ unsafe fn data_offset(ptr: *const T) -> usize { // Because RcInner is repr(C), it will always be the last field in memory. // SAFETY: since the only unsized types possible are slices, trait objects, // and extern types, the input safety requirement is currently enough to - // satisfy the requirements of align_of_val_raw; this is an implementation + // satisfy the requirements of Alignment::of_val_raw; this is an implementation // detail of the language that must not be relied upon outside of std. - unsafe { data_offset_align(align_of_val_raw(ptr)) } + unsafe { data_offset_alignment(Alignment::of_val_raw(ptr)) } } #[inline] -fn data_offset_align(align: usize) -> usize { +fn data_offset_alignment(alignment: Alignment) -> usize { let layout = Layout::new::>(); - layout.size() + layout.padding_needed_for(align) + layout.size() + layout.padding_needed_for(alignment) } /// A uniquely owned [`Rc`]. @@ -4358,16 +4441,15 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for UniqueRc { /// This is a helper for [`Rc::make_mut()`] to ensure correct cleanup on panic. /// It is nearly a duplicate of `UniqueRc, A>` except that it allows `T: !Sized`, /// which `MaybeUninit` does not. -#[cfg(not(no_global_oom_handling))] struct UniqueRcUninit { ptr: NonNull>, layout_for_value: Layout, alloc: Option, } -#[cfg(not(no_global_oom_handling))] impl UniqueRcUninit { /// Allocates a RcInner with layout suitable to contain `for_value` or a clone of it. + #[cfg(not(no_global_oom_handling))] fn new(for_value: &T, alloc: A) -> UniqueRcUninit { let layout = Layout::for_value(for_value); let ptr = unsafe { @@ -4380,9 +4462,23 @@ impl UniqueRcUninit { Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) } } + /// Allocates a RcInner with layout suitable to contain `for_value` or a clone of it, + /// returning an error if allocation fails. + fn try_new(for_value: &T, alloc: A) -> Result, AllocError> { + let layout = Layout::for_value(for_value); + let ptr = unsafe { + Rc::try_allocate_for_layout( + layout, + |layout_for_rc_inner| alloc.allocate(layout_for_rc_inner), + |mem| mem.with_metadata_of(ptr::from_ref(for_value) as *const RcInner), + )? + }; + Ok(Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) }) + } + /// Returns the pointer to be written into to initialize the [`Rc`]. fn data_ptr(&mut self) -> *mut T { - let offset = data_offset_align(self.layout_for_value.align()); + let offset = data_offset_alignment(self.layout_for_value.alignment()); unsafe { self.ptr.as_ptr().byte_add(offset) as *mut T } } @@ -4402,7 +4498,6 @@ impl UniqueRcUninit { } } -#[cfg(not(no_global_oom_handling))] impl Drop for UniqueRcUninit { fn drop(&mut self) { // SAFETY: diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index a83b51ccb60c3..cc8d80aee4569 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -18,7 +18,7 @@ use core::cmp::Ordering::{self, Less}; use core::mem::MaybeUninit; #[cfg(not(no_global_oom_handling))] use core::ptr; -#[unstable(feature = "array_windows", issue = "75027")] +#[stable(feature = "array_windows", since = "1.94.0")] pub use core::slice::ArrayWindows; #[stable(feature = "inherent_ascii_escape", since = "1.60.0")] pub use core::slice::EscapeAscii; @@ -444,13 +444,16 @@ impl [T] { impl ConvertVec for T { #[inline] fn to_vec(s: &[Self], alloc: A) -> Vec { - let mut v = Vec::with_capacity_in(s.len(), alloc); + let len = s.len(); + let mut v = Vec::with_capacity_in(len, alloc); // SAFETY: // allocated above with the capacity of `s`, and initialize to `s.len()` in // ptr::copy_to_non_overlapping below. - unsafe { - s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), s.len()); - v.set_len(s.len()); + if len > 0 { + unsafe { + s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), len); + v.set_len(len); + } } v } diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 4a2689e01ff17..4100ee55a4c7b 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -47,11 +47,11 @@ use core::iter::FusedIterator; #[cfg(not(no_global_oom_handling))] use core::iter::from_fn; #[cfg(not(no_global_oom_handling))] +use core::num::Saturating; +#[cfg(not(no_global_oom_handling))] use core::ops::Add; #[cfg(not(no_global_oom_handling))] use core::ops::AddAssign; -#[cfg(not(no_global_oom_handling))] -use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{self, Range, RangeBounds}; use core::str::pattern::{Pattern, Utf8Pattern}; use core::{fmt, hash, ptr, slice}; @@ -454,6 +454,10 @@ impl String { /// /// [`new`]: String::new /// + /// # Panics + /// + /// Panics if the capacity exceeds `isize::MAX` _bytes_. + /// /// # Examples /// /// ``` @@ -615,16 +619,14 @@ impl String { pub fn from_utf8_lossy(v: &[u8]) -> Cow<'_, str> { let mut iter = v.utf8_chunks(); - let first_valid = if let Some(chunk) = iter.next() { - let valid = chunk.valid(); - if chunk.invalid().is_empty() { - debug_assert_eq!(valid.len(), v.len()); - return Cow::Borrowed(valid); - } - valid - } else { + let Some(chunk) = iter.next() else { return Cow::Borrowed(""); }; + let first_valid = chunk.valid(); + if chunk.invalid().is_empty() { + debug_assert_eq!(first_valid.len(), v.len()); + return Cow::Borrowed(first_valid); + } const REPLACEMENT: &str = "\u{FFFD}"; @@ -716,11 +718,10 @@ impl String { // FIXME: the function can be simplified again when #48994 is closed. let mut ret = String::with_capacity(v.len()); for c in char::decode_utf16(v.iter().cloned()) { - if let Ok(c) = c { - ret.push(c); - } else { + let Ok(c) = c else { return Err(FromUtf16Error(())); - } + }; + ret.push(c); } Ok(ret) } @@ -933,7 +934,7 @@ impl String { /// assert_eq!(rebuilt, "hello"); /// ``` #[must_use = "losing the pointer will leak memory"] - #[stable(feature = "vec_into_raw_parts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "vec_into_raw_parts", since = "1.93.0")] pub fn into_raw_parts(self) -> (*mut u8, usize, usize) { self.vec.into_raw_parts() } @@ -1079,6 +1080,10 @@ impl String { /// Appends a given string slice onto the end of this `String`. /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// /// # Examples /// /// ``` @@ -1097,12 +1102,30 @@ impl String { self.vec.extend_from_slice(string.as_bytes()) } + #[cfg(not(no_global_oom_handling))] + #[inline] + fn push_str_slice(&mut self, slice: &[&str]) { + // use saturating arithmetic to ensure that in the case of an overflow, reserve() throws OOM + let additional: Saturating = slice.iter().map(|x| Saturating(x.len())).sum(); + self.reserve(additional.0); + let (ptr, len, cap) = core::mem::take(self).into_raw_parts(); + unsafe { + let mut dst = ptr.add(len); + for new in slice { + core::ptr::copy_nonoverlapping(new.as_ptr(), dst, new.len()); + dst = dst.add(new.len()); + } + *self = String::from_raw_parts(ptr, len + additional.0, cap); + } + } + /// Copies elements from `src` range to the end of the string. /// /// # Panics /// - /// Panics if the range has `start_bound > end_bound`, or, if the range is - /// bounded on either end and does not lie on a [`char`] boundary. + /// Panics if the range has `start_bound > end_bound`, if the range is + /// bounded on either end and does not lie on a [`char`] boundary, or if the + /// new capacity exceeds `isize::MAX` bytes. /// /// # Examples /// @@ -1158,7 +1181,7 @@ impl String { /// /// # Panics /// - /// Panics if the new capacity overflows [`usize`]. + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. /// /// # Examples /// @@ -1208,7 +1231,7 @@ impl String { /// /// # Panics /// - /// Panics if the new capacity overflows [`usize`]. + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. /// /// # Examples /// @@ -1372,6 +1395,10 @@ impl String { /// Appends the given [`char`] to the end of this `String`. /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// /// # Examples /// /// ``` @@ -1536,7 +1563,7 @@ impl String { /// assert_eq!("bna", s); /// ``` #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "string_remove_matches", reason = "new API", issue = "72826")] + #[unstable(feature = "string_remove_matches", issue = "72826")] pub fn remove_matches(&mut self, pat: P) { use core::str::pattern::Searcher; @@ -2049,30 +2076,19 @@ impl String { where R: RangeBounds, { - // Memory safety - // - // Replace_range does not have the memory safety issues of a vector Splice. - // of the vector version. The data is just plain bytes. + // We avoid #81138 (nondeterministic RangeBounds impls) because we only use `range` once, here. + let checked_range = slice::range(range, ..self.len()); - // WARNING: Inlining this variable would be unsound (#81138) - let start = range.start_bound(); - match start { - Included(&n) => assert!(self.is_char_boundary(n)), - Excluded(&n) => assert!(self.is_char_boundary(n + 1)), - Unbounded => {} - }; - // WARNING: Inlining this variable would be unsound (#81138) - let end = range.end_bound(); - match end { - Included(&n) => assert!(self.is_char_boundary(n + 1)), - Excluded(&n) => assert!(self.is_char_boundary(n)), - Unbounded => {} - }; + assert!( + self.is_char_boundary(checked_range.start), + "start of range should be a character boundary" + ); + assert!( + self.is_char_boundary(checked_range.end), + "end of range should be a character boundary" + ); - // Using `range` again would be unsound (#81138) - // We assume the bounds reported by `range` remain the same, but - // an adversarial implementation could change between calls - unsafe { self.as_mut_vec() }.splice((start, end), replace_with.bytes()); + unsafe { self.as_mut_vec() }.splice(checked_range, replace_with.bytes()); } /// Replaces the leftmost occurrence of a pattern with another string, in-place. @@ -2489,7 +2505,7 @@ impl<'a> Extend<&'a char> for String { #[stable(feature = "rust1", since = "1.0.0")] impl<'a> Extend<&'a str> for String { fn extend>(&mut self, iter: I) { - iter.into_iter().for_each(move |s| self.push_str(s)); + ::spec_extend_into(iter, self) } #[inline] @@ -2498,6 +2514,32 @@ impl<'a> Extend<&'a str> for String { } } +#[cfg(not(no_global_oom_handling))] +trait SpecExtendStr { + fn spec_extend_into(self, s: &mut String); +} + +#[cfg(not(no_global_oom_handling))] +impl<'a, T: IntoIterator> SpecExtendStr for T { + default fn spec_extend_into(self, target: &mut String) { + self.into_iter().for_each(move |s| target.push_str(s)); + } +} + +#[cfg(not(no_global_oom_handling))] +impl SpecExtendStr for [&str] { + fn spec_extend_into(self, target: &mut String) { + target.push_str_slice(&self); + } +} + +#[cfg(not(no_global_oom_handling))] +impl SpecExtendStr for [&str; N] { + fn spec_extend_into(self, target: &mut String) { + target.push_str_slice(&self[..]); + } +} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "box_str2", since = "1.45.0")] impl Extend> for String { diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index c302f35e5ed6e..a5e4fab916aba 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -11,24 +11,22 @@ use core::any::Any; use core::cell::CloneFromCell; #[cfg(not(no_global_oom_handling))] -use core::clone::CloneToUninit; -#[cfg(not(no_global_oom_handling))] use core::clone::TrivialClone; -use core::clone::UseCloned; +use core::clone::{CloneToUninit, UseCloned}; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::intrinsics::abort; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::{PhantomData, Unsize}; -use core::mem::{self, ManuallyDrop, align_of_val_raw}; +use core::mem::{self, ManuallyDrop}; use core::num::NonZeroUsize; use core::ops::{CoerceUnsized, Deref, DerefMut, DerefPure, DispatchFromDyn, LegacyReceiver}; #[cfg(not(no_global_oom_handling))] use core::ops::{Residual, Try}; use core::panic::{RefUnwindSafe, UnwindSafe}; use core::pin::{Pin, PinCoerceUnsized}; -use core::ptr::{self, NonNull}; +use core::ptr::{self, Alignment, NonNull}; #[cfg(not(no_global_oom_handling))] use core::slice::from_raw_parts_mut; use core::sync::atomic::Ordering::{Acquire, Relaxed, Release}; @@ -1442,6 +1440,104 @@ impl Arc, A> { } } +impl Arc { + /// Constructs a new `Arc` with a clone of `value`. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// use std::sync::Arc; + /// + /// let hello: Arc = Arc::clone_from_ref("hello"); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "clone_from_ref", issue = "149075")] + pub fn clone_from_ref(value: &T) -> Arc { + Arc::clone_from_ref_in(value, Global) + } + + /// Constructs a new `Arc` with a clone of `value`, returning an error if allocation fails + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// use std::sync::Arc; + /// + /// let hello: Arc = Arc::try_clone_from_ref("hello")?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + pub fn try_clone_from_ref(value: &T) -> Result, AllocError> { + Arc::try_clone_from_ref_in(value, Global) + } +} + +impl Arc { + /// Constructs a new `Arc` with a clone of `value` in the provided allocator. + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let hello: Arc = Arc::clone_from_ref_in("hello", System); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + pub fn clone_from_ref_in(value: &T, alloc: A) -> Arc { + // `in_progress` drops the allocation if we panic before finishing initializing it. + let mut in_progress: UniqueArcUninit = UniqueArcUninit::new(value, alloc); + + // Initialize with clone of value. + let initialized_clone = unsafe { + // Clone. If the clone panics, `in_progress` will be dropped and clean up. + value.clone_to_uninit(in_progress.data_ptr().cast()); + // Cast type of pointer, now that it is initialized. + in_progress.into_arc() + }; + + initialized_clone + } + + /// Constructs a new `Arc` with a clone of `value` in the provided allocator, returning an error if allocation fails + /// + /// # Examples + /// + /// ``` + /// #![feature(clone_from_ref)] + /// #![feature(allocator_api)] + /// use std::sync::Arc; + /// use std::alloc::System; + /// + /// let hello: Arc = Arc::try_clone_from_ref_in("hello", System)?; + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + #[unstable(feature = "clone_from_ref", issue = "149075")] + //#[unstable(feature = "allocator_api", issue = "32838")] + pub fn try_clone_from_ref_in(value: &T, alloc: A) -> Result, AllocError> { + // `in_progress` drops the allocation if we panic before finishing initializing it. + let mut in_progress: UniqueArcUninit = UniqueArcUninit::try_new(value, alloc)?; + + // Initialize with clone of value. + let initialized_clone = unsafe { + // Clone. If the clone panics, `in_progress` will be dropped and clean up. + value.clone_to_uninit(in_progress.data_ptr().cast()); + // Cast type of pointer, now that it is initialized. + in_progress.into_arc() + }; + + Ok(initialized_clone) + } +} + impl Arc<[mem::MaybeUninit], A> { /// Converts to `Arc<[T]>`. /// @@ -2327,10 +2423,10 @@ impl Deref for Arc { } } -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for Arc {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for Weak {} #[unstable(feature = "deref_pure_trait", issue = "87121")] @@ -2406,19 +2502,7 @@ impl Arc { // deallocated. if this.inner().strong.compare_exchange(1, 0, Acquire, Relaxed).is_err() { // Another strong pointer exists, so we must clone. - - let this_data_ref: &T = &**this; - // `in_progress` drops the allocation if we panic before finishing initializing it. - let mut in_progress: UniqueArcUninit = - UniqueArcUninit::new(this_data_ref, this.alloc.clone()); - - let initialized_clone = unsafe { - // Clone. If the clone panics, `in_progress` will be dropped and clean up. - this_data_ref.clone_to_uninit(in_progress.data_ptr().cast()); - // Cast type of pointer, now that it is initialized. - in_progress.into_arc() - }; - *this = initialized_clone; + *this = Arc::clone_from_ref_in(&**this, this.alloc.clone()); } else if this.inner().weak.load(Relaxed) != 1 { // Relaxed suffices in the above because this is fundamentally an // optimization: we are always racing with weak pointers being @@ -4122,31 +4206,30 @@ unsafe fn data_offset(ptr: *const T) -> usize { // Because ArcInner is repr(C), it will always be the last field in memory. // SAFETY: since the only unsized types possible are slices, trait objects, // and extern types, the input safety requirement is currently enough to - // satisfy the requirements of align_of_val_raw; this is an implementation + // satisfy the requirements of Alignment::of_val_raw; this is an implementation // detail of the language that must not be relied upon outside of std. - unsafe { data_offset_align(align_of_val_raw(ptr)) } + unsafe { data_offset_alignment(Alignment::of_val_raw(ptr)) } } #[inline] -fn data_offset_align(align: usize) -> usize { +fn data_offset_alignment(alignment: Alignment) -> usize { let layout = Layout::new::>(); - layout.size() + layout.padding_needed_for(align) + layout.size() + layout.padding_needed_for(alignment) } /// A unique owning pointer to an [`ArcInner`] **that does not imply the contents are initialized,** /// but will deallocate it (without dropping the value) when dropped. /// /// This is a helper for [`Arc::make_mut()`] to ensure correct cleanup on panic. -#[cfg(not(no_global_oom_handling))] struct UniqueArcUninit { ptr: NonNull>, layout_for_value: Layout, alloc: Option, } -#[cfg(not(no_global_oom_handling))] impl UniqueArcUninit { /// Allocates an ArcInner with layout suitable to contain `for_value` or a clone of it. + #[cfg(not(no_global_oom_handling))] fn new(for_value: &T, alloc: A) -> UniqueArcUninit { let layout = Layout::for_value(for_value); let ptr = unsafe { @@ -4159,9 +4242,23 @@ impl UniqueArcUninit { Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) } } + /// Allocates an ArcInner with layout suitable to contain `for_value` or a clone of it, + /// returning an error if allocation fails. + fn try_new(for_value: &T, alloc: A) -> Result, AllocError> { + let layout = Layout::for_value(for_value); + let ptr = unsafe { + Arc::try_allocate_for_layout( + layout, + |layout_for_arcinner| alloc.allocate(layout_for_arcinner), + |mem| mem.with_metadata_of(ptr::from_ref(for_value) as *const ArcInner), + )? + }; + Ok(Self { ptr: NonNull::new(ptr).unwrap(), layout_for_value: layout, alloc: Some(alloc) }) + } + /// Returns the pointer to be written into to initialize the [`Arc`]. fn data_ptr(&mut self) -> *mut T { - let offset = data_offset_align(self.layout_for_value.align()); + let offset = data_offset_alignment(self.layout_for_value.alignment()); unsafe { self.ptr.as_ptr().byte_add(offset) as *mut T } } @@ -4755,7 +4852,7 @@ impl Deref for UniqueArc { } // #[unstable(feature = "unique_rc_arc", issue = "112566")] -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for UniqueArc {} #[unstable(feature = "unique_rc_arc", issue = "112566")] diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index b4116f4988b64..aa1901314e37c 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -127,6 +127,44 @@ impl From> for RawWaker { } } +/// Converts a closure into a [`Waker`]. +/// +/// The closure gets called every time the waker is woken. +/// +/// # Examples +/// +/// ``` +/// #![feature(waker_fn)] +/// use std::task::waker_fn; +/// +/// let waker = waker_fn(|| println!("woken")); +/// +/// waker.wake_by_ref(); // Prints "woken". +/// waker.wake(); // Prints "woken". +/// ``` +#[cfg(target_has_atomic = "ptr")] +#[unstable(feature = "waker_fn", issue = "149580")] +pub fn waker_fn(f: F) -> Waker { + struct WakeFn { + f: F, + } + + impl Wake for WakeFn + where + F: Fn(), + { + fn wake(self: Arc) { + (self.f)() + } + + fn wake_by_ref(self: &Arc) { + (self.f)() + } + } + + Waker::from(Arc::new(WakeFn { f })) +} + // NB: This private function for constructing a RawWaker is used, rather than // inlining this into the `From> for RawWaker` impl, to ensure that // the safety of `From> for Waker` does not depend on the correct @@ -306,6 +344,45 @@ impl From> for RawWaker { } } +/// Converts a closure into a [`LocalWaker`]. +/// +/// The closure gets called every time the local waker is woken. +/// +/// # Examples +/// +/// ``` +/// #![feature(local_waker)] +/// #![feature(waker_fn)] +/// use std::task::local_waker_fn; +/// +/// let waker = local_waker_fn(|| println!("woken")); +/// +/// waker.wake_by_ref(); // Prints "woken". +/// waker.wake(); // Prints "woken". +/// ``` +// #[unstable(feature = "local_waker", issue = "118959")] +#[unstable(feature = "waker_fn", issue = "149580")] +pub fn local_waker_fn(f: F) -> LocalWaker { + struct LocalWakeFn { + f: F, + } + + impl LocalWake for LocalWakeFn + where + F: Fn(), + { + fn wake(self: Rc) { + (self.f)() + } + + fn wake_by_ref(self: &Rc) { + (self.f)() + } + } + + LocalWaker::from(Rc::new(LocalWakeFn { f })) +} + // NB: This private function for constructing a RawWaker is used, rather than // inlining this into the `From> for RawWaker` impl, to ensure that // the safety of `From> for Waker` does not depend on the correct diff --git a/library/alloc/src/vec/in_place_drop.rs b/library/alloc/src/vec/in_place_drop.rs index 997c4c7525b5a..c8cc758ac15c4 100644 --- a/library/alloc/src/vec/in_place_drop.rs +++ b/library/alloc/src/vec/in_place_drop.rs @@ -1,6 +1,5 @@ use core::marker::PhantomData; use core::ptr::{self, NonNull, drop_in_place}; -use core::slice::{self}; use crate::alloc::Global; use crate::raw_vec::RawVec; @@ -22,7 +21,7 @@ impl Drop for InPlaceDrop { #[inline] fn drop(&mut self) { unsafe { - ptr::drop_in_place(slice::from_raw_parts_mut(self.inner, self.len())); + ptr::drop_in_place(ptr::slice_from_raw_parts_mut(self.inner, self.len())); } } } diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 5af34609a12ad..8cbf1088ff845 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -417,7 +417,12 @@ impl DoubleEndedIterator for IntoIter { // SAFETY: same as for advance_by() self.end = unsafe { self.end.sub(step_size) }; } - let to_drop = ptr::slice_from_raw_parts_mut(self.end as *mut T, step_size); + let to_drop = if T::IS_ZST { + // ZST may cause unalignment + ptr::slice_from_raw_parts_mut(ptr::NonNull::::dangling().as_ptr(), step_size) + } else { + ptr::slice_from_raw_parts_mut(self.end as *mut T, step_size) + }; // SAFETY: same as for advance_by() unsafe { ptr::drop_in_place(to_drop); diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 251521805db5b..7530192fcb3e7 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -81,12 +81,14 @@ use core::cmp::Ordering; use core::hash::{Hash, Hasher}; #[cfg(not(no_global_oom_handling))] use core::iter; -use core::marker::PhantomData; +#[cfg(not(no_global_oom_handling))] +use core::marker::Destruct; +use core::marker::{Freeze, PhantomData}; use core::mem::{self, Assume, ManuallyDrop, MaybeUninit, SizedTypeProperties, TransmuteFrom}; use core::ops::{self, Index, IndexMut, Range, RangeBounds}; use core::ptr::{self, NonNull}; use core::slice::{self, SliceIndex}; -use core::{fmt, intrinsics, ub_checks}; +use core::{fmt, hint, intrinsics, ub_checks}; #[stable(feature = "extract_if", since = "1.87.0")] pub use self::extract_if::ExtractIf; @@ -519,7 +521,8 @@ impl Vec { #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[rustc_diagnostic_item = "vec_with_capacity"] - pub fn with_capacity(capacity: usize) -> Self { + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + pub const fn with_capacity(capacity: usize) -> Self { Self::with_capacity_in(capacity, Global) } @@ -740,11 +743,64 @@ impl Vec { /// } /// ``` #[inline] - #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] + #[unstable(feature = "box_vec_non_null", issue = "130364")] pub unsafe fn from_parts(ptr: NonNull, length: usize, capacity: usize) -> Self { unsafe { Self::from_parts_in(ptr, length, capacity, Global) } } + /// Creates a `Vec` where each element is produced by calling `f` with + /// that element's index while walking forward through the `Vec`. + /// + /// This is essentially the same as writing + /// + /// ```text + /// vec![f(0), f(1), f(2), …, f(length - 2), f(length - 1)] + /// ``` + /// and is similar to `(0..i).map(f)`, just for `Vec`s not iterators. + /// + /// If `length == 0`, this produces an empty `Vec` without ever calling `f`. + /// + /// # Example + /// + /// ```rust + /// #![feature(vec_from_fn)] + /// + /// let vec = Vec::from_fn(5, |i| i); + /// + /// // indexes are: 0 1 2 3 4 + /// assert_eq!(vec, [0, 1, 2, 3, 4]); + /// + /// let vec2 = Vec::from_fn(8, |i| i * 2); + /// + /// // indexes are: 0 1 2 3 4 5 6 7 + /// assert_eq!(vec2, [0, 2, 4, 6, 8, 10, 12, 14]); + /// + /// let bool_vec = Vec::from_fn(5, |i| i % 2 == 0); + /// + /// // indexes are: 0 1 2 3 4 + /// assert_eq!(bool_vec, [true, false, true, false, true]); + /// ``` + /// + /// The `Vec` is generated in ascending index order, starting from the front + /// and going towards the back, so you can use closures with mutable state: + /// ``` + /// #![feature(vec_from_fn)] + /// + /// let mut state = 1; + /// let a = Vec::from_fn(6, |_| { let x = state; state *= 2; x }); + /// + /// assert_eq!(a, [1, 2, 4, 8, 16, 32]); + /// ``` + #[cfg(not(no_global_oom_handling))] + #[inline] + #[unstable(feature = "vec_from_fn", issue = "149698")] + pub fn from_fn(length: usize, f: F) -> Self + where + F: FnMut(usize) -> T, + { + (0..length).map(f).collect() + } + /// Decomposes a `Vec` into its raw components: `(pointer, length, capacity)`. /// /// Returns the raw pointer to the underlying data, the length of @@ -781,7 +837,7 @@ impl Vec { /// assert_eq!(rebuilt, [4294967295, 0, 1]); /// ``` #[must_use = "losing the pointer will leak memory"] - #[stable(feature = "vec_into_raw_parts", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "vec_into_raw_parts", since = "1.93.0")] pub fn into_raw_parts(self) -> (*mut T, usize, usize) { let mut me = ManuallyDrop::new(self); (me.as_mut_ptr(), me.len(), me.capacity()) @@ -822,35 +878,34 @@ impl Vec { /// assert_eq!(rebuilt, [4294967295, 0, 1]); /// ``` #[must_use = "losing the pointer will leak memory"] - #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] + #[unstable(feature = "box_vec_non_null", issue = "130364")] pub fn into_parts(self) -> (NonNull, usize, usize) { let (ptr, len, capacity) = self.into_raw_parts(); // SAFETY: A `Vec` always has a non-null pointer. (unsafe { NonNull::new_unchecked(ptr) }, len, capacity) } -} -impl Vec { - /// Constructs a new, empty `Vec`. - /// - /// The vector will not allocate until elements are pushed onto it. + /// Interns the `Vec`, making the underlying memory read-only. This method should be + /// called during compile time. (This is a no-op if called during runtime) /// - /// # Examples - /// - /// ``` - /// #![feature(allocator_api)] - /// - /// use std::alloc::System; - /// - /// # #[allow(unused_mut)] - /// let mut vec: Vec = Vec::new_in(System); - /// ``` - #[inline] - #[unstable(feature = "allocator_api", issue = "32838")] - pub const fn new_in(alloc: A) -> Self { - Vec { buf: RawVec::new_in(alloc), len: 0 } + /// This method must be called if the memory used by `Vec` needs to appear in the final + /// values of constants. + #[unstable(feature = "const_heap", issue = "79597")] + #[rustc_const_unstable(feature = "const_heap", issue = "79597")] + pub const fn const_make_global(mut self) -> &'static [T] + where + T: Freeze, + { + unsafe { core::intrinsics::const_make_global(self.as_mut_ptr().cast()) }; + let me = ManuallyDrop::new(self); + unsafe { slice::from_raw_parts(me.as_ptr(), me.len) } } +} +#[cfg(not(no_global_oom_handling))] +#[rustc_const_unstable(feature = "const_heap", issue = "79597")] +#[rustfmt::skip] // FIXME(fee1-dead): temporary measure before rustfmt is bumped +const impl Vec { /// Constructs a new, empty `Vec` with at least the specified capacity /// with the provided allocator. /// @@ -906,13 +961,106 @@ impl Vec { /// let vec_units = Vec::<(), System>::with_capacity_in(10, System); /// assert_eq!(vec_units.capacity(), usize::MAX); /// ``` - #[cfg(not(no_global_oom_handling))] #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { Vec { buf: RawVec::with_capacity_in(capacity, alloc), len: 0 } } + /// Appends an element to the back of a collection. + /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec![1, 2]; + /// vec.push(3); + /// assert_eq!(vec, [1, 2, 3]); + /// ``` + /// + /// # Time complexity + /// + /// Takes amortized *O*(1) time. If the vector's length would exceed its + /// capacity after the push, *O*(*capacity*) time is taken to copy the + /// vector's elements to a larger allocation. This expensive operation is + /// offset by the *capacity* *O*(1) insertions it allows. + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_confusables("push_back", "put", "append")] + pub fn push(&mut self, value: T) { + let _ = self.push_mut(value); + } + + /// Appends an element to the back of a collection, returning a reference to it. + /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// + /// # Examples + /// + /// ``` + /// let mut vec = vec![1, 2]; + /// let last = vec.push_mut(3); + /// assert_eq!(*last, 3); + /// assert_eq!(vec, [1, 2, 3]); + /// + /// let last = vec.push_mut(3); + /// *last += 1; + /// assert_eq!(vec, [1, 2, 3, 4]); + /// ``` + /// + /// # Time complexity + /// + /// Takes amortized *O*(1) time. If the vector's length would exceed its + /// capacity after the push, *O*(*capacity*) time is taken to copy the + /// vector's elements to a larger allocation. This expensive operation is + /// offset by the *capacity* *O*(1) insertions it allows. + #[inline] + #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] + #[must_use = "if you don't need a reference to the value, use `Vec::push` instead"] + pub fn push_mut(&mut self, value: T) -> &mut T { + // Inform codegen that the length does not change across grow_one(). + let len = self.len; + // This will panic or abort if we would allocate > isize::MAX bytes + // or if the length increment would overflow for zero-sized types. + if len == self.buf.capacity() { + self.buf.grow_one(); + } + unsafe { + let end = self.as_mut_ptr().add(len); + ptr::write(end, value); + self.len = len + 1; + // SAFETY: We just wrote a value to the pointer that will live the lifetime of the reference. + &mut *end + } + } +} + +impl Vec { + /// Constructs a new, empty `Vec`. + /// + /// The vector will not allocate until elements are pushed onto it. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// + /// # #[allow(unused_mut)] + /// let mut vec: Vec = Vec::new_in(System); + /// ``` + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub const fn new_in(alloc: A) -> Self { + Vec { buf: RawVec::new_in(alloc), len: 0 } + } + /// Constructs a new, empty `Vec` with at least the specified capacity /// with the provided allocator. /// @@ -1140,7 +1288,7 @@ impl Vec { /// } /// ``` #[inline] - #[unstable(feature = "allocator_api", reason = "new API", issue = "32838")] + #[unstable(feature = "allocator_api", issue = "32838")] // #[unstable(feature = "box_vec_non_null", issue = "130364")] pub unsafe fn from_parts_in(ptr: NonNull, length: usize, capacity: usize, alloc: A) -> Self { ub_checks::assert_unsafe_precondition!( @@ -1239,7 +1387,7 @@ impl Vec { /// ``` #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] + // #[unstable(feature = "box_vec_non_null", issue = "130364")] pub fn into_parts_with_alloc(self) -> (NonNull, usize, usize, A) { let (ptr, len, capacity, alloc) = self.into_raw_parts_with_alloc(); // SAFETY: A `Vec` always has a non-null pointer. @@ -1596,7 +1744,11 @@ impl Vec { // * We only construct `&mut` references to `self.buf` through `&mut self` methods; borrow- // check ensures that it is not possible to mutably alias `self.buf` within the // returned lifetime. - unsafe { slice::from_raw_parts(self.as_ptr(), self.len) } + unsafe { + // normally this would use `slice::from_raw_parts`, but it's + // instantiated often enough that avoiding the UB check is worth it + &*core::intrinsics::aggregate_raw_ptr::<*const [T], _, _>(self.as_ptr(), self.len) + } } /// Extracts a mutable slice of the entire vector. @@ -1628,7 +1780,11 @@ impl Vec { // * We only construct references to `self.buf` through `&self` and `&mut self` methods; // borrow-check ensures that it is not possible to construct a reference to `self.buf` // within the returned lifetime. - unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) } + unsafe { + // normally this would use `slice::from_raw_parts_mut`, but it's + // instantiated often enough that avoiding the UB check is worth it + &mut *core::intrinsics::aggregate_raw_ptr::<*mut [T], _, _>(self.as_mut_ptr(), self.len) + } } /// Returns a raw pointer to the vector's buffer, or a dangling raw pointer @@ -1835,8 +1991,8 @@ impl Vec { /// [`as_mut_ptr`]: Vec::as_mut_ptr /// [`as_ptr`]: Vec::as_ptr /// [`as_non_null`]: Vec::as_non_null - #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] - #[rustc_const_unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] + #[unstable(feature = "box_vec_non_null", issue = "130364")] + #[rustc_const_unstable(feature = "box_vec_non_null", issue = "130364")] #[inline] pub const fn as_non_null(&mut self) -> NonNull { self.buf.non_null() @@ -2037,7 +2193,6 @@ impl Vec { /// # Examples /// /// ``` - /// #![feature(push_mut)] /// let mut vec = vec![1, 3, 5, 9]; /// let x = vec.insert_mut(3, 6); /// *x += 1; @@ -2051,7 +2206,7 @@ impl Vec { /// the insertion index is 0. #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "push_mut", issue = "135974")] + #[stable(feature = "push_mut", since = "CURRENT_RUSTC_VERSION")] #[track_caller] #[must_use = "if you don't need a reference to the value, use `Vec::insert` instead"] pub fn insert_mut(&mut self, index: usize, element: T) -> &mut T { @@ -2239,13 +2394,8 @@ impl Vec { return; } - // Avoid double drop if the drop guard is not executed, - // since we may make some holes during the process. - unsafe { self.set_len(0) }; - // Vec: [Kept, Kept, Hole, Hole, Hole, Hole, Unchecked, Unchecked] - // |<- processed len ->| ^- next to check - // |<- deleted cnt ->| + // | ^- write ^- read | // |<- original_len ->| // Kept: Elements which predicate returns true on. // Hole: Moved or dropped element slot. @@ -2254,77 +2404,77 @@ impl Vec { // This drop guard will be invoked when predicate or `drop` of element panicked. // It shifts unchecked elements to cover holes and `set_len` to the correct length. // In cases when predicate and `drop` never panick, it will be optimized out. - struct BackshiftOnDrop<'a, T, A: Allocator> { + struct PanicGuard<'a, T, A: Allocator> { v: &'a mut Vec, - processed_len: usize, - deleted_cnt: usize, + read: usize, + write: usize, original_len: usize, } - impl Drop for BackshiftOnDrop<'_, T, A> { + impl Drop for PanicGuard<'_, T, A> { + #[cold] fn drop(&mut self) { - if self.deleted_cnt > 0 { - // SAFETY: Trailing unchecked items must be valid since we never touch them. - unsafe { - ptr::copy( - self.v.as_ptr().add(self.processed_len), - self.v.as_mut_ptr().add(self.processed_len - self.deleted_cnt), - self.original_len - self.processed_len, - ); - } + let remaining = self.original_len - self.read; + // SAFETY: Trailing unchecked items must be valid since we never touch them. + unsafe { + ptr::copy( + self.v.as_ptr().add(self.read), + self.v.as_mut_ptr().add(self.write), + remaining, + ); } // SAFETY: After filling holes, all items are in contiguous memory. unsafe { - self.v.set_len(self.original_len - self.deleted_cnt); + self.v.set_len(self.write + remaining); } } } - let mut g = BackshiftOnDrop { v: self, processed_len: 0, deleted_cnt: 0, original_len }; + let mut read = 0; + loop { + // SAFETY: read < original_len + let cur = unsafe { self.get_unchecked_mut(read) }; + if hint::unlikely(!f(cur)) { + break; + } + read += 1; + if read == original_len { + // All elements are kept, return early. + return; + } + } - fn process_loop( - original_len: usize, - f: &mut F, - g: &mut BackshiftOnDrop<'_, T, A>, - ) where - F: FnMut(&mut T) -> bool, - { - while g.processed_len != original_len { - // SAFETY: Unchecked element must be valid. - let cur = unsafe { &mut *g.v.as_mut_ptr().add(g.processed_len) }; - if !f(cur) { - // Advance early to avoid double drop if `drop_in_place` panicked. - g.processed_len += 1; - g.deleted_cnt += 1; - // SAFETY: We never touch this element again after dropped. - unsafe { ptr::drop_in_place(cur) }; - // We already advanced the counter. - if DELETED { - continue; - } else { - break; - } - } - if DELETED { - // SAFETY: `deleted_cnt` > 0, so the hole slot must not overlap with current element. - // We use copy for move, and never touch this element again. - unsafe { - let hole_slot = g.v.as_mut_ptr().add(g.processed_len - g.deleted_cnt); - ptr::copy_nonoverlapping(cur, hole_slot, 1); - } + // Critical section starts here and at least one element is going to be removed. + // Advance `g.read` early to avoid double drop if `drop_in_place` panicked. + let mut g = PanicGuard { v: self, read: read + 1, write: read, original_len }; + // SAFETY: previous `read` is always less than original_len. + unsafe { ptr::drop_in_place(&mut *g.v.as_mut_ptr().add(read)) }; + + while g.read < g.original_len { + // SAFETY: `read` is always less than original_len. + let cur = unsafe { &mut *g.v.as_mut_ptr().add(g.read) }; + if !f(cur) { + // Advance `read` early to avoid double drop if `drop_in_place` panicked. + g.read += 1; + // SAFETY: We never touch this element again after dropped. + unsafe { ptr::drop_in_place(cur) }; + } else { + // SAFETY: `read` > `write`, so the slots don't overlap. + // We use copy for move, and never touch the source element again. + unsafe { + let hole = g.v.as_mut_ptr().add(g.write); + ptr::copy_nonoverlapping(cur, hole, 1); } - g.processed_len += 1; + g.write += 1; + g.read += 1; } } - // Stage 1: Nothing was deleted. - process_loop::(original_len, &mut f, &mut g); - - // Stage 2: Some elements were deleted. - process_loop::(original_len, &mut f, &mut g); - - // All item are processed. This can be optimized to `set_len` by LLVM. - drop(g); + // We are leaving the critical section and no panic happened, + // Commit the length change and forget the guard. + // SAFETY: `write` is always less than or equal to original_len. + unsafe { g.v.set_len(g.write) }; + mem::forget(g); } /// Removes all but the first of consecutive elements in the vector that resolve to the same @@ -2498,34 +2648,6 @@ impl Vec { } } - /// Appends an element to the back of a collection. - /// - /// # Panics - /// - /// Panics if the new capacity exceeds `isize::MAX` _bytes_. - /// - /// # Examples - /// - /// ``` - /// let mut vec = vec![1, 2]; - /// vec.push(3); - /// assert_eq!(vec, [1, 2, 3]); - /// ``` - /// - /// # Time complexity - /// - /// Takes amortized *O*(1) time. If the vector's length would exceed its - /// capacity after the push, *O*(*capacity*) time is taken to copy the - /// vector's elements to a larger allocation. This expensive operation is - /// offset by the *capacity* *O*(1) insertions it allows. - #[cfg(not(no_global_oom_handling))] - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_confusables("push_back", "put", "append")] - pub fn push(&mut self, value: T) { - let _ = self.push_mut(value); - } - /// Appends an element and returns a reference to it if there is sufficient spare capacity, /// otherwise an error is returned with the element. /// @@ -2563,7 +2685,6 @@ impl Vec { /// Takes *O*(1) time. #[inline] #[unstable(feature = "vec_push_within_capacity", issue = "100486")] - // #[unstable(feature = "push_mut", issue = "135974")] pub fn push_within_capacity(&mut self, value: T) -> Result<&mut T, T> { if self.len == self.buf.capacity() { return Err(value); @@ -2579,55 +2700,6 @@ impl Vec { } } - /// Appends an element to the back of a collection, returning a reference to it. - /// - /// # Panics - /// - /// Panics if the new capacity exceeds `isize::MAX` _bytes_. - /// - /// # Examples - /// - /// ``` - /// #![feature(push_mut)] - /// - /// - /// let mut vec = vec![1, 2]; - /// let last = vec.push_mut(3); - /// assert_eq!(*last, 3); - /// assert_eq!(vec, [1, 2, 3]); - /// - /// let last = vec.push_mut(3); - /// *last += 1; - /// assert_eq!(vec, [1, 2, 3, 4]); - /// ``` - /// - /// # Time complexity - /// - /// Takes amortized *O*(1) time. If the vector's length would exceed its - /// capacity after the push, *O*(*capacity*) time is taken to copy the - /// vector's elements to a larger allocation. This expensive operation is - /// offset by the *capacity* *O*(1) insertions it allows. - #[cfg(not(no_global_oom_handling))] - #[inline] - #[unstable(feature = "push_mut", issue = "135974")] - #[must_use = "if you don't need a reference to the value, use `Vec::push` instead"] - pub fn push_mut(&mut self, value: T) -> &mut T { - // Inform codegen that the length does not change across grow_one(). - let len = self.len; - // This will panic or abort if we would allocate > isize::MAX bytes - // or if the length increment would overflow for zero-sized types. - if len == self.buf.capacity() { - self.buf.grow_one(); - } - unsafe { - let end = self.as_mut_ptr().add(len); - ptr::write(end, value); - self.len = len + 1; - // SAFETY: We just wrote a value to the pointer that will live the lifetime of the reference. - &mut *end - } - } - /// Removes the last element from a vector and returns it, or [`None`] if it /// is empty. /// @@ -2741,7 +2813,11 @@ impl Vec { let count = other.len(); self.reserve(count); let len = self.len(); - unsafe { ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count) }; + if count > 0 { + unsafe { + ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count) + }; + } self.len += count; } @@ -3340,6 +3416,10 @@ impl Vec { /// except that it also works with slice elements that are Clone but not Copy. /// If Rust gets specialization this function may be deprecated. /// + /// # Panics + /// + /// Panics if the new capacity exceeds `isize::MAX` _bytes_. + /// /// # Examples /// /// ``` @@ -3361,8 +3441,9 @@ impl Vec { /// /// # Panics /// - /// Panics if starting index is greater than the end index - /// or if the index is greater than the length of the vector. + /// Panics if starting index is greater than the end index, if the index is + /// greater than the length of the vector, or if the new capacity exceeds + /// `isize::MAX` _bytes_. /// /// # Examples /// diff --git a/library/alloc/src/vec/spec_extend.rs b/library/alloc/src/vec/spec_extend.rs index f5bcd3ec9d82d..7c908841c90ec 100644 --- a/library/alloc/src/vec/spec_extend.rs +++ b/library/alloc/src/vec/spec_extend.rs @@ -28,8 +28,8 @@ where } } -impl SpecExtend> for Vec { - fn spec_extend(&mut self, mut iterator: IntoIter) { +impl SpecExtend> for Vec { + fn spec_extend(&mut self, mut iterator: IntoIter) { unsafe { self.append_elements(iterator.as_slice() as _); } diff --git a/library/alloc/src/vec/splice.rs b/library/alloc/src/vec/splice.rs index d571e35828aeb..46611f611dc2c 100644 --- a/library/alloc/src/vec/splice.rs +++ b/library/alloc/src/vec/splice.rs @@ -50,6 +50,7 @@ impl DoubleEndedIterator for Splice<'_, I, A> { #[stable(feature = "vec_splice", since = "1.21.0")] impl ExactSizeIterator for Splice<'_, I, A> {} +// See also: [`crate::collections::vec_deque::Splice`]. #[stable(feature = "vec_splice", since = "1.21.0")] impl Drop for Splice<'_, I, A> { fn drop(&mut self) { @@ -112,12 +113,11 @@ impl Drain<'_, T, A> { }; for place in range_slice { - if let Some(new_item) = replace_with.next() { - unsafe { ptr::write(place, new_item) }; - vec.len += 1; - } else { + let Some(new_item) = replace_with.next() else { return false; - } + }; + unsafe { ptr::write(place, new_item) }; + vec.len += 1; } true } diff --git a/library/alloctests/benches/btree/map.rs b/library/alloctests/benches/btree/map.rs index 778065fd96575..c2bf43caf131e 100644 --- a/library/alloctests/benches/btree/map.rs +++ b/library/alloctests/benches/btree/map.rs @@ -2,6 +2,7 @@ use std::collections::BTreeMap; use std::ops::RangeBounds; use rand::Rng; +use rand::distr::{Distribution, Uniform}; use rand::seq::SliceRandom; use test::{Bencher, black_box}; @@ -106,7 +107,8 @@ macro_rules! map_find_rand_bench { // setup let mut rng = crate::bench_rng(); - let mut keys: Vec<_> = (0..n).map(|_| rng.random::() % n).collect(); + let mut keys: Vec<_> = + Uniform::new(0, n).unwrap().sample_iter(&mut rng).take(n as usize).collect(); for &k in &keys { map.insert(k, k); diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index 73c25679d05ba..fe14480102e32 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -14,11 +14,18 @@ // // Library features: // tidy-alphabetical-start -#![feature(alloc_layout_extra)] #![feature(allocator_api)] #![feature(array_into_iter_constructors)] #![feature(assert_matches)] +#![feature(box_vec_non_null)] #![feature(char_internals)] +#![feature(const_alloc_error)] +#![feature(const_cmp)] +#![feature(const_convert)] +#![feature(const_destruct)] +#![feature(const_heap)] +#![feature(const_option_ops)] +#![feature(const_try)] #![feature(copied_into_inner)] #![feature(core_intrinsics)] #![feature(exact_size_is_empty)] @@ -28,7 +35,6 @@ #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] -#![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array_transpose)] #![feature(ptr_alignment_type)] #![feature(ptr_internals)] @@ -58,6 +64,7 @@ #![feature(negative_impls)] #![feature(never_type)] #![feature(optimize_attribute)] +#![feature(prelude_import)] #![feature(rustc_allow_const_fn_unstable)] #![feature(rustc_attrs)] #![feature(staged_api)] @@ -67,11 +74,17 @@ // Allow testing this library extern crate alloc as realalloc; -#[macro_use] + +// This is needed to provide macros to the directly imported alloc modules below. extern crate std; +#[prelude_import] +#[allow(unused_imports)] +use std::prelude::rust_2024::*; + #[cfg(test)] extern crate test; mod testing; + use realalloc::*; // We are directly including collections, raw_vec, and wtf8 here as they use non-public @@ -95,8 +108,7 @@ pub(crate) mod test_helpers { let mut hasher = std::hash::RandomState::new().build_hasher(); std::panic::Location::caller().hash(&mut hasher); let hc64 = hasher.finish(); - let seed_vec = - hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); + let seed_vec = hc64.to_le_bytes().into_iter().chain(0u8..8).collect::>(); let seed: [u8; 16] = seed_vec.as_slice().try_into().unwrap(); rand::SeedableRng::from_seed(seed) } diff --git a/library/alloctests/testing/ord_chaos.rs b/library/alloctests/testing/ord_chaos.rs index 55e1ae5e3deaa..f90ba1c69921e 100644 --- a/library/alloctests/testing/ord_chaos.rs +++ b/library/alloctests/testing/ord_chaos.rs @@ -2,6 +2,8 @@ use std::cell::Cell; use std::cmp::Ordering::{self, *}; use std::ptr; +use crate::string::String; + // Minimal type with an `Ord` implementation violating transitivity. #[derive(Debug)] pub(crate) enum Cyclic3 { @@ -79,3 +81,31 @@ impl PartialEq for Governed<'_, T> { } impl Eq for Governed<'_, T> {} + +// Comparison based only on the ID, the name is ignored. +#[derive(Debug)] +pub(crate) struct IdBased { + pub id: u32, + #[allow(dead_code)] + pub name: String, +} + +impl PartialEq for IdBased { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Eq for IdBased {} + +impl PartialOrd for IdBased { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for IdBased { + fn cmp(&self, other: &Self) -> Ordering { + self.id.cmp(&other.id) + } +} diff --git a/library/alloctests/tests/boxed.rs b/library/alloctests/tests/boxed.rs index 94389cf2de933..83fd1ef7449a3 100644 --- a/library/alloctests/tests/boxed.rs +++ b/library/alloctests/tests/boxed.rs @@ -104,7 +104,7 @@ pub struct ConstAllocator; unsafe impl Allocator for ConstAllocator { fn allocate(&self, layout: Layout) -> Result, AllocError> { match layout.size() { - 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), + 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling_ptr(), 0)), _ => unsafe { let ptr = core::intrinsics::const_allocate(layout.size(), layout.align()); Ok(NonNull::new_unchecked(ptr as *mut [u8; 0] as *mut [u8])) diff --git a/library/alloctests/tests/c_str2.rs b/library/alloctests/tests/c_str2.rs index fe7686bd1c592..e714b9825d2d0 100644 --- a/library/alloctests/tests/c_str2.rs +++ b/library/alloctests/tests/c_str2.rs @@ -1,11 +1,9 @@ use alloc::ffi::CString; use alloc::rc::Rc; use alloc::sync::Arc; -use core::assert_matches::assert_matches; +use core::assert_matches; use core::ffi::{CStr, FromBytesUntilNulError, c_char}; -#[allow(deprecated)] -use core::hash::SipHasher13 as DefaultHasher; -use core::hash::{Hash, Hasher}; +use core::hash::{Hash, Hasher, SipHasher13 as DefaultHasher}; #[test] fn c_to_rust() { @@ -57,11 +55,9 @@ fn equal_hash() { let ptr = data.as_ptr() as *const c_char; let cstr: &'static CStr = unsafe { CStr::from_ptr(ptr) }; - #[allow(deprecated)] let mut s = DefaultHasher::new(); cstr.hash(&mut s); let cstr_hash = s.finish(); - #[allow(deprecated)] let mut s = DefaultHasher::new(); CString::new(&data[..data.len() - 1]).unwrap().hash(&mut s); let cstring_hash = s.finish(); diff --git a/library/alloctests/tests/collections/binary_heap.rs b/library/alloctests/tests/collections/binary_heap.rs index 95f4c3e614f5e..e1484c32a4f8a 100644 --- a/library/alloctests/tests/collections/binary_heap.rs +++ b/library/alloctests/tests/collections/binary_heap.rs @@ -136,6 +136,18 @@ fn test_peek_and_pop() { } } +#[test] +fn test_pop_if() { + let data = vec![9, 8, 7, 6, 5, 4, 3, 2, 1, 0]; + let mut sorted = data.clone(); + sorted.sort(); + let mut heap = BinaryHeap::from(data); + while let Some(popped) = heap.pop_if(|x| *x > 2) { + assert_eq!(popped, sorted.pop().unwrap()); + } + assert_eq!(heap.into_sorted_vec(), vec![0, 1, 2]); +} + #[test] fn test_peek_mut() { let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]; diff --git a/library/alloctests/tests/collections/eq_diff_len.rs b/library/alloctests/tests/collections/eq_diff_len.rs new file mode 100644 index 0000000000000..ee1e294d37c67 --- /dev/null +++ b/library/alloctests/tests/collections/eq_diff_len.rs @@ -0,0 +1,96 @@ +//! Regression tests which fail if some collections' `PartialEq::eq` impls compare +//! elements when the collections have different sizes. +//! This behavior is not guaranteed either way, so regressing these tests is fine +//! if it is done on purpose. +use std::cmp::Ordering; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, LinkedList}; + +/// This intentionally has a panicking `PartialEq` impl, to test that various +/// collections' `PartialEq` impls don't actually compare elements if their sizes +/// are unequal. +/// +/// This is not advisable in normal code. +#[derive(Debug, Clone, Copy, Hash)] +struct Evil; + +impl PartialEq for Evil { + fn eq(&self, _: &Self) -> bool { + panic!("Evil::eq is evil"); + } +} +impl Eq for Evil {} + +impl PartialOrd for Evil { + fn partial_cmp(&self, _: &Self) -> Option { + Some(Ordering::Equal) + } +} + +impl Ord for Evil { + fn cmp(&self, _: &Self) -> Ordering { + // Constructing a `BTreeSet`/`BTreeMap` uses `cmp` on the elements, + // but comparing it with with `==` uses `eq` on the elements, + // so Evil::cmp doesn't need to be evil. + Ordering::Equal + } +} + +// check Evil works +#[test] +#[should_panic = "Evil::eq is evil"] +fn evil_eq_works() { + let v1 = vec![Evil]; + let v2 = vec![Evil]; + + _ = v1 == v2; +} + +// check various containers don't compare if their sizes are different + +#[test] +fn vec_evil_eq() { + let v1 = vec![Evil]; + let v2 = vec![Evil; 2]; + + assert_eq!(false, v1 == v2); +} + +#[test] +fn hashset_evil_eq() { + let s1 = HashSet::from([(0, Evil)]); + let s2 = HashSet::from([(0, Evil), (1, Evil)]); + + assert_eq!(false, s1 == s2); +} + +#[test] +fn hashmap_evil_eq() { + let m1 = HashMap::from([(0, Evil)]); + let m2 = HashMap::from([(0, Evil), (1, Evil)]); + + assert_eq!(false, m1 == m2); +} + +#[test] +fn btreeset_evil_eq() { + let s1 = BTreeSet::from([(0, Evil)]); + let s2 = BTreeSet::from([(0, Evil), (1, Evil)]); + + assert_eq!(false, s1 == s2); +} + +#[test] +fn btreemap_evil_eq() { + let m1 = BTreeMap::from([(0, Evil)]); + let m2 = BTreeMap::from([(0, Evil), (1, Evil)]); + + assert_eq!(false, m1 == m2); +} + +#[test] +fn linkedlist_evil_eq() { + let m1 = LinkedList::from([Evil]); + let m2 = LinkedList::from([Evil; 2]); + + assert_eq!(false, m1 == m2); +} diff --git a/library/alloctests/tests/collections/mod.rs b/library/alloctests/tests/collections/mod.rs index e73f3aaef8c83..2d387f0e77eb5 100644 --- a/library/alloctests/tests/collections/mod.rs +++ b/library/alloctests/tests/collections/mod.rs @@ -1 +1,2 @@ mod binary_heap; +mod eq_diff_len; diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index b4d3e75b09942..e15c86496cf1b 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -1,11 +1,12 @@ #![feature(allocator_api)] -#![feature(alloc_layout_extra)] +#![feature(binary_heap_pop_if)] +#![feature(const_heap)] +#![feature(deque_extend_front)] #![feature(iter_array_chunks)] #![feature(assert_matches)] #![feature(wtf8_internals)] #![feature(cow_is_borrowed)] #![feature(core_intrinsics)] -#![feature(deque_extend_front)] #![feature(downcast_unchecked)] #![feature(exact_size_is_empty)] #![feature(hashmap_internals)] @@ -20,6 +21,8 @@ #![feature(binary_heap_into_iter_sorted)] #![feature(binary_heap_drain_sorted)] #![feature(slice_ptr_get)] +#![feature(slice_range)] +#![feature(slice_partial_sort_unstable)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] diff --git a/library/alloctests/tests/sort/mod.rs b/library/alloctests/tests/sort/mod.rs index 0e2494ca9d34e..e2e141a02b597 100644 --- a/library/alloctests/tests/sort/mod.rs +++ b/library/alloctests/tests/sort/mod.rs @@ -12,6 +12,7 @@ pub trait Sort { mod ffi_types; mod known_good_stable_sort; +mod partial; mod patterns; mod tests; mod zipf; diff --git a/library/alloctests/tests/sort/partial.rs b/library/alloctests/tests/sort/partial.rs new file mode 100644 index 0000000000000..679841b91e8db --- /dev/null +++ b/library/alloctests/tests/sort/partial.rs @@ -0,0 +1,88 @@ +use std::fmt::Debug; +use std::ops::{Range, RangeBounds}; +use std::slice; + +use super::patterns; + +fn check_is_partial_sorted>(v: &mut [T], range: R) { + let Range { start, end } = slice::range(range, ..v.len()); + v.partial_sort_unstable(start..end); + + let max_before = v[..start].iter().max().into_iter(); + let sorted_range = v[start..end].into_iter(); + let min_after = v[end..].iter().min().into_iter(); + let seq = max_before.chain(sorted_range).chain(min_after); + assert!(seq.is_sorted()); +} + +fn check_is_partial_sorted_ranges(v: &[T]) { + let len = v.len(); + + check_is_partial_sorted::(&mut v.to_vec(), ..); + check_is_partial_sorted::(&mut v.to_vec(), 0..0); + check_is_partial_sorted::(&mut v.to_vec(), len..len); + + if len > 0 { + check_is_partial_sorted::(&mut v.to_vec(), len - 1..len - 1); + check_is_partial_sorted::(&mut v.to_vec(), 0..1); + check_is_partial_sorted::(&mut v.to_vec(), len - 1..len); + + for mid in 1..len { + check_is_partial_sorted::(&mut v.to_vec(), 0..mid); + check_is_partial_sorted::(&mut v.to_vec(), mid..len); + check_is_partial_sorted::(&mut v.to_vec(), mid..mid); + check_is_partial_sorted::(&mut v.to_vec(), mid - 1..mid + 1); + check_is_partial_sorted::(&mut v.to_vec(), mid - 1..mid); + check_is_partial_sorted::(&mut v.to_vec(), mid..mid + 1); + } + + let quarters = [0, len / 4, len / 2, (3 * len) / 4, len]; + for &start in &quarters { + for &end in &quarters { + if start < end { + check_is_partial_sorted::(&mut v.to_vec(), start..end); + } + } + } + } +} + +#[test] +fn basic_impl() { + check_is_partial_sorted::(&mut [], ..); + check_is_partial_sorted::<(), _>(&mut [], ..); + check_is_partial_sorted::<(), _>(&mut [()], ..); + check_is_partial_sorted::<(), _>(&mut [(), ()], ..); + check_is_partial_sorted::<(), _>(&mut [(), (), ()], ..); + check_is_partial_sorted::(&mut [], ..); + + check_is_partial_sorted::(&mut [77], ..); + check_is_partial_sorted::(&mut [2, 3], ..); + check_is_partial_sorted::(&mut [2, 3, 6], ..); + check_is_partial_sorted::(&mut [2, 3, 99, 6], ..); + check_is_partial_sorted::(&mut [2, 7709, 400, 90932], ..); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], ..); + + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 0..0); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 0..1); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 0..5); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 0..7); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 7..7); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 6..7); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 5..7); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 5..5); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 4..5); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 4..6); +} + +#[test] +fn random_patterns() { + check_is_partial_sorted_ranges(&patterns::random(10)); + check_is_partial_sorted_ranges(&patterns::random(50)); + + // Longer tests would take hours to run under Miri. + if !cfg!(miri) { + check_is_partial_sorted_ranges(&patterns::random(100)); + check_is_partial_sorted_ranges(&patterns::random(1000)); + } +} diff --git a/library/alloctests/tests/sort/patterns.rs b/library/alloctests/tests/sort/patterns.rs index 0f1ec664d3d4f..1ed645cf99dca 100644 --- a/library/alloctests/tests/sort/patterns.rs +++ b/library/alloctests/tests/sort/patterns.rs @@ -27,21 +27,20 @@ where { // :.:.:.:: - let mut rng: XorShiftRng = rand::SeedableRng::seed_from_u64(get_or_init_rand_seed()); + let rng: XorShiftRng = rand::SeedableRng::seed_from_u64(get_or_init_rand_seed()); // Abstracting over ranges in Rust :( let dist = Uniform::try_from(range).unwrap(); - (0..len).map(|_| dist.sample(&mut rng)).collect() + rng.sample_iter(dist).take(len).collect() } pub fn random_zipf(len: usize, exponent: f64) -> Vec { // https://en.wikipedia.org/wiki/Zipf's_law - let mut rng: XorShiftRng = rand::SeedableRng::seed_from_u64(get_or_init_rand_seed()); + let rng: XorShiftRng = rand::SeedableRng::seed_from_u64(get_or_init_rand_seed()); - // Abstracting over ranges in Rust :( let dist = ZipfDistribution::new(len, exponent).unwrap(); - (0..len).map(|_| dist.sample(&mut rng) as i32).collect() + rng.sample_iter(dist).map(|val| val as i32).take(len).collect() } pub fn random_sorted(len: usize, sorted_percent: f64) -> Vec { @@ -68,7 +67,7 @@ pub fn all_equal(len: usize) -> Vec { // ...... // :::::: - (0..len).map(|_| 66).collect::>() + vec![66; len] } pub fn ascending(len: usize) -> Vec { @@ -206,6 +205,6 @@ fn rand_root_seed() -> u64 { } fn random_vec(len: usize) -> Vec { - let mut rng: XorShiftRng = rand::SeedableRng::seed_from_u64(get_or_init_rand_seed()); - (0..len).map(|_| rng.random::()).collect() + let rng: XorShiftRng = rand::SeedableRng::seed_from_u64(get_or_init_rand_seed()); + rng.random_iter().take(len).collect() } diff --git a/library/alloctests/tests/sort/tests.rs b/library/alloctests/tests/sort/tests.rs index d321f8df51898..09b76773d6b24 100644 --- a/library/alloctests/tests/sort/tests.rs +++ b/library/alloctests/tests/sort/tests.rs @@ -362,6 +362,13 @@ fn sort_vs_sort_by_impl() { assert_eq!(input_sort_by, expected); } +pub fn box_value_impl() { + for len in [3, 9, 35, 56, 132] { + test_is_sorted::, S>(len, Box::new, patterns::random); + test_is_sorted::, S>(len, Box::new, |len| patterns::random_sorted(len, 80.0)); + } +} + gen_sort_test_fns_with_default_patterns!( correct_i32, |len, pattern_fn| test_is_sorted::(len, |val| val, pattern_fn), @@ -967,6 +974,7 @@ define_instantiate_sort_tests!( [miri_yes, fixed_seed_rand_vec_prefix], [miri_yes, int_edge], [miri_yes, sort_vs_sort_by], + [miri_yes, box_value], [miri_yes, correct_i32_random], [miri_yes, correct_i32_random_z1], [miri_yes, correct_i32_random_d2], diff --git a/library/alloctests/tests/str.rs b/library/alloctests/tests/str.rs index fbb3b01fd67f9..fcc4aaaa1dcd1 100644 --- a/library/alloctests/tests/str.rs +++ b/library/alloctests/tests/str.rs @@ -1,6 +1,6 @@ #![allow(invalid_from_utf8)] -use std::assert_matches::assert_matches; +use std::assert_matches; use std::borrow::Cow; use std::cmp::Ordering::{Equal, Greater, Less}; use std::str::{from_utf8, from_utf8_unchecked}; diff --git a/library/alloctests/tests/string.rs b/library/alloctests/tests/string.rs index ecc5b9dc82ed0..08eb1855a4824 100644 --- a/library/alloctests/tests/string.rs +++ b/library/alloctests/tests/string.rs @@ -1,10 +1,9 @@ -use std::assert_matches::assert_matches; use std::borrow::Cow; use std::cell::Cell; use std::collections::TryReserveErrorKind::*; use std::ops::Bound::*; use std::ops::{Bound, RangeBounds}; -use std::{panic, str}; +use std::{assert_matches, panic, str}; pub trait IntoCow<'a, B: ?Sized> where @@ -616,8 +615,15 @@ fn test_replace_range() { } #[test] -#[should_panic] -fn test_replace_range_char_boundary() { +#[should_panic = "start of range should be a character boundary"] +fn test_replace_range_start_char_boundary() { + let mut s = "Hello, 世界!".to_owned(); + s.replace_range(8.., ""); +} + +#[test] +#[should_panic = "end of range should be a character boundary"] +fn test_replace_range_end_char_boundary() { let mut s = "Hello, 世界!".to_owned(); s.replace_range(..8, ""); } @@ -632,28 +638,32 @@ fn test_replace_range_inclusive_range() { } #[test] -#[should_panic] +#[should_panic = "range end index 6 out of range for slice of length 5"] fn test_replace_range_out_of_bounds() { let mut s = String::from("12345"); s.replace_range(5..6, "789"); } #[test] -#[should_panic] +#[should_panic = "range end index 5 out of range for slice of length 5"] fn test_replace_range_inclusive_out_of_bounds() { let mut s = String::from("12345"); s.replace_range(5..=5, "789"); } +// The overflowed index value is target-dependent, +// so we don't check for its exact value in the panic message #[test] -#[should_panic] +#[should_panic = "out of range for slice of length 3"] fn test_replace_range_start_overflow() { let mut s = String::from("123"); s.replace_range((Excluded(usize::MAX), Included(0)), ""); } +// The overflowed index value is target-dependent, +// so we don't check for its exact value in the panic message #[test] -#[should_panic] +#[should_panic = "out of range for slice of length 3"] fn test_replace_range_end_overflow() { let mut s = String::from("456"); s.replace_range((Included(0), Included(usize::MAX)), ""); diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index dd42230d2e003..5ab305f7a5048 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -3,12 +3,10 @@ use core::num::NonZero; use core::ptr::NonNull; use core::{assert_eq, assert_ne}; use std::alloc::System; -use std::assert_matches::assert_matches; use std::borrow::Cow; use std::cell::Cell; use std::collections::TryReserveErrorKind::*; use std::fmt::Debug; -use std::hint; use std::iter::InPlaceIterable; use std::mem::swap; use std::ops::Bound::*; @@ -16,6 +14,7 @@ use std::panic::{AssertUnwindSafe, catch_unwind}; use std::rc::Rc; use std::sync::atomic::{AtomicU32, Ordering}; use std::vec::{Drain, IntoIter, PeekMut}; +use std::{assert_matches, hint}; use crate::testing::macros::struct_with_counted_drop; @@ -2717,3 +2716,51 @@ fn vec_null_ptr_roundtrip() { let new = roundtripped.with_addr(ptr.addr()); unsafe { new.read() }; } + +// Regression test for Undefined Behavior (UB) caused by IntoIter::nth_back (#148682) +// when dealing with high-aligned Zero-Sized Types (ZSTs). +use std::collections::{BTreeMap, BinaryHeap, HashMap, LinkedList, VecDeque}; +#[test] +fn zst_collections_iter_nth_back_regression() { + #[repr(align(8))] + #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)] + struct Thing; + let v = vec![Thing, Thing]; + let _ = v.into_iter().nth_back(1); + let mut d = VecDeque::new(); + d.push_back(Thing); + d.push_back(Thing); + let _ = d.into_iter().nth_back(1); + let mut map = BTreeMap::new(); + map.insert(0, Thing); + map.insert(1, Thing); + let _ = map.into_values().nth_back(0); + let mut hash_map = HashMap::new(); + hash_map.insert(1, Thing); + hash_map.insert(2, Thing); + let _ = hash_map.into_values().nth(1); + let mut heap = BinaryHeap::new(); + heap.push(Thing); + heap.push(Thing); + let _ = heap.into_iter().nth_back(1); + let mut list = LinkedList::new(); + list.push_back(Thing); + list.push_back(Thing); + let _ = list.into_iter().nth_back(1); +} + +#[test] +fn const_heap() { + const X: &'static [u32] = { + let mut v = Vec::with_capacity(6); + let mut x = 1; + while x < 42 { + v.push(x); + x *= 2; + } + assert!(v.len() == 6); + v.const_make_global() + }; + + assert_eq!([1, 2, 4, 8, 16, 32], X); +} diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs index cf31613577f74..92853fe00fd63 100644 --- a/library/alloctests/tests/vec_deque.rs +++ b/library/alloctests/tests/vec_deque.rs @@ -1,6 +1,6 @@ use core::cell::Cell; use core::num::NonZero; -use std::assert_matches::assert_matches; +use std::assert_matches; use std::collections::TryReserveErrorKind::*; use std::collections::VecDeque; use std::collections::vec_deque::Drain; @@ -2155,3 +2155,195 @@ fn test_extend_front_specialization_copy_slice() { // check it really wrapped assert_eq!(v.as_slices(), ([5].as_slice(), [4, 3, 2].as_slice())); } + +#[test] +fn test_extend_front_specialization_deque_drain() { + // trigger 8 code paths: all combinations of prepend and extend_front, wrap and no wrap (src deque), wrap and no wrap (dst deque) + + /// Get deque containing `[1, 2, 3, 4]`, possibly wrapping in the middle (between the 2 and 3). + fn test_deque(wrap: bool) -> VecDeque { + if wrap { + let mut v = VecDeque::with_capacity(4); + v.extend([3, 4]); + v.prepend([1, 2]); + assert_eq!(v.as_slices(), ([1, 2].as_slice(), [3, 4].as_slice())); + v + } else { + VecDeque::from([1, 2, 3, 4]) + } + } + + // prepend, v2.head == 0 + + let mut v1 = VecDeque::with_capacity(7); + + let mut v2 = test_deque(false); + v1.prepend(v2.drain(..)); + // drain removes all elements but keeps the buffer + assert_eq!(v2, []); + assert!(v2.capacity() >= 4); + + assert_eq!(v1, [1, 2, 3, 4]); + v1.pop_back(); + + let mut v2 = test_deque(false); + // this should wrap around the physical buffer + v1.prepend(v2.drain(..)); + // drain removes all elements but keeps the buffer + assert_eq!(v2, []); + assert!(v2.capacity() >= 4); + + // check it really wrapped + assert_eq!(v1.as_slices(), ([1].as_slice(), [2, 3, 4, 1, 2, 3].as_slice())); + + // extend_front, v2.head == 0 + + let mut v1 = VecDeque::with_capacity(7); + + let mut v2 = test_deque(false); + v1.extend_front(v2.drain(..)); + // drain removes all elements but keeps the buffer + assert_eq!(v2, []); + assert!(v2.capacity() >= 4); + + assert_eq!(v1, [4, 3, 2, 1]); + v1.pop_back(); + + let mut v2 = test_deque(false); + // this should wrap around the physical buffer + v1.extend_front(v2.drain(..)); + // drain removes all elements but keeps the buffer + assert_eq!(v2, []); + assert!(v2.capacity() >= 4); + + // check it really wrapped + assert_eq!(v1.as_slices(), ([4].as_slice(), [3, 2, 1, 4, 3, 2].as_slice())); + + // prepend, v2.head != 0 + + let mut v1 = VecDeque::with_capacity(7); + + let mut v2 = test_deque(true); + v1.prepend(v2.drain(..)); + // drain removes all elements but keeps the buffer + assert_eq!(v2, []); + assert!(v2.capacity() >= 4); + + assert_eq!(v1, [1, 2, 3, 4]); + v1.pop_back(); + + let mut v2 = test_deque(true); + // this should wrap around the physical buffer + v1.prepend(v2.drain(..)); + // drain removes all elements but keeps the buffer + assert_eq!(v2, []); + assert!(v2.capacity() >= 4); + + // check it really wrapped + assert_eq!(v1.as_slices(), ([1].as_slice(), [2, 3, 4, 1, 2, 3].as_slice())); + + // extend_front, v2.head != 0 + + let mut v1 = VecDeque::with_capacity(7); + + let mut v2 = test_deque(true); + v1.extend_front(v2.drain(..)); + // drain removes all elements but keeps the buffer + assert_eq!(v2, []); + assert!(v2.capacity() >= 4); + + assert_eq!(v1, [4, 3, 2, 1]); + v1.pop_back(); + + let mut v2 = test_deque(true); + // this should wrap around the physical buffer + v1.extend_front(v2.drain(..)); + // drain removes all elements but keeps the buffer + assert_eq!(v2, []); + assert!(v2.capacity() >= 4); + + // check it really wrapped + assert_eq!(v1.as_slices(), ([4].as_slice(), [3, 2, 1, 4, 3, 2].as_slice())); +} + +#[test] +fn test_splice() { + let mut v = VecDeque::from(vec![1, 2, 3, 4, 5]); + let a = [10, 11, 12]; + v.splice(2..4, a); + assert_eq!(v, &[1, 2, 10, 11, 12, 5]); + v.splice(1..3, Some(20)); + assert_eq!(v, &[1, 20, 11, 12, 5]); +} + +#[test] +fn test_splice_inclusive_range() { + let mut v = VecDeque::from(vec![1, 2, 3, 4, 5]); + let a = [10, 11, 12]; + let t1: Vec<_> = v.splice(2..=3, a).collect(); + assert_eq!(v, &[1, 2, 10, 11, 12, 5]); + assert_eq!(t1, &[3, 4]); + let t2: Vec<_> = v.splice(1..=2, Some(20)).collect(); + assert_eq!(v, &[1, 20, 11, 12, 5]); + assert_eq!(t2, &[2, 10]); +} + +#[test] +fn test_splice_inclusive_range2() { + let mut v = VecDeque::from(vec![1, 2, 10, 11, 12, 5]); + let t2: Vec<_> = v.splice(1..=2, Some(20)).collect(); + assert_eq!(v, &[1, 20, 11, 12, 5]); + assert_eq!(t2, &[2, 10]); +} + +#[test] +#[should_panic] +fn test_splice_out_of_bounds() { + let mut v = VecDeque::from(vec![1, 2, 3, 4, 5]); + let a = [10, 11, 12]; + v.splice(5..6, a); +} + +#[test] +#[should_panic] +fn test_splice_inclusive_out_of_bounds() { + let mut v = VecDeque::from(vec![1, 2, 3, 4, 5]); + let a = [10, 11, 12]; + v.splice(5..=5, a); +} + +#[test] +fn test_splice_items_zero_sized() { + let mut vec = VecDeque::from(vec![(), (), ()]); + let vec2 = VecDeque::from(vec![]); + let t: Vec<_> = vec.splice(1..2, vec2.iter().cloned()).collect(); + assert_eq!(vec, &[(), ()]); + assert_eq!(t, &[()]); +} + +#[test] +fn test_splice_unbounded() { + let mut vec = VecDeque::from(vec![1, 2, 3, 4, 5]); + let t: Vec<_> = vec.splice(.., None).collect(); + assert_eq!(vec, &[]); + assert_eq!(t, &[1, 2, 3, 4, 5]); +} + +#[test] +fn test_splice_forget() { + let mut v = VecDeque::from(vec![1, 2, 3, 4, 5]); + let a = [10, 11, 12]; + std::mem::forget(v.splice(2..4, a)); + assert_eq!(v, &[1, 2]); +} + +#[test] +fn test_splice_wrapping() { + let mut vec = VecDeque::with_capacity(10); + vec.push_front(7u8); + vec.push_back(9); + + vec.splice(1..1, [8]); + + assert_eq!(Vec::from(vec), [7, 8, 9]); +} diff --git a/library/backtrace b/library/backtrace index b65ab935fb2e0..28ec93b503bf0 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit b65ab935fb2e0d59dba8966ffca09c9cc5a5f57c +Subproject commit 28ec93b503bf0410745bc3d571bf3dc1caac3019 diff --git a/library/compiler-builtins/.github/workflows/main.yaml b/library/compiler-builtins/.github/workflows/main.yaml index 3afadbfe89418..699a9c417ddef 100644 --- a/library/compiler-builtins/.github/workflows/main.yaml +++ b/library/compiler-builtins/.github/workflows/main.yaml @@ -1,6 +1,6 @@ name: CI on: - push: { branches: [master] } + push: { branches: [main] } pull_request: concurrency: @@ -13,7 +13,7 @@ env: RUSTDOCFLAGS: -Dwarnings RUSTFLAGS: -Dwarnings RUST_BACKTRACE: full - BENCHMARK_RUSTC: nightly-2025-05-28 # Pin the toolchain for reproducable results + BENCHMARK_RUSTC: nightly-2025-12-01 # Pin the toolchain for reproducable results jobs: # Determine which tests should be run based on changed files. @@ -89,7 +89,7 @@ jobs: - target: x86_64-unknown-linux-gnu os: ubuntu-24.04 - target: x86_64-apple-darwin - os: macos-13 + os: macos-15-intel - target: i686-pc-windows-msvc os: windows-2025 - target: x86_64-pc-windows-msvc @@ -132,10 +132,7 @@ jobs: rustup default "$channel" rustup target add "${{ matrix.target }}" - # Our scripts use nextest if possible. This is skipped on the native ppc - # and s390x runners since install-action doesn't support them. - uses: taiki-e/install-action@nextest - if: "!(matrix.os == 'ubuntu-24.04-ppc64le' || matrix.os == 'ubuntu-24.04-s390x')" - uses: Swatinem/rust-cache@v2 with: @@ -206,7 +203,7 @@ jobs: # Unlike rustfmt, stable clippy does not work on code with nightly features. - name: Install nightly `clippy` run: | - rustup set profile minimal + rustup update nightly --no-self-update rustup default nightly rustup component add clippy - uses: Swatinem/rust-cache@v2 @@ -242,22 +239,15 @@ jobs: include: - target: x86_64-unknown-linux-gnu os: ubuntu-24.04 + - target: aarch64-unknown-linux-gnu + os: ubuntu-24.04-arm runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@master - uses: taiki-e/install-action@cargo-binstall - name: Set up dependencies - run: | - sudo apt-get update - sudo apt-get install -y valgrind gdb libc6-dbg # Needed for iai-callgrind - rustup update "$BENCHMARK_RUSTC" --no-self-update - rustup default "$BENCHMARK_RUSTC" - # Install the version of iai-callgrind-runner that is specified in Cargo.toml - iai_version="$(cargo metadata --format-version=1 --features icount | - jq -r '.packages[] | select(.name == "iai-callgrind").version')" - cargo binstall -y iai-callgrind-runner --version "$iai_version" - sudo apt-get install valgrind + run: ./ci/install-bench-deps.sh - uses: Swatinem/rust-cache@v2 with: key: ${{ matrix.target }} @@ -277,13 +267,7 @@ jobs: path: ${{ env.BASELINE_NAME }}.tar.xz - name: Run wall time benchmarks - run: | - # Always use the same seed for benchmarks. Ideally we should switch to a - # non-random generator. - export LIBM_SEED=benchesbenchesbenchesbencheswoo! - cargo bench --package libm-test \ - --no-default-features \ - --features short-benchmarks,build-musl,libm/force-soft-floats + run: ./ci/bench-runtime.sh - name: Print test logs if available if: always() diff --git a/library/compiler-builtins/.github/workflows/publish.yaml b/library/compiler-builtins/.github/workflows/publish.yaml index 85a33c039d2a1..d6f1dc398e8ec 100644 --- a/library/compiler-builtins/.github/workflows/publish.yaml +++ b/library/compiler-builtins/.github/workflows/publish.yaml @@ -5,7 +5,7 @@ permissions: contents: write on: - push: { branches: [master] } + push: { branches: [main] } jobs: release-plz: diff --git a/library/compiler-builtins/.github/workflows/rustc-pull.yml b/library/compiler-builtins/.github/workflows/rustc-pull.yml index ad7693e17b0ee..617db14f46eea 100644 --- a/library/compiler-builtins/.github/workflows/rustc-pull.yml +++ b/library/compiler-builtins/.github/workflows/rustc-pull.yml @@ -17,7 +17,7 @@ jobs: zulip-stream-id: 219381 zulip-topic: 'compiler-builtins subtree sync automation' zulip-bot-email: "compiler-builtins-ci-bot@rust-lang.zulipchat.com" - pr-base-branch: master + pr-base-branch: main branch-name: rustc-pull secrets: zulip-api-token: ${{ secrets.ZULIP_API_TOKEN }} diff --git a/library/compiler-builtins/.gitignore b/library/compiler-builtins/.gitignore index f12b871c2f783..abe346659d4c7 100644 --- a/library/compiler-builtins/.gitignore +++ b/library/compiler-builtins/.gitignore @@ -9,6 +9,7 @@ compiler-rt # Benchmark cache baseline-* iai-home +gungraun-home # Temporary files *.bk diff --git a/library/compiler-builtins/CONTRIBUTING.md b/library/compiler-builtins/CONTRIBUTING.md index 9ae4f893c60d1..f74d3f8ba1276 100644 --- a/library/compiler-builtins/CONTRIBUTING.md +++ b/library/compiler-builtins/CONTRIBUTING.md @@ -150,8 +150,8 @@ cargo bench --no-default-features \ ``` There are also benchmarks that check instruction count behind the `icount` -feature. These require [`iai-callgrind-runner`] (via Cargo) and [Valgrind] -to be installed, which means these only run on limited platforms. +feature. These require [`gungraun-runner`] (via Cargo) and [Valgrind] to be +installed, which means these only run on limited platforms. Instruction count benchmarks are run as part of CI to flag performance regresions. @@ -163,7 +163,7 @@ cargo bench --no-default-features \ --bench icount --bench mem_icount ``` -[`iai-callgrind-runner`]: https://crates.io/crates/iai-callgrind-runner +[`gungraun-runner`]: https://crates.io/crates/gungraun-runner [Valgrind]: https://valgrind.org/ ## Subtree synchronization diff --git a/library/compiler-builtins/Cargo.toml b/library/compiler-builtins/Cargo.toml index 956d738f3b1f1..8501f4e630b55 100644 --- a/library/compiler-builtins/Cargo.toml +++ b/library/compiler-builtins/Cargo.toml @@ -51,5 +51,6 @@ codegen-units = 1 lto = "fat" [profile.bench] -# Required for iai-callgrind +# Required for gungraun debug = true +strip = false diff --git a/library/compiler-builtins/PUBLISHING.md b/library/compiler-builtins/PUBLISHING.md index 3df682ab04a4b..c521910641f55 100644 --- a/library/compiler-builtins/PUBLISHING.md +++ b/library/compiler-builtins/PUBLISHING.md @@ -5,7 +5,7 @@ It's not great, but it works for now. PRs to improve this process would be greatly appreciated! 1. Make sure you've got a clean working tree and it's updated with the latest - changes on `master` + changes on `main` 2. Edit `Cargo.toml` to bump the version number 3. Commit this change 4. Run `git tag` to create a tag for this version diff --git a/library/compiler-builtins/builtins-shim/Cargo.toml b/library/compiler-builtins/builtins-shim/Cargo.toml index 707ebdbc77b27..746d5b21dc3f1 100644 --- a/library/compiler-builtins/builtins-shim/Cargo.toml +++ b/library/compiler-builtins/builtins-shim/Cargo.toml @@ -11,7 +11,12 @@ [package] name = "compiler_builtins" version = "0.1.160" -authors = ["Jorge Aparicio "] +authors = [ + "Alex Crichton ", + "Amanieu d'Antras ", + "Jorge Aparicio ", + "Trevor Gross ", +] description = "Compiler intrinsics used by the Rust compiler." repository = "https://github.com/rust-lang/compiler-builtins" license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)" @@ -42,10 +47,6 @@ c = ["dep:cc"] # the generic versions on all platforms. no-asm = [] -# Workaround for codegen backends which haven't yet implemented `f16` and -# `f128` support. Disabled any intrinsics which use those types. -no-f16-f128 = [] - # Flag this library as the unstable compiler-builtins lib compiler-builtins = [] diff --git a/library/compiler-builtins/builtins-test/Cargo.toml b/library/compiler-builtins/builtins-test/Cargo.toml index 00a9d8579d119..550f736a76dbb 100644 --- a/library/compiler-builtins/builtins-test/Cargo.toml +++ b/library/compiler-builtins/builtins-test/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "builtins-test" version = "0.1.0" -authors = ["Alex Crichton "] edition = "2024" publish = false license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)" @@ -14,7 +13,7 @@ rand_xoshiro = "0.7" # To compare float builtins against rustc_apfloat = "0.2.3" # Really a dev dependency, but dev dependencies can't be optional -iai-callgrind = { version = "0.15.2", optional = true } +gungraun = { version = "0.17.0", optional = true } [dependencies.compiler_builtins] path = "../builtins-shim" @@ -34,7 +33,6 @@ utest-macros = { git = "https://github.com/japaric/utest" } default = ["mangled-names"] c = ["compiler_builtins/c"] no-asm = ["compiler_builtins/no-asm"] -no-f16-f128 = ["compiler_builtins/no-f16-f128"] mem = ["compiler_builtins/mem"] mangled-names = ["compiler_builtins/mangled-names"] # Skip tests that rely on f128 symbols being available on the system @@ -46,8 +44,8 @@ no-sys-f16-f64-convert = [] # Skip tests that rely on f16 symbols being available on the system no-sys-f16 = ["no-sys-f16-f64-convert"] -# Enable icount benchmarks (requires iai-callgrind and valgrind) -icount = ["dep:iai-callgrind"] +# Enable icount benchmarks (requires gungraun-runner and valgrind locally) +icount = ["dep:gungraun"] # Enable report generation without bringing in more dependencies by default benchmarking-reports = ["criterion/plotters", "criterion/html_reports"] diff --git a/library/compiler-builtins/builtins-test/benches/mem_icount.rs b/library/compiler-builtins/builtins-test/benches/mem_icount.rs index bd88cf80c7de2..37595e8258436 100644 --- a/library/compiler-builtins/builtins-test/benches/mem_icount.rs +++ b/library/compiler-builtins/builtins-test/benches/mem_icount.rs @@ -1,11 +1,11 @@ -//! Benchmarks that use Callgrind (via `iai_callgrind`) to report instruction count metrics. This +//! Benchmarks that use Callgrind (via `gungraun`) to report instruction count metrics. This //! is stable enough to be tested in CI. use std::hint::black_box; use std::{ops, slice}; use compiler_builtins::mem::{memcmp, memcpy, memmove, memset}; -use iai_callgrind::{library_benchmark, library_benchmark_group, main}; +use gungraun::{library_benchmark, library_benchmark_group, main}; const PAGE_SIZE: usize = 0x1000; // 4 kiB const MAX_ALIGN: usize = 512; // assume we may use avx512 operations one day @@ -108,7 +108,7 @@ mod mcpy { ], setup = setup, )] - fn bench((len, mut dst, src): (usize, AlignedSlice, AlignedSlice)) { + fn bench_cpy((len, mut dst, src): (usize, AlignedSlice, AlignedSlice)) { unsafe { black_box(memcpy( black_box(dst.as_mut_ptr()), @@ -118,7 +118,7 @@ mod mcpy { } } - library_benchmark_group!(name = memcpy; benchmarks = bench); + library_benchmark_group!(name = memcpy; benchmarks = bench_cpy); } mod mset { @@ -157,7 +157,7 @@ mod mset { ], setup = setup, )] - fn bench((len, mut dst): (usize, AlignedSlice)) { + fn bench_set((len, mut dst): (usize, AlignedSlice)) { unsafe { black_box(memset( black_box(dst.as_mut_ptr()), @@ -167,7 +167,7 @@ mod mset { } } - library_benchmark_group!(name = memset; benchmarks = bench); + library_benchmark_group!(name = memset; benchmarks = bench_set); } mod mcmp { @@ -225,7 +225,7 @@ mod mcmp { ], setup = setup )] - fn bench((len, mut dst, src): (usize, AlignedSlice, AlignedSlice)) { + fn bench_cmp((len, mut dst, src): (usize, AlignedSlice, AlignedSlice)) { unsafe { black_box(memcmp( black_box(dst.as_mut_ptr()), @@ -235,7 +235,7 @@ mod mcmp { } } - library_benchmark_group!(name = memcmp; benchmarks = bench); + library_benchmark_group!(name = memcmp; benchmarks = bench_cmp); } mod mmove { @@ -384,7 +384,7 @@ mod mmove { ], setup = setup_forward )] - fn forward((len, spread, mut buf): (usize, usize, AlignedSlice)) { + fn forward_move((len, spread, mut buf): (usize, usize, AlignedSlice)) { // Test moving from the start of the buffer toward the end unsafe { black_box(memmove( @@ -478,7 +478,7 @@ mod mmove { ], setup = setup_backward )] - fn backward((len, spread, mut buf): (usize, usize, AlignedSlice)) { + fn backward_move((len, spread, mut buf): (usize, usize, AlignedSlice)) { // Test moving from the end of the buffer toward the start unsafe { black_box(memmove( @@ -489,7 +489,7 @@ mod mmove { } } - library_benchmark_group!(name = memmove; benchmarks = forward, backward); + library_benchmark_group!(name = memmove; benchmarks = forward_move, backward_move); } use mcmp::memcmp; diff --git a/library/compiler-builtins/builtins-test/tests/lse.rs b/library/compiler-builtins/builtins-test/tests/lse.rs index 5d59fbb7f44d2..56891be8a8ac1 100644 --- a/library/compiler-builtins/builtins-test/tests/lse.rs +++ b/library/compiler-builtins/builtins-test/tests/lse.rs @@ -19,7 +19,11 @@ mod cas { let mut target = expected.wrapping_add(10); assert_eq!( unsafe { - compiler_builtins::aarch64_linux::$name::$name(expected, new, &mut target) + compiler_builtins::aarch64_outline_atomics::$name::$name( + expected, + new, + &mut target, + ) }, expected.wrapping_add(10), "return value should always be the previous value", @@ -33,7 +37,11 @@ mod cas { target = expected; assert_eq!( unsafe { - compiler_builtins::aarch64_linux::$name::$name(expected, new, &mut target) + compiler_builtins::aarch64_outline_atomics::$name::$name( + expected, + new, + &mut target, + ) }, expected ); @@ -54,7 +62,9 @@ mod swap { builtins_test::fuzz_2(10000, |left: super::int_ty!($bytes), mut right| { let orig_right = right; assert_eq!( - unsafe { compiler_builtins::aarch64_linux::$name::$name(left, &mut right) }, + unsafe { + compiler_builtins::aarch64_outline_atomics::$name::$name(left, &mut right) + }, orig_right ); assert_eq!(left, right); @@ -74,7 +84,7 @@ macro_rules! test_op { let mut target = old; let op: fn(super::int_ty!($bytes), super::int_ty!($bytes)) -> _ = $($op)*; let expected = op(old, val); - assert_eq!(old, unsafe { compiler_builtins::aarch64_linux::$name::$name(val, &mut target) }, "{} should return original value", stringify!($name)); + assert_eq!(old, unsafe { compiler_builtins::aarch64_outline_atomics::$name::$name(val, &mut target) }, "{} should return original value", stringify!($name)); assert_eq!(expected, target, "{} should store to target", stringify!($name)); }); } diff --git a/library/compiler-builtins/ci/bench-icount.sh b/library/compiler-builtins/ci/bench-icount.sh index 12228b9da971b..6d92b50a6dae7 100755 --- a/library/compiler-builtins/ci/bench-icount.sh +++ b/library/compiler-builtins/ci/bench-icount.sh @@ -10,35 +10,43 @@ if [ -z "$target" ]; then target="$host_target" fi -iai_home="iai-home" +# Print machine information +uname -a +lscpu || true + +gungraun_home="gungraun-home" # Use the arch as a tag to disambiguate artifacts tag="$(echo "$target" | cut -d'-' -f1)" -# Download the baseline from master +# Download the baseline from main ./ci/ci-util.py locate-baseline --download --extract --tag "$tag" +# FIXME: migration from iai-named baselines to gungraun, can be dropped +# after the first run with gungraun. +[ -d "iai-home" ] && mv "iai-home" "$gungraun_home" + # Run benchmarks once function run_icount_benchmarks() { cargo_args=( - "--bench" "icount" + "--bench" "*icount*" "--no-default-features" "--features" "unstable,unstable-float,icount" ) - iai_args=( - "--home" "$(pwd)/$iai_home" - "--callgrind-limits=ir=5.0" + gungraun_args=( + "--home" "$(pwd)/$gungraun_home" + "--callgrind-limits=ir=5.0%" "--save-summary" ) - # Parse `cargo_arg0 cargo_arg1 -- iai_arg0 iai_arg1` syntax - parsing_iai_args=0 + # Parse `cargo_arg0 cargo_arg1 -- gungraun_arg0 gungraun_arg1` syntax + parsing_gungraun_args=0 while [ "$#" -gt 0 ]; do - if [ "$parsing_iai_args" == "1" ]; then - iai_args+=("$1") + if [ "$parsing_gungraun_args" == "1" ]; then + gungraun_args+=("$1") elif [ "$1" == "--" ]; then - parsing_iai_args=1 + parsing_gungraun_args=1 else cargo_args+=("$1") fi @@ -46,9 +54,9 @@ function run_icount_benchmarks() { shift done - # Run iai-callgrind benchmarks. Do this in a subshell with `&& true` to - # capture rather than exit on error. - (cargo bench "${cargo_args[@]}" -- "${iai_args[@]}") && true + # Run gungraun benchmarks. Do this in a subshell with `&& true` to capture + # rather than exit on error. + (cargo bench "${cargo_args[@]}" -- "${gungraun_args[@]}") && true exit_code="$?" if [ "$exit_code" -eq 0 ]; then @@ -68,4 +76,4 @@ run_icount_benchmarks -- --save-baseline=hardfloat # Name and tar the new baseline name="baseline-icount-$tag-$(date -u +'%Y%m%d%H%M')-${GITHUB_SHA:0:12}" echo "BASELINE_NAME=$name" >>"$GITHUB_ENV" -tar cJf "$name.tar.xz" "$iai_home" +tar cJf "$name.tar.xz" "$gungraun_home" diff --git a/library/compiler-builtins/ci/bench-runtime.sh b/library/compiler-builtins/ci/bench-runtime.sh new file mode 100755 index 0000000000000..d272cf33463ed --- /dev/null +++ b/library/compiler-builtins/ci/bench-runtime.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# Run wall time benchmarks as we do on CI. + +# Always use the same seed for benchmarks. Ideally we should switch to a +# non-random generator. +export LIBM_SEED=benchesbenchesbenchesbencheswoo! +cargo bench --package libm-test \ + --no-default-features \ + --features short-benchmarks,build-musl,libm/force-soft-floats diff --git a/library/compiler-builtins/ci/ci-util.py b/library/compiler-builtins/ci/ci-util.py index c1db17c6c9010..ef9ce455178ec 100755 --- a/library/compiler-builtins/ci/ci-util.py +++ b/library/compiler-builtins/ci/ci-util.py @@ -38,7 +38,7 @@ `--tag` can be specified to look for artifacts with a specific tag, such as for a specific architecture. - Note that `--extract` will overwrite files in `iai-home`. + Note that `--extract` will overwrite files in `gungraun-home`. handle-bench-regressions PR_NUMBER Exit with success if the pull request contains a line starting with @@ -49,7 +49,7 @@ REPO_ROOT = Path(__file__).parent.parent GIT = ["git", "-C", REPO_ROOT] -DEFAULT_BRANCH = "master" +DEFAULT_BRANCH = "main" WORKFLOW_NAME = "CI" # Workflow that generates the benchmark artifacts ARTIFACT_PREFIX = "baseline-icount*" @@ -186,7 +186,7 @@ def __init__(self) -> None: def _init_change_list(self): """Create a list of files that have been changed. This uses GITHUB_REF if - available, otherwise a diff between `HEAD` and `master`. + available, otherwise a diff between `HEAD` and `main`. """ # For pull requests, GitHub creates a ref `refs/pull/1234/merge` (1234 being @@ -390,6 +390,7 @@ def locate_baseline(flags: list[str]) -> None: artifact_glob = f"{ARTIFACT_PREFIX}{f"-{tag}" if tag else ""}*" + # Skip checking because this will fail if the file already exists, which is fine. sp.run( ["gh", "run", "download", str(job_id), f"--pattern={artifact_glob}"], check=False, @@ -409,7 +410,17 @@ def locate_baseline(flags: list[str]) -> None: candidate_baselines.sort(reverse=True) baseline_archive = candidate_baselines[0] eprint(f"extracting {baseline_archive}") - sp.run(["tar", "xJvf", baseline_archive], check=True) + + all_paths = sp.check_output(["tar", "tJf", baseline_archive], encoding="utf8") + sp.run(["tar", "xJf", baseline_archive], check=True) + + # Print a short summary of paths, we don't use `tar v` since the list is huge + short_paths = re.findall(r"^(?:[^/\n]+/?){1,3}", all_paths, re.MULTILINE) + + print("Extracted:") + for path in sorted(set(short_paths)): + print(f"* {path}") + eprint("baseline extracted successfully") diff --git a/library/compiler-builtins/ci/install-bench-deps.sh b/library/compiler-builtins/ci/install-bench-deps.sh new file mode 100755 index 0000000000000..61f4723c0358d --- /dev/null +++ b/library/compiler-builtins/ci/install-bench-deps.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# Install needed dependencies for gungraun. + +sudo apt-get update +sudo apt-get install -y valgrind gdb libc6-dbg # Needed for gungraun +rustup update "$BENCHMARK_RUSTC" --no-self-update +rustup default "$BENCHMARK_RUSTC" +# Install the version of gungraun-runner that is specified in Cargo.toml +gungraun_version="$(cargo metadata --format-version=1 --features icount | + jq -r '.packages[] | select(.name == "gungraun").version')" +cargo binstall -y gungraun-runner --version "$gungraun_version" diff --git a/library/compiler-builtins/ci/run.sh b/library/compiler-builtins/ci/run.sh index bc94d42fe837a..0c07b32c74b93 100755 --- a/library/compiler-builtins/ci/run.sh +++ b/library/compiler-builtins/ci/run.sh @@ -36,8 +36,6 @@ else "${test_builtins[@]}" --features c --release "${test_builtins[@]}" --features no-asm "${test_builtins[@]}" --features no-asm --release - "${test_builtins[@]}" --features no-f16-f128 - "${test_builtins[@]}" --features no-f16-f128 --release "${test_builtins[@]}" --benches "${test_builtins[@]}" --benches --release @@ -63,8 +61,6 @@ symcheck+=(-- build-and-check) "${symcheck[@]}" "$target" -- -p compiler_builtins --features c --release "${symcheck[@]}" "$target" -- -p compiler_builtins --features no-asm "${symcheck[@]}" "$target" -- -p compiler_builtins --features no-asm --release -"${symcheck[@]}" "$target" -- -p compiler_builtins --features no-f16-f128 -"${symcheck[@]}" "$target" -- -p compiler_builtins --features no-f16-f128 --release run_intrinsics_test() { build_args=(--verbose --manifest-path builtins-test-intrinsics/Cargo.toml) diff --git a/library/compiler-builtins/compiler-builtins/Cargo.toml b/library/compiler-builtins/compiler-builtins/Cargo.toml index 8bbe136ce33e3..496dde2d4cf25 100644 --- a/library/compiler-builtins/compiler-builtins/Cargo.toml +++ b/library/compiler-builtins/compiler-builtins/Cargo.toml @@ -7,7 +7,12 @@ [package] name = "compiler_builtins" version = "0.1.160" -authors = ["Jorge Aparicio "] +authors = [ + "Alex Crichton ", + "Amanieu d'Antras ", + "Jorge Aparicio ", + "Trevor Gross ", +] description = "Compiler intrinsics used by the Rust compiler." repository = "https://github.com/rust-lang/compiler-builtins" license = "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)" @@ -40,10 +45,6 @@ c = ["dep:cc"] # the generic versions on all platforms. no-asm = [] -# Workaround for codegen backends which haven't yet implemented `f16` and -# `f128` support. Disabled any intrinsics which use those types. -no-f16-f128 = [] - # Flag this library as the unstable compiler-builtins lib compiler-builtins = [] diff --git a/library/compiler-builtins/compiler-builtins/README.md b/library/compiler-builtins/compiler-builtins/README.md index 2d92b7651f980..a12bd2ee73499 100644 --- a/library/compiler-builtins/compiler-builtins/README.md +++ b/library/compiler-builtins/compiler-builtins/README.md @@ -374,7 +374,7 @@ Miscellaneous functionality that is not used by Rust. - ~~i386/fp_mode.c~~ - ~~int_util.c~~ - ~~loongarch/fp_mode.c~~ -- ~~os_version_check.c~~ +- ~~os_version_check.c~~ (implemented in `std` instead) - ~~riscv/fp_mode.c~~ - ~~riscv/restore.S~~ (callee-saved registers) - ~~riscv/save.S~~ (callee-saved registers) diff --git a/library/compiler-builtins/compiler-builtins/configure.rs b/library/compiler-builtins/compiler-builtins/configure.rs index 79e238abc0f62..f16da6b58f812 100644 --- a/library/compiler-builtins/compiler-builtins/configure.rs +++ b/library/compiler-builtins/compiler-builtins/configure.rs @@ -95,16 +95,13 @@ pub fn configure_aliases(target: &Target) { * * https://github.com/rust-lang/rustc_codegen_cranelift/blob/c713ffab3c6e28ab4b4dd4e392330f786ea657ad/src/lib.rs#L196-L226 */ - // If the feature is set, disable both of these types. - let no_f16_f128 = target.cargo_features.iter().any(|s| s == "no-f16-f128"); - println!("cargo::rustc-check-cfg=cfg(f16_enabled)"); - if target.reliable_f16 && !no_f16_f128 { + if target.reliable_f16 { println!("cargo::rustc-cfg=f16_enabled"); } println!("cargo::rustc-check-cfg=cfg(f128_enabled)"); - if target.reliable_f128 && !no_f16_f128 { + if target.reliable_f128 { println!("cargo::rustc-cfg=f128_enabled"); } } diff --git a/library/compiler-builtins/compiler-builtins/src/aarch64.rs b/library/compiler-builtins/compiler-builtins/src/aarch64.rs index 039fab2061c5f..1b230a214eefe 100644 --- a/library/compiler-builtins/compiler-builtins/src/aarch64.rs +++ b/library/compiler-builtins/compiler-builtins/src/aarch64.rs @@ -4,7 +4,7 @@ use core::intrinsics; intrinsics! { #[unsafe(naked)] - #[cfg(target_os = "uefi")] + #[cfg(any(all(windows, target_env = "gnu"), target_os = "uefi"))] pub unsafe extern "custom" fn __chkstk() { core::arch::naked_asm!( ".p2align 2", diff --git a/library/compiler-builtins/compiler-builtins/src/aarch64_linux.rs b/library/compiler-builtins/compiler-builtins/src/aarch64_outline_atomics.rs similarity index 97% rename from library/compiler-builtins/compiler-builtins/src/aarch64_linux.rs rename to library/compiler-builtins/compiler-builtins/src/aarch64_outline_atomics.rs index 01d7fb4732918..df0cf76502223 100644 --- a/library/compiler-builtins/compiler-builtins/src/aarch64_linux.rs +++ b/library/compiler-builtins/compiler-builtins/src/aarch64_outline_atomics.rs @@ -196,7 +196,7 @@ macro_rules! compare_and_swap { "cbnz w17, 0b", "1:", "ret", - have_lse = sym crate::aarch64_linux::HAVE_LSE_ATOMICS, + have_lse = sym crate::aarch64_outline_atomics::HAVE_LSE_ATOMICS, } } } @@ -228,7 +228,7 @@ macro_rules! compare_and_swap_i128 { "cbnz w15, 0b", "1:", "ret", - have_lse = sym crate::aarch64_linux::HAVE_LSE_ATOMICS, + have_lse = sym crate::aarch64_outline_atomics::HAVE_LSE_ATOMICS, } } } @@ -256,7 +256,7 @@ macro_rules! swap { concat!(stxr!($ordering, $bytes), " w17, ", reg!($bytes, 16), ", [x1]"), "cbnz w17, 0b", "ret", - have_lse = sym crate::aarch64_linux::HAVE_LSE_ATOMICS, + have_lse = sym crate::aarch64_outline_atomics::HAVE_LSE_ATOMICS, } } } @@ -286,7 +286,7 @@ macro_rules! fetch_op { concat!(stxr!($ordering, $bytes), " w15, ", reg!($bytes, 17), ", [x1]"), "cbnz w15, 0b", "ret", - have_lse = sym crate::aarch64_linux::HAVE_LSE_ATOMICS, + have_lse = sym crate::aarch64_outline_atomics::HAVE_LSE_ATOMICS, } } } diff --git a/library/compiler-builtins/compiler-builtins/src/int/specialized_div_rem/mod.rs b/library/compiler-builtins/compiler-builtins/src/int/specialized_div_rem/mod.rs index 7841e4f33cd66..5ffe1f59b4db6 100644 --- a/library/compiler-builtins/compiler-builtins/src/int/specialized_div_rem/mod.rs +++ b/library/compiler-builtins/compiler-builtins/src/int/specialized_div_rem/mod.rs @@ -196,13 +196,12 @@ unsafe fn u128_by_u64_div_rem(duo: u128, div: u64) -> (u64, u64) { unsafe { // divides the combined registers rdx:rax (`duo` is split into two 64 bit parts to do this) // by `div`. The quotient is stored in rax and the remainder in rdx. - // FIXME: Use the Intel syntax once we drop LLVM 9 support on rust-lang/rust. core::arch::asm!( "div {0}", in(reg) div, inlateout("rax") duo_lo => quo, inlateout("rdx") duo_hi => rem, - options(att_syntax, pure, nomem, nostack) + options(pure, nomem, nostack), ); } (quo, rem) @@ -283,13 +282,12 @@ unsafe fn u64_by_u32_div_rem(duo: u64, div: u32) -> (u32, u32) { unsafe { // divides the combined registers rdx:rax (`duo` is split into two 32 bit parts to do this) // by `div`. The quotient is stored in rax and the remainder in rdx. - // FIXME: Use the Intel syntax once we drop LLVM 9 support on rust-lang/rust. core::arch::asm!( "div {0}", in(reg) div, inlateout("rax") duo_lo => quo, inlateout("rdx") duo_hi => rem, - options(att_syntax, pure, nomem, nostack) + options(pure, nomem, nostack), ); } (quo, rem) diff --git a/library/compiler-builtins/compiler-builtins/src/lib.rs b/library/compiler-builtins/compiler-builtins/src/lib.rs index b111dc0bd18f3..342427aca9c7d 100644 --- a/library/compiler-builtins/compiler-builtins/src/lib.rs +++ b/library/compiler-builtins/compiler-builtins/src/lib.rs @@ -7,11 +7,11 @@ #![feature(compiler_builtins)] #![feature(core_intrinsics)] #![feature(linkage)] -#![feature(asm_cfg)] #![feature(naked_functions)] #![feature(repr_simd)] #![feature(macro_metavar_expr_concat)] #![feature(rustc_attrs)] +#![feature(float_bits_const)] #![cfg_attr(f16_enabled, feature(f16))] #![cfg_attr(f128_enabled, feature(f128))] #![no_builtins] @@ -56,8 +56,8 @@ pub mod arm; #[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] pub mod aarch64; -#[cfg(all(target_arch = "aarch64", target_os = "linux"))] -pub mod aarch64_linux; +#[cfg(all(target_arch = "aarch64", target_feature = "outline-atomics"))] +pub mod aarch64_outline_atomics; #[cfg(all( kernel_user_helpers, diff --git a/library/compiler-builtins/compiler-builtins/src/mem/x86_64.rs b/library/compiler-builtins/compiler-builtins/src/mem/x86_64.rs index fb29eb11b231e..bf36a286ac951 100644 --- a/library/compiler-builtins/compiler-builtins/src/mem/x86_64.rs +++ b/library/compiler-builtins/compiler-builtins/src/mem/x86_64.rs @@ -22,13 +22,12 @@ use core::{intrinsics, mem}; #[inline(always)] #[cfg(target_feature = "ermsb")] pub unsafe fn copy_forward(dest: *mut u8, src: *const u8, count: usize) { - // FIXME: Use the Intel syntax once we drop LLVM 9 support on rust-lang/rust. - core::arch::asm!( - "repe movsb (%rsi), (%rdi)", + asm!( + "rep movsb [rdi], [rsi]", inout("rcx") count => _, inout("rdi") dest => _, inout("rsi") src => _, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ); } @@ -42,21 +41,21 @@ pub unsafe fn copy_forward(mut dest: *mut u8, mut src: *const u8, count: usize) inout("ecx") pre_byte_count => _, inout("rdi") dest => dest, inout("rsi") src => src, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ); asm!( "rep movsq", inout("rcx") qword_count => _, inout("rdi") dest => dest, inout("rsi") src => src, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ); asm!( "rep movsb", inout("ecx") byte_count => _, inout("rdi") dest => _, inout("rsi") src => _, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ); } @@ -67,14 +66,13 @@ pub unsafe fn copy_backward(dest: *mut u8, src: *const u8, count: usize) { asm!( "std", "rep movsb", - "sub $7, %rsi", - "sub $7, %rdi", - "mov {qword_count:r}, %rcx", + "sub rsi, 7", + "sub rdi, 7", + "mov rcx, {qword_count:r}", "rep movsq", - "test {pre_byte_count:e}, {pre_byte_count:e}", - "add $7, %rsi", - "add $7, %rdi", - "mov {pre_byte_count:e}, %ecx", + "add rsi, 7", + "add rdi, 7", + "mov ecx, {pre_byte_count:e}", "rep movsb", "cld", pre_byte_count = in(reg) pre_byte_count, @@ -82,21 +80,19 @@ pub unsafe fn copy_backward(dest: *mut u8, src: *const u8, count: usize) { inout("ecx") byte_count => _, inout("rdi") dest.add(count - 1) => _, inout("rsi") src.add(count - 1) => _, - // We modify flags, but we restore it afterwards - options(att_syntax, nostack, preserves_flags) + options(nostack) ); } #[inline(always)] #[cfg(target_feature = "ermsb")] pub unsafe fn set_bytes(dest: *mut u8, c: u8, count: usize) { - // FIXME: Use the Intel syntax once we drop LLVM 9 support on rust-lang/rust. - core::arch::asm!( - "repe stosb %al, (%rdi)", + asm!( + "rep stosb [rdi], al", inout("rcx") count => _, inout("rdi") dest => _, inout("al") c => _, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ) } @@ -111,21 +107,21 @@ pub unsafe fn set_bytes(mut dest: *mut u8, c: u8, count: usize) { inout("ecx") pre_byte_count => _, inout("rdi") dest => dest, in("rax") c, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ); asm!( "rep stosq", inout("rcx") qword_count => _, inout("rdi") dest => dest, in("rax") c, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ); asm!( "rep stosb", inout("ecx") byte_count => _, inout("rdi") dest => _, in("rax") c, - options(att_syntax, nostack, preserves_flags) + options(nostack, preserves_flags) ); } @@ -212,10 +208,10 @@ pub unsafe fn c_string_length(mut s: *const core::ffi::c_char) -> usize { let x = { let r; asm!( - "movdqa ({addr:r}), {dest}", + "movdqa {dest}, [{addr:r}]", addr = in(reg) s, dest = out(xmm_reg) r, - options(att_syntax, nostack), + options(nostack, preserves_flags), ); r }; @@ -232,10 +228,10 @@ pub unsafe fn c_string_length(mut s: *const core::ffi::c_char) -> usize { let x = { let r; asm!( - "movdqa ({addr:r}), {dest}", + "movdqa {dest}, [{addr:r}]", addr = in(reg) s, dest = out(xmm_reg) r, - options(att_syntax, nostack), + options(nostack, preserves_flags), ); r }; @@ -277,10 +273,10 @@ pub unsafe fn c_string_length(mut s: *const core::ffi::c_char) -> usize { let mut cs = { let r: u64; asm!( - "mov ({addr}), {dest}", + "mov {dest}, [{addr}]", addr = in(reg) s, dest = out(reg) r, - options(att_syntax, nostack), + options(nostack, preserves_flags), ); r }; diff --git a/library/compiler-builtins/compiler-builtins/src/probestack.rs b/library/compiler-builtins/compiler-builtins/src/probestack.rs index 72975485a7765..1cab64ea113c5 100644 --- a/library/compiler-builtins/compiler-builtins/src/probestack.rs +++ b/library/compiler-builtins/compiler-builtins/src/probestack.rs @@ -47,11 +47,11 @@ // We only define stack probing for these architectures today. #![cfg(any(target_arch = "x86_64", target_arch = "x86"))] -// Our goal here is to touch each page between %rsp+8 and %rsp+8-%rax, +// Our goal here is to touch each page between `rsp+8` and `rsp+8-rax`, // ensuring that if any pages are unmapped we'll make a page fault. // -// The ABI here is that the stack frame size is located in `%rax`. Upon -// return we're not supposed to modify `%rsp` or `%rax`. +// The ABI here is that the stack frame size is located in `rax`. Upon +// return we're not supposed to modify `rsp` or `rax`. #[cfg(target_arch = "x86_64")] #[unsafe(naked)] #[rustc_std_internal_symbol] @@ -59,50 +59,50 @@ pub unsafe extern "custom" fn __rust_probestack() { core::arch::naked_asm!( " .cfi_startproc - pushq %rbp + push rbp .cfi_adjust_cfa_offset 8 - .cfi_offset %rbp, -16 - movq %rsp, %rbp - .cfi_def_cfa_register %rbp + .cfi_offset rbp, -16 + mov rbp, rsp + .cfi_def_cfa_register rbp - mov %rax,%r11 // duplicate %rax as we're clobbering %r11 + mov r11, rax // duplicate rax as we're clobbering r11 // Main loop, taken in one page increments. We're decrementing rsp by // a page each time until there's less than a page remaining. We're // guaranteed that this function isn't called unless there's more than a // page needed. // - // Note that we're also testing against `8(%rsp)` to account for the 8 + // Note that we're also testing against `[rsp + 8]` to account for the 8 // bytes pushed on the stack originally with our return address. Using - // `8(%rsp)` simulates us testing the stack pointer in the caller's + // `[rsp + 8]` simulates us testing the stack pointer in the caller's // context. - // It's usually called when %rax >= 0x1000, but that's not always true. + // It's usually called when rax >= 0x1000, but that's not always true. // Dynamic stack allocation, which is needed to implement unsized - // rvalues, triggers stackprobe even if %rax < 0x1000. - // Thus we have to check %r11 first to avoid segfault. - cmp $0x1000,%r11 + // rvalues, triggers stackprobe even if rax < 0x1000. + // Thus we have to check r11 first to avoid segfault. + cmp r11, 0x1000 jna 3f 2: - sub $0x1000,%rsp - test %rsp,8(%rsp) - sub $0x1000,%r11 - cmp $0x1000,%r11 + sub rsp, 0x1000 + test qword ptr [rsp + 8], rsp + sub r11, 0x1000 + cmp r11, 0x1000 ja 2b 3: // Finish up the last remaining stack space requested, getting the last // bits out of r11 - sub %r11,%rsp - test %rsp,8(%rsp) + sub rsp, r11 + test qword ptr [rsp + 8], rsp // Restore the stack pointer to what it previously was when entering // this function. The caller will readjust the stack pointer after we // return. - add %rax,%rsp + add rsp, rax leave - .cfi_def_cfa_register %rsp + .cfi_def_cfa_register rsp .cfi_adjust_cfa_offset -8 ", #[cfg(not(all(target_env = "sgx", target_vendor = "fortanix")))] @@ -112,14 +112,13 @@ pub unsafe extern "custom" fn __rust_probestack() { // for this target, [manually patch for LVI]. // // [manually patch for LVI]: https://software.intel.com/security-software-guidance/insights/deep-dive-load-value-injection#specialinstructions - pop %r11 + pop r11 lfence - jmp *%r11 + jmp r11 ", " .cfi_endproc ", - options(att_syntax) ) } @@ -135,36 +134,35 @@ pub unsafe extern "custom" fn __rust_probestack() { core::arch::naked_asm!( " .cfi_startproc - push %ebp + push ebp .cfi_adjust_cfa_offset 4 - .cfi_offset %ebp, -8 - mov %esp, %ebp - .cfi_def_cfa_register %ebp - push %ecx - mov %eax,%ecx + .cfi_offset ebp, -8 + mov ebp, esp + .cfi_def_cfa_register ebp + push ecx + mov ecx, eax - cmp $0x1000,%ecx + cmp ecx, 0x1000 jna 3f 2: - sub $0x1000,%esp - test %esp,8(%esp) - sub $0x1000,%ecx - cmp $0x1000,%ecx + sub esp, 0x1000 + test dword ptr [esp + 8], esp + sub ecx, 0x1000 + cmp ecx, 0x1000 ja 2b 3: - sub %ecx,%esp - test %esp,8(%esp) + sub esp, ecx + test dword ptr [esp + 8], esp - add %eax,%esp - pop %ecx + add esp, eax + pop ecx leave - .cfi_def_cfa_register %esp + .cfi_def_cfa_register esp .cfi_adjust_cfa_offset -4 ret .cfi_endproc - ", - options(att_syntax) + ", ) } @@ -176,8 +174,8 @@ pub unsafe extern "custom" fn __rust_probestack() { // REF: Rust commit(74e80468347) // rust\src\llvm-project\llvm\lib\Target\X86\X86FrameLowering.cpp: 805 // Comments in LLVM: -// MSVC x32's _chkstk and cygwin/mingw's _alloca adjust %esp themselves. -// MSVC x64's __chkstk and cygwin/mingw's ___chkstk_ms do not adjust %rsp +// MSVC x32's _chkstk and cygwin/mingw's _alloca adjust esp themselves. +// MSVC x64's __chkstk and cygwin/mingw's ___chkstk_ms do not adjust `rsp` // themselves. #[unsafe(naked)] #[rustc_std_internal_symbol] @@ -185,40 +183,39 @@ pub unsafe extern "custom" fn __rust_probestack() { core::arch::naked_asm!( " .cfi_startproc - push %ebp + push ebp .cfi_adjust_cfa_offset 4 - .cfi_offset %ebp, -8 - mov %esp, %ebp - .cfi_def_cfa_register %ebp - push %ecx - push %edx - mov %eax,%ecx - - cmp $0x1000,%ecx + .cfi_offset ebp, -8 + mov ebp, esp + .cfi_def_cfa_register ebp + push ecx + push edx + mov ecx, eax + + cmp ecx, 0x1000 jna 3f 2: - sub $0x1000,%esp - test %esp,8(%esp) - sub $0x1000,%ecx - cmp $0x1000,%ecx + sub esp, 0x1000 + test dword ptr [esp + 8], esp + sub ecx, 0x1000 + cmp ecx, 0x1000 ja 2b 3: - sub %ecx,%esp - test %esp,8(%esp) - mov 4(%ebp),%edx - mov %edx, 12(%esp) - add %eax,%esp - pop %edx - pop %ecx + sub esp, ecx + test dword ptr [esp + 8], esp + mov edx, dword ptr [ebp + 4] + mov dword ptr [esp + 12], edx + add esp, eax + pop edx + pop ecx leave - sub %eax, %esp - .cfi_def_cfa_register %esp + sub esp, eax + .cfi_def_cfa_register esp .cfi_adjust_cfa_offset -4 ret .cfi_endproc - ", - options(att_syntax) + ", ) } diff --git a/library/compiler-builtins/compiler-builtins/src/x86.rs b/library/compiler-builtins/compiler-builtins/src/x86.rs index 51940b3b338a2..1a3c418609451 100644 --- a/library/compiler-builtins/compiler-builtins/src/x86.rs +++ b/library/compiler-builtins/compiler-builtins/src/x86.rs @@ -22,26 +22,25 @@ intrinsics! { pub unsafe extern "custom" fn _alloca() { // __chkstk and _alloca are the same function core::arch::naked_asm!( - "push %ecx", - "cmp $0x1000,%eax", - "lea 8(%esp),%ecx", // esp before calling this routine -> ecx - "jb 1f", + "push ecx", + "cmp eax, 0x1000", + "lea ecx, [esp + 8]", // esp before calling this routine -> ecx + "jb 3f", "2:", - "sub $0x1000,%ecx", - "test %ecx,(%ecx)", - "sub $0x1000,%eax", - "cmp $0x1000,%eax", + "sub ecx, 0x1000", + "test [ecx], ecx", + "sub eax, 0x1000", + "cmp eax, 0x1000", "ja 2b", - "1:", - "sub %eax,%ecx", - "test %ecx,(%ecx)", - "lea 4(%esp),%eax", // load pointer to the return address into eax - "mov %ecx,%esp", // install the new top of stack pointer into esp - "mov -4(%eax),%ecx", // restore ecx - "push (%eax)", // push return address onto the stack - "sub %esp,%eax", // restore the original value in eax + "3:", + "sub ecx, eax", + "test [ecx], ecx", + "lea eax, [esp + 4]", // load pointer to the return address into eax + "mov esp, ecx", // install the new top of stack pointer into esp + "mov ecx, [eax - 4]", // restore ecx + "push [eax]", // push return address onto the stack + "sub eax, esp", // restore the original value in eax "ret", - options(att_syntax) ); } } diff --git a/library/compiler-builtins/compiler-builtins/src/x86_64.rs b/library/compiler-builtins/compiler-builtins/src/x86_64.rs index f9ae784d57520..99a527ee9ac5e 100644 --- a/library/compiler-builtins/compiler-builtins/src/x86_64.rs +++ b/library/compiler-builtins/compiler-builtins/src/x86_64.rs @@ -12,24 +12,23 @@ intrinsics! { #[cfg(any(all(windows, target_env = "gnu"), target_os = "cygwin", target_os = "uefi"))] pub unsafe extern "custom" fn ___chkstk_ms() { core::arch::naked_asm!( - "push %rcx", - "push %rax", - "cmp $0x1000,%rax", - "lea 24(%rsp),%rcx", - "jb 1f", + "push rcx", + "push rax", + "cmp rax, 0x1000", + "lea rcx, [rsp + 24]", + "jb 3f", "2:", - "sub $0x1000,%rcx", - "test %rcx,(%rcx)", - "sub $0x1000,%rax", - "cmp $0x1000,%rax", + "sub rcx, 0x1000", + "test [rcx], rcx", + "sub rax, 0x1000", + "cmp rax, 0x1000", "ja 2b", - "1:", - "sub %rax,%rcx", - "test %rcx,(%rcx)", - "pop %rax", - "pop %rcx", + "3:", + "sub rcx, rax", + "test [rcx], rcx", + "pop rax", + "pop rcx", "ret", - options(att_syntax) ); } } diff --git a/library/compiler-builtins/crates/musl-math-sys/build.rs b/library/compiler-builtins/crates/musl-math-sys/build.rs index 59e42f2d2e67f..3bab5f2eb112a 100644 --- a/library/compiler-builtins/crates/musl-math-sys/build.rs +++ b/library/compiler-builtins/crates/musl-math-sys/build.rs @@ -46,7 +46,7 @@ fn main() { let cfg = Config::from_env(); if cfg.target_env == "msvc" - || cfg.target_family == "wasm" + || cfg.target_families.iter().any(|f| f == "wasm") || cfg.target_features.iter().any(|f| f == "thumb-mode") { println!( @@ -69,7 +69,7 @@ struct Config { musl_arch: String, target_arch: String, target_env: String, - target_family: String, + target_families: Vec, target_os: String, target_string: String, target_vendor: String, @@ -79,6 +79,9 @@ struct Config { impl Config { fn from_env() -> Self { let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let target_families = env::var("CARGO_CFG_TARGET_FAMILY") + .map(|feats| feats.split(',').map(ToOwned::to_owned).collect()) + .unwrap_or_default(); let target_features = env::var("CARGO_CFG_TARGET_FEATURE") .map(|feats| feats.split(',').map(ToOwned::to_owned).collect()) .unwrap_or_default(); @@ -104,7 +107,7 @@ impl Config { musl_arch, target_arch, target_env: env::var("CARGO_CFG_TARGET_ENV").unwrap(), - target_family: env::var("CARGO_CFG_TARGET_FAMILY").unwrap(), + target_families, target_os: env::var("CARGO_CFG_TARGET_OS").unwrap(), target_string: env::var("TARGET").unwrap(), target_vendor: env::var("CARGO_CFG_TARGET_VENDOR").unwrap(), diff --git a/library/compiler-builtins/crates/panic-handler/Cargo.toml b/library/compiler-builtins/crates/panic-handler/Cargo.toml index a6764fc481b64..70898368d8e73 100644 --- a/library/compiler-builtins/crates/panic-handler/Cargo.toml +++ b/library/compiler-builtins/crates/panic-handler/Cargo.toml @@ -1,7 +1,6 @@ [package] name = "panic-handler" version = "0.1.0" -authors = ["Alex Crichton "] edition = "2024" publish = false diff --git a/library/compiler-builtins/crates/symbol-check/src/main.rs b/library/compiler-builtins/crates/symbol-check/src/main.rs index 4e94552331a0f..7d0b7e90addb5 100644 --- a/library/compiler-builtins/crates/symbol-check/src/main.rs +++ b/library/compiler-builtins/crates/symbol-check/src/main.rs @@ -7,9 +7,10 @@ use std::io::{BufRead, BufReader}; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; -use object::read::archive::{ArchiveFile, ArchiveMember}; +use object::read::archive::ArchiveFile; use object::{ - File as ObjFile, Object, ObjectSection, ObjectSymbol, Symbol, SymbolKind, SymbolScope, + File as ObjFile, Object, ObjectSection, ObjectSymbol, Result as ObjResult, Symbol, SymbolKind, + SymbolScope, }; use serde_json::Value; @@ -24,6 +25,11 @@ Cargo will get invoked with `CARGO_ARGS` and the specified target. All output `compiler_builtins*.rlib` files will be checked. If TARGET is not specified, the host target is used. + + check PATHS ... + +Run the same checks on the given set of paths, without invoking Cargo. Paths +may be either archives or object files. "; fn main() { @@ -33,12 +39,14 @@ fn main() { match &args_ref[1..] { ["build-and-check", target, "--", args @ ..] if !args.is_empty() => { - check_cargo_args(args); run_build_and_check(target, args); } ["build-and-check", "--", args @ ..] if !args.is_empty() => { - check_cargo_args(args); - run_build_and_check(&host_target(), args); + let target = &host_target(); + run_build_and_check(target, args); + } + ["check", paths @ ..] if !paths.is_empty() => { + check_paths(paths); } _ => { println!("{USAGE}"); @@ -47,22 +55,25 @@ fn main() { } } -/// Make sure `--target` isn't passed to avoid confusion (since it should be proivded only once, -/// positionally). -fn check_cargo_args(args: &[&str]) { +fn run_build_and_check(target: &str, args: &[&str]) { + // Make sure `--target` isn't passed to avoid confusion (since it should be + // proivded only once, positionally). for arg in args { assert!( !arg.contains("--target"), "target must be passed positionally. {USAGE}" ); } -} -fn run_build_and_check(target: &str, args: &[&str]) { let paths = exec_cargo_with_args(target, args); + check_paths(&paths); +} + +fn check_paths>(paths: &[P]) { for path in paths { + let path = path.as_ref(); println!("Checking {}", path.display()); - let archive = Archive::from_path(&path); + let archive = BinFile::from_path(path); verify_no_duplicates(&archive); verify_core_symbols(&archive); @@ -165,7 +176,7 @@ struct SymInfo { } impl SymInfo { - fn new(sym: &Symbol, obj: &ObjFile, member: &ArchiveMember) -> Self { + fn new(sym: &Symbol, obj: &ObjFile, obj_path: &str) -> Self { // Include the section name if possible. Fall back to the `Section` debug impl if not. let section = sym.section(); let section_name = sym @@ -187,7 +198,7 @@ impl SymInfo { is_weak: sym.is_weak(), is_common: sym.is_common(), address: sym.address(), - object: String::from_utf8_lossy(member.name()).into_owned(), + object: obj_path.to_owned(), } } } @@ -197,7 +208,7 @@ impl SymInfo { /// Note that this will also locate cases where a symbol is weakly defined in more than one place. /// Technically there are no linker errors that will come from this, but it keeps our binary more /// straightforward and saves some distribution size. -fn verify_no_duplicates(archive: &Archive) { +fn verify_no_duplicates(archive: &BinFile) { let mut syms = BTreeMap::::new(); let mut dups = Vec::new(); let mut found_any = false; @@ -254,7 +265,7 @@ fn verify_no_duplicates(archive: &Archive) { } /// Ensure that there are no references to symbols from `core` that aren't also (somehow) defined. -fn verify_core_symbols(archive: &Archive) { +fn verify_core_symbols(archive: &BinFile) { let mut defined = BTreeSet::new(); let mut undefined = Vec::new(); let mut has_symbols = false; @@ -289,39 +300,63 @@ fn verify_core_symbols(archive: &Archive) { } /// Thin wrapper for owning data used by `object`. -struct Archive { +struct BinFile { + path: PathBuf, data: Vec, } -impl Archive { +impl BinFile { fn from_path(path: &Path) -> Self { Self { + path: path.to_owned(), data: fs::read(path).expect("reading file failed"), } } - fn file(&self) -> ArchiveFile<'_> { - ArchiveFile::parse(self.data.as_slice()).expect("archive parse failed") + fn as_archive_file(&self) -> ObjResult> { + ArchiveFile::parse(self.data.as_slice()) } - /// For a given archive, do something with each object file. - fn for_each_object(&self, mut f: impl FnMut(ObjFile, &ArchiveMember)) { - let archive = self.file(); - - for member in archive.members() { - let member = member.expect("failed to access member"); - let obj_data = member - .data(self.data.as_slice()) - .expect("failed to access object"); - let obj = ObjFile::parse(obj_data).expect("failed to parse object"); - f(obj, &member); + fn as_obj_file(&self) -> ObjResult> { + ObjFile::parse(self.data.as_slice()) + } + + /// For a given archive, do something with each object file. For an object file, do + /// something once. + fn for_each_object(&self, mut f: impl FnMut(ObjFile, &str)) { + // Try as an archive first. + let as_archive = self.as_archive_file(); + if let Ok(archive) = as_archive { + for member in archive.members() { + let member = member.expect("failed to access member"); + let obj_data = member + .data(self.data.as_slice()) + .expect("failed to access object"); + let obj = ObjFile::parse(obj_data).expect("failed to parse object"); + f(obj, &String::from_utf8_lossy(member.name())); + } + + return; } + + // Fall back to parsing as an object file. + let as_obj = self.as_obj_file(); + if let Ok(obj) = as_obj { + f(obj, &self.path.to_string_lossy()); + return; + } + + panic!( + "failed to parse as either archive or object file: {:?}, {:?}", + as_archive.unwrap_err(), + as_obj.unwrap_err(), + ); } - /// For a given archive, do something with each symbol. - fn for_each_symbol(&self, mut f: impl FnMut(Symbol, &ObjFile, &ArchiveMember)) { - self.for_each_object(|obj, member| { - obj.symbols().for_each(|sym| f(sym, &obj, member)); + /// D something with each symbol in an archive or object file. + fn for_each_symbol(&self, mut f: impl FnMut(Symbol, &ObjFile, &str)) { + self.for_each_object(|obj, obj_path| { + obj.symbols().for_each(|sym| f(sym, &obj, obj_path)); }); } } diff --git a/library/compiler-builtins/libm-test/Cargo.toml b/library/compiler-builtins/libm-test/Cargo.toml index 0af6b0c1da5ca..adecfc1af6b87 100644 --- a/library/compiler-builtins/libm-test/Cargo.toml +++ b/library/compiler-builtins/libm-test/Cargo.toml @@ -21,8 +21,8 @@ build-musl = ["dep:musl-math-sys"] # Enable report generation without bringing in more dependencies by default benchmarking-reports = ["criterion/plotters", "criterion/html_reports"] -# Enable icount benchmarks (requires iai-callgrind and valgrind) -icount = ["dep:iai-callgrind"] +# Enable icount benchmarks (requires gungraun-runner and valgrind locally) +icount = ["dep:gungraun"] # Run with a reduced set of benchmarks, such as for CI short-benchmarks = [] @@ -31,7 +31,7 @@ short-benchmarks = [] anyhow = "1.0.98" # This is not directly used but is required so we can enable `gmp-mpfr-sys/force-cross`. gmp-mpfr-sys = { version = "1.6.5", optional = true, default-features = false } -iai-callgrind = { version = "0.15.2", optional = true } +gungraun = { version = "0.17.0", optional = true } indicatif = { version = "0.18.0", default-features = false } libm = { path = "../libm", features = ["unstable-public-internals"] } libm-macros = { path = "../crates/libm-macros" } diff --git a/library/compiler-builtins/libm-test/benches/icount.rs b/library/compiler-builtins/libm-test/benches/icount.rs index 02ee13f804f16..fb856d9be4517 100644 --- a/library/compiler-builtins/libm-test/benches/icount.rs +++ b/library/compiler-builtins/libm-test/benches/icount.rs @@ -1,10 +1,10 @@ -//! Benchmarks that use `iai-cachegrind` to be reasonably CI-stable. +//! Benchmarks that use `gungraun` to be reasonably CI-stable. #![feature(f16)] #![feature(f128)] use std::hint::black_box; -use iai_callgrind::{library_benchmark, library_benchmark_group, main}; +use gungraun::{library_benchmark, library_benchmark_group, main}; use libm::support::{HInt, Hexf, hf16, hf32, hf64, hf128, u256}; use libm_test::generate::spaced; use libm_test::{CheckBasis, CheckCtx, GeneratorKind, MathOp, OpRustArgs, TupleCall, op}; @@ -111,6 +111,17 @@ fn icount_bench_u128_widen_mul(cases: Vec<(u128, u128)>) { } } +#[library_benchmark] +#[bench::linspace(setup_u128_mul())] +fn icount_bench_u256_narrowing_div(cases: Vec<(u128, u128)>) { + use libm::support::NarrowingDiv; + for (x, y) in cases.iter().copied() { + let x = black_box(x.widen_hi()); + let y = black_box(y); + black_box(x.checked_narrowing_div_rem(y)); + } +} + #[library_benchmark] #[bench::linspace(setup_u256_add())] fn icount_bench_u256_add(cases: Vec<(u256, u256)>) { @@ -145,7 +156,13 @@ fn icount_bench_u256_shr(cases: Vec<(u256, u32)>) { library_benchmark_group!( name = icount_bench_u128_group; - benchmarks = icount_bench_u128_widen_mul, icount_bench_u256_add, icount_bench_u256_sub, icount_bench_u256_shl, icount_bench_u256_shr + benchmarks = + icount_bench_u128_widen_mul, + icount_bench_u256_narrowing_div, + icount_bench_u256_add, + icount_bench_u256_sub, + icount_bench_u256_shl, + icount_bench_u256_shr ); #[library_benchmark] diff --git a/library/compiler-builtins/libm-test/src/precision.rs b/library/compiler-builtins/libm-test/src/precision.rs index 3fb8c1b371090..7887c032394b8 100644 --- a/library/compiler-builtins/libm-test/src/precision.rs +++ b/library/compiler-builtins/libm-test/src/precision.rs @@ -1,8 +1,6 @@ //! Configuration for skipping or changing the result for individual test cases (inputs) rather //! than ignoring entire tests. -use core::f32; - use CheckBasis::{Mpfr, Musl}; use libm::support::CastFrom; use {BaseName as Bn, Identifier as Id}; @@ -85,6 +83,19 @@ pub fn default_ulp(ctx: &CheckCtx) -> u32 { Bn::Tgamma => 20, }; + // These have a separate implementation on i586 + if cfg!(x86_no_sse) { + match ctx.fn_ident { + Id::Exp => ulp = 1, + Id::Exp2 => ulp = 1, + Id::Exp10 => ulp = 1, + Id::Expf => ulp = 0, + Id::Exp2f => ulp = 0, + Id::Exp10f => ulp = 0, + _ => (), + } + } + // There are some cases where musl's approximation is less accurate than ours. For these // cases, increase the ULP. if ctx.basis == Musl { @@ -100,6 +111,8 @@ pub fn default_ulp(ctx: &CheckCtx) -> u32 { Id::Cbrt => ulp = 2, // FIXME(#401): musl has an incorrect result here. Id::Fdim => ulp = 2, + Id::Exp2f => ulp = 1, + Id::Expf => ulp = 1, Id::Sincosf => ulp = 500, Id::Tgamma => ulp = 20, _ => (), @@ -126,8 +139,6 @@ pub fn default_ulp(ctx: &CheckCtx) -> u32 { Id::Asinh => ulp = 3, Id::Asinhf => ulp = 3, Id::Cbrt => ulp = 1, - Id::Exp10 | Id::Exp10f => ulp = 1_000_000, - Id::Exp2 | Id::Exp2f => ulp = 10_000_000, Id::Log1p | Id::Log1pf => ulp = 2, Id::Tan => ulp = 2, _ => (), @@ -207,36 +218,6 @@ impl MaybeOverride<(f16,)> for SpecialCase {} impl MaybeOverride<(f32,)> for SpecialCase { fn check_float(input: (f32,), actual: F, expected: F, ctx: &CheckCtx) -> CheckAction { - if ctx.base_name == BaseName::Expm1 - && !input.0.is_infinite() - && input.0 > 80.0 - && actual.is_infinite() - && !expected.is_infinite() - { - // we return infinity but the number is representable - if ctx.basis == CheckBasis::Musl { - return XFAIL_NOCHECK; - } - return XFAIL("expm1 representable numbers"); - } - - if cfg!(x86_no_sse) - && ctx.base_name == BaseName::Exp2 - && !expected.is_infinite() - && actual.is_infinite() - { - // We return infinity when there is a representable value. Test input: 127.97238 - return XFAIL("586 exp2 representable numbers"); - } - - if ctx.base_name == BaseName::Sinh && input.0.abs() > 80.0 && actual.is_nan() { - // we return some NaN that should be real values or infinite - if ctx.basis == CheckBasis::Musl { - return XFAIL_NOCHECK; - } - return XFAIL("sinh unexpected NaN"); - } - if (ctx.base_name == BaseName::Lgamma || ctx.base_name == BaseName::LgammaR) && input.0 > 4e36 && expected.is_infinite() @@ -280,14 +261,6 @@ impl MaybeOverride<(f64,)> for SpecialCase { return XFAIL("i586 rint rounding mode"); } - if cfg!(x86_no_sse) - && (ctx.fn_ident == Identifier::Exp10 || ctx.fn_ident == Identifier::Exp2) - { - // FIXME: i586 has very imprecise results with ULP > u32::MAX for these - // operations so we can't reasonably provide a limit. - return XFAIL_NOCHECK; - } - if ctx.base_name == BaseName::J0 && input.0 < -1e300 { // Errors get huge close to -inf return XFAIL_NOCHECK; diff --git a/library/compiler-builtins/libm/Cargo.toml b/library/compiler-builtins/libm/Cargo.toml index 63b4d3c277989..5b5ca34fd2c9e 100644 --- a/library/compiler-builtins/libm/Cargo.toml +++ b/library/compiler-builtins/libm/Cargo.toml @@ -1,7 +1,12 @@ [package] name = "libm" version = "0.2.15" -authors = ["Jorge Aparicio "] +authors = [ + "Alex Crichton ", + "Amanieu d'Antras ", + "Jorge Aparicio ", + "Trevor Gross ", +] description = "libm in pure Rust" categories = ["no-std"] keywords = ["libm", "math"] diff --git a/library/compiler-builtins/libm/configure.rs b/library/compiler-builtins/libm/configure.rs index 76186e6365274..ee65a3a8d6243 100644 --- a/library/compiler-builtins/libm/configure.rs +++ b/library/compiler-builtins/libm/configure.rs @@ -13,7 +13,7 @@ pub struct Config { pub target_triple: String, pub target_arch: String, pub target_env: String, - pub target_family: Option, + pub target_families: Vec, pub target_os: String, pub target_string: String, pub target_vendor: String, @@ -25,6 +25,9 @@ pub struct Config { impl Config { pub fn from_env() -> Self { let target_triple = env::var("TARGET").unwrap(); + let target_families = env::var("CARGO_CFG_TARGET_FAMILY") + .map(|feats| feats.split(',').map(ToOwned::to_owned).collect()) + .unwrap_or_default(); let target_features = env::var("CARGO_CFG_TARGET_FEATURE") .map(|feats| feats.split(',').map(ToOwned::to_owned).collect()) .unwrap_or_default(); @@ -41,7 +44,7 @@ impl Config { cargo_features, target_arch: env::var("CARGO_CFG_TARGET_ARCH").unwrap(), target_env: env::var("CARGO_CFG_TARGET_ENV").unwrap(), - target_family: env::var("CARGO_CFG_TARGET_FAMILY").ok(), + target_families, target_os: env::var("CARGO_CFG_TARGET_OS").unwrap(), target_string: env::var("TARGET").unwrap(), target_vendor: env::var("CARGO_CFG_TARGET_VENDOR").unwrap(), @@ -140,16 +143,13 @@ fn emit_f16_f128_cfg(cfg: &Config) { /* See the compiler-builtins configure file for info about the meaning of these options */ - // If the feature is set, disable both of these types. - let no_f16_f128 = cfg.cargo_features.iter().any(|s| s == "no-f16-f128"); - println!("cargo:rustc-check-cfg=cfg(f16_enabled)"); - if cfg.reliable_f16 && !no_f16_f128 { + if cfg.reliable_f16 { println!("cargo:rustc-cfg=f16_enabled"); } println!("cargo:rustc-check-cfg=cfg(f128_enabled)"); - if cfg.reliable_f128 && !no_f16_f128 { + if cfg.reliable_f128 { println!("cargo:rustc-cfg=f128_enabled"); } } diff --git a/library/compiler-builtins/libm/src/libm_helper.rs b/library/compiler-builtins/libm/src/libm_helper.rs index dfa1ff77bf2e4..0bb669398657e 100644 --- a/library/compiler-builtins/libm/src/libm_helper.rs +++ b/library/compiler-builtins/libm/src/libm_helper.rs @@ -168,7 +168,7 @@ libm_helper! { (fn remquo(x: f64, y: f64) -> (f64, i32); => remquo); (fn rint(x: f64) -> (f64); => rint); (fn round(x: f64) -> (f64); => round); - (fn roundevem(x: f64) -> (f64); => roundeven); + (fn roundeven(x: f64) -> (f64); => roundeven); (fn scalbn(x: f64, n: i32) -> (f64); => scalbn); (fn sin(x: f64) -> (f64); => sin); (fn sincos(x: f64) -> (f64, f64); => sincos); diff --git a/library/compiler-builtins/libm/src/math/arch/i586.rs b/library/compiler-builtins/libm/src/math/arch/i586.rs index b9a66762063db..d9bb93fbf5852 100644 --- a/library/compiler-builtins/libm/src/math/arch/i586.rs +++ b/library/compiler-builtins/libm/src/math/arch/i586.rs @@ -60,3 +60,62 @@ pub fn floor(mut x: f64) -> f64 { } x } +/// Implements the exponential functions with `x87` assembly. +/// +/// This relies on the instruction `f2xm1`, which computes `2^x - 1` (for +/// |x| < 1). This transcendental instruction is documented to produce results +/// with error below 1ulp (in the native double-extended precision format). This +/// translates to correctly rounded results for f32, but results in f64 may have +/// 1ulp error, which may depend on the hardware. +macro_rules! x87exp { + ($float_ty:ident, $word_size:literal, $fn_name:ident, $load_op:literal) => { + pub fn $fn_name(mut x: $float_ty) -> $float_ty { unsafe { + core::arch::asm!( + // Prepare the register stack as + // ``` + // st(0) = y = x*log2(base) + // st(1) = 1.0 + // st(2) = round(y) + // ``` + concat!($load_op, " ", $word_size, " ptr [{x}]"), + "fld1", + "fld st(1)", + "frndint", + "fxch st(2)", + + // Compare y with round(y) to determine if y is finite and + // not an integer. If so, compute `exp2(y - round(y))` into + // st(1). Otherwise skip ahead with `st(1) = 1.0` + "fucom st(2)", + "fstsw ax", + "test ax, 0x4000", + "jnz 2f", + "fsub st(0), st(2)", // st(0) = y - round(y) + "f2xm1", // st(0) = 2^st(0) - 1.0 + "fadd st(1), st(0)", // st(1) = 1 + st(0) = exp2(y - round(y)) + "2:", + + // Finally, scale by `exp2(round(y))` and clear the stack. + "fstp st(0)", + "fscale", + concat!("fstp ", $word_size, " ptr [{x}]"), + "fstp st(0)", + x = in(reg) &mut x, + out("ax") _, + out("st(0)") _, out("st(1)") _, + out("st(2)") _, out("st(3)") _, + out("st(4)") _, out("st(5)") _, + out("st(6)") _, out("st(7)") _, + options(nostack), + ); + x + }} + }; +} + +x87exp!(f32, "dword", x87_exp2f, "fld"); +x87exp!(f64, "qword", x87_exp2, "fld"); +x87exp!(f32, "dword", x87_exp10f, "fldl2t\nfmul"); +x87exp!(f64, "qword", x87_exp10, "fldl2t\nfmul"); +x87exp!(f32, "dword", x87_expf, "fldl2e\nfmul"); +x87exp!(f64, "qword", x87_exp, "fldl2e\nfmul"); diff --git a/library/compiler-builtins/libm/src/math/arch/mod.rs b/library/compiler-builtins/libm/src/math/arch/mod.rs index 984ae7f3129f9..ba859c679d0db 100644 --- a/library/compiler-builtins/libm/src/math/arch/mod.rs +++ b/library/compiler-builtins/libm/src/math/arch/mod.rs @@ -48,3 +48,8 @@ cfg_if! { pub use i586::{ceil, floor}; } } +cfg_if! { + if #[cfg(x86_no_sse)] { + pub use i586::{x87_exp10f, x87_exp10, x87_expf, x87_exp, x87_exp2f, x87_exp2}; + } +} diff --git a/library/compiler-builtins/libm/src/math/arch/x86/detect.rs b/library/compiler-builtins/libm/src/math/arch/x86/detect.rs index e6d9b040bfaff..ca785470b806d 100644 --- a/library/compiler-builtins/libm/src/math/arch/x86/detect.rs +++ b/library/compiler-builtins/libm/src/math/arch/x86/detect.rs @@ -39,6 +39,8 @@ pub fn get_cpu_features() -> Flags { /// Implementation is taken from [std-detect][std-detect]. /// /// [std-detect]: https://github.com/rust-lang/stdarch/blob/690b3a6334d482874163bd6fcef408e0518febe9/crates/std_detect/src/detect/os/x86.rs#L142 +// FIXME(msrv): Remove unsafe block around __cpuid once https://github.com/rust-lang/stdarch/pull/1935 is available in MSRV. +#[allow(unused_unsafe)] fn load_x86_features() -> Flags { let mut value = Flags::empty(); diff --git a/library/compiler-builtins/libm/src/math/atan.rs b/library/compiler-builtins/libm/src/math/atan.rs index 0590ba87cf851..a303ebd42f0d2 100644 --- a/library/compiler-builtins/libm/src/math/atan.rs +++ b/library/compiler-builtins/libm/src/math/atan.rs @@ -29,8 +29,6 @@ * to produce the hexadecimal values shown. */ -use core::f64; - use super::fabs; const ATANHI: [f64; 4] = [ @@ -134,19 +132,19 @@ pub fn atan(x: f64) -> f64 { #[cfg(test)] mod tests { - use core::f64; + use core::f64::consts; use super::atan; #[test] fn sanity_check() { for (input, answer) in [ - (3.0_f64.sqrt() / 3.0, f64::consts::FRAC_PI_6), - (1.0, f64::consts::FRAC_PI_4), - (3.0_f64.sqrt(), f64::consts::FRAC_PI_3), - (-3.0_f64.sqrt() / 3.0, -f64::consts::FRAC_PI_6), - (-1.0, -f64::consts::FRAC_PI_4), - (-3.0_f64.sqrt(), -f64::consts::FRAC_PI_3), + (3.0_f64.sqrt() / 3.0, consts::FRAC_PI_6), + (1.0, consts::FRAC_PI_4), + (3.0_f64.sqrt(), consts::FRAC_PI_3), + (-3.0_f64.sqrt() / 3.0, -consts::FRAC_PI_6), + (-1.0, -consts::FRAC_PI_4), + (-3.0_f64.sqrt(), -consts::FRAC_PI_3), ] .iter() { @@ -167,12 +165,12 @@ mod tests { #[test] fn infinity() { - assert_eq!(atan(f64::INFINITY), f64::consts::FRAC_PI_2); + assert_eq!(atan(f64::INFINITY), consts::FRAC_PI_2); } #[test] fn minus_infinity() { - assert_eq!(atan(f64::NEG_INFINITY), -f64::consts::FRAC_PI_2); + assert_eq!(atan(f64::NEG_INFINITY), -consts::FRAC_PI_2); } #[test] diff --git a/library/compiler-builtins/libm/src/math/cbrtf.rs b/library/compiler-builtins/libm/src/math/cbrtf.rs index 9d69584834a3e..6916ca6735b63 100644 --- a/library/compiler-builtins/libm/src/math/cbrtf.rs +++ b/library/compiler-builtins/libm/src/math/cbrtf.rs @@ -17,8 +17,6 @@ * Return cube root of x */ -use core::f32; - const B1: u32 = 709958130; /* B1 = (127-127.0/3-0.03306235651)*2**23 */ const B2: u32 = 642849266; /* B2 = (127-127.0/3-24/3-0.03306235651)*2**23 */ diff --git a/library/compiler-builtins/libm/src/math/exp.rs b/library/compiler-builtins/libm/src/math/exp.rs index 78ce5dd134ac3..cb939ad5d8bf2 100644 --- a/library/compiler-builtins/libm/src/math/exp.rs +++ b/library/compiler-builtins/libm/src/math/exp.rs @@ -83,6 +83,12 @@ const P5: f64 = 4.13813679705723846039e-08; /* 0x3E663769, 0x72BEA4D0 */ /// (where *e* is the base of the natural system of logarithms, approximately 2.71828). #[cfg_attr(assert_no_panic, no_panic::no_panic)] pub fn exp(mut x: f64) -> f64 { + select_implementation! { + name: x87_exp, + use_arch_required: x86_no_sse, + args: x, + } + let x1p1023 = f64::from_bits(0x7fe0000000000000); // 0x1p1023 === 2 ^ 1023 let x1p_149 = f64::from_bits(0x36a0000000000000); // 0x1p-149 === 2 ^ -149 diff --git a/library/compiler-builtins/libm/src/math/exp10.rs b/library/compiler-builtins/libm/src/math/exp10.rs index 1f49f5e96979c..e0af1945b922a 100644 --- a/library/compiler-builtins/libm/src/math/exp10.rs +++ b/library/compiler-builtins/libm/src/math/exp10.rs @@ -9,6 +9,12 @@ const P10: &[f64] = &[ /// Calculates 10 raised to the power of `x` (f64). #[cfg_attr(assert_no_panic, no_panic::no_panic)] pub fn exp10(x: f64) -> f64 { + select_implementation! { + name: x87_exp10, + use_arch_required: x86_no_sse, + args: x, + } + let (mut y, n) = modf(x); let u: u64 = n.to_bits(); /* fabs(n) < 16 without raising invalid on nan */ diff --git a/library/compiler-builtins/libm/src/math/exp10f.rs b/library/compiler-builtins/libm/src/math/exp10f.rs index 22a264211d03e..f0a311c2d1915 100644 --- a/library/compiler-builtins/libm/src/math/exp10f.rs +++ b/library/compiler-builtins/libm/src/math/exp10f.rs @@ -9,6 +9,12 @@ const P10: &[f32] = &[ /// Calculates 10 raised to the power of `x` (f32). #[cfg_attr(assert_no_panic, no_panic::no_panic)] pub fn exp10f(x: f32) -> f32 { + select_implementation! { + name: x87_exp10f, + use_arch_required: x86_no_sse, + args: x, + } + let (mut y, n) = modff(x); let u = n.to_bits(); /* fabsf(n) < 8 without raising invalid on nan */ diff --git a/library/compiler-builtins/libm/src/math/exp2.rs b/library/compiler-builtins/libm/src/math/exp2.rs index 6e4cbc29dcc99..08b71587f6de5 100644 --- a/library/compiler-builtins/libm/src/math/exp2.rs +++ b/library/compiler-builtins/libm/src/math/exp2.rs @@ -324,6 +324,12 @@ static TBL: [u64; TBLSIZE * 2] = [ /// Calculate `2^x`, that is, 2 raised to the power `x`. #[cfg_attr(assert_no_panic, no_panic::no_panic)] pub fn exp2(mut x: f64) -> f64 { + select_implementation! { + name: x87_exp2, + use_arch_required: x86_no_sse, + args: x, + } + let redux = f64::from_bits(0x4338000000000000) / TBLSIZE as f64; let p1 = f64::from_bits(0x3fe62e42fefa39ef); let p2 = f64::from_bits(0x3fcebfbdff82c575); diff --git a/library/compiler-builtins/libm/src/math/exp2f.rs b/library/compiler-builtins/libm/src/math/exp2f.rs index 733d2f1a84738..ceff6822c5969 100644 --- a/library/compiler-builtins/libm/src/math/exp2f.rs +++ b/library/compiler-builtins/libm/src/math/exp2f.rs @@ -75,6 +75,12 @@ static EXP2FT: [u64; TBLSIZE] = [ /// Calculate `2^x`, that is, 2 raised to the power `x`. #[cfg_attr(assert_no_panic, no_panic::no_panic)] pub fn exp2f(mut x: f32) -> f32 { + select_implementation! { + name: x87_exp2f, + use_arch_required: x86_no_sse, + args: x, + } + let redux = f32::from_bits(0x4b400000) / TBLSIZE as f32; let p1 = f32::from_bits(0x3f317218); let p2 = f32::from_bits(0x3e75fdf0); diff --git a/library/compiler-builtins/libm/src/math/expf.rs b/library/compiler-builtins/libm/src/math/expf.rs index dbbfdbba9253b..5541ab79a9c14 100644 --- a/library/compiler-builtins/libm/src/math/expf.rs +++ b/library/compiler-builtins/libm/src/math/expf.rs @@ -32,6 +32,12 @@ const P2: f32 = -2.7667332906e-3; /* -0xb55215.0p-32 */ /// (where *e* is the base of the natural system of logarithms, approximately 2.71828). #[cfg_attr(assert_no_panic, no_panic::no_panic)] pub fn expf(mut x: f32) -> f32 { + select_implementation! { + name: x87_expf, + use_arch_required: x86_no_sse, + args: x, + } + let x1p127 = f32::from_bits(0x7f000000); // 0x1p127f === 2 ^ 127 let x1p_126 = f32::from_bits(0x800000); // 0x1p-126f === 2 ^ -126 /*original 0x1p-149f ??????????? */ let mut hx = x.to_bits(); diff --git a/library/compiler-builtins/libm/src/math/expm1.rs b/library/compiler-builtins/libm/src/math/expm1.rs index 3714bf3afc934..3ce1d886bb194 100644 --- a/library/compiler-builtins/libm/src/math/expm1.rs +++ b/library/compiler-builtins/libm/src/math/expm1.rs @@ -10,8 +10,6 @@ * ==================================================== */ -use core::f64; - const O_THRESHOLD: f64 = 7.09782712893383973096e+02; /* 0x40862E42, 0xFEFA39EF */ const LN2_HI: f64 = 6.93147180369123816490e-01; /* 0x3fe62e42, 0xfee00000 */ const LN2_LO: f64 = 1.90821492927058770002e-10; /* 0x3dea39ef, 0x35793c76 */ diff --git a/library/compiler-builtins/libm/src/math/expm1f.rs b/library/compiler-builtins/libm/src/math/expm1f.rs index f77515a4b99b3..388da3f30173d 100644 --- a/library/compiler-builtins/libm/src/math/expm1f.rs +++ b/library/compiler-builtins/libm/src/math/expm1f.rs @@ -13,7 +13,6 @@ * ==================================================== */ -const O_THRESHOLD: f32 = 8.8721679688e+01; /* 0x42b17180 */ const LN2_HI: f32 = 6.9313812256e-01; /* 0x3f317180 */ const LN2_LO: f32 = 9.0580006145e-06; /* 0x3717f7d1 */ const INV_LN2: f32 = 1.4426950216e+00; /* 0x3fb8aa3b */ @@ -50,7 +49,8 @@ pub fn expm1f(mut x: f32) -> f32 { if sign { return -1.; } - if x > O_THRESHOLD { + if hx > 0x42b17217 { + /* x > log(FLT_MAX) */ x *= x1p127; return x; } diff --git a/library/compiler-builtins/libm/src/math/generic/fmod.rs b/library/compiler-builtins/libm/src/math/generic/fmod.rs index 29acc8a4d5df5..3c3fd44b27cc2 100644 --- a/library/compiler-builtins/libm/src/math/generic/fmod.rs +++ b/library/compiler-builtins/libm/src/math/generic/fmod.rs @@ -1,8 +1,12 @@ /* SPDX-License-Identifier: MIT OR Apache-2.0 */ -use crate::support::{CastFrom, Float, Int, MinInt}; +use crate::support::{CastFrom, CastInto, Float, HInt, Int, MinInt, NarrowingDiv}; #[inline] -pub fn fmod(x: F, y: F) -> F { +pub fn fmod(x: F, y: F) -> F +where + F::Int: HInt, + ::D: NarrowingDiv, +{ let _1 = F::Int::ONE; let sx = x.to_bits() & F::SIGN_MASK; let ux = x.to_bits() & !F::SIGN_MASK; @@ -29,7 +33,7 @@ pub fn fmod(x: F, y: F) -> F { // To compute `(num << ex) % (div << ey)`, first // evaluate `rem = (num << (ex - ey)) % div` ... - let rem = reduction(num, ex - ey, div); + let rem = reduction::(num, ex - ey, div); // ... so the result will be `rem << ey` if rem.is_zero() { @@ -58,11 +62,55 @@ fn into_sig_exp(mut bits: F::Int) -> (F::Int, u32) { } /// Compute the remainder `(x * 2.pow(e)) % y` without overflow. -fn reduction(mut x: I, e: u32, y: I) -> I { - x %= y; - for _ in 0..e { - x <<= 1; - x = x.checked_sub(y).unwrap_or(x); +fn reduction(mut x: F::Int, e: u32, y: F::Int) -> F::Int +where + F: Float, + F::Int: HInt, + <::Int as HInt>::D: NarrowingDiv, +{ + // `f16` only has 5 exponent bits, so even `f16::MAX = 65504.0` is only + // a 40-bit integer multiple of the smallest subnormal. + if F::BITS == 16 { + debug_assert!(F::EXP_MAX - F::EXP_MIN == 29); + debug_assert!(e <= 29); + let u: u16 = x.cast(); + let v: u16 = y.cast(); + let u = (u as u64) << e; + let v = v as u64; + return F::Int::cast_from((u % v) as u16); } - x + + // Ensure `x < 2y` for later steps + if x >= (y << 1) { + // This case is only reached with subnormal divisors, + // but it might be better to just normalize all significands + // to make this unnecessary. The further calls could potentially + // benefit from assuming a specific fixed leading bit position. + x %= y; + } + + // The simple implementation seems to be fastest for a short reduction + // at this size. The limit here was chosen empirically on an Intel Nehalem. + // Less old CPUs that have faster `u64 * u64 -> u128` might not benefit, + // and 32-bit systems or architectures without hardware multipliers might + // want to do this in more cases. + if F::BITS == 64 && e < 32 { + // Assumes `x < 2y` + for _ in 0..e { + x = x.checked_sub(y).unwrap_or(x); + x <<= 1; + } + return x.checked_sub(y).unwrap_or(x); + } + + // Fast path for short reductions + if e < F::BITS { + let w = x.widen() << e; + if let Some((_, r)) = w.checked_narrowing_div_rem(y) { + return r; + } + } + + // Assumes `x < 2y` + crate::support::linear_mul_reduction(x, e, y) } diff --git a/library/compiler-builtins/libm/src/math/generic/scalbn.rs b/library/compiler-builtins/libm/src/math/generic/scalbn.rs index 6dd9b1a9b84a4..68de41757913a 100644 --- a/library/compiler-builtins/libm/src/math/generic/scalbn.rs +++ b/library/compiler-builtins/libm/src/math/generic/scalbn.rs @@ -96,14 +96,14 @@ where // Work aroudn this by using a different algorithm that calculates the prescale // dynamically based on the maximum possible value. This adds more operations per round // since it needs to construct the scale, but works better in the general case. - let add = -(n + sig_total_bits as i32).clamp(exp_min, sig_total_bits as i32); + let add = -(n + sig_total_bits as i32).max(exp_min); let mul = F::from_parts(false, (F::EXP_BIAS as i32 - add) as u32, zero); x *= mul; n += add; if n < exp_min { - let add = -(n + sig_total_bits as i32).clamp(exp_min, sig_total_bits as i32); + let add = -(n + sig_total_bits as i32).max(exp_min); let mul = F::from_parts(false, (F::EXP_BIAS as i32 - add) as u32, zero); x *= mul; diff --git a/library/compiler-builtins/libm/src/math/hypot.rs b/library/compiler-builtins/libm/src/math/hypot.rs index b92ee18ca1100..c0b2a19370cd7 100644 --- a/library/compiler-builtins/libm/src/math/hypot.rs +++ b/library/compiler-builtins/libm/src/math/hypot.rs @@ -1,5 +1,3 @@ -use core::f64; - use super::sqrt; const SPLIT: f64 = 134217728. + 1.; // 0x1p27 + 1 === (2 ^ 27) + 1 diff --git a/library/compiler-builtins/libm/src/math/hypotf.rs b/library/compiler-builtins/libm/src/math/hypotf.rs index e7635ffc9a0bf..dfb36d4b23eda 100644 --- a/library/compiler-builtins/libm/src/math/hypotf.rs +++ b/library/compiler-builtins/libm/src/math/hypotf.rs @@ -1,5 +1,3 @@ -use core::f32; - use super::sqrtf; #[cfg_attr(assert_no_panic, no_panic::no_panic)] diff --git a/library/compiler-builtins/libm/src/math/log10.rs b/library/compiler-builtins/libm/src/math/log10.rs index 29f25d944af9a..228c00a5b2ffb 100644 --- a/library/compiler-builtins/libm/src/math/log10.rs +++ b/library/compiler-builtins/libm/src/math/log10.rs @@ -17,8 +17,6 @@ * log10(x) = (f - f*f/2 + r)/log(10) + k*log10(2) */ -use core::f64; - const IVLN10HI: f64 = 4.34294481878168880939e-01; /* 0x3fdbcb7b, 0x15200000 */ const IVLN10LO: f64 = 2.50829467116452752298e-11; /* 0x3dbb9438, 0xca9aadd5 */ const LOG10_2HI: f64 = 3.01029995663611771306e-01; /* 0x3FD34413, 0x509F6000 */ diff --git a/library/compiler-builtins/libm/src/math/log10f.rs b/library/compiler-builtins/libm/src/math/log10f.rs index f89584bf9c999..f72fcf9e1e27c 100644 --- a/library/compiler-builtins/libm/src/math/log10f.rs +++ b/library/compiler-builtins/libm/src/math/log10f.rs @@ -13,8 +13,6 @@ * See comments in log10.c. */ -use core::f32; - const IVLN10HI: f32 = 4.3432617188e-01; /* 0x3ede6000 */ const IVLN10LO: f32 = -3.1689971365e-05; /* 0xb804ead9 */ const LOG10_2HI: f32 = 3.0102920532e-01; /* 0x3e9a2080 */ diff --git a/library/compiler-builtins/libm/src/math/log1p.rs b/library/compiler-builtins/libm/src/math/log1p.rs index c991cce60df07..c2f9eb89be609 100644 --- a/library/compiler-builtins/libm/src/math/log1p.rs +++ b/library/compiler-builtins/libm/src/math/log1p.rs @@ -53,8 +53,6 @@ * See HP-15C Advanced Functions Handbook, p.193. */ -use core::f64; - const LN2_HI: f64 = 6.93147180369123816490e-01; /* 3fe62e42 fee00000 */ const LN2_LO: f64 = 1.90821492927058770002e-10; /* 3dea39ef 35793c76 */ const LG1: f64 = 6.666666666666735130e-01; /* 3FE55555 55555593 */ diff --git a/library/compiler-builtins/libm/src/math/log1pf.rs b/library/compiler-builtins/libm/src/math/log1pf.rs index 89a92fac98ee3..2e4775b8de904 100644 --- a/library/compiler-builtins/libm/src/math/log1pf.rs +++ b/library/compiler-builtins/libm/src/math/log1pf.rs @@ -10,8 +10,6 @@ * ==================================================== */ -use core::f32; - const LN2_HI: f32 = 6.9313812256e-01; /* 0x3f317180 */ const LN2_LO: f32 = 9.0580006145e-06; /* 0x3717f7d1 */ /* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ diff --git a/library/compiler-builtins/libm/src/math/log2.rs b/library/compiler-builtins/libm/src/math/log2.rs index 9b750c9a2a6cb..0f72fe0b84dc9 100644 --- a/library/compiler-builtins/libm/src/math/log2.rs +++ b/library/compiler-builtins/libm/src/math/log2.rs @@ -17,8 +17,6 @@ * log2(x) = (f - f*f/2 + r)/log(2) + k */ -use core::f64; - const IVLN2HI: f64 = 1.44269504072144627571e+00; /* 0x3ff71547, 0x65200000 */ const IVLN2LO: f64 = 1.67517131648865118353e-10; /* 0x3de705fc, 0x2eefa200 */ const LG1: f64 = 6.666666666666735130e-01; /* 3FE55555 55555593 */ diff --git a/library/compiler-builtins/libm/src/math/log2f.rs b/library/compiler-builtins/libm/src/math/log2f.rs index 0e5177d7afa83..78673675a915b 100644 --- a/library/compiler-builtins/libm/src/math/log2f.rs +++ b/library/compiler-builtins/libm/src/math/log2f.rs @@ -13,8 +13,6 @@ * See comments in log2.c. */ -use core::f32; - const IVLN2HI: f32 = 1.4428710938e+00; /* 0x3fb8b000 */ const IVLN2LO: f32 = -1.7605285393e-04; /* 0xb9389ad4 */ /* |(log(1+s)-log(1-s))/s - Lg(s)| < 2**-34.24 (~[-4.95e-11, 4.97e-11]). */ diff --git a/library/compiler-builtins/libm/src/math/rem_pio2f.rs b/library/compiler-builtins/libm/src/math/rem_pio2f.rs index 0472a10355a03..481f7ee830bbb 100644 --- a/library/compiler-builtins/libm/src/math/rem_pio2f.rs +++ b/library/compiler-builtins/libm/src/math/rem_pio2f.rs @@ -14,8 +14,6 @@ * ==================================================== */ -use core::f64; - use super::rem_pio2_large; const TOINT: f64 = 1.5 / f64::EPSILON; diff --git a/library/compiler-builtins/libm/src/math/support/big/tests.rs b/library/compiler-builtins/libm/src/math/support/big/tests.rs index d54706c726072..0c32f445c1368 100644 --- a/library/compiler-builtins/libm/src/math/support/big/tests.rs +++ b/library/compiler-builtins/libm/src/math/support/big/tests.rs @@ -3,6 +3,7 @@ use std::string::String; use std::{eprintln, format}; use super::{HInt, MinInt, i256, u256}; +use crate::support::{Int as _, NarrowingDiv}; const LOHI_SPLIT: u128 = 0xaaaaaaaaaaaaaaaaffffffffffffffff; @@ -336,3 +337,28 @@ fn i256_shifts() { x = y; } } +#[test] +fn div_u256_by_u128() { + for j in i8::MIN..=i8::MAX { + let y: u128 = (j as i128).rotate_right(4).unsigned(); + if y == 0 { + continue; + } + for i in i8::MIN..=i8::MAX { + let x: u128 = (i as i128).rotate_right(4).unsigned(); + let xy = x.widen_mul(y); + assert_eq!(xy.checked_narrowing_div_rem(y), Some((x, 0))); + if y != 1 { + assert_eq!((xy + u256::ONE).checked_narrowing_div_rem(y), Some((x, 1))); + } + if x != 0 { + assert_eq!( + (xy - u256::ONE).checked_narrowing_div_rem(y), + Some((x - 1, y - 1)) + ); + } + let r = ((y as f64) * 0.12345) as u128; + assert_eq!((xy + r.widen()).checked_narrowing_div_rem(y), Some((x, r))); + } + } +} diff --git a/library/compiler-builtins/libm/src/math/support/float_traits.rs b/library/compiler-builtins/libm/src/math/support/float_traits.rs index b5ee6413d5539..4e5011f62e0f6 100644 --- a/library/compiler-builtins/libm/src/math/support/float_traits.rs +++ b/library/compiler-builtins/libm/src/math/support/float_traits.rs @@ -289,7 +289,10 @@ macro_rules! float_impl { cfg_if! { // fma is not yet available in `core` if #[cfg(intrinsics_enabled)] { - core::intrinsics::$fma_intrinsic(self, y, z) + // FIXME(msrv,bench): once our benchmark rustc version is above the + // 2022-09-23 nightly, this can be removed. + #[allow(unused_unsafe)] + unsafe { core::intrinsics::$fma_intrinsic(self, y, z) } } else { super::super::$fma_fn(self, y, z) } diff --git a/library/compiler-builtins/libm/src/math/support/int_traits.rs b/library/compiler-builtins/libm/src/math/support/int_traits.rs index 9d8826dfed3fe..55b609affd2e6 100644 --- a/library/compiler-builtins/libm/src/math/support/int_traits.rs +++ b/library/compiler-builtins/libm/src/math/support/int_traits.rs @@ -1,5 +1,8 @@ use core::{cmp, fmt, ops}; +mod narrowing_div; +pub use narrowing_div::NarrowingDiv; + /// Minimal integer implementations needed on all integer types, including wide integers. #[allow(dead_code)] // Some constants are only used with tests pub trait MinInt: @@ -293,7 +296,14 @@ int_impl!(i128, u128); /// Trait for integers twice the bit width of another integer. This is implemented for all /// primitives except for `u8`, because there is not a smaller primitive. -pub trait DInt: MinInt { +pub trait DInt: + MinInt + + ops::Add + + ops::Sub + + ops::Shl + + ops::Shr + + Ord +{ /// Integer that is half the bit width of the integer this trait is implemented for type H: HInt; diff --git a/library/compiler-builtins/libm/src/math/support/int_traits/narrowing_div.rs b/library/compiler-builtins/libm/src/math/support/int_traits/narrowing_div.rs new file mode 100644 index 0000000000000..e76fc5ae9f4ca --- /dev/null +++ b/library/compiler-builtins/libm/src/math/support/int_traits/narrowing_div.rs @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: MIT OR Apache-2.0 */ +use crate::support::{CastInto, DInt, HInt, Int, MinInt, u256}; + +/// Trait for unsigned division of a double-wide integer +/// when the quotient doesn't overflow. +/// +/// This is the inverse of widening multiplication: +/// - for any `x` and nonzero `y`: `x.widen_mul(y).checked_narrowing_div_rem(y) == Some((x, 0))`, +/// - and for any `r in 0..y`: `x.carrying_mul(y, r).checked_narrowing_div_rem(y) == Some((x, r))`, +pub trait NarrowingDiv: DInt + MinInt { + /// Computes `(self / n, self % n))` + /// + /// # Safety + /// The caller must ensure that `self.hi() < n`, or equivalently, + /// that the quotient does not overflow. + unsafe fn unchecked_narrowing_div_rem(self, n: Self::H) -> (Self::H, Self::H); + + /// Returns `Some((self / n, self % n))` when `self.hi() < n`. + fn checked_narrowing_div_rem(self, n: Self::H) -> Option<(Self::H, Self::H)> { + if self.hi() < n { + Some(unsafe { self.unchecked_narrowing_div_rem(n) }) + } else { + None + } + } +} + +// For primitive types we can just use the standard +// division operators in the double-wide type. +macro_rules! impl_narrowing_div_primitive { + ($D:ident) => { + impl NarrowingDiv for $D { + unsafe fn unchecked_narrowing_div_rem(self, n: Self::H) -> (Self::H, Self::H) { + if self.hi() >= n { + unsafe { core::hint::unreachable_unchecked() } + } + ((self / n.widen()).cast(), (self % n.widen()).cast()) + } + } + }; +} + +// Extend division from `u2N / uN` to `u4N / u2N` +// This is not the most efficient algorithm, but it is +// relatively simple. +macro_rules! impl_narrowing_div_recurse { + ($D:ident) => { + impl NarrowingDiv for $D { + unsafe fn unchecked_narrowing_div_rem(self, n: Self::H) -> (Self::H, Self::H) { + if self.hi() >= n { + unsafe { core::hint::unreachable_unchecked() } + } + + // Normalize the divisor by shifting the most significant one + // to the leading position. `n != 0` is implied by `self.hi() < n` + let lz = n.leading_zeros(); + let a = self << lz; + let b = n << lz; + + let ah = a.hi(); + let (a0, a1) = a.lo().lo_hi(); + // SAFETY: For both calls, `b.leading_zeros() == 0` by the above shift. + // SAFETY: `ah < b` follows from `self.hi() < n` + let (q1, r) = unsafe { div_three_digits_by_two(a1, ah, b) }; + // SAFETY: `r < b` is given as the postcondition of the previous call + let (q0, r) = unsafe { div_three_digits_by_two(a0, r, b) }; + + // Undo the earlier normalization for the remainder + (Self::H::from_lo_hi(q0, q1), r >> lz) + } + } + }; +} + +impl_narrowing_div_primitive!(u16); +impl_narrowing_div_primitive!(u32); +impl_narrowing_div_primitive!(u64); +impl_narrowing_div_primitive!(u128); +impl_narrowing_div_recurse!(u256); + +/// Implement `u3N / u2N`-division on top of `u2N / uN`-division. +/// +/// Returns the quotient and remainder of `(a * R + a0) / n`, +/// where `R = (1 << U::BITS)` is the digit size. +/// +/// # Safety +/// Requires that `n.leading_zeros() == 0` and `a < n`. +unsafe fn div_three_digits_by_two(a0: U, a: U::D, n: U::D) -> (U, U::D) +where + U: HInt, + U::D: Int + NarrowingDiv, +{ + if n.leading_zeros() > 0 || a >= n { + unsafe { core::hint::unreachable_unchecked() } + } + + // n = n1R + n0 + let (n0, n1) = n.lo_hi(); + // a = a2R + a1 + let (a1, a2) = a.lo_hi(); + + let mut q; + let mut r; + let mut wrap; + // `a < n` is guaranteed by the caller, but `a2 == n1 && a1 < n0` is possible + if let Some((q0, r1)) = a.checked_narrowing_div_rem(n1) { + q = q0; + // a = qn1 + r1, where 0 <= r1 < n1 + + // Include the remainder with the low bits: + // r = a0 + r1R + r = U::D::from_lo_hi(a0, r1); + + // Subtract the contribution of the divisor low bits with the estimated quotient + let d = q.widen_mul(n0); + (r, wrap) = r.overflowing_sub(d); + + // Since `q` is the quotient of dividing with a slightly smaller divisor, + // it may be an overapproximation, but is never too small, and similarly, + // `r` is now either the correct remainder ... + if !wrap { + return (q, r); + } + // ... or the remainder went "negative" (by as much as `d = qn0 < RR`) + // and we have to adjust. + q -= U::ONE; + } else { + debug_assert!(a2 == n1 && a1 < n0); + // Otherwise, `a2 == n1`, and the estimated quotient would be + // `R + (a1 % n1)`, but the correct quotient can't overflow. + // We'll start from `q = R = (1 << U::BITS)`, + // so `r = aR + a0 - qn = (a - n)R + a0` + r = U::D::from_lo_hi(a0, a1.wrapping_sub(n0)); + // Since `a < n`, the first decrement is always needed: + q = U::MAX; /* R - 1 */ + } + + (r, wrap) = r.overflowing_add(n); + if wrap { + return (q, r); + } + + // If the remainder still didn't wrap, we need another step. + q -= U::ONE; + (r, wrap) = r.overflowing_add(n); + // Since `n >= RR/2`, at least one of the two `r += n` must have wrapped. + debug_assert!(wrap, "estimated quotient should be off by at most two"); + (q, r) +} + +#[cfg(test)] +mod test { + use super::{HInt, NarrowingDiv}; + + #[test] + fn inverse_mul() { + for x in 0..=u8::MAX { + for y in 1..=u8::MAX { + let xy = x.widen_mul(y); + assert_eq!(xy.checked_narrowing_div_rem(y), Some((x, 0))); + assert_eq!( + (xy + (y - 1) as u16).checked_narrowing_div_rem(y), + Some((x, y - 1)) + ); + if y > 1 { + assert_eq!((xy + 1).checked_narrowing_div_rem(y), Some((x, 1))); + assert_eq!( + (xy + (y - 2) as u16).checked_narrowing_div_rem(y), + Some((x, y - 2)) + ); + } + } + } + } +} diff --git a/library/compiler-builtins/libm/src/math/support/mod.rs b/library/compiler-builtins/libm/src/math/support/mod.rs index b2d7bd8d55672..15ab010dc8d5f 100644 --- a/library/compiler-builtins/libm/src/math/support/mod.rs +++ b/library/compiler-builtins/libm/src/math/support/mod.rs @@ -8,6 +8,7 @@ pub(crate) mod feature_detect; mod float_traits; pub mod hex_float; mod int_traits; +mod modular; #[allow(unused_imports)] pub use big::{i256, u256}; @@ -28,7 +29,8 @@ pub use hex_float::hf16; pub use hex_float::hf128; #[allow(unused_imports)] pub use hex_float::{hf32, hf64}; -pub use int_traits::{CastFrom, CastInto, DInt, HInt, Int, MinInt}; +pub use int_traits::{CastFrom, CastInto, DInt, HInt, Int, MinInt, NarrowingDiv}; +pub use modular::linear_mul_reduction; /// Hint to the compiler that the current path is cold. pub fn cold_path() { diff --git a/library/compiler-builtins/libm/src/math/support/modular.rs b/library/compiler-builtins/libm/src/math/support/modular.rs new file mode 100644 index 0000000000000..cc0edf2f2bc04 --- /dev/null +++ b/library/compiler-builtins/libm/src/math/support/modular.rs @@ -0,0 +1,304 @@ +/* SPDX-License-Identifier: MIT OR Apache-2.0 */ + +//! This module provides accelerated modular multiplication by large powers +//! of two, which is needed for computing floating point remainders in `fmod` +//! and similar functions. +//! +//! To keep the equations somewhat concise, the following conventions are used: +//! - all integer operations are in the mathematical sense, without overflow +//! - concatenation means multiplication: `2xq = 2 * x * q` +//! - `R = (1 << U::BITS)` is the modulus of wrapping arithmetic in `U` + +use crate::support::int_traits::NarrowingDiv; +use crate::support::{DInt, HInt, Int}; + +/// Compute the remainder `(x << e) % y` with unbounded integers. +/// Requires `x < 2y` and `y.leading_zeros() >= 2` +pub fn linear_mul_reduction(x: U, mut e: u32, mut y: U) -> U +where + U: HInt + Int, + U::D: NarrowingDiv, +{ + assert!(y <= U::MAX >> 2); + assert!(x < (y << 1)); + let _0 = U::ZERO; + let _1 = U::ONE; + + // power of two divisors + if (y & (y - _1)).is_zero() { + if e < U::BITS { + // shift and only keep low bits + return (x << e) & (y - _1); + } else { + // would shift out all the bits + return _0; + } + } + + // Use the identity `(x << e) % y == ((x << (e + s)) % (y << s)) >> s` + // to shift the divisor so it has exactly two leading zeros to satisfy + // the precondition of `Reducer::new` + let s = y.leading_zeros() - 2; + e += s; + y <<= s; + + // `m: Reducer` keeps track of the remainder `x` in a form that makes it + // very efficient to do `x <<= k` modulo `y` for integers `k < U::BITS` + let mut m = Reducer::new(x, y); + + // Use the faster special case with constant `k == U::BITS - 1` while we can + while e >= U::BITS - 1 { + m.word_reduce(); + e -= U::BITS - 1; + } + // Finish with the variable shift operation + m.shift_reduce(e); + + // The partial remainder is in `[0, 2y)` ... + let r = m.partial_remainder(); + // ... so check and correct, and compensate for the earlier shift. + r.checked_sub(y).unwrap_or(r) >> s +} + +/// Helper type for computing the reductions. The implementation has a number +/// of seemingly weird choices, but everything is aimed at streamlining +/// `Reducer::word_reduce` into its current form. +/// +/// Implicitly contains: +/// n in (R/8, R/4) +/// x in [0, 2n) +/// The value of `n` is fixed for a given `Reducer`, +/// but the value of `x` is modified by the methods. +#[derive(Debug, Clone, PartialEq, Eq)] +struct Reducer { + // m = 2n + m: U, + // q = (RR/2) / m + // r = (RR/2) % m + // Then RR/2 = qm + r, where `0 <= r < m` + // The value `q` is only needed during construction, so isn't saved. + r: U, + // The value `x` is implicitly stored as `2 * q * x`: + _2xq: U::D, +} + +impl Reducer +where + U: HInt, + U: Int, +{ + /// Construct a reducer for `(x << _) mod n`. + /// + /// Requires `R/8 < n < R/4` and `x < 2n`. + fn new(x: U, n: U) -> Self + where + U::D: NarrowingDiv, + { + let _1 = U::ONE; + assert!(n > (_1 << (U::BITS - 3))); + assert!(n < (_1 << (U::BITS - 2))); + let m = n << 1; + assert!(x < m); + + // We need to compute the parameters + // `q = (RR/2) / m` + // `r = (RR/2) % m` + + // Since `m` is in `(R/4, R/2)`, the quotient `q` is in `[R, 2R)`, and + // it would overflow in `U` if computed directly. Instead, we compute + // `f = q - R`, which is in `[0, R)`. To do so, we simply subtract `Rm` + // from the dividend, which doesn't change the remainder: + // `f = R(R/2 - m) / m` + // `r = R(R/2 - m) % m` + let dividend = ((_1 << (U::BITS - 1)) - m).widen_hi(); + let (f, r) = dividend.checked_narrowing_div_rem(m).unwrap(); + + // As `x < m`, `xq < qm <= RR/2` + // Thus `2xq = 2xR + 2xf` does not overflow in `U::D`. + let _2x = x + x; + let _2xq = _2x.widen_hi() + _2x.widen_mul(f); + Self { m, r, _2xq } + } + + /// Extract the current remainder `x` in the range `[0, 2n)` + fn partial_remainder(&self) -> U { + // `RR/2 = qm + r`, where `0 <= r < m` + // `2xq = uR + v`, where `0 <= v < R` + + // The goal is to extract the current value of `x` from the value `2xq` + // that we actually have. A bit simplified, we could multiply it by `m` + // to obtain `2xqm == 2x(RR/2 - r) == xRR - 2xr`, where `2xr < RR`. + // We could just round that up to the next multiple of `RR` to get `x`, + // but we can avoid having to multiply the full double-wide `2xq` by + // making a couple of adjustments: + + // First, let's only use the high half `u` for the product, and + // include an additional error term due to the truncation: + // `mu = xR - (2xr + mv)/R` + + // Next, show bounds for the error term + // `0 <= mv < mR` follows from `0 <= v < R` + // `0 <= 2xr < mR` follows from `0 <= x < m < R/2` and `0 <= r < m` + // Adding those together, we have: + // `0 <= (mv + 2xr)/R < 2m` + // Which also implies: + // `0 < 2m - (mv + 2xr)/R <= 2m < R` + + // For that reason, we can use `u + 2` as the factor to obtain + // `m(u + 2) = xR + (2m - (mv + 2xr)/R)` + // By the previous inequality, the second term fits neatly in the lower + // half, so we get exactly `x` as the high half. + let u = self._2xq.hi(); + let _2 = U::ONE + U::ONE; + self.m.widen_mul(u + _2).hi() + + // Additionally, we should ensure that `u + 2` cannot overflow: + // Since `x < m` and `2qm <= RR`, + // `2xq <= 2q(m-1) <= RR - 2q` + // As we also have `q > R`, + // `2xq < RR - 2R` + // which is sufficient. + } + + /// Replace the remainder `x` with `(x << k) - un`, + /// for a suitable quotient `u`, which is returned. + /// + /// Requires that `k < U::BITS`. + fn shift_reduce(&mut self, k: u32) -> U { + assert!(k < U::BITS); + + // First, split the shifted value: + // `2xq << k = aRR/2 + b`, where `0 <= b < RR/2` + let a = self._2xq.hi() >> (U::BITS - 1 - k); + let (low, high) = (self._2xq << k).lo_hi(); + let b = U::D::from_lo_hi(low, high & (U::MAX >> 1)); + + // Then, subtract `2anq = aqm`: + // ``` + // (2xq << k) - aqm + // = aRR/2 + b - aqm + // = a(RR/2 - qm) + b + // = ar + b + // ``` + self._2xq = a.widen_mul(self.r) + b; + a + + // Since `a` is at most the high half of `2xq`, we have + // `a + 2 < R` (shown above, in `partial_remainder`) + // Using that together with `b < RR/2` and `r < m < R/2`, + // we get `(a + 2)r + b < RR`, so + // `ar + b < RR - 2r = 2mq` + // which shows that the new remainder still satisfies `x < m`. + } + + // NB: `word_reduce()` is just the special case `shift_reduce(U::BITS - 1)` + // that optimizes especially well. The correspondence is that `a == u` and + // `b == (v >> 1).widen_hi()` + // + /// Replace the remainder `x` with `x(R/2) - un`, + /// for a suitable quotient `u`, which is returned. + fn word_reduce(&mut self) -> U { + // To do so, we replace `2xq = uR + v` with + // ``` + // 2 * (x(R/2) - un) * q + // = xqR - 2unq + // = xqR - uqm + // = uRR/2 + vR/2 - uRR/2 + ur + // = ur + (v/2)R + // ``` + let (v, u) = self._2xq.lo_hi(); + self._2xq = u.widen_mul(self.r) + U::widen_hi(v >> 1); + u + + // Additional notes: + // 1. As `v` is the low bits of `2xq`, it is even and can be halved. + // 2. The new remainder is `(xr + mv/2) / R` (see below) + // and since `v < R`, `r < m`, `x < m < R/2`, + // that is also strictly less than `m`. + // ``` + // (x(R/2) - un)R + // = xRR/2 - (m/2)uR + // = x(qm + r) - (m/2)(2xq - v) + // = xqm + xr - xqm + mv/2 + // = xr + mv/2 + // ``` + } +} + +#[cfg(test)] +mod test { + use crate::support::linear_mul_reduction; + use crate::support::modular::Reducer; + + #[test] + fn reducer_ops() { + for n in 33..=63_u8 { + for x in 0..2 * n { + let temp = Reducer::new(x, n); + let n = n as u32; + let x0 = temp.partial_remainder() as u32; + assert_eq!(x as u32, x0); + for k in 0..=7 { + let mut red = temp.clone(); + let u = red.shift_reduce(k) as u32; + let x1 = red.partial_remainder() as u32; + assert_eq!(x1, (x0 << k) - u * n); + assert!(x1 < 2 * n); + assert!((red._2xq as u32).is_multiple_of(2 * x1)); + + // `word_reduce` is equivalent to + // `shift_reduce(U::BITS - 1)` + if k == 7 { + let mut alt = temp.clone(); + let w = alt.word_reduce(); + assert_eq!(u, w as u32); + assert_eq!(alt, red); + } + } + } + } + } + #[test] + fn reduction_u8() { + for y in 1..64u8 { + for x in 0..2 * y { + let mut r = x % y; + for e in 0..100 { + assert_eq!(r, linear_mul_reduction(x, e, y)); + // maintain the correct expected remainder + r <<= 1; + if r >= y { + r -= y; + } + } + } + } + } + #[test] + fn reduction_u128() { + assert_eq!( + linear_mul_reduction::(17, 100, 123456789), + (17 << 100) % 123456789 + ); + + // power-of-two divisor + assert_eq!( + linear_mul_reduction(0xdead_beef, 100, 1_u128 << 116), + 0xbeef << 100 + ); + + let x = 10_u128.pow(37); + let y = 11_u128.pow(36); + assert!(x < y); + let mut r = x; + for e in 0..1000 { + assert_eq!(r, linear_mul_reduction(x, e, y)); + // maintain the correct expected remainder + r <<= 1; + if r >= y { + r -= y; + } + assert!(r != 0); + } + } +} diff --git a/library/compiler-builtins/rust-version b/library/compiler-builtins/rust-version index 8489eacfcda28..6a2835bc2d9eb 100644 --- a/library/compiler-builtins/rust-version +++ b/library/compiler-builtins/rust-version @@ -1 +1 @@ -d36f964125163c2e698de5559efefb8217b8b7f0 +23d01cd2412583491621ab1ca4f1b01e37d11e39 diff --git a/library/compiler-builtins/triagebot.toml b/library/compiler-builtins/triagebot.toml index eba5cdd88b941..b210a5fb52563 100644 --- a/library/compiler-builtins/triagebot.toml +++ b/library/compiler-builtins/triagebot.toml @@ -12,10 +12,11 @@ exclude_titles = ["Rustc pull update"] [issue-links] check-commits = false -# Prevents mentions in commits to avoid users being spammed -# Documentation at: https://forge.rust-lang.org/triagebot/no-mentions.html -[no-mentions] - # Enable issue transfers within the org # Documentation at: https://forge.rust-lang.org/triagebot/transfer.html [transfer] + +# Enable comments linking to triagebot range-diff when a PR is rebased +# onto a different base commit +# Documentation at: https://forge.rust-lang.org/triagebot/range-diff.html +[range-diff] diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index e2413b619f9fa..9b80e3b70fa2f 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -115,6 +115,31 @@ use crate::{cmp, ptr}; /// Whether allocations happen or not is not part of the program behavior, even if it /// could be detected via an allocator that tracks allocations by printing or otherwise /// having side effects. +/// +/// # Re-entrance +/// +/// When implementing a global allocator, one has to be careful not to create an infinitely recursive +/// implementation by accident, as many constructs in the Rust standard library may allocate in +/// their implementation. For example, on some platforms, [`std::sync::Mutex`] may allocate, so using +/// it is highly problematic in a global allocator. +/// +/// For this reason, one should generally stick to library features available through +/// [`core`], and avoid using [`std`] in a global allocator. A few features from [`std`] are +/// guaranteed to not use `#[global_allocator]` to allocate: +/// +/// - [`std::thread_local`], +/// - [`std::thread::current`], +/// - [`std::thread::park`] and [`std::thread::Thread`]'s [`unpark`] method and +/// [`Clone`] implementation. +/// +/// [`std`]: ../../std/index.html +/// [`std::sync::Mutex`]: ../../std/sync/struct.Mutex.html +/// [`std::thread_local`]: ../../std/macro.thread_local.html +/// [`std::thread::current`]: ../../std/thread/fn.current.html +/// [`std::thread::park`]: ../../std/thread/fn.park.html +/// [`std::thread::Thread`]: ../../std/thread/struct.Thread.html +/// [`unpark`]: ../../std/thread/struct.Thread.html#method.unpark + #[stable(feature = "global_alloc", since = "1.28.0")] pub unsafe trait GlobalAlloc { /// Allocates memory as described by the given `layout`. diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index c194f04d384f6..67f917345894c 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -80,16 +80,18 @@ impl Layout { } } + #[inline] const fn is_size_align_valid(size: usize, align: usize) -> bool { - let Some(align) = Alignment::new(align) else { return false }; - if size > Self::max_size_for_align(align) { - return false; - } - true + let Some(alignment) = Alignment::new(align) else { return false }; + Self::is_size_alignment_valid(size, alignment) + } + + const fn is_size_alignment_valid(size: usize, alignment: Alignment) -> bool { + size <= Self::max_size_for_alignment(alignment) } #[inline(always)] - const fn max_size_for_align(align: Alignment) -> usize { + const fn max_size_for_alignment(alignment: Alignment) -> usize { // (power-of-two implies align != 0.) // Rounded up size is: @@ -107,18 +109,28 @@ impl Layout { // SAFETY: the maximum possible alignment is `isize::MAX + 1`, // so the subtraction cannot overflow. - unsafe { unchecked_sub(isize::MAX as usize + 1, align.as_usize()) } + unsafe { unchecked_sub(isize::MAX as usize + 1, alignment.as_usize()) } } - /// Internal helper constructor to skip revalidating alignment validity. + /// Constructs a `Layout` from a given `size` and `alignment`, + /// or returns `LayoutError` if any of the following conditions + /// are not met: + /// + /// * `size`, when rounded up to the nearest multiple of `alignment`, + /// must not overflow `isize` (i.e., the rounded value must be + /// less than or equal to `isize::MAX`). + #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] - const fn from_size_alignment(size: usize, align: Alignment) -> Result { - if size > Self::max_size_for_align(align) { - return Err(LayoutError); + pub const fn from_size_alignment( + size: usize, + alignment: Alignment, + ) -> Result { + if Layout::is_size_alignment_valid(size, alignment) { + // SAFETY: Layout::size invariants checked above. + Ok(Layout { size, align: alignment }) + } else { + Err(LayoutError) } - - // SAFETY: Layout::size invariants checked above. - Ok(Layout { size, align }) } /// Creates a layout, bypassing all checks. @@ -150,6 +162,30 @@ impl Layout { unsafe { Layout { size, align: mem::transmute(align) } } } + /// Creates a layout, bypassing all checks. + /// + /// # Safety + /// + /// This function is unsafe as it does not verify the preconditions from + /// [`Layout::from_size_alignment`]. + #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[must_use] + #[inline] + #[track_caller] + pub const unsafe fn from_size_alignment_unchecked(size: usize, alignment: Alignment) -> Self { + assert_unsafe_precondition!( + check_library_ub, + "Layout::from_size_alignment_unchecked requires \ + that the rounded-up allocation size does not exceed isize::MAX", + ( + size: usize = size, + alignment: Alignment = alignment, + ) => Layout::is_size_alignment_valid(size, alignment) + ); + // SAFETY: the caller is required to uphold the preconditions. + Layout { size, align: alignment } + } + /// The minimum size in bytes for a memory block of this layout. #[stable(feature = "alloc_layout", since = "1.28.0")] #[rustc_const_stable(feature = "const_alloc_layout_size_align", since = "1.50.0")] @@ -171,6 +207,16 @@ impl Layout { self.align.as_usize() } + /// The minimum byte alignment for a memory block of this layout. + /// + /// The returned alignment is guaranteed to be a power of two. + #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[must_use = "this returns the minimum alignment, without modifying the layout"] + #[inline] + pub const fn alignment(&self) -> Alignment { + self.align + } + /// Constructs a `Layout` suitable for holding a value of type `T`. #[stable(feature = "alloc_layout", since = "1.28.0")] #[rustc_const_stable(feature = "alloc_layout_const_new", since = "1.42.0")] @@ -192,9 +238,9 @@ impl Layout { #[requires(mem::align_of_val(t).is_power_of_two())] #[ensures(|result| result.align() == mem::align_of_val(t))] pub const fn for_value(t: &T) -> Self { - let (size, align) = (size_of_val(t), align_of_val(t)); + let (size, alignment) = (size_of_val(t), Alignment::of_val(t)); // SAFETY: see rationale in `new` for why this is using the unsafe variant - unsafe { Layout::from_size_align_unchecked(size, align) } + unsafe { Layout::from_size_alignment_unchecked(size, alignment) } } /// Produces layout describing a record that could be used to @@ -226,15 +272,16 @@ impl Layout { /// [extern type]: ../../unstable-book/language-features/extern-types.html #[unstable(feature = "layout_for_ptr", issue = "69835")] #[must_use] + #[inline] // TODO: we should try to capture the above constraints on T in a `requires` clause, and the // metadata helpers from https://github.com/model-checking/verify-rust-std/pull/37 may be able // to accomplish this. #[ensures(|result| result.align().is_power_of_two())] pub const unsafe fn for_value_raw(t: *const T) -> Self { // SAFETY: we pass along the prerequisites of these functions to the caller - let (size, align) = unsafe { (mem::size_of_val_raw(t), mem::align_of_val_raw(t)) }; + let (size, alignment) = unsafe { (mem::size_of_val_raw(t), Alignment::of_val_raw(t)) }; // SAFETY: see rationale in `new` for why this is using the unsafe variant - unsafe { Layout::from_size_align_unchecked(size, align) } + unsafe { Layout::from_size_alignment_unchecked(size, alignment) } } /// Creates a `NonNull` that is dangling, but well-aligned for this Layout. @@ -243,11 +290,12 @@ impl Layout { /// be that of a valid pointer, which means this must not be used /// as a "not yet initialized" sentinel value. /// Types that lazily allocate must track initialization by some other means. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] #[ensures(|result| result.is_aligned())] - pub const fn dangling(&self) -> NonNull { + pub const fn dangling_ptr(&self) -> NonNull { NonNull::without_provenance(self.align.as_nonzero()) } @@ -271,38 +319,52 @@ impl Layout { #[ensures(|result| result.is_err() || result.as_ref().unwrap().align() >= align)] #[ensures(|result| result.is_err() || result.as_ref().unwrap().align().is_power_of_two())] pub const fn align_to(&self, align: usize) -> Result { - if let Some(align) = Alignment::new(align) { - Layout::from_size_alignment(self.size, Alignment::max(self.align, align)) + if let Some(alignment) = Alignment::new(align) { + self.adjust_alignment_to(alignment) } else { Err(LayoutError) } } + /// Creates a layout describing the record that can hold a value + /// of the same layout as `self`, but that also is aligned to + /// alignment `alignment`. + /// + /// If `self` already meets the prescribed alignment, then returns + /// `self`. + /// + /// Note that this method does not add any padding to the overall + /// size, regardless of whether the returned layout has a different + /// alignment. In other words, if `K` has size 16, `K.align_to(32)` + /// will *still* have size 16. + /// + /// Returns an error if the combination of `self.size()` and the given + /// `alignment` violates the conditions listed in [`Layout::from_size_alignment`]. + #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[inline] + pub const fn adjust_alignment_to(&self, alignment: Alignment) -> Result { + Layout::from_size_alignment(self.size, Alignment::max(self.align, alignment)) + } + /// Returns the amount of padding we must insert after `self` - /// to ensure that the following address will satisfy `align` - /// (measured in bytes). + /// to ensure that the following address will satisfy `alignment`. /// - /// e.g., if `self.size()` is 9, then `self.padding_needed_for(4)` + /// e.g., if `self.size()` is 9, then `self.padding_needed_for(alignment4)` + /// (where `alignment4.as_usize() == 4`) /// returns 3, because that is the minimum number of bytes of /// padding required to get a 4-aligned address (assuming that the /// corresponding memory block starts at a 4-aligned address). /// - /// The return value of this function has no meaning if `align` is - /// not a power-of-two. - /// - /// Note that the utility of the returned value requires `align` + /// Note that the utility of the returned value requires `alignment` /// to be less than or equal to the alignment of the starting /// address for the whole allocated block of memory. One way to - /// satisfy this constraint is to ensure `align <= self.align()`. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] - #[must_use = "this returns the padding needed, \ - without modifying the `Layout`"] + /// satisfy this constraint is to ensure `alignment.as_usize() <= self.align()`. + #[unstable(feature = "ptr_alignment_type", issue = "102070")] + #[must_use = "this returns the padding needed, without modifying the `Layout`"] #[inline] - #[ensures(|result| *result <= align)] - pub const fn padding_needed_for(&self, align: usize) -> usize { - // FIXME: Can we just change the type on this to `Alignment`? - let Some(align) = Alignment::new(align) else { return usize::MAX }; - let len_rounded_up = self.size_rounded_up_to_custom_align(align); + #[ensures(|result| *result <= alignment.as_usize())] + pub const fn padding_needed_for(&self, alignment: Alignment) -> usize { + let len_rounded_up = self.size_rounded_up_to_custom_alignment(alignment); // SAFETY: Cannot overflow because the rounded-up value is never less unsafe { unchecked_sub(len_rounded_up, self.size) } } @@ -312,7 +374,7 @@ impl Layout { /// This can return at most `Alignment::MAX` (aka `isize::MAX + 1`) /// because the original size is at most `isize::MAX`. #[inline] - const fn size_rounded_up_to_custom_align(&self, align: Alignment) -> usize { + const fn size_rounded_up_to_custom_alignment(&self, alignment: Alignment) -> usize { // SAFETY: // Rounded up value is: // size_rounded_up = (size + align - 1) & !(align - 1); @@ -332,7 +394,7 @@ impl Layout { // (Size 0 Align MAX is already aligned, so stays the same, but things like // Size 1 Align MAX or Size isize::MAX Align 2 round up to `isize::MAX + 1`.) unsafe { - let align_m1 = unchecked_sub(align.as_usize(), 1); + let align_m1 = unchecked_sub(alignment.as_usize(), 1); unchecked_add(self.size, align_m1) & !align_m1 } } @@ -350,16 +412,16 @@ impl Layout { #[ensures(|result| result.size() >= self.size())] #[ensures(|result| result.align() == self.align())] #[ensures(|result| result.size() % result.align() == 0)] - #[ensures(|result| self.size() + self.padding_needed_for(self.align()) == result.size())] + #[ensures(|result| self.size() + self.padding_needed_for(self.alignment()) == result.size())] pub const fn pad_to_align(&self) -> Layout { // This cannot overflow. Quoting from the invariant of Layout: // > `size`, when rounded up to the nearest multiple of `align`, // > must not overflow isize (i.e., the rounded value must be // > less than or equal to `isize::MAX`) - let new_size = self.size_rounded_up_to_custom_align(self.align); + let new_size = self.size_rounded_up_to_custom_alignment(self.align); // SAFETY: padded size is guaranteed to not exceed `isize::MAX`. - unsafe { Layout::from_size_align_unchecked(new_size, self.align()) } + unsafe { Layout::from_size_alignment_unchecked(new_size, self.alignment()) } } /// Creates a layout describing the record for `n` instances of @@ -369,6 +431,8 @@ impl Layout { /// layout of the array and `offs` is the distance between the start /// of each element in the array. /// + /// Does not include padding after the trailing element. + /// /// (That distance between elements is sometimes known as "stride".) /// /// On arithmetic overflow, returns `LayoutError`. @@ -376,7 +440,6 @@ impl Layout { /// # Examples /// /// ``` - /// #![feature(alloc_layout_extra)] /// use std::alloc::Layout; /// /// // All rust types have a size that's a multiple of their alignment. @@ -387,31 +450,48 @@ impl Layout { /// // But you can manually make layouts which don't meet that rule. /// let padding_needed = Layout::from_size_align(6, 4).unwrap(); /// let repeated = padding_needed.repeat(3).unwrap(); - /// assert_eq!(repeated, (Layout::from_size_align(24, 4).unwrap(), 8)); + /// assert_eq!(repeated, (Layout::from_size_align(22, 4).unwrap(), 8)); + /// + /// // Repeating an element zero times has zero size, but keeps the alignment (like `[T; 0]`) + /// let repeated = normal.repeat(0).unwrap(); + /// assert_eq!(repeated, (Layout::from_size_align(0, 4).unwrap(), 12)); + /// let repeated = padding_needed.repeat(0).unwrap(); + /// assert_eq!(repeated, (Layout::from_size_align(0, 4).unwrap(), 8)); /// ``` - #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] #[inline] - // for Kani (v0.54.0), the below modulo operation is too costly to prove (running into the - // 6-hours timeout on GitHub); we use a weaker postcondition instead + // Since rust-lang/rust#148769, the result does not include padding after the trailing + // element, i.e., on success the resulting size is (n - 1) * stride + self.size() for n > 0 + // (and 0 for n == 0), where stride is the returned offset. + // for Kani (v0.54.0), multiplication by a symbolic n is too costly to prove (running into + // the 6-hours timeout on GitHub); we use weaker postconditions instead #[cfg_attr(not(kani), - ensures(|result| result.is_err() || n == 0 || result.as_ref().unwrap().0.size() % n == 0))] + ensures(|result| result.is_err() || n == 0 || + result.as_ref().unwrap().0.size() == + (n - 1) * result.as_ref().unwrap().1 + self.size()))] #[cfg_attr(kani, ensures(|result| result.is_err() || n == 0 || result.as_ref().unwrap().0.size() >= self.size()))] - // for Kani (v0.54.0), the below multiplication is too costly to prove (running into the - // 6-hours timeout on GitHub); we use a weaker postcondition instead #[cfg_attr(not(kani), - ensures(|result| result.is_err() || - result.as_ref().unwrap().0.size() == n * result.as_ref().unwrap().1))] + ensures(|result| result.is_err() || n != 0 || result.as_ref().unwrap().0.size() == 0))] #[cfg_attr(kani, - ensures(|result| result.is_err() || n == 0 || + ensures(|result| result.is_err() || n <= 1 || result.as_ref().unwrap().0.size() >= result.as_ref().unwrap().1))] pub const fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> { + // FIXME(const-hack): the following could be way shorter with `?` let padded = self.pad_to_align(); - if let Ok(repeated) = padded.repeat_packed(n) { - Ok((repeated, padded.size())) + let Ok(result) = (if let Some(k) = n.checked_sub(1) { + let Ok(repeated) = padded.repeat_packed(k) else { + return Err(LayoutError); + }; + repeated.extend_packed(*self) } else { - Err(LayoutError) - } + debug_assert!(n == 0); + self.repeat_packed(0) + }) else { + return Err(LayoutError); + }; + Ok((result, padded.size())) } /// Creates a layout describing the record for `self` followed by @@ -467,8 +547,8 @@ impl Layout { #[ensures(|result| result.is_err() || result.as_ref().unwrap().1 >= self.size())] #[ensures(|result| result.is_err() || result.as_ref().unwrap().1 <= result.as_ref().unwrap().0.size())] pub const fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> { - let new_align = Alignment::max(self.align, next.align); - let offset = self.size_rounded_up_to_custom_align(next.align); + let new_alignment = Alignment::max(self.align, next.align); + let offset = self.size_rounded_up_to_custom_alignment(next.align); // SAFETY: `offset` is at most `isize::MAX + 1` (such as from aligning // to `Alignment::MAX`) and `next.size` is at most `isize::MAX` (from the @@ -476,7 +556,7 @@ impl Layout { // `isize::MAX + 1 + isize::MAX`, which is `usize::MAX`, and cannot overflow. let new_size = unsafe { unchecked_add(offset, next.size) }; - if let Ok(layout) = Layout::from_size_alignment(new_size, new_align) { + if let Ok(layout) = Layout::from_size_alignment(new_size, new_alignment) { Ok((layout, offset)) } else { Err(LayoutError) @@ -495,7 +575,8 @@ impl Layout { /// aligned. /// /// On arithmetic overflow, returns `LayoutError`. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] #[inline] // for Kani (v0.54.0), the below multiplication is too costly to prove (running into the // 6-hours timeout on GitHub); we use a weaker postcondition instead @@ -519,7 +600,8 @@ impl Layout { /// and is not incorporated *at all* into the resulting layout. /// /// On arithmetic overflow, returns `LayoutError`. - #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "alloc_layout_extra", since = "CURRENT_RUSTC_VERSION")] #[inline] #[ensures(|result| result.is_err() || result.as_ref().unwrap().size() == self.size() + next.size())] #[ensures(|result| result.is_err() || result.as_ref().unwrap().align() == self.align())] @@ -546,7 +628,7 @@ impl Layout { #[inline] const fn inner(element_layout: Layout, n: usize) -> Result { - let Layout { size: element_size, align } = element_layout; + let Layout { size: element_size, align: alignment } = element_layout; // We need to check two things about the size: // - That the total size won't overflow a `usize`, and @@ -554,7 +636,7 @@ impl Layout { // By using division we can check them both with a single threshold. // That'd usually be a bad idea, but thankfully here the element size // and alignment are constants, so the compiler will fold all of it. - if element_size != 0 && n > Layout::max_size_for_align(align) / element_size { + if element_size != 0 && n > Layout::max_size_for_alignment(alignment) / element_size { return Err(LayoutError); } @@ -567,17 +649,9 @@ impl Layout { // SAFETY: We just checked above that the `array_size` will not // exceed `isize::MAX` even when rounded up to the alignment. // And `Alignment` guarantees it's a power of two. - unsafe { Ok(Layout::from_size_align_unchecked(array_size, align.as_usize())) } + unsafe { Ok(Layout::from_size_alignment_unchecked(array_size, alignment)) } } } - - /// Perma-unstable access to `align` as `Alignment` type. - #[unstable(issue = "none", feature = "std_internals")] - #[doc(hidden)] - #[inline] - pub const fn alignment(&self) -> Alignment { - self.align - } } #[stable(feature = "alloc_layout", since = "1.28.0")] @@ -672,11 +746,11 @@ mod verify { } } - // pub const fn dangling(&self) -> NonNull - #[kani::proof_for_contract(Layout::dangling)] + // pub const fn dangling_ptr(&self) -> NonNull + #[kani::proof_for_contract(Layout::dangling_ptr)] pub fn check_dangling() { let layout = kani::any::(); - let _ = layout.dangling(); + let _ = layout.dangling_ptr(); } // pub fn align_to(&self, align: usize) -> Result @@ -687,12 +761,12 @@ mod verify { let _ = layout.align_to(a2); } - // pub const fn padding_needed_for(&self, align: usize) -> usize + // pub const fn padding_needed_for(&self, alignment: Alignment) -> usize #[kani::proof_for_contract(Layout::padding_needed_for)] pub fn check_padding_needed_for() { let layout = kani::any::(); - let a2 = kani::any::(); - if (a2.is_power_of_two() && a2 <= layout.align()) { + let a2 = kani::any::(); + if (a2.as_usize() <= layout.align()) { let _ = layout.padding_needed_for(a2); } } diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index 9d608d5e83c40..959407b753487 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -102,7 +102,8 @@ impl fmt::Display for AllocError { /// /// [*currently allocated*]: #currently-allocated-memory #[unstable(feature = "allocator_api", issue = "32838")] -pub unsafe trait Allocator { +#[rustc_const_unstable(feature = "const_heap", issue = "79597")] +pub const unsafe trait Allocator { /// Attempts to allocate a block of memory. /// /// On success, returns a [`NonNull<[u8]>`][NonNull] meeting the size and alignment guarantees of `layout`. @@ -368,7 +369,63 @@ pub unsafe trait Allocator { } #[unstable(feature = "allocator_api", issue = "32838")] -unsafe impl Allocator for &A +#[rustc_const_unstable(feature = "const_heap", issue = "79597")] +unsafe impl const Allocator for &A +where + A: [const] Allocator + ?Sized, +{ + #[inline] + fn allocate(&self, layout: Layout) -> Result, AllocError> { + (**self).allocate(layout) + } + + #[inline] + fn allocate_zeroed(&self, layout: Layout) -> Result, AllocError> { + (**self).allocate_zeroed(layout) + } + + #[inline] + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).deallocate(ptr, layout) } + } + + #[inline] + unsafe fn grow( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).grow(ptr, old_layout, new_layout) } + } + + #[inline] + unsafe fn grow_zeroed( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) } + } + + #[inline] + unsafe fn shrink( + &self, + ptr: NonNull, + old_layout: Layout, + new_layout: Layout, + ) -> Result, AllocError> { + // SAFETY: the safety contract must be upheld by the caller + unsafe { (**self).shrink(ptr, old_layout, new_layout) } + } +} + +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl Allocator for &mut A where A: Allocator + ?Sized, { diff --git a/library/core/src/any.rs b/library/core/src/any.rs index ff55793340bd0..42f332f7d8ba8 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -86,7 +86,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::{fmt, hash, intrinsics}; +use crate::{fmt, hash, intrinsics, ptr}; /////////////////////////////////////////////////////////////////////////////// // Any trait @@ -906,3 +906,109 @@ pub const fn type_name() -> &'static str { pub const fn type_name_of_val(_val: &T) -> &'static str { type_name::() } + +/// Returns `Some(&U)` if `T` can be coerced to the trait object type `U`. Otherwise, it returns `None`. +/// +/// # Compile-time failures +/// Determining whether `T` can be coerced to the trait object type `U` requires compiler trait resolution. +/// In some cases, that resolution can exceed the recursion limit, +/// and compilation will fail instead of this function returning `None`. +/// # Examples +/// +/// ```rust +/// #![feature(try_as_dyn)] +/// +/// use core::any::try_as_dyn; +/// +/// trait Animal { +/// fn speak(&self) -> &'static str; +/// } +/// +/// struct Dog; +/// impl Animal for Dog { +/// fn speak(&self) -> &'static str { "woof" } +/// } +/// +/// struct Rock; // does not implement Animal +/// +/// let dog = Dog; +/// let rock = Rock; +/// +/// let as_animal: Option<&dyn Animal> = try_as_dyn::(&dog); +/// assert_eq!(as_animal.unwrap().speak(), "woof"); +/// +/// let not_an_animal: Option<&dyn Animal> = try_as_dyn::(&rock); +/// assert!(not_an_animal.is_none()); +/// ``` +#[must_use] +#[unstable(feature = "try_as_dyn", issue = "144361")] +pub const fn try_as_dyn< + T: Any + 'static, + U: ptr::Pointee> + ?Sized + 'static, +>( + t: &T, +) -> Option<&U> { + let vtable: Option> = const { intrinsics::vtable_for::() }; + match vtable { + Some(dyn_metadata) => { + let pointer = ptr::from_raw_parts(t, dyn_metadata); + // SAFETY: `t` is a reference to a type, so we know it is valid. + // `dyn_metadata` is a vtable for T, implementing the trait of `U`. + Some(unsafe { &*pointer }) + } + None => None, + } +} + +/// Returns `Some(&mut U)` if `T` can be coerced to the trait object type `U`. Otherwise, it returns `None`. +/// +/// # Compile-time failures +/// Determining whether `T` can be coerced to the trait object type `U` requires compiler trait resolution. +/// In some cases, that resolution can exceed the recursion limit, +/// and compilation will fail instead of this function returning `None`. +/// # Examples +/// +/// ```rust +/// #![feature(try_as_dyn)] +/// +/// use core::any::try_as_dyn_mut; +/// +/// trait Animal { +/// fn speak(&self) -> &'static str; +/// } +/// +/// struct Dog; +/// impl Animal for Dog { +/// fn speak(&self) -> &'static str { "woof" } +/// } +/// +/// struct Rock; // does not implement Animal +/// +/// let mut dog = Dog; +/// let mut rock = Rock; +/// +/// let as_animal: Option<&mut dyn Animal> = try_as_dyn_mut::(&mut dog); +/// assert_eq!(as_animal.unwrap().speak(), "woof"); +/// +/// let not_an_animal: Option<&mut dyn Animal> = try_as_dyn_mut::(&mut rock); +/// assert!(not_an_animal.is_none()); +/// ``` +#[must_use] +#[unstable(feature = "try_as_dyn", issue = "144361")] +pub const fn try_as_dyn_mut< + T: Any + 'static, + U: ptr::Pointee> + ?Sized + 'static, +>( + t: &mut T, +) -> Option<&mut U> { + let vtable: Option> = const { intrinsics::vtable_for::() }; + match vtable { + Some(dyn_metadata) => { + let pointer = ptr::from_raw_parts_mut(t, dyn_metadata); + // SAFETY: `t` is a reference to a type, so we know it is valid. + // `dyn_metadata` is a vtable for T, implementing the trait of `U`. + Some(unsafe { &mut *pointer }) + } + None => None, + } +} diff --git a/library/core/src/array/drain.rs b/library/core/src/array/drain.rs index 6545ebebed425..0a497d3943ea6 100644 --- a/library/core/src/array/drain.rs +++ b/library/core/src/array/drain.rs @@ -3,79 +3,111 @@ use safety::requires; use crate::iter::{TrustedLen, UncheckedIterator}; #[cfg(kani)] use crate::kani; -use crate::mem::ManuallyDrop; -use crate::ptr::drop_in_place; -use crate::slice; +use crate::marker::{Destruct, PhantomData}; +use crate::mem::{ManuallyDrop, SizedTypeProperties, conjure_zst}; +use crate::ptr::{NonNull, drop_in_place, from_raw_parts_mut, null_mut}; -/// A situationally-optimized version of `array.into_iter().for_each(func)`. -/// -/// [`crate::array::IntoIter`]s are great when you need an owned iterator, but -/// storing the entire array *inside* the iterator like that can sometimes -/// pessimize code. Notable, it can be more bytes than you really want to move -/// around, and because the array accesses index into it SRoA has a harder time -/// optimizing away the type than it does iterators that just hold a couple pointers. -/// -/// Thus this function exists, which gives a way to get *moved* access to the -/// elements of an array using a small iterator -- no bigger than a slice iterator. -/// -/// The function-taking-a-closure structure makes it safe, as it keeps callers -/// from looking at already-dropped elements. -pub(crate) fn drain_array_with( - array: [T; N], - func: impl for<'a> FnOnce(Drain<'a, T>) -> R, -) -> R { - let mut array = ManuallyDrop::new(array); - // SAFETY: Now that the local won't drop it, it's ok to construct the `Drain` which will. - let drain = Drain(array.iter_mut()); - func(drain) +impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> { + /// This function returns a function that lets you index the given array in const. + /// As implemented it can optimize better than iterators, and can be constified. + /// It acts like a sort of guard (owns the array) and iterator combined, which can be implemented + /// as it is a struct that implements const fn; + /// in that regard it is somewhat similar to an array::Iter implementing `UncheckedIterator`. + /// The only method you're really allowed to call is `next()`, + /// anything else is more or less UB, hence this function being unsafe. + /// Moved elements will not be dropped. + /// This will also not actually store the array. + /// + /// SAFETY: must only be called `N` times. Thou shalt not drop the array either. + // FIXME(const-hack): this is a hack for `let guard = Guard(array); |i| f(guard[i])`. + #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] + pub(super) const unsafe fn new(array: &'l mut ManuallyDrop<[T; N]>, f: &'f mut F) -> Self { + // dont drop the array, transfers "ownership" to Self + let ptr: NonNull = NonNull::from_mut(array).cast(); + // SAFETY: + // Adding `slice.len()` to the starting pointer gives a pointer + // at the end of `slice`. `end` will never be dereferenced, only checked + // for direct pointer equality with `ptr` to check if the drainer is done. + unsafe { + let end = if T::IS_ZST { null_mut() } else { ptr.as_ptr().add(N) }; + Self { ptr, end, f, l: PhantomData } + } + } } -/// See [`drain_array_with`] -- this is `pub(crate)` only so it's allowed to be -/// mentioned in the signature of that method. (Otherwise it hits `E0446`.) -// INVARIANT: It's ok to drop the remainder of the inner iterator. -pub(crate) struct Drain<'a, T>(slice::IterMut<'a, T>); +/// See [`Drain::new`]; this is our fake iterator. +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] +#[unstable(feature = "array_try_map", issue = "79711")] +pub(super) struct Drain<'l, 'f, T, const N: usize, F> { + // FIXME(const-hack): This is essentially a slice::IterMut<'static>, replace when possible. + /// The pointer to the next element to return, or the past-the-end location + /// if the drainer is empty. + /// + /// This address will be used for all ZST elements, never changed. + /// As we "own" this array, we dont need to store any lifetime. + ptr: NonNull, + /// For non-ZSTs, the non-null pointer to the past-the-end element. + /// For ZSTs, this is null. + end: *mut T, -impl Drop for Drain<'_, T> { - fn drop(&mut self) { - // SAFETY: By the type invariant, we're allowed to drop all these. - unsafe { drop_in_place(self.0.as_mut_slice()) } - } + f: &'f mut F, + l: PhantomData<&'l mut [T; N]>, } -impl Iterator for Drain<'_, T> { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - let p: *const T = self.0.next()?; - // SAFETY: The iterator was already advanced, so we won't drop this later. - Some(unsafe { p.read() }) - } +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] +#[unstable(feature = "array_try_map", issue = "79711")] +impl const FnOnce<(usize,)> for &mut Drain<'_, '_, T, N, F> +where + F: [const] FnMut(T) -> U, +{ + type Output = U; - #[inline] - fn size_hint(&self) -> (usize, Option) { - let n = self.len(); - (n, Some(n)) + /// This implementation is useless. + extern "rust-call" fn call_once(mut self, args: (usize,)) -> Self::Output { + self.call_mut(args) } } - -impl ExactSizeIterator for Drain<'_, T> { - #[inline] - fn len(&self) -> usize { - self.0.len() +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] +#[unstable(feature = "array_try_map", issue = "79711")] +impl const FnMut<(usize,)> for &mut Drain<'_, '_, T, N, F> +where + F: [const] FnMut(T) -> U, +{ + // FIXME(const-hack): ideally this would be an unsafe fn `next()`, and to use it you would instead `|_| unsafe { drain.next() }`. + extern "rust-call" fn call_mut( + &mut self, + (_ /* ignore argument */,): (usize,), + ) -> Self::Output { + if T::IS_ZST { + // its UB to call this more than N times, so returning more ZSTs is valid. + // SAFETY: its a ZST? we conjur. + (self.f)(unsafe { conjure_zst::() }) + } else { + // increment before moving; if `f` panics, we drop the rest. + let p = self.ptr; + // SAFETY: caller guarantees never called more than N times (see `Drain::new`) + self.ptr = unsafe { self.ptr.add(1) }; + // SAFETY: we are allowed to move this. + (self.f)(unsafe { p.read() }) + } } } +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] +#[unstable(feature = "array_try_map", issue = "79711")] +impl const Drop for Drain<'_, '_, T, N, F> { + fn drop(&mut self) { + if !T::IS_ZST { + // SAFETY: we cant read more than N elements + let slice = unsafe { + from_raw_parts_mut::<[T]>( + self.ptr.as_ptr(), + // SAFETY: `start <= end` + self.end.offset_from_unsigned(self.ptr.as_ptr()), + ) + }; -// SAFETY: This is a 1:1 wrapper for a slice iterator, which is also `TrustedLen`. -unsafe impl TrustedLen for Drain<'_, T> {} - -impl UncheckedIterator for Drain<'_, T> { - #[requires(self.0.len() > 0)] - unsafe fn next_unchecked(&mut self) -> T { - // SAFETY: `Drain` is 1:1 with the inner iterator, so if the caller promised - // that there's an element left, the inner iterator has one too. - let p: *const T = unsafe { self.0.next_unchecked() }; - // SAFETY: The iterator was already advanced, so we won't drop this later. - unsafe { p.read() } + // SAFETY: By the type invariant, we're allowed to drop all these. (we own it, after all) + unsafe { drop_in_place(slice) } + } } } diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs index 8c76dc61130b0..575c6673e1565 100644 --- a/library/core/src/array/iter.rs +++ b/library/core/src/array/iter.rs @@ -70,7 +70,7 @@ impl IntoIterator for [T; N] { // FIXME: If normal `transmute` ever gets smart enough to allow this // directly, use it instead of `transmute_unchecked`. let data: [MaybeUninit; N] = unsafe { transmute_unchecked(self) }; - // SAFETY: The original array was entirely initialized and the the alive + // SAFETY: The original array was entirely initialized and the alive // range we're passing here represents that fact. let inner = unsafe { InnerSized::new_unchecked(IndexRange::zero_to(N), data) }; IntoIter { inner: ManuallyDrop::new(inner) } diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 2dd639d68f0ea..6cca2e6358b63 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -12,7 +12,8 @@ use crate::error::Error; use crate::hash::{self, Hash}; use crate::intrinsics::transmute_unchecked; use crate::iter::{UncheckedIterator, repeat_n}; -use crate::mem::{self, MaybeUninit}; +use crate::marker::Destruct; +use crate::mem::{self, ManuallyDrop, MaybeUninit}; use crate::ops::{ ChangeOutputType, ControlFlow, FromResidual, Index, IndexMut, NeverShortCircuit, Residual, Try, }; @@ -25,7 +26,6 @@ mod drain; mod equality; mod iter; -pub(crate) use drain::drain_array_with; #[stable(feature = "array_value_iter", since = "1.51.0")] pub use iter::IntoIter; @@ -105,9 +105,10 @@ pub fn repeat(val: T) -> [T; N] { /// ``` #[inline] #[stable(feature = "array_from_fn", since = "1.63.0")] -pub fn from_fn(f: F) -> [T; N] +#[rustc_const_unstable(feature = "const_array", issue = "147606")] +pub const fn from_fn(f: F) -> [T; N] where - F: FnMut(usize) -> T, + F: [const] FnMut(usize) -> T + [const] Destruct, { try_from_fn(NeverShortCircuit::wrap_mut_1(f)).0 } @@ -143,11 +144,11 @@ where /// ``` #[inline] #[unstable(feature = "array_try_from_fn", issue = "89379")] -pub fn try_from_fn(cb: F) -> ChangeOutputType +#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] +pub const fn try_from_fn(cb: F) -> ChangeOutputType where - F: FnMut(usize) -> R, - R: Try, - R::Residual: Residual<[R::Output; N]>, + R: [const] Try, Output: [const] Destruct>, + F: [const] FnMut(usize) -> R + [const] Destruct, { let mut array = [const { MaybeUninit::uninit() }; N]; match try_from_fn_erased(&mut array, cb) { @@ -549,9 +550,12 @@ impl [T; N] { /// ``` #[must_use] #[stable(feature = "array_map", since = "1.55.0")] - pub fn map(self, f: F) -> [U; N] + #[rustc_const_unstable(feature = "const_array", issue = "147606")] + pub const fn map(self, f: F) -> [U; N] where - F: FnMut(T) -> U, + F: [const] FnMut(T) -> U + [const] Destruct, + U: [const] Destruct, + T: [const] Destruct, { self.try_map(NeverShortCircuit::wrap_mut_1(f)).0 } @@ -587,11 +591,19 @@ impl [T; N] { /// assert_eq!(c, Some(a)); /// ``` #[unstable(feature = "array_try_map", issue = "79711")] - pub fn try_map(self, f: impl FnMut(T) -> R) -> ChangeOutputType + #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] + pub const fn try_map( + self, + mut f: impl [const] FnMut(T) -> R + [const] Destruct, + ) -> ChangeOutputType where - R: Try>, + R: [const] Try, Output: [const] Destruct>, + T: [const] Destruct, { - drain_array_with(self, |iter| try_from_trusted_iterator(iter.map(f))) + let mut me = ManuallyDrop::new(self); + // SAFETY: try_from_fn calls `f` N times. + let mut f = unsafe { drain::Drain::new(&mut me, &mut f) }; + try_from_fn(&mut f) } /// Returns a slice containing the entire array. Equivalent to `&s[..]`. @@ -885,13 +897,11 @@ where /// not optimizing away. So if you give it a shot, make sure to watch what /// happens in the codegen tests. #[inline] -fn try_from_fn_erased( - buffer: &mut [MaybeUninit], - mut generator: impl FnMut(usize) -> R, -) -> ControlFlow -where - R: Try, -{ +#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] +const fn try_from_fn_erased>( + buffer: &mut [MaybeUninit], + mut generator: impl [const] FnMut(usize) -> R + [const] Destruct, +) -> ControlFlow { let mut guard = Guard { array_mut: buffer, initialized: 0 }; while guard.initialized < guard.array_mut.len() { @@ -914,7 +924,7 @@ where /// All write accesses to this structure are unsafe and must maintain a correct /// count of `initialized` elements. /// -/// To minimize indirection fields are still pub but callers should at least use +/// To minimize indirection, fields are still pub but callers should at least use /// `push_unchecked` to signal that something unsafe is going on. struct Guard<'a, T> { /// The array to be initialized. @@ -930,9 +940,10 @@ impl Guard<'_, T> { /// /// No more than N elements must be initialized. #[inline] - pub(crate) unsafe fn push_unchecked(&mut self, item: T) { + #[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] + pub(crate) const unsafe fn push_unchecked(&mut self, item: T) { // SAFETY: If `initialized` was correct before and the caller does not - // invoke this method more than N times then writes will be in-bounds + // invoke this method more than N times, then writes will be in-bounds // and slots will not be initialized more than once. unsafe { self.array_mut.get_unchecked_mut(self.initialized).write(item); @@ -941,11 +952,11 @@ impl Guard<'_, T> { } } -impl Drop for Guard<'_, T> { +#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")] +impl const Drop for Guard<'_, T> { #[inline] fn drop(&mut self) { debug_assert!(self.initialized <= self.array_mut.len()); - // SAFETY: this slice will contain only initialized objects. unsafe { self.array_mut.get_unchecked_mut(..self.initialized).assume_init_drop(); @@ -961,7 +972,7 @@ impl Drop for Guard<'_, T> { /// `next` at most `N` times, the iterator can still be used afterwards to /// retrieve the remaining items. /// -/// If `iter.next()` panicks, all items already yielded by the iterator are +/// If `iter.next()` panics, all items already yielded by the iterator are /// dropped. /// /// Used for [`Iterator::next_chunk`]. @@ -993,6 +1004,7 @@ fn iter_next_chunk_erased( buffer: &mut [MaybeUninit], iter: &mut impl Iterator, ) -> Result<(), usize> { + // if `Iterator::next` panics, this guard will drop already initialized items let mut guard = Guard { array_mut: buffer, initialized: 0 }; while guard.initialized < guard.array_mut.len() { let Some(item) = iter.next() else { diff --git a/library/core/src/bool.rs b/library/core/src/bool.rs index 99268d6182f6c..9b9d2f02550bd 100644 --- a/library/core/src/bool.rs +++ b/library/core/src/bool.rs @@ -1,5 +1,7 @@ //! impl bool {} +use crate::marker::Destruct; + impl bool { /// Returns `Some(t)` if the `bool` is [`true`](../std/keyword.true.html), /// or `None` otherwise. @@ -29,8 +31,9 @@ impl bool { /// assert_eq!(a, 2); /// ``` #[stable(feature = "bool_to_option", since = "1.62.0")] + #[rustc_const_unstable(feature = "const_bool", issue = "151531")] #[inline] - pub fn then_some(self, t: T) -> Option { + pub const fn then_some(self, t: T) -> Option { if self { Some(t) } else { None } } @@ -57,8 +60,9 @@ impl bool { #[doc(alias = "then_with")] #[stable(feature = "lazy_bool_to_option", since = "1.50.0")] #[rustc_diagnostic_item = "bool_then"] + #[rustc_const_unstable(feature = "const_bool", issue = "151531")] #[inline] - pub fn then T>(self, f: F) -> Option { + pub const fn then T + [const] Destruct>(self, f: F) -> Option { if self { Some(f()) } else { None } } @@ -94,8 +98,9 @@ impl bool { /// assert_eq!(a, 2); /// ``` #[unstable(feature = "bool_to_result", issue = "142748")] + #[rustc_const_unstable(feature = "const_bool", issue = "151531")] #[inline] - pub fn ok_or(self, err: E) -> Result<(), E> { + pub const fn ok_or(self, err: E) -> Result<(), E> { if self { Ok(()) } else { Err(err) } } @@ -124,8 +129,12 @@ impl bool { /// assert_eq!(a, 1); /// ``` #[unstable(feature = "bool_to_result", issue = "142748")] + #[rustc_const_unstable(feature = "const_bool", issue = "151531")] #[inline] - pub fn ok_or_else E>(self, f: F) -> Result<(), E> { + pub const fn ok_or_else E + [const] Destruct>( + self, + f: F, + ) -> Result<(), E> { if self { Ok(()) } else { Err(f()) } } } diff --git a/library/core/src/borrow.rs b/library/core/src/borrow.rs index 78ba69fec1422..eb4562bda4ce3 100644 --- a/library/core/src/borrow.rs +++ b/library/core/src/borrow.rs @@ -187,7 +187,7 @@ pub const trait Borrow { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "BorrowMut"] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] -pub const trait BorrowMut: Borrow { +pub const trait BorrowMut: [const] Borrow { /// Mutably borrows from an owned value. /// /// # Examples diff --git a/library/core/src/bstr/mod.rs b/library/core/src/bstr/mod.rs index e13dc5cd44d5c..34e1ea66c99ad 100644 --- a/library/core/src/bstr/mod.rs +++ b/library/core/src/bstr/mod.rs @@ -68,6 +68,30 @@ impl ByteStr { ByteStr::from_bytes(bytes.as_ref()) } + /// Returns the same string as `&ByteStr`. + /// + /// This method is redundant when used directly on `&ByteStr`, but + /// it helps dereferencing other "container" types, + /// for example `Box` or `Arc`. + #[inline] + // #[unstable(feature = "str_as_str", issue = "130366")] + #[unstable(feature = "bstr", issue = "134915")] + pub const fn as_byte_str(&self) -> &ByteStr { + self + } + + /// Returns the same string as `&mut ByteStr`. + /// + /// This method is redundant when used directly on `&mut ByteStr`, but + /// it helps dereferencing other "container" types, + /// for example `Box` or `MutexGuard`. + #[inline] + // #[unstable(feature = "str_as_str", issue = "130366")] + #[unstable(feature = "bstr", issue = "134915")] + pub const fn as_mut_byte_str(&mut self) -> &mut ByteStr { + self + } + #[doc(hidden)] #[unstable(feature = "bstr_internals", issue = "none")] #[inline] diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index c8340c328be12..661ea4ab6a27f 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -27,10 +27,9 @@ //! //! ## `Cell` //! -//! [`Cell`] implements interior mutability by moving values in and out of the cell. That is, an -//! `&mut T` to the inner value can never be obtained, and the value itself cannot be directly -//! obtained without replacing it with something else. Both of these rules ensure that there is -//! never more than one reference pointing to the inner value. This type provides the following +//! [`Cell`] implements interior mutability by moving values in and out of the cell. That is, a +//! `&T` to the inner value can never be obtained, and the value itself cannot be directly +//! obtained without replacing it with something else. This type provides the following //! methods: //! //! - For types that implement [`Copy`], the [`get`](Cell::get) method retrieves the current @@ -2696,20 +2695,20 @@ fn assert_coerce_unsized( let _: RefCell<&dyn Send> = d; } -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for UnsafeCell {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for SyncUnsafeCell {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for Cell {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for RefCell {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl<'b, T: ?Sized> PinCoerceUnsized for Ref<'b, T> {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl<'b, T: ?Sized> PinCoerceUnsized for RefMut<'b, T> {} diff --git a/library/core/src/cell/lazy.rs b/library/core/src/cell/lazy.rs index 3dae6c64200c1..28a76569c1d03 100644 --- a/library/core/src/cell/lazy.rs +++ b/library/core/src/cell/lazy.rs @@ -165,7 +165,6 @@ impl T> LazyCell { /// # Examples /// /// ``` - /// #![feature(lazy_get)] /// use std::cell::LazyCell; /// /// let mut lazy = LazyCell::new(|| 92); @@ -176,7 +175,7 @@ impl T> LazyCell { /// assert_eq!(*lazy, 44); /// ``` #[inline] - #[unstable(feature = "lazy_get", issue = "129333")] + #[stable(feature = "lazy_get", since = "1.94.0")] pub fn force_mut(this: &mut LazyCell) -> &mut T { #[cold] /// # Safety @@ -264,8 +263,6 @@ impl LazyCell { /// # Examples /// /// ``` - /// #![feature(lazy_get)] - /// /// use std::cell::LazyCell; /// /// let mut lazy = LazyCell::new(|| 92); @@ -276,7 +273,7 @@ impl LazyCell { /// assert_eq!(*lazy, 44); /// ``` #[inline] - #[unstable(feature = "lazy_get", issue = "129333")] + #[stable(feature = "lazy_get", since = "1.94.0")] pub fn get_mut(this: &mut LazyCell) -> Option<&mut T> { let state = this.state.get_mut(); match state { @@ -291,8 +288,6 @@ impl LazyCell { /// # Examples /// /// ``` - /// #![feature(lazy_get)] - /// /// use std::cell::LazyCell; /// /// let lazy = LazyCell::new(|| 92); @@ -302,7 +297,7 @@ impl LazyCell { /// assert_eq!(LazyCell::get(&lazy), Some(&92)); /// ``` #[inline] - #[unstable(feature = "lazy_get", issue = "129333")] + #[stable(feature = "lazy_get", since = "1.94.0")] pub fn get(this: &LazyCell) -> Option<&T> { // SAFETY: // This is sound for the same reason as in `force`: once the state is diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs index 1e211ae66e79c..af4469fad2cc6 100644 --- a/library/core/src/cell/once.rs +++ b/library/core/src/cell/once.rs @@ -353,7 +353,8 @@ impl OnceCell { } #[stable(feature = "once_cell", since = "1.70.0")] -impl Default for OnceCell { +#[rustc_const_unstable(feature = "const_default", issue = "143894")] +impl const Default for OnceCell { #[inline] fn default() -> Self { Self::new() @@ -376,14 +377,10 @@ impl fmt::Debug for OnceCell { impl Clone for OnceCell { #[inline] fn clone(&self) -> OnceCell { - let res = OnceCell::new(); - if let Some(value) = self.get() { - match res.set(value.clone()) { - Ok(()) => (), - Err(_) => unreachable!(), - } + match self.get() { + Some(value) => OnceCell::from(value.clone()), + None => OnceCell::new(), } - res } } diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 6e65cb21de9cc..da80568a3c6d1 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -51,6 +51,7 @@ impl const From for u32 { /// ``` /// let c = 'c'; /// let u = u32::from(c); + /// /// assert!(4 == size_of_val(&u)) /// ``` #[inline] @@ -69,6 +70,7 @@ impl const From for u64 { /// ``` /// let c = '👤'; /// let u = u64::from(c); + /// /// assert!(8 == size_of_val(&u)) /// ``` #[inline] @@ -89,6 +91,7 @@ impl const From for u128 { /// ``` /// let c = '⚙'; /// let u = u128::from(c); + /// /// assert!(16 == size_of_val(&u)) /// ``` #[inline] @@ -99,8 +102,8 @@ impl const From for u128 { } } -/// Maps a `char` with code point in U+0000..=U+00FF to a byte in 0x00..=0xFF with same value, -/// failing if the code point is greater than U+00FF. +/// Maps a `char` with a code point from U+0000 to U+00FF (inclusive) to a byte in `0x00..=0xFF` with +/// the same value, failing if the code point is greater than U+00FF. /// /// See [`impl From for char`](char#impl-From-for-char) for details on the encoding. #[stable(feature = "u8_from_char", since = "1.59.0")] @@ -115,6 +118,7 @@ impl const TryFrom for u8 { /// ``` /// let a = 'ÿ'; // U+00FF /// let b = 'Ā'; // U+0100 + /// /// assert_eq!(u8::try_from(a), Ok(0xFF_u8)); /// assert!(u8::try_from(b).is_err()); /// ``` @@ -128,8 +132,8 @@ impl const TryFrom for u8 { } } -/// Maps a `char` with code point in U+0000..=U+FFFF to a `u16` in 0x0000..=0xFFFF with same value, -/// failing if the code point is greater than U+FFFF. +/// Maps a `char` with a code point from U+0000 to U+FFFF (inclusive) to a `u16` in `0x0000..=0xFFFF` +/// with the same value, failing if the code point is greater than U+FFFF. /// /// This corresponds to the UCS-2 encoding, as specified in ISO/IEC 10646:2003. #[stable(feature = "u16_from_char", since = "1.74.0")] @@ -144,6 +148,7 @@ impl const TryFrom for u16 { /// ``` /// let trans_rights = '⚧'; // U+26A7 /// let ninjas = '🥷'; // U+1F977 + /// /// assert_eq!(u16::try_from(trans_rights), Ok(0x26A7_u16)); /// assert!(u16::try_from(ninjas).is_err()); /// ``` @@ -157,7 +162,45 @@ impl const TryFrom for u16 { } } -/// Maps a byte in 0x00..=0xFF to a `char` whose code point has the same value, in U+0000..=U+00FF. +/// Maps a `char` with a code point from U+0000 to U+10FFFF (inclusive) to a `usize` in +/// `0x0000..=0x10FFFF` with the same value, failing if the final value is unrepresentable by +/// `usize`. +/// +/// Generally speaking, this conversion can be seen as obtaining the character's corresponding +/// UTF-32 code point to the extent representable by pointer addresses. +#[stable(feature = "usize_try_from_char", since = "1.94.0")] +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const TryFrom for usize { + type Error = TryFromCharError; + + /// Tries to convert a [`char`] into a [`usize`]. + /// + /// # Examples + /// + /// ``` + /// let a = '\u{FFFF}'; // Always succeeds. + /// let b = '\u{10FFFF}'; // Conditionally succeeds. + /// + /// assert_eq!(usize::try_from(a), Ok(0xFFFF)); + /// + /// if size_of::() >= size_of::() { + /// assert_eq!(usize::try_from(b), Ok(0x10FFFF)); + /// } else { + /// assert!(matches!(usize::try_from(b), Err(_))); + /// } + /// ``` + #[inline] + fn try_from(c: char) -> Result { + // FIXME(const-hack): this should use map_err instead + match usize::try_from(u32::from(c)) { + Ok(x) => Ok(x), + Err(_) => Err(TryFromCharError(())), + } + } +} + +/// Maps a byte in `0x00..=0xFF` to a `char` whose code point has the same value from U+0000 to U+00FF +/// (inclusive). /// /// Unicode is designed such that this effectively decodes bytes /// with the character encoding that IANA calls ISO-8859-1. @@ -185,6 +228,7 @@ impl const From for char { /// ``` /// let u = 32 as u8; /// let c = char::from(u); + /// /// assert!(4 == size_of_val(&c)) /// ``` #[inline] @@ -252,7 +296,6 @@ const fn char_try_from_u32(i: u32) -> Result { // Subtracting 0x800 causes 0x0000..0x0800 to wrap, meaning that a single // unsigned comparison against 0x110000 - 0x800 will detect both the wrapped // surrogate range as well as the numbers originally larger than 0x110000. - // if (i ^ 0xD800).wrapping_sub(0x800) >= 0x110000 - 0x800 { Err(CharTryFromError(())) } else { diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 64a9793e49078..8424fd709db56 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -76,12 +76,12 @@ impl char { /// The maximum number of bytes required to [encode](char::encode_utf8) a `char` to /// UTF-8 encoding. - #[stable(feature = "char_max_len_assoc", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "char_max_len_assoc", since = "1.93.0")] pub const MAX_LEN_UTF8: usize = 4; /// The maximum number of two-byte units required to [encode](char::encode_utf16) a `char` /// to UTF-16 encoding. - #[stable(feature = "char_max_len_assoc", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "char_max_len_assoc", since = "1.93.0")] pub const MAX_LEN_UTF16: usize = 2; /// `U+FFFD REPLACEMENT CHARACTER` (�) is used in Unicode to represent a @@ -1145,11 +1145,12 @@ impl char { /// [Unicode Standard]: https://www.unicode.org/versions/latest/ /// /// # Examples + /// `'ſt'` (U+FB05) is a single Unicode code point (a ligature) that maps to "ST" in uppercase. /// /// As an iterator: /// /// ``` - /// for c in 'ß'.to_uppercase() { + /// for c in 'ſt'.to_uppercase() { /// print!("{c}"); /// } /// println!(); @@ -1158,13 +1159,13 @@ impl char { /// Using `println!` directly: /// /// ``` - /// println!("{}", 'ß'.to_uppercase()); + /// println!("{}", 'ſt'.to_uppercase()); /// ``` /// /// Both are equivalent to: /// /// ``` - /// println!("SS"); + /// println!("ST"); /// ``` /// /// Using [`to_string`](../std/string/trait.ToString.html#tymethod.to_string): @@ -1173,7 +1174,7 @@ impl char { /// assert_eq!('c'.to_uppercase().to_string(), "C"); /// /// // Sometimes the result is more than one character: - /// assert_eq!('ß'.to_uppercase().to_string(), "SS"); + /// assert_eq!('ſt'.to_uppercase().to_string(), "ST"); /// /// // Characters that do not have both uppercase and lowercase /// // convert into themselves. diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index bf8875098edfa..85b09ee06f1fd 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -52,12 +52,12 @@ mod uninit; /// original. /// /// Differs from [`Copy`] in that [`Copy`] is implicit and an inexpensive bit-wise copy, while -/// `Clone` is always explicit and may or may not be expensive. In order to enforce -/// these characteristics, Rust does not allow you to reimplement [`Copy`], but you -/// may reimplement `Clone` and run arbitrary code. +/// `Clone` is always explicit and may or may not be expensive. [`Copy`] has no methods, so you +/// cannot change its behavior, but when implementing `Clone`, the `clone` method you provide +/// may run arbitrary code. /// -/// Since `Clone` is more general than [`Copy`], you can automatically make anything -/// [`Copy`] be `Clone` as well. +/// Since `Clone` is a supertrait of [`Copy`], any type that implements `Copy` must also implement +/// `Clone`. /// /// ## Derivable /// @@ -285,7 +285,7 @@ pub const unsafe trait TrivialClone: [const] Clone {} /// Derive macro generating an impl of the trait `Clone`. #[rustc_builtin_macro] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow_internal_unstable(core_intrinsics, derive_clone_copy, trivial_clone)] +#[allow_internal_unstable(core_intrinsics, derive_clone_copy_internals, trivial_clone)] pub macro Clone($item:item) { /* compiler built-in */ } @@ -350,7 +350,7 @@ impl_use_cloned! { #[doc(hidden)] #[allow(missing_debug_implementations)] #[unstable( - feature = "derive_clone_copy", + feature = "derive_clone_copy_internals", reason = "deriving hack, should not be public", issue = "none" )] @@ -360,7 +360,7 @@ pub struct AssertParamIsClone { #[doc(hidden)] #[allow(missing_debug_implementations)] #[unstable( - feature = "derive_clone_copy", + feature = "derive_clone_copy_internals", reason = "deriving hack, should not be public", issue = "none" )] diff --git a/library/core/src/clone/uninit.rs b/library/core/src/clone/uninit.rs index 8d1185067eb88..b6e351fc7c96c 100644 --- a/library/core/src/clone/uninit.rs +++ b/library/core/src/clone/uninit.rs @@ -114,16 +114,10 @@ impl<'a, T> InitializingSlice<'a, T> { impl<'a, T> Drop for InitializingSlice<'a, T> { #[cold] // will only be invoked on unwind fn drop(&mut self) { - let initialized_slice = ptr::slice_from_raw_parts_mut( - MaybeUninit::slice_as_mut_ptr(self.data), - self.initialized_len, - ); // SAFETY: // * the pointer is valid because it was made from a mutable reference // * `initialized_len` counts the initialized elements as an invariant of this type, // so each of the pointed-to elements is initialized and may be dropped. - unsafe { - ptr::drop_in_place::<[T]>(initialized_slice); - } + unsafe { self.data[..self.initialized_len].assume_init_drop() }; } } diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 7f369d19c3d12..78ea1f1113258 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -351,7 +351,7 @@ pub const trait Eq: [const] PartialEq + PointeeSized { /// Derive macro generating an impl of the trait [`Eq`]. #[rustc_builtin_macro] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match)] +#[allow_internal_unstable(core_intrinsics, derive_eq_internals, structural_match)] #[allow_internal_unstable(coverage_attribute)] pub macro Eq($item:item) { /* compiler built-in */ @@ -363,7 +363,11 @@ pub macro Eq($item:item) { // This struct should never appear in user code. #[doc(hidden)] #[allow(missing_debug_implementations)] -#[unstable(feature = "derive_eq", reason = "deriving hack, should not be public", issue = "none")] +#[unstable( + feature = "derive_eq_internals", + reason = "deriving hack, should not be public", + issue = "none" +)] pub struct AssertParamIsEq { _field: crate::marker::PhantomData, } @@ -1186,7 +1190,7 @@ pub macro Ord($item:item) { /// every `a`. This isn't always the case for types that implement `PartialOrd`, for example: /// /// ``` -/// let a = f64::sqrt(-1.0); +/// let a = f64::NAN; /// assert_eq!(a <= a, false); /// ``` /// @@ -1855,6 +1859,7 @@ mod impls { use crate::hint::unreachable_unchecked; use crate::marker::PointeeSized; use crate::ops::ControlFlow::{self, Break, Continue}; + use crate::panic::const_assert; macro_rules! partial_eq_impl { ($($t:ty)*) => ($( @@ -1997,6 +2002,26 @@ mod impls { fn cmp(&self, other: &Self) -> Ordering { crate::intrinsics::three_way_compare(*self, *other) } + + #[inline] + #[track_caller] + fn clamp(self, min: Self, max: Self) -> Self + { + const_assert!( + min <= max, + "min > max", + "min > max. min = {min:?}, max = {max:?}", + min: $t, + max: $t, + ); + if self < min { + min + } else if self > max { + max + } else { + self + } + } } )*) } diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 89cda30c03036..ef4ab15f93c0b 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -308,8 +308,8 @@ pub const trait AsRef: PointeeSized { /// both `AsMut>` and `AsMut<[T]>`. /// /// In the following, the example functions `caesar` and `null_terminate` provide a generic -/// interface which work with any type that can be converted by cheap mutable-to-mutable conversion -/// into a byte slice (`[u8]`) or byte vector (`Vec`), respectively. +/// interface which works with any type that can be converted by cheap mutable-to-mutable conversion +/// into a byte slice (`[u8]`) or a byte vector (`Vec`), respectively. /// /// [dereference]: core::ops::DerefMut /// [target type]: core::ops::Deref::Target diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index 0430fa4e0c315..3625234b301dc 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -48,64 +48,54 @@ impl_float_to_int!(f32 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i12 impl_float_to_int!(f64 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); impl_float_to_int!(f128 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); -// Conversion traits for primitive integer and float types -// Conversions T -> T are covered by a blanket impl and therefore excluded -// Some conversions from and to usize/isize are not implemented due to portability concerns +/// Implement `From` for integers +macro_rules! impl_from_bool { + ($($int:ty)*) => {$( + #[stable(feature = "from_bool", since = "1.28.0")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + impl const From for $int { + /// Converts from [`bool`] to + #[doc = concat!("[`", stringify!($int), "`]")] + /// , by turning `false` into `0` and `true` into `1`. + /// + /// # Examples + /// + /// ``` + #[doc = concat!("assert_eq!(", stringify!($int), "::from(false), 0);")] + /// + #[doc = concat!("assert_eq!(", stringify!($int), "::from(true), 1);")] + /// ``` + #[inline(always)] + #[cfg_attr(flux, flux::spec(fn(b:bool) -> Self[cast(b)]))] + fn from(b: bool) -> Self { + b as Self + } + } + )*} +} + +// boolean -> integer +impl_from_bool!(u8 u16 u32 u64 u128 usize); +impl_from_bool!(i8 i16 i32 i64 i128 isize); + +/// Implement `From<$small>` for `$large` macro_rules! impl_from { - (bool => $Int:ty $(,)?) => { - impl_from!( - bool => $Int, - #[stable(feature = "from_bool", since = "1.28.0")], - concat!( - "Converts a [`bool`] to [`", stringify!($Int), "`] losslessly.\n", - "The resulting value is `0` for `false` and `1` for `true` values.\n", - "\n", - "# Examples\n", - "\n", - "```\n", - "assert_eq!(", stringify!($Int), "::from(true), 1);\n", - "assert_eq!(", stringify!($Int), "::from(false), 0);\n", - "```\n", - ), - ); - }; - ($Small:ty => $Large:ty, #[$attr:meta] $(,)?) => { - impl_from!( - $Small => $Large, - #[$attr], - concat!("Converts [`", stringify!($Small), "`] to [`", stringify!($Large), "`] losslessly."), - ); - }; - ($Small:ty => $Large:ty, #[$attr:meta], $doc:expr $(,)?) => { + ($small:ty => $large:ty, #[$attr:meta]) => { #[$attr] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] - impl const From<$Small> for $Large { - // Rustdocs on the impl block show a "[+] show undocumented items" toggle. - // Rustdocs on functions do not. - #[doc = $doc] - #[cfg_attr(flux, flux::spec(fn(small:$Small) -> $Large[cast(small)]))] + impl const From<$small> for $large { + #[doc = concat!("Converts from [`", stringify!($small), "`] to [`", stringify!($large), "`] losslessly.")] + #[cfg_attr(flux, flux::spec(fn(small: $small) -> $large[cast(small)]))] #[inline(always)] - fn from(small: $Small) -> Self { + fn from(small: $small) -> Self { + debug_assert!(<$large>::MIN as i128 <= <$small>::MIN as i128); + debug_assert!(<$small>::MAX as u128 <= <$large>::MAX as u128); small as Self } } - }; + } } -// boolean -> integer -impl_from!(bool => u8); -impl_from!(bool => u16); -impl_from!(bool => u32); -impl_from!(bool => u64); -impl_from!(bool => u128); -impl_from!(bool => usize); -impl_from!(bool => i8); -impl_from!(bool => i16); -impl_from!(bool => i32); -impl_from!(bool => i64); -impl_from!(bool => i128); -impl_from!(bool => isize); - // unsigned integer -> unsigned integer impl_from!(u8 => u16, #[stable(feature = "lossless_int_conv", since = "1.5.0")]); impl_from!(u8 => u32, #[stable(feature = "lossless_int_conv", since = "1.5.0")]); @@ -164,7 +154,7 @@ impl_from!(i16 => isize, #[stable(feature = "lossless_iusize_conv", since = "1.2 // * 53 bits in f64 // * 113 bits in f128 // Lossy float conversions are not implemented at this time. -// FIXME(f16_f128): The `f16`/`f128` impls `#[stable]` attributes should be changed to reference +// FIXME(f16,f128): The `f16`/`f128` impls `#[stable]` attributes should be changed to reference // `f16`/`f128` when they are stabilised (trait impls have to have a `#[stable]` attribute, but none // of the `f16`/`f128` impls can be used on stable as the `f16` and `f128` types are unstable). @@ -178,7 +168,7 @@ impl_from!(i16 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0" impl_from!(i16 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(i32 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(i32 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); -// FIXME(f16_f128): This impl would allow using `f128` on stable before it is stabilised. +// FIXME(f128): This impl would allow using `f128` on stable before it is stabilised. // impl_from!(i64 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); // unsigned integer -> float @@ -191,11 +181,11 @@ impl_from!(u16 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0" impl_from!(u16 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(u32 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(u32 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); -// FIXME(f16_f128): This impl would allow using `f128` on stable before it is stabilised. +// FIXME(f128): This impl would allow using `f128` on stable before it is stabilised. // impl_from!(u64 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); // float -> float -// FIXME(f16_f128): adding additional `From<{float}>` impls to `f32` breaks inference. See +// FIXME(f16,f128): adding additional `From<{float}>` impls to `f32` breaks inference. See // impl_from!(f16 => f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); impl_from!(f16 => f128, #[stable(feature = "lossless_float_conv", since = "1.6.0")]); @@ -348,12 +338,48 @@ macro_rules! impl_try_from_both_bounded { )*} } +/// Implement `TryFrom` for `bool` +macro_rules! impl_try_from_integer_for_bool { + ($($int:ty)+) => {$( + #[stable(feature = "try_from", since = "1.34.0")] + #[rustc_const_unstable(feature = "const_convert", issue = "143773")] + impl const TryFrom<$int> for bool { + type Error = TryFromIntError; + + /// Tries to create a bool from an integer type. + /// Returns an error if the integer is not 0 or 1. + /// + /// # Examples + /// + /// ``` + #[doc = concat!("assert_eq!(0_", stringify!($int), ".try_into(), Ok(false));")] + /// + #[doc = concat!("assert_eq!(1_", stringify!($int), ".try_into(), Ok(true));")] + /// + #[doc = concat!("assert!(<", stringify!($int), " as TryInto>::try_into(2).is_err());")] + /// ``` + #[inline] + fn try_from(i: $int) -> Result { + match i { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(TryFromIntError(())), + } + } + } + )*} +} + macro_rules! rev { ($mac:ident, $source:ty => $($target:ty),+) => {$( $mac!($target => $source); )*} } +// integer -> bool +impl_try_from_integer_for_bool!(u128 u64 u32 u16 u8); +impl_try_from_integer_for_bool!(i128 i64 i32 i16 i8); + // unsigned integer -> unsigned integer impl_try_from_upper_bounded!(u16 => u8); impl_try_from_upper_bounded!(u32 => u8, u16); diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 9ca91ee009ee9..011d6ac4a1c78 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -205,6 +205,56 @@ pub trait Error: Debug + Display { /// assert!(request_ref::(dyn_error).is_none()); /// } /// ``` + /// + /// # Delegating Impls + /// + ///
+ /// + /// **Warning**: We recommend implementors avoid delegating implementations of `provide` to + /// source error implementations. + /// + ///
+ /// + /// This method should expose context from the current piece of the source chain only, not from + /// sources that are exposed in the chain of sources. Delegating `provide` implementations cause + /// the same context to be provided by multiple errors in the chain of sources which can cause + /// unintended duplication of information in error reports or require heuristics to deduplicate. + /// + /// In other words, the following implementation pattern for `provide` is discouraged and should + /// not be used for [`Error`] types exposed in public APIs to third parties. + /// + /// ```rust + /// # #![feature(error_generic_member_access)] + /// # use core::fmt; + /// # use core::error::Request; + /// # #[derive(Debug)] + /// struct MyError { + /// source: Error, + /// } + /// # #[derive(Debug)] + /// # struct Error; + /// # impl fmt::Display for Error { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "Example Source Error") + /// # } + /// # } + /// # impl fmt::Display for MyError { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "Example Error") + /// # } + /// # } + /// # impl std::error::Error for Error { } + /// + /// impl std::error::Error for MyError { + /// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + /// Some(&self.source) + /// } + /// + /// fn provide<'a>(&'a self, request: &mut Request<'a>) { + /// self.source.provide(request) // <--- Discouraged + /// } + /// } + /// ``` #[unstable(feature = "error_generic_member_access", issue = "99301")] #[allow(unused_variables)] fn provide<'a>(&'a self, request: &mut Request<'a>) {} diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index b471eb5b7ff5d..8666190913578 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -696,6 +696,17 @@ impl CStr { pub fn display(&self) -> impl fmt::Display { crate::bstr::ByteStr::from_bytes(self.to_bytes()) } + + /// Returns the same string as a string slice `&CStr`. + /// + /// This method is redundant when used directly on `&CStr`, but + /// it helps dereferencing other string-like types to string slices, + /// for example references to `Box` or `Arc`. + #[inline] + #[unstable(feature = "str_as_str", issue = "130366")] + pub const fn as_c_str(&self) -> &CStr { + self + } } #[stable(feature = "c_string_eq_c_str", since = "1.90.0")] @@ -987,9 +998,13 @@ mod verify { // pub const fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError> #[kani::proof] - #[kani::unwind(17)] + #[kani::unwind(9)] fn check_from_bytes_with_nul() { - const MAX_SIZE: usize = 16; + // FIXME(kani): reduced from 16 to 8 because CBMC 6.10 (as pinned by the current Kani + // version) needs ~10 GB for MAX_SIZE == 16, which exhausts the memory of the + // macos-latest CI runners and makes the harness exceed the 10-minute autoharness + // timeout there. + const MAX_SIZE: usize = 8; let string: [u8; MAX_SIZE] = kani::any(); let slice = kani::slice::any_slice_of_array(&string); diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 1356ca217c9a2..f1b928da7ef3c 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -28,7 +28,7 @@ pub mod c_str; issue = "44930", reason = "the `c_variadic` feature has not been properly tested on all supported platforms" )] -pub use self::va_list::{VaArgSafe, VaList, VaListImpl}; +pub use self::va_list::{VaArgSafe, VaList}; #[unstable( feature = "c_variadic", diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 233a2ee3e484e..d0f155316a109 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -4,15 +4,40 @@ #[cfg(not(target_arch = "xtensa"))] use crate::ffi::c_void; -#[allow(unused_imports)] use crate::fmt; use crate::intrinsics::{va_arg, va_copy, va_end}; -use crate::marker::{PhantomData, PhantomInvariantLifetime}; -use crate::ops::{Deref, DerefMut}; +use crate::marker::PhantomCovariantLifetime; -// The name is WIP, using `VaListImpl` for now. +// There are currently three flavors of how a C `va_list` is implemented for +// targets that Rust supports: // -// Most targets explicitly specify the layout of `va_list`, this layout is matched here. +// - `va_list` is an opaque pointer +// - `va_list` is a struct +// - `va_list` is a single-element array, containing a struct +// +// The opaque pointer approach is the simplest to implement: the pointer just +// points to an array of arguments on the caller's stack. +// +// The struct and single-element array variants are more complex, but +// potentially more efficient because the additional state makes it +// possible to pass variadic arguments via registers. +// +// The Rust `VaList` type is ABI-compatible with the C `va_list`. +// The struct and pointer cases straightforwardly map to their Rust equivalents, +// but the single-element array case is special: in C, this type is subject to +// array-to-pointer decay. +// +// The `#[rustc_pass_indirectly_in_non_rustic_abis]` attribute is used to match +// the pointer decay behavior in Rust, while otherwise matching Rust semantics. +// This attribute ensures that the compiler uses the correct ABI for functions +// like `extern "C" fn takes_va_list(va: VaList<'_>)` by passing `va` indirectly. +// +// The Clang `BuiltinVaListKind` enumerates the `va_list` variations that Clang supports, +// and we mirror these here. +// +// For all current LLVM targets, `va_copy` lowers to `memcpy`. Hence the inner structs below all +// derive `Copy`. However, in the future we might want to support a target where `va_copy` +// allocates, or otherwise violates the requirements of `Copy`. Therefore `VaList` is only `Clone`. crate::cfg_select! { all( target_arch = "aarch64", @@ -20,73 +45,119 @@ crate::cfg_select! { not(target_os = "uefi"), not(windows), ) => { - /// AArch64 ABI implementation of a `va_list`. See the - /// [AArch64 Procedure Call Standard] for more details. + /// AArch64 ABI implementation of a `va_list`. + /// + /// See the [AArch64 Procedure Call Standard] for more details. + /// + /// `va_copy` is `memcpy`: /// /// [AArch64 Procedure Call Standard]: /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf #[repr(C)] - #[derive(Debug)] - #[lang = "va_list"] - pub struct VaListImpl<'f> { - stack: *mut c_void, - gr_top: *mut c_void, - vr_top: *mut c_void, + #[derive(Debug, Clone, Copy)] + struct VaListInner { + stack: *const c_void, + gr_top: *const c_void, + vr_top: *const c_void, gr_offs: i32, vr_offs: i32, - _marker: PhantomInvariantLifetime<'f>, } } all(target_arch = "powerpc", not(target_os = "uefi"), not(windows)) => { /// PowerPC ABI implementation of a `va_list`. + /// + /// See the [LLVM source] and [GCC header] for more details. + /// + /// `va_copy` is `memcpy`: + /// + /// [LLVM source]: + /// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/PowerPC/PPCISelLowering.cpp#L4089-L4111 + /// [GCC header]: https://web.mit.edu/darwin/src/modules/gcc/gcc/ginclude/va-ppc.h #[repr(C)] - #[derive(Debug)] - #[lang = "va_list"] - pub struct VaListImpl<'f> { + #[derive(Debug, Clone, Copy)] + #[rustc_pass_indirectly_in_non_rustic_abis] + struct VaListInner { gpr: u8, fpr: u8, reserved: u16, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomInvariantLifetime<'f>, + overflow_arg_area: *const c_void, + reg_save_area: *const c_void, } } target_arch = "s390x" => { /// s390x ABI implementation of a `va_list`. + /// + /// See the [S/390x ELF Application Binary Interface Supplement] for more details. + /// + /// `va_copy` is `memcpy`: + /// + /// [S/390x ELF Application Binary Interface Supplement]: + /// https://docs.google.com/gview?embedded=true&url=https://github.com/IBM/s390x-abi/releases/download/v1.7/lzsabi_s390x.pdf #[repr(C)] - #[derive(Debug)] - #[lang = "va_list"] - pub struct VaListImpl<'f> { + #[derive(Debug, Clone, Copy)] + #[rustc_pass_indirectly_in_non_rustic_abis] + struct VaListInner { gpr: i64, fpr: i64, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomInvariantLifetime<'f>, + overflow_arg_area: *const c_void, + reg_save_area: *const c_void, } } all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)) => { - /// x86_64 ABI implementation of a `va_list`. + /// x86_64 System V ABI implementation of a `va_list`. + /// + /// See the [System V AMD64 ABI] for more details. + /// + /// `va_copy` is `memcpy`: + /// (github won't render that file, look for `SDValue LowerVACOPY`) + /// + /// [System V AMD64 ABI]: + /// https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf #[repr(C)] - #[derive(Debug)] - #[lang = "va_list"] - pub struct VaListImpl<'f> { + #[derive(Debug, Clone, Copy)] + #[rustc_pass_indirectly_in_non_rustic_abis] + struct VaListInner { gp_offset: i32, fp_offset: i32, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomInvariantLifetime<'f>, + overflow_arg_area: *const c_void, + reg_save_area: *const c_void, } } target_arch = "xtensa" => { /// Xtensa ABI implementation of a `va_list`. + /// + /// See the [LLVM source] for more details. + /// + /// `va_copy` is `memcpy`: + /// + /// [LLVM source]: + /// https://github.com/llvm/llvm-project/blob/af9a4263a1a209953a1d339ef781a954e31268ff/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp#L1211-L1215 #[repr(C)] - #[derive(Debug)] - #[lang = "va_list"] - pub struct VaListImpl<'f> { - stk: *mut i32, - reg: *mut i32, + #[derive(Debug, Clone, Copy)] + #[rustc_pass_indirectly_in_non_rustic_abis] + struct VaListInner { + stk: *const i32, + reg: *const i32, ndx: i32, - _marker: PhantomInvariantLifetime<'f>, + } + } + + all(target_arch = "hexagon", target_env = "musl") => { + /// Hexagon Musl implementation of a `va_list`. + /// + /// See the [LLVM source] for more details. On bare metal Hexagon uses an opaque pointer. + /// + /// `va_copy` is `memcpy`: + /// + /// [LLVM source]: + /// https://github.com/llvm/llvm-project/blob/0cdc1b6dd4a870fc41d4b15ad97e0001882aba58/clang/lib/CodeGen/Targets/Hexagon.cpp#L407-L417 + #[repr(C)] + #[derive(Debug, Clone, Copy)] + #[rustc_pass_indirectly_in_non_rustic_abis] + struct VaListInner { + __current_saved_reg_area_pointer: *const c_void, + __saved_reg_area_end_pointer: *const c_void, + __overflow_area_pointer: *const c_void, } } @@ -94,95 +165,61 @@ crate::cfg_select! { // // - apple aarch64 (see https://github.com/rust-lang/rust/pull/56599) // - windows + // - powerpc64 & powerpc64le // - uefi - // - any other target for which we don't specify the `VaListImpl` above + // - any other target for which we don't specify the `VaListInner` above // // In this implementation the `va_list` type is just an alias for an opaque pointer. // That pointer is probably just the next variadic argument on the caller's stack. _ => { /// Basic implementation of a `va_list`. + /// + /// `va_copy` is `memcpy`: #[repr(transparent)] - #[lang = "va_list"] - pub struct VaListImpl<'f> { - ptr: *mut c_void, - - // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to - // the region of the function it's defined in - _marker: PhantomInvariantLifetime<'f>, - } - - impl<'f> fmt::Debug for VaListImpl<'f> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "va_list* {:p}", self.ptr) - } + #[derive(Debug, Clone, Copy)] + struct VaListInner { + ptr: *const c_void, } } } -crate::cfg_select! { - all( - any( - target_arch = "aarch64", - target_arch = "powerpc", - target_arch = "s390x", - target_arch = "x86_64" - ), - not(target_arch = "xtensa"), - any(not(target_arch = "aarch64"), not(target_vendor = "apple")), - not(target_family = "wasm"), - not(target_os = "uefi"), - not(windows), - ) => { - /// A wrapper for a `va_list` - #[repr(transparent)] - #[derive(Debug)] - pub struct VaList<'a, 'f: 'a> { - inner: &'a mut VaListImpl<'f>, - _marker: PhantomData<&'a mut VaListImpl<'f>>, - } - +/// A variable argument list, equivalent to `va_list` in C. +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomCovariantLifetime<'a>, +} - impl<'f> VaListImpl<'f> { - /// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`. - #[inline] - pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { - VaList { inner: self, _marker: PhantomData } - } - } +impl fmt::Debug for VaList<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // No need to include `_marker` in debug output. + f.debug_tuple("VaList").field(&self.inner).finish() } +} - _ => { - /// A wrapper for a `va_list` - #[repr(transparent)] - #[derive(Debug)] - pub struct VaList<'a, 'f: 'a> { - inner: VaListImpl<'f>, - _marker: PhantomData<&'a mut VaListImpl<'f>>, - } - - impl<'f> VaListImpl<'f> { - /// Converts a [`VaListImpl`] into a [`VaList`] that is binary-compatible with C's `va_list`. - #[inline] - pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { - VaList { inner: VaListImpl { ..*self }, _marker: PhantomData } - } - } +impl VaList<'_> { + // Helper used in the implementation of the `va_copy` intrinsic. + pub(crate) fn duplicate(&self) -> Self { + Self { inner: self.inner.clone(), _marker: self._marker } } } -impl<'a, 'f: 'a> Deref for VaList<'a, 'f> { - type Target = VaListImpl<'f>; - +impl Clone for VaList<'_> { #[inline] - fn deref(&self) -> &VaListImpl<'f> { - &self.inner + fn clone(&self) -> Self { + // We only implement Clone and not Copy because some future target might not be able to + // implement Copy (e.g. because it allocates). For the same reason we use an intrinsic + // to do the copying: the fact that on all current targets, this is just `memcpy`, is an implementation + // detail. The intrinsic lets Miri catch UB from code incorrectly relying on that implementation detail. + va_copy(self) } } -impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> { - #[inline] - fn deref_mut(&mut self) -> &mut VaListImpl<'f> { - &mut self.inner +impl<'f> Drop for VaList<'f> { + fn drop(&mut self) { + // SAFETY: this variable argument list is being dropped, so won't be read from again. + unsafe { va_end(self) } } } @@ -203,7 +240,7 @@ mod sealed { impl Sealed for *const T {} } -/// Types that are valid to read using [`VaListImpl::arg`]. +/// Types that are valid to read using [`VaList::arg`]. /// /// # Safety /// @@ -238,7 +275,7 @@ unsafe impl VaArgSafe for f64 {} unsafe impl VaArgSafe for *mut T {} unsafe impl VaArgSafe for *const T {} -impl<'f> VaListImpl<'f> { +impl<'f> VaList<'f> { /// Advance to and read the next variable argument. /// /// # Safety @@ -258,47 +295,6 @@ impl<'f> VaListImpl<'f> { // SAFETY: the caller must uphold the safety contract for `va_arg`. unsafe { va_arg(self) } } - - /// Copies the `va_list` at the current location. - pub unsafe fn with_copy(&self, f: F) -> R - where - F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R, - { - let mut ap = self.clone(); - let ret = f(ap.as_va_list()); - // SAFETY: the caller must uphold the safety contract for `va_end`. - unsafe { - va_end(&mut ap); - } - ret - } -} - -impl<'f> Clone for VaListImpl<'f> { - #[inline] - fn clone(&self) -> Self { - let mut dest = crate::mem::MaybeUninit::uninit(); - // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal - unsafe { - va_copy(dest.as_mut_ptr(), self); - dest.assume_init() - } - } -} - -impl<'f> Drop for VaListImpl<'f> { - fn drop(&mut self) { - // FIXME: this should call `va_end`, but there's no clean way to - // guarantee that `drop` always gets inlined into its caller, - // so the `va_end` would get directly called from the same function as - // the corresponding `va_copy`. `man va_end` states that C requires this, - // and LLVM basically follows the C semantics, so we need to make sure - // that `va_end` is always called from the same function as `va_copy`. - // For more details, see https://github.com/rust-lang/rust/pull/59625 - // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic. - // - // This works for now, since `va_end` is a no-op on all current LLVM targets. - } } // Checks (via an assert in `compiler/rustc_ty_utils/src/abi.rs`) that the C ABI for the current diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 4ea6c6ba8fb9c..7550dac45cd02 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1226,19 +1226,20 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// assert_eq!(format!("{}", wrapped), "'a'"); /// assert_eq!(format!("{:?}", wrapped), "'a'"); /// ``` -#[stable(feature = "fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "fmt_from_fn", since = "1.93.0")] +#[rustc_const_stable(feature = "const_fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] #[must_use = "returns a type implementing Debug and Display, which do not have any effects unless they are used"] -pub fn from_fn) -> fmt::Result>(f: F) -> FromFn { +pub const fn from_fn) -> fmt::Result>(f: F) -> FromFn { FromFn(f) } /// Implements [`fmt::Debug`] and [`fmt::Display`] via the provided closure. /// /// Created with [`from_fn`]. -#[stable(feature = "fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "fmt_from_fn", since = "1.93.0")] pub struct FromFn(F); -#[stable(feature = "fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "fmt_from_fn", since = "1.93.0")] impl fmt::Debug for FromFn where F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result, @@ -1248,7 +1249,7 @@ where } } -#[stable(feature = "fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "fmt_from_fn", since = "1.93.0")] impl fmt::Display for FromFn where F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result, diff --git a/library/core/src/fmt/float.rs b/library/core/src/fmt/float.rs index 556db239f2499..6a458436726cf 100644 --- a/library/core/src/fmt/float.rs +++ b/library/core/src/fmt/float.rs @@ -13,8 +13,14 @@ macro_rules! impl_general_format { ($($t:ident)*) => { $(impl GeneralFormat for $t { fn already_rounded_value_should_use_exponential(&self) -> bool { + // `max_abs` rounds to infinity for `f16`. This is fine to save us from a more + // complex macro, it just means a positive-exponent `f16` will never print as + // scientific notation by default (reasonably, the max is 65504.0). + #[allow(overflowing_literals)] + let max_abs = 1e+16; + let abs = $t::abs(*self); - (abs != 0.0 && abs < 1e-4) || abs >= 1e+16 + (abs != 0.0 && abs < 1e-4) || abs >= max_abs } })* } @@ -236,7 +242,7 @@ floating! { f32 f64 } #[cfg(target_has_reliable_f16)] floating! { f16 } -// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order +// FIXME(f16): A fallback is used when the backend+target does not support f16 well, in order // to avoid ICEs. #[cfg(not(target_has_reliable_f16))] diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 4b1e013c2b445..e20109c3cc9a8 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -41,7 +41,7 @@ pub use num_buffer::{NumBuffer, NumBufferTrait}; #[stable(feature = "debug_builders", since = "1.2.0")] pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; -#[stable(feature = "fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] +#[stable(feature = "fmt_from_fn", since = "1.93.0")] pub use self::builders::{FromFn, from_fn}; /// The type returned by formatter methods. @@ -625,7 +625,7 @@ impl<'a> Formatter<'a> { // // 2) Placeholders representation (e.g. format_args!("hello {name}\n")) // ┌────────────────────────────────┐ -// template: │ *const u8 │ ─▷ b"\x06hello \x80\x01\n\x00" +// template: │ *const u8 │ ─▷ b"\x06hello \xC0\x01\n\x00" // ├────────────────────────────────┤ // args: │ &'a [Argument<'a>; _] 0│ (lower bit is 0 due to alignment of Argument type) // └────────────────────────────────┘ diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index c3f3cd7294254..eea6118571204 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -87,7 +87,6 @@ #[allow(deprecated)] pub use self::sip::SipHasher; #[unstable(feature = "hashmap_internals", issue = "none")] -#[allow(deprecated)] #[doc(hidden)] pub use self::sip::SipHasher13; use crate::{fmt, marker}; @@ -784,7 +783,8 @@ impl Clone for BuildHasherDefault { } #[stable(since = "1.7.0", feature = "build_hasher")] -impl Default for BuildHasherDefault { +#[rustc_const_unstable(feature = "const_default", issue = "143894")] +impl const Default for BuildHasherDefault { fn default() -> BuildHasherDefault { Self::new() } diff --git a/library/core/src/hash/sip.rs b/library/core/src/hash/sip.rs index 780e522c48ebf..eed1044bd2a92 100644 --- a/library/core/src/hash/sip.rs +++ b/library/core/src/hash/sip.rs @@ -10,9 +10,12 @@ use crate::{cmp, ptr}; /// This is currently the default hashing function used by standard library /// (e.g., `collections::HashMap` uses it by default). /// -/// See: -#[unstable(feature = "hashmap_internals", issue = "none")] -#[deprecated(since = "1.13.0", note = "use `std::hash::DefaultHasher` instead")] +/// See: +#[unstable( + feature = "hashmap_internals", + issue = "none", + reason = "use `std::hash::DefaultHasher` instead" +)] #[derive(Debug, Clone, Default)] #[doc(hidden)] pub struct SipHasher13 { @@ -21,9 +24,8 @@ pub struct SipHasher13 { /// An implementation of SipHash 2-4. /// -/// See: +/// See: #[unstable(feature = "hashmap_internals", issue = "none")] -#[deprecated(since = "1.13.0", note = "use `std::hash::DefaultHasher` instead")] #[derive(Debug, Clone, Default)] struct SipHasher24 { hasher: Hasher, @@ -31,7 +33,7 @@ struct SipHasher24 { /// An implementation of SipHash 2-4. /// -/// See: +/// See: /// /// SipHash is a general-purpose hashing function: it runs at a good /// speed (competitive with Spooky and City) and permits strong _keyed_ @@ -137,8 +139,7 @@ unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 { out |= (unsafe { *buf.get_unchecked(start + i) } as u64) << (i * 8); i += 1; } - //FIXME(fee1-dead): use debug_assert_eq - debug_assert!(i == len); + debug_assert_eq!(i, len); out } @@ -166,16 +167,16 @@ impl SipHasher13 { /// Creates a new `SipHasher13` with the two initial keys set to 0. #[inline] #[unstable(feature = "hashmap_internals", issue = "none")] - #[deprecated(since = "1.13.0", note = "use `std::hash::DefaultHasher` instead")] - pub fn new() -> SipHasher13 { + #[rustc_const_unstable(feature = "const_default", issue = "143894")] + pub const fn new() -> SipHasher13 { SipHasher13::new_with_keys(0, 0) } /// Creates a `SipHasher13` that is keyed off the provided keys. #[inline] #[unstable(feature = "hashmap_internals", issue = "none")] - #[deprecated(since = "1.13.0", note = "use `std::hash::DefaultHasher` instead")] - pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher13 { + #[rustc_const_unstable(feature = "const_default", issue = "143894")] + pub const fn new_with_keys(key0: u64, key1: u64) -> SipHasher13 { SipHasher13 { hasher: Hasher::new_with_keys(key0, key1) } } } @@ -338,7 +339,8 @@ impl Clone for Hasher { } } -impl Default for Hasher { +#[rustc_const_unstable(feature = "const_default", issue = "143894")] +impl const Default for Hasher { /// Creates a `Hasher` with the two initial keys set to 0. #[inline] fn default() -> Hasher { diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 8dd4f5f7dcf14..fe59166363b9c 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -275,6 +275,15 @@ pub const unsafe fn assert_unchecked(cond: bool) { #[stable(feature = "renamed_spin_loop", since = "1.49.0")] pub fn spin_loop() { crate::cfg_select! { + miri => { + unsafe extern "Rust" { + safe fn miri_spin_loop(); + } + + // Miri does support some of the intrinsics that are called below, but to guarantee + // consistent behavior across targets, this custom function is used. + miri_spin_loop(); + } target_arch = "x86" => { // SAFETY: the `cfg` attr ensures that we only execute this on x86 targets. crate::arch::x86::_mm_pause() @@ -289,9 +298,18 @@ pub fn spin_loop() { // SAFETY: the `cfg` attr ensures that we only execute this on aarch64 targets. unsafe { crate::arch::aarch64::__isb(crate::arch::aarch64::SY) } } - all(target_arch = "arm", target_feature = "v6") => { - // SAFETY: the `cfg` attr ensures that we only execute this on arm targets - // with support for the v6 feature. + all( + target_arch = "arm", + any( + all(target_feature = "v6k", not(target_feature = "thumb-mode")), + target_feature = "v6t2", + all(target_feature = "v6", target_feature = "mclass"), + ) + ) => { + // SAFETY: the `cfg` attr ensures that we only execute this on arm + // targets with support for the this feature. On ARMv6 in Thumb + // mode, T2 is required (see Arm DDI0406C Section A8.8.427), + // otherwise ARMv6-M or ARMv6K is enough unsafe { crate::arch::arm::__yield() } } target_arch = "loongarch32" => crate::arch::loongarch32::ibar::<0>(), @@ -646,7 +664,7 @@ pub const fn must_use(value: T) -> T { /// } /// } /// ``` -#[unstable(feature = "likely_unlikely", issue = "136873")] +#[unstable(feature = "likely_unlikely", issue = "151619")] #[inline(always)] pub const fn likely(b: bool) -> bool { crate::intrinsics::likely(b) @@ -696,7 +714,7 @@ pub const fn likely(b: bool) -> bool { /// } /// } /// ``` -#[unstable(feature = "likely_unlikely", issue = "136873")] +#[unstable(feature = "likely_unlikely", issue = "151619")] #[inline(always)] pub const fn unlikely(b: bool) -> bool { crate::intrinsics::unlikely(b) @@ -705,6 +723,10 @@ pub const fn unlikely(b: bool) -> bool { /// Hints to the compiler that given path is cold, i.e., unlikely to be taken. The compiler may /// choose to optimize paths that are not cold at the expense of paths that are cold. /// +/// Note that like all hints, the exact effect to codegen is not guaranteed. Using `cold_path` +/// can actually *decrease* performance if the branch is called more than expected. It is advisable +/// to perform benchmarks to tell if this function is useful. +/// /// # Examples /// /// ``` @@ -729,6 +751,38 @@ pub const fn unlikely(b: bool) -> bool { /// } /// } /// ``` +/// +/// This can also be used to implement `likely` and `unlikely` helpers to hint the condition rather +/// than the branch: +/// +/// ``` +/// #![feature(cold_path)] +/// use core::hint::cold_path; +/// +/// #[inline(always)] +/// pub const fn likely(b: bool) -> bool { +/// if !b { +/// cold_path(); +/// } +/// b +/// } +/// +/// #[inline(always)] +/// pub const fn unlikely(b: bool) -> bool { +/// if b { +/// cold_path(); +/// } +/// b +/// } +/// +/// fn foo(x: i32) { +/// if likely(x > 0) { +/// println!("this branch is likely to be taken"); +/// } else { +/// println!("this branch is unlikely to be taken"); +/// } +/// } +/// ``` #[unstable(feature = "cold_path", issue = "136873")] #[inline(always)] pub const fn cold_path() { @@ -829,3 +883,152 @@ where crate::intrinsics::select_unpredictable(condition, true_val, false_val).assume_init() } } + +/// The expected temporal locality of a memory prefetch operation. +/// +/// Locality expresses how likely the prefetched data is to be reused soon, +/// and therefore which level of cache it should be brought into. +/// +/// The locality is just a hint, and may be ignored on some targets or by the hardware. +/// +/// Used with functions like [`prefetch_read`] and [`prefetch_write`]. +/// +/// [`prefetch_read`]: crate::hint::prefetch_read +/// [`prefetch_write`]: crate::hint::prefetch_write +#[unstable(feature = "hint_prefetch", issue = "146941")] +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Locality { + /// Data is expected to be reused eventually. + /// + /// Typically prefetches into L3 cache (if the CPU supports it). + L3, + /// Data is expected to be reused in the near future. + /// + /// Typically prefetches into L2 cache. + L2, + /// Data is expected to be reused very soon. + /// + /// Typically prefetches into L1 cache. + L1, +} + +impl Locality { + /// Convert to the constant that LLVM associates with a locality. + const fn to_llvm(self) -> i32 { + match self { + Self::L3 => 1, + Self::L2 => 2, + Self::L1 => 3, + } + } +} + +/// Prefetch the cache line containing `ptr` for a future read. +/// +/// A strategically placed prefetch can reduce cache miss latency if the data is accessed +/// soon after, but may also increase bandwidth usage or evict other cache lines. +/// +/// A prefetch is a *hint*, and may be ignored on certain targets or by the hardware. +/// +/// Passing a dangling or invalid pointer is permitted: the memory will not +/// actually be dereferenced, and no faults are raised. +/// +/// # Examples +/// +/// ``` +/// #![feature(hint_prefetch)] +/// use std::hint::{Locality, prefetch_read}; +/// use std::mem::size_of_val; +/// +/// // Prefetch all of `slice` into the L1 cache. +/// fn prefetch_slice(slice: &[T]) { +/// // On most systems the cache line size is 64 bytes. +/// for offset in (0..size_of_val(slice)).step_by(64) { +/// prefetch_read(slice.as_ptr().wrapping_add(offset), Locality::L1); +/// } +/// } +/// ``` +#[inline(always)] +#[unstable(feature = "hint_prefetch", issue = "146941")] +pub const fn prefetch_read(ptr: *const T, locality: Locality) { + match locality { + Locality::L3 => intrinsics::prefetch_read_data::(ptr), + Locality::L2 => intrinsics::prefetch_read_data::(ptr), + Locality::L1 => intrinsics::prefetch_read_data::(ptr), + } +} + +/// Prefetch the cache line containing `ptr` for a single future read, but attempt to avoid +/// polluting the cache. +/// +/// A strategically placed prefetch can reduce cache miss latency if the data is accessed +/// soon after, but may also increase bandwidth usage or evict other cache lines. +/// +/// A prefetch is a *hint*, and may be ignored on certain targets or by the hardware. +/// +/// Passing a dangling or invalid pointer is permitted: the memory will not +/// actually be dereferenced, and no faults are raised. +#[inline(always)] +#[unstable(feature = "hint_prefetch", issue = "146941")] +pub const fn prefetch_read_non_temporal(ptr: *const T, locality: Locality) { + // The LLVM intrinsic does not currently support specifying the locality. + let _ = locality; + intrinsics::prefetch_read_data::(ptr) +} + +/// Prefetch the cache line containing `ptr` for a future write. +/// +/// A strategically placed prefetch can reduce cache miss latency if the data is accessed +/// soon after, but may also increase bandwidth usage or evict other cache lines. +/// +/// A prefetch is a *hint*, and may be ignored on certain targets or by the hardware. +/// +/// Passing a dangling or invalid pointer is permitted: the memory will not +/// actually be dereferenced, and no faults are raised. +#[inline(always)] +#[unstable(feature = "hint_prefetch", issue = "146941")] +pub const fn prefetch_write(ptr: *mut T, locality: Locality) { + match locality { + Locality::L3 => intrinsics::prefetch_write_data::(ptr), + Locality::L2 => intrinsics::prefetch_write_data::(ptr), + Locality::L1 => intrinsics::prefetch_write_data::(ptr), + } +} + +/// Prefetch the cache line containing `ptr` for a single future write, but attempt to avoid +/// polluting the cache. +/// +/// A strategically placed prefetch can reduce cache miss latency if the data is accessed +/// soon after, but may also increase bandwidth usage or evict other cache lines. +/// +/// A prefetch is a *hint*, and may be ignored on certain targets or by the hardware. +/// +/// Passing a dangling or invalid pointer is permitted: the memory will not +/// actually be dereferenced, and no faults are raised. +#[inline(always)] +#[unstable(feature = "hint_prefetch", issue = "146941")] +pub const fn prefetch_write_non_temporal(ptr: *const T, locality: Locality) { + // The LLVM intrinsic does not currently support specifying the locality. + let _ = locality; + intrinsics::prefetch_write_data::(ptr) +} + +/// Prefetch the cache line containing `ptr` into the instruction cache for a future read. +/// +/// A strategically placed prefetch can reduce cache miss latency if the instructions are +/// accessed soon after, but may also increase bandwidth usage or evict other cache lines. +/// +/// A prefetch is a *hint*, and may be ignored on certain targets or by the hardware. +/// +/// Passing a dangling or invalid pointer is permitted: the memory will not +/// actually be dereferenced, and no faults are raised. +#[inline(always)] +#[unstable(feature = "hint_prefetch", issue = "146941")] +pub const fn prefetch_read_instruction(ptr: *const T, locality: Locality) { + match locality { + Locality::L3 => intrinsics::prefetch_read_instruction::(ptr), + Locality::L2 => intrinsics::prefetch_read_instruction::(ptr), + Locality::L1 => intrinsics::prefetch_read_instruction::(ptr), + } +} diff --git a/library/core/src/intrinsics/gpu.rs b/library/core/src/intrinsics/gpu.rs new file mode 100644 index 0000000000000..9e7624841d0c6 --- /dev/null +++ b/library/core/src/intrinsics/gpu.rs @@ -0,0 +1,23 @@ +//! Intrinsics for GPU targets. +//! +//! Intrinsics in this module are intended for use on GPU targets. +//! They can be target specific but in general GPU targets are similar. + +#![unstable(feature = "gpu_intrinsics", issue = "none")] + +/// Returns a pointer to the HSA kernel dispatch packet. +/// +/// A `gpu-kernel` on amdgpu is always launched through a kernel dispatch packet. +/// The dispatch packet contains the workgroup size, launch size and other data. +/// The content is defined by the [HSA Platform System Architecture Specification], +/// which is implemented e.g. in AMD's [hsa.h]. +/// The intrinsic returns a unit pointer so that rustc does not need to know the packet struct. +/// The pointer is valid for the whole lifetime of the program. +/// +/// [HSA Platform System Architecture Specification]: https://hsafoundation.com/wp-content/uploads/2021/02/HSA-SysArch-1.2.pdf +/// [hsa.h]: https://github.com/ROCm/rocm-systems/blob/rocm-7.1.0/projects/rocr-runtime/runtime/hsa-runtime/inc/hsa.h#L2959 +#[rustc_nounwind] +#[rustc_intrinsic] +#[cfg(target_arch = "amdgpu")] +#[must_use = "returns a pointer that does nothing unless used"] +pub fn amdgpu_dispatch_ptr() -> *const (); diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index 5b3c924152552..939298268c934 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -289,7 +289,7 @@ reason = "MIR is an implementation detail and extremely unstable", issue = "none" )] -#![allow(unused_variables, non_snake_case, missing_debug_implementations)] +#![allow(unused_variables, non_snake_case, missing_debug_implementations, missing_docs)] /// Type representing basic blocks. /// diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index ddadeeb3c786a..c87e94eff9995 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -52,20 +52,20 @@ in the rest of the standard library", issue = "none" )] -#![allow(missing_docs)] use safety::{ensures, requires}; -use crate::ffi::va_list::{VaArgSafe, VaListImpl}; +use crate::ffi::va_list::{VaArgSafe, VaList}; #[cfg(kani)] use crate::kani; -use crate::marker::{ConstParamTy, Destruct, DiscriminantKind, PointeeSized, Tuple}; +use crate::marker::{ConstParamTy, DiscriminantKind, PointeeSized, Tuple}; #[cfg(kani)] use crate::ub_checks; use crate::{mem, ptr}; mod bounds; pub mod fallback; +pub mod gpu; pub mod mir; pub mod simd; @@ -77,6 +77,7 @@ use crate::sync::atomic::{self, AtomicBool, AtomicI32, AtomicIsize, AtomicU32, O /// A type for atomic ordering parameters for intrinsics. This is a separate type from /// `atomic::Ordering` so that we can make it `ConstParamTy` and fix the values used here without a /// risk of leaking that to stable code. +#[allow(missing_docs)] #[derive(Debug, ConstParamTy, PartialEq, Eq)] pub enum AtomicOrdering { // These values must match the compiler's `AtomicOrdering` defined in @@ -488,11 +489,14 @@ pub const fn unlikely(b: bool) -> bool { #[rustc_nounwind] #[miri::intrinsic_fallback_is_spec] #[inline] -pub const fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T -where - T: [const] Destruct, -{ - if b { true_val } else { false_val } +pub const fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T { + if b { + forget(false_val); + true_val + } else { + forget(true_val); + false_val + } } /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited: @@ -1316,6 +1320,7 @@ pub fn log2f128(x: f128) -> f128; /// /// The stabilized version of this intrinsic is /// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add) +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] pub const fn fmaf16(a: f16, b: f16, c: f16) -> f16; @@ -1323,6 +1328,7 @@ pub const fn fmaf16(a: f16, b: f16, c: f16) -> f16; /// /// The stabilized version of this intrinsic is /// [`f32::mul_add`](../../std/primitive.f32.html#method.mul_add) +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] pub const fn fmaf32(a: f32, b: f32, c: f32) -> f32; @@ -1330,6 +1336,7 @@ pub const fn fmaf32(a: f32, b: f32, c: f32) -> f32; /// /// The stabilized version of this intrinsic is /// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add) +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] pub const fn fmaf64(a: f64, b: f64, c: f64) -> f64; @@ -1337,6 +1344,7 @@ pub const fn fmaf64(a: f64, b: f64, c: f64) -> f64; /// /// The stabilized version of this intrinsic is /// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add) +#[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] #[rustc_nounwind] pub const fn fmaf128(a: f128, b: f128, c: f128) -> f128; @@ -2404,8 +2412,7 @@ where /// The `@capture` block declares which surrounding variables / expressions can be /// used inside the `if const`. /// Note that the two arms of this `if` really each become their own function, which is why the -/// macro supports setting attributes for those functions. The runtime function is always -/// marked as `#[inline]`. +/// macro supports setting attributes for those functions. Both functions are marked as `#[inline]`. /// /// See [`const_eval_select()`] for the rules and requirements around that intrinsic. pub(crate) macro const_eval_select { @@ -2415,35 +2422,14 @@ pub(crate) macro const_eval_select { $(#[$compiletime_attr:meta])* $compiletime:block else $(#[$runtime_attr:meta])* $runtime:block - ) => { - // Use the `noinline` arm, after adding explicit `inline` attributes - $crate::intrinsics::const_eval_select!( - @capture$([$($binders)*])? { $($arg : $ty = $val),* } $(-> $ret)? : - #[noinline] - if const - #[inline] // prevent codegen on this function - $(#[$compiletime_attr])* - $compiletime - else - #[inline] // avoid the overhead of an extra fn call - $(#[$runtime_attr])* - $runtime - ) - }, - // With a leading #[noinline], we don't add inline attributes - ( - @capture$([$($binders:tt)*])? { $($arg:ident : $ty:ty = $val:expr),* $(,)? } $( -> $ret:ty )? : - #[noinline] - if const - $(#[$compiletime_attr:meta])* $compiletime:block - else - $(#[$runtime_attr:meta])* $runtime:block ) => {{ + #[inline] $(#[$runtime_attr])* fn runtime$(<$($binders)*>)?($($arg: $ty),*) $( -> $ret )? { $runtime } + #[inline] $(#[$compiletime_attr])* const fn compiletime$(<$($binders)*>)?($($arg: $ty),*) $( -> $ret )? { // Don't warn if one of the arguments is unused. @@ -2649,7 +2635,7 @@ pub const unsafe fn const_allocate(_size: usize, _align: usize) -> *mut u8 { } /// Deallocates a memory which allocated by `intrinsics::const_allocate` at compile time. -/// At runtime, does nothing. +/// At runtime, it does nothing. /// /// # Safety /// @@ -2667,6 +2653,9 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) // Runtime NOP } +/// Convert the allocation this pointer points to into immutable global memory. +/// The pointer must point to the beginning of a heap allocation. +/// This operation only makes sense during compile time. At runtime, it does nothing. #[rustc_const_unstable(feature = "const_heap", issue = "79597")] #[rustc_nounwind] #[rustc_intrinsic] @@ -2775,6 +2764,18 @@ pub unsafe fn vtable_size(_ptr: *const ()) -> usize; // #[requires(ub_checks::can_dereference(_ptr as *const [usize; 3]))] pub unsafe fn vtable_align(_ptr: *const ()) -> usize; +/// The intrinsic returns the `U` vtable for `T` if `T` can be coerced to the trait object type `U`. +/// +/// # Compile-time failures +/// Determining whether `T` can be coerced to the trait object type `U` requires trait resolution by the compiler. +/// In some cases, that resolution can exceed the recursion limit, +/// and compilation will fail instead of this function returning `None`. +#[rustc_nounwind] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_intrinsic] +pub const fn vtable_for> + ?Sized>() +-> Option>; + /// The size of a type in bytes. /// /// Note that, unlike most intrinsics, this is safe to call; @@ -2876,6 +2877,15 @@ pub const unsafe fn size_of_val(ptr: *const T) -> usize; #[rustc_intrinsic_const_stable_indirect] pub const unsafe fn align_of_val(ptr: *const T) -> usize; +/// Compute the type information of a concrete type. +/// It can only be called at compile time, the backends do +/// not implement it. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +pub const fn type_of(_id: crate::any::TypeId) -> crate::mem::type_info::Type { + panic!("`TypeId::info` can only be called at compile-time") +} + /// Gets a static string slice containing the name of a type. /// /// Note that, unlike most intrinsics, this can only be called at compile-time @@ -3028,61 +3038,77 @@ pub const unsafe fn copy(src: *const T, dst: *mut T, count: usize); // #[cfg_attr(kani, kani::modifies(crate::ptr::slice_from_raw_parts(dst, count)))] pub const unsafe fn write_bytes(dst: *mut T, val: u8, count: usize); -/// Returns the minimum (IEEE 754-2008 minNum) of two `f16` values. +/// Returns the minimum of two `f16` values, ignoring NaN. +/// +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f16::min`] +/// The stabilized version of this intrinsic is [`f16::min`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn minnumf16(x: f16, y: f16) -> f16; -/// Returns the minimum (IEEE 754-2008 minNum) of two `f32` values. +/// Returns the minimum of two `f32` values, ignoring NaN. +/// +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f32::min`] +/// The stabilized version of this intrinsic is [`f32::min`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn minnumf32(x: f32, y: f32) -> f32; -/// Returns the minimum (IEEE 754-2008 minNum) of two `f64` values. +/// Returns the minimum of two `f64` values, ignoring NaN. +/// +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f64::min`] +/// The stabilized version of this intrinsic is [`f64::min`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn minnumf64(x: f64, y: f64) -> f64; -/// Returns the minimum (IEEE 754-2008 minNum) of two `f128` values. +/// Returns the minimum of two `f128` values, ignoring NaN. +/// +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f128::min`] +/// The stabilized version of this intrinsic is [`f128::min`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn minnumf128(x: f128, y: f128) -> f128; -/// Returns the minimum (IEEE 754-2019 minimum) of two `f16` values. +/// Returns the minimum of two `f16` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3103,7 +3129,11 @@ pub const fn minimumf16(x: f16, y: f16) -> f16 { } } -/// Returns the minimum (IEEE 754-2019 minimum) of two `f32` values. +/// Returns the minimum of two `f32` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3124,7 +3154,11 @@ pub const fn minimumf32(x: f32, y: f32) -> f32 { } } -/// Returns the minimum (IEEE 754-2019 minimum) of two `f64` values. +/// Returns the minimum of two `f64` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3145,7 +3179,11 @@ pub const fn minimumf64(x: f64, y: f64) -> f64 { } } -/// Returns the minimum (IEEE 754-2019 minimum) of two `f128` values. +/// Returns the minimum of two `f128` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 minimum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3166,61 +3204,77 @@ pub const fn minimumf128(x: f128, y: f128) -> f128 { } } -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f16` values. +/// Returns the maximum of two `f16` values, ignoring NaN. +/// +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f16::max`] +/// The stabilized version of this intrinsic is [`f16::max`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn maxnumf16(x: f16, y: f16) -> f16; -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f32` values. +/// Returns the maximum of two `f32` values, ignoring NaN. +/// +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f32::max`] +/// The stabilized version of this intrinsic is [`f32::max`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn maxnumf32(x: f32, y: f32) -> f32; -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f64` values. +/// Returns the maximum of two `f64` values, ignoring NaN. +/// +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f64::max`] +/// The stabilized version of this intrinsic is [`f64::max`]. #[rustc_nounwind] #[rustc_intrinsic_const_stable_indirect] #[rustc_intrinsic] pub const fn maxnumf64(x: f64, y: f64) -> f64; -/// Returns the maximum (IEEE 754-2008 maxNum) of two `f128` values. +/// Returns the maximum of two `f128` values, ignoring NaN. +/// +/// If one of the arguments is NaN (quiet or signaling), then the other argument is returned. If +/// both arguments are NaN, returns NaN. If the inputs compare equal (such as for the case of `+0.0` +/// and `-0.0`), either input may be returned non-deterministically. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. /// Therefore, implementations must not require the user to uphold /// any safety invariants. /// -/// The stabilized version of this intrinsic is -/// [`f128::max`] +/// The stabilized version of this intrinsic is [`f128::max`]. #[rustc_nounwind] #[rustc_intrinsic] pub const fn maxnumf128(x: f128, y: f128) -> f128; -/// Returns the maximum (IEEE 754-2019 maximum) of two `f16` values. +/// Returns the maximum of two `f16` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3240,7 +3294,11 @@ pub const fn maximumf16(x: f16, y: f16) -> f16 { } } -/// Returns the maximum (IEEE 754-2019 maximum) of two `f32` values. +/// Returns the maximum of two `f32` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3260,7 +3318,11 @@ pub const fn maximumf32(x: f32, y: f32) -> f32 { } } -/// Returns the maximum (IEEE 754-2019 maximum) of two `f64` values. +/// Returns the maximum of two `f64` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3280,7 +3342,11 @@ pub const fn maximumf64(x: f64, y: f64) -> f64 { } } -/// Returns the maximum (IEEE 754-2019 maximum) of two `f128` values. +/// Returns the maximum of two `f128` values, propagating NaN. +/// +/// This behaves like IEEE 754-2019 maximum. In particular: +/// If one of the arguments is NaN, then a NaN is returned using the usual NaN propagation rules. +/// For this operation, -0.0 is considered to be strictly less than +0.0. /// /// Note that, unlike most intrinsics, this is safe to call; /// it does not require an `unsafe` block. @@ -3405,6 +3471,49 @@ pub const fn copysignf128(x: f128, y: f128) -> f128; #[rustc_intrinsic] pub const fn autodiff(f: F, df: G, args: T) -> R; +/// Generates the LLVM body of a wrapper function to offload a kernel `f`. +/// +/// Type Parameters: +/// - `F`: The kernel to offload. Must be a function item. +/// - `T`: A tuple of arguments passed to `f`. +/// - `R`: The return type of the kernel. +/// +/// Arguments: +/// - `f`: The kernel function to offload. +/// - `workgroup_dim`: A 3D size specifying the number of workgroups to launch. +/// - `thread_dim`: A 3D size specifying the number of threads per workgroup. +/// - `args`: A tuple of arguments forwarded to `f`. +/// +/// Example usage (pseudocode): +/// +/// ```rust,ignore (pseudocode) +/// fn kernel(x: *mut [f64; 128]) { +/// core::intrinsics::offload(kernel_1, [256, 1, 1], [32, 1, 1], (x,)) +/// } +/// +/// #[cfg(target_os = "linux")] +/// extern "C" { +/// pub fn kernel_1(array_b: *mut [f64; 128]); +/// } +/// +/// #[cfg(not(target_os = "linux"))] +/// #[rustc_offload_kernel] +/// extern "gpu-kernel" fn kernel_1(x: *mut [f64; 128]) { +/// unsafe { (*x)[0] = 21.0 }; +/// } +/// ``` +/// +/// For reference, see the Clang documentation on offloading: +/// . +#[rustc_nounwind] +#[rustc_intrinsic] +pub const fn offload( + f: F, + workgroup_dim: [u32; 3], + thread_dim: [u32; 3], + args: T, +) -> R; + /// Inform Miri that a given pointer definitely has a certain alignment. #[cfg(miri)] #[rustc_allow_const_fn_unstable(const_eval_select)] @@ -3429,19 +3538,6 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize ) } -/// Copies the current location of arglist `src` to the arglist `dst`. -/// -/// # Safety -/// -/// You must check the following invariants before you call this function: -/// -/// - `dest` must be non-null and point to valid, writable memory. -/// - `dest` must not alias `src`. -/// -#[rustc_intrinsic] -#[rustc_nounwind] -pub unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); - /// Loads an argument of type `T` from the `va_list` `ap` and increment the /// argument `ap` points to. /// @@ -3458,9 +3554,30 @@ pub unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); /// #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn va_arg(ap: &mut VaListImpl<'_>) -> T; +pub unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +/// Duplicates a variable argument list. The returned list is initially at the same position as +/// the one in `src`, but can be advanced independently. +/// +/// Codegen backends should not have custom behavior for this intrinsic, they should always use +/// this fallback implementation. This intrinsic *does not* map to the LLVM `va_copy` intrinsic. +/// +/// This intrinsic exists only as a hook for Miri and constant evaluation, and is used to detect UB +/// when a variable argument list is used incorrectly. +#[rustc_intrinsic] +#[rustc_nounwind] +pub fn va_copy<'f>(src: &VaList<'f>) -> VaList<'f> { + src.duplicate() +} -/// Destroy the arglist `ap` after initialization with `va_start` or `va_copy`. +/// Destroy the variable argument list `ap` after initialization with `va_start` (part of the +/// desugaring of `...`) or `va_copy`. +/// +/// Code generation backends should not provide a custom implementation for this intrinsic. This +/// intrinsic *does not* map to the LLVM `va_end` intrinsic. +/// +/// This function is a no-op on all current targets, but used as a hook for const evaluation to +/// detect UB when a variable argument list is used incorrectly. /// /// # Safety /// @@ -3468,7 +3585,9 @@ pub unsafe fn va_arg(ap: &mut VaListImpl<'_>) -> T; /// #[rustc_intrinsic] #[rustc_nounwind] -pub unsafe fn va_end(ap: &mut VaListImpl<'_>); +pub unsafe fn va_end(ap: &mut VaList<'_>) { + /* deliberately does nothing */ +} #[cfg(kani)] #[unstable(feature = "kani", issue = "none")] diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index 722a765cd01ee..f70262c38ae50 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -37,11 +37,7 @@ pub const unsafe fn simd_extract(x: T, idx: u32) -> U; /// `idx` must be in-bounds of the vector. #[rustc_nounwind] #[rustc_intrinsic] -pub unsafe fn simd_insert_dyn(mut x: T, idx: u32, val: U) -> T { - // SAFETY: `idx` must be in-bounds - unsafe { (&raw mut x).cast::().add(idx as usize).write(val) } - x -} +pub const unsafe fn simd_insert_dyn(x: T, idx: u32, val: U) -> T; /// Extracts an element from a vector. /// @@ -54,10 +50,14 @@ pub unsafe fn simd_insert_dyn(mut x: T, idx: u32, val: U) -> T { /// `idx` must be in-bounds of the vector. #[rustc_nounwind] #[rustc_intrinsic] -pub unsafe fn simd_extract_dyn(x: T, idx: u32) -> U { - // SAFETY: `idx` must be in-bounds - unsafe { (&raw const x).cast::().add(idx as usize).read() } -} +pub const unsafe fn simd_extract_dyn(x: T, idx: u32) -> U; + +/// Creates a vector where every lane has the provided value. +/// +/// `T` must be a vector with element type `U`. +#[rustc_nounwind] +#[rustc_intrinsic] +pub const unsafe fn simd_splat(value: U) -> T; /// Adds two simd vectors elementwise. /// diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs index 967136288865c..7c003cff10c7b 100644 --- a/library/core/src/iter/adapters/array_chunks.rs +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -15,7 +15,7 @@ use crate::ops::{ControlFlow, NeverShortCircuit, Try}; /// method on [`Iterator`]. See its documentation for more. #[derive(Debug, Clone)] #[must_use = "iterators are lazy and do nothing unless consumed"] -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +#[unstable(feature = "iter_array_chunks", issue = "100450")] pub struct ArrayChunks { iter: I, remainder: Option>, @@ -44,7 +44,7 @@ where /// assert_eq!(rem.next(), Some(5)); /// assert_eq!(rem.next(), None); /// ``` - #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] + #[unstable(feature = "iter_array_chunks", issue = "100450")] #[inline] pub fn into_remainder(mut self) -> array::IntoIter { if self.remainder.is_none() { @@ -54,7 +54,7 @@ where } } -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +#[unstable(feature = "iter_array_chunks", issue = "100450")] impl Iterator for ArrayChunks where I: Iterator, @@ -89,7 +89,7 @@ where match self.iter.next_chunk() { Ok(chunk) => acc = f(acc, chunk)?, Err(remainder) => { - // Make sure to not override `self.remainder` with an empty array + // Make sure to not overwrite `self.remainder` with an empty array // when `next` is called after `ArrayChunks` exhaustion. self.remainder.get_or_insert(remainder); @@ -108,7 +108,7 @@ where } } -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +#[unstable(feature = "iter_array_chunks", issue = "100450")] impl DoubleEndedIterator for ArrayChunks where I: DoubleEndedIterator + ExactSizeIterator, @@ -173,13 +173,13 @@ where } } -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +#[unstable(feature = "iter_array_chunks", issue = "100450")] impl FusedIterator for ArrayChunks where I: FusedIterator {} #[unstable(issue = "none", feature = "trusted_fused")] unsafe impl TrustedFused for ArrayChunks where I: TrustedFused + Iterator {} -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +#[unstable(feature = "iter_array_chunks", issue = "100450")] impl ExactSizeIterator for ArrayChunks where I: ExactSizeIterator, diff --git a/library/core/src/iter/adapters/filter.rs b/library/core/src/iter/adapters/filter.rs index dd08cd6f61c4c..b22419ccf080a 100644 --- a/library/core/src/iter/adapters/filter.rs +++ b/library/core/src/iter/adapters/filter.rs @@ -4,7 +4,7 @@ use core::ops::ControlFlow; use crate::fmt; use crate::iter::adapters::SourceIter; -use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused}; +use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused, TrustedLen}; use crate::num::NonZero; use crate::ops::Try; @@ -138,7 +138,13 @@ where move |x| predicate(&x) as usize } - self.iter.map(to_usize(self.predicate)).sum() + let before = self.iter.size_hint().1.unwrap_or(usize::MAX); + let total = self.iter.map(to_usize(self.predicate)).sum(); + // SAFETY: `total` and `before` came from the same iterator of type `I` + unsafe { + ::assume_count_le_upper_bound(total, before); + } + total } #[inline] @@ -214,3 +220,34 @@ unsafe impl InPlaceIterable for Filter { const EXPAND_BY: Option> = I::EXPAND_BY; const MERGE_BY: Option> = I::MERGE_BY; } + +trait SpecAssumeCount { + /// # Safety + /// + /// `count` must be an number of items actually read from the iterator. + /// + /// `upper` must either: + /// - have come from `size_hint().1` on the iterator, or + /// - be `usize::MAX` which will vacuously do nothing. + unsafe fn assume_count_le_upper_bound(count: usize, upper: usize); +} + +impl SpecAssumeCount for I { + #[inline] + #[rustc_inherit_overflow_checks] + default unsafe fn assume_count_le_upper_bound(count: usize, upper: usize) { + // In the default we can't trust the `upper` for soundness + // because it came from an untrusted `size_hint`. + + // In debug mode we might as well check that the size_hint wasn't too small + let _ = upper - count; + } +} + +impl SpecAssumeCount for I { + #[inline] + unsafe fn assume_count_le_upper_bound(count: usize, upper: usize) { + // SAFETY: The `upper` is trusted because it came from a `TrustedLen` iterator. + unsafe { crate::hint::assert_unchecked(count <= upper) } + } +} diff --git a/library/core/src/iter/adapters/intersperse.rs b/library/core/src/iter/adapters/intersperse.rs index 843479e2a27a7..bb94ed0a0a170 100644 --- a/library/core/src/iter/adapters/intersperse.rs +++ b/library/core/src/iter/adapters/intersperse.rs @@ -5,7 +5,7 @@ use crate::iter::{Fuse, FusedIterator}; /// /// This `struct` is created by [`Iterator::intersperse`]. See its documentation /// for more information. -#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +#[unstable(feature = "iter_intersperse", issue = "79524")] #[derive(Debug, Clone)] pub struct Intersperse where @@ -17,7 +17,7 @@ where iter: Fuse, } -#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +#[unstable(feature = "iter_intersperse", issue = "79524")] impl FusedIterator for Intersperse where I: FusedIterator, @@ -34,7 +34,7 @@ where } } -#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +#[unstable(feature = "iter_intersperse", issue = "79524")] impl Iterator for Intersperse where I: Iterator, @@ -87,7 +87,7 @@ where /// /// This `struct` is created by [`Iterator::intersperse_with`]. See its /// documentation for more information. -#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +#[unstable(feature = "iter_intersperse", issue = "79524")] pub struct IntersperseWith where I: Iterator, @@ -98,7 +98,7 @@ where iter: Fuse, } -#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +#[unstable(feature = "iter_intersperse", issue = "79524")] impl FusedIterator for IntersperseWith where I: FusedIterator, @@ -106,7 +106,7 @@ where { } -#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +#[unstable(feature = "iter_intersperse", issue = "79524")] impl fmt::Debug for IntersperseWith where I: Iterator + fmt::Debug, @@ -123,7 +123,7 @@ where } } -#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +#[unstable(feature = "iter_intersperse", issue = "79524")] impl Clone for IntersperseWith where I: Iterator + Clone, @@ -150,7 +150,7 @@ where } } -#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +#[unstable(feature = "iter_intersperse", issue = "79524")] impl Iterator for IntersperseWith where I: Iterator, diff --git a/library/core/src/iter/adapters/map_windows.rs b/library/core/src/iter/adapters/map_windows.rs index 536251a137da0..3d5918a552c61 100644 --- a/library/core/src/iter/adapters/map_windows.rs +++ b/library/core/src/iter/adapters/map_windows.rs @@ -8,7 +8,7 @@ use crate::{fmt, ptr}; /// This `struct` is created by the [`Iterator::map_windows`]. See its /// documentation for more information. #[must_use = "iterators are lazy and do nothing unless consumed"] -#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +#[unstable(feature = "iter_map_windows", issue = "87155")] pub struct MapWindows { f: F, inner: MapWindowsInner, @@ -235,7 +235,7 @@ impl Drop for Buffer { } } -#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +#[unstable(feature = "iter_map_windows", issue = "87155")] impl Iterator for MapWindows where I: Iterator, @@ -256,7 +256,7 @@ where // Note that even if the inner iterator not fused, the `MapWindows` is still fused, // because we don't allow "holes" in the mapping window. -#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +#[unstable(feature = "iter_map_windows", issue = "87155")] impl FusedIterator for MapWindows where I: Iterator, @@ -264,7 +264,7 @@ where { } -#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +#[unstable(feature = "iter_map_windows", issue = "87155")] impl ExactSizeIterator for MapWindows where I: ExactSizeIterator, @@ -272,14 +272,14 @@ where { } -#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +#[unstable(feature = "iter_map_windows", issue = "87155")] impl fmt::Debug for MapWindows { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("MapWindows").field("iter", &self.inner.iter).finish() } } -#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +#[unstable(feature = "iter_map_windows", issue = "87155")] impl Clone for MapWindows where I: Iterator + Clone, diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 1ff5093922b6d..d0b89fdbb5843 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -28,7 +28,7 @@ mod take; mod take_while; mod zip; -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +#[unstable(feature = "iter_array_chunks", issue = "100450")] pub use self::array_chunks::ArrayChunks; #[unstable(feature = "std_internals", issue = "none")] pub use self::by_ref_sized::ByRefSized; @@ -40,11 +40,11 @@ pub use self::cloned::Cloned; pub use self::copied::Copied; #[stable(feature = "iterator_flatten", since = "1.29.0")] pub use self::flatten::Flatten; -#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +#[unstable(feature = "iter_intersperse", issue = "79524")] pub use self::intersperse::{Intersperse, IntersperseWith}; #[stable(feature = "iter_map_while", since = "1.57.0")] pub use self::map_while::MapWhile; -#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +#[unstable(feature = "iter_map_windows", issue = "87155")] pub use self::map_windows::MapWindows; #[stable(feature = "iterator_step_by", since = "1.28.0")] pub use self::step_by::StepBy; diff --git a/library/core/src/iter/adapters/peekable.rs b/library/core/src/iter/adapters/peekable.rs index a55de75d56c6e..9f6d1df57dbe8 100644 --- a/library/core/src/iter/adapters/peekable.rs +++ b/library/core/src/iter/adapters/peekable.rs @@ -331,11 +331,12 @@ impl Peekable { /// If the closure panics, the next value will always be consumed and dropped /// even if the panic is caught, because the closure never returned an `Err` value to put back. /// + /// See also: [`next_if_map_mut`](Self::next_if_map_mut). + /// /// # Examples /// /// Parse the leading decimal number from an iterator of characters. /// ``` - /// #![feature(peekable_next_if_map)] /// let mut iter = "125 GOTO 10".chars().peekable(); /// let mut line_num = 0_u32; /// while let Some(digit) = iter.next_if_map(|c| c.to_digit(10).ok_or(c)) { @@ -347,7 +348,6 @@ impl Peekable { /// /// Matching custom types. /// ``` - /// #![feature(peekable_next_if_map)] /// /// #[derive(Debug, PartialEq, Eq)] /// enum Node { @@ -406,7 +406,7 @@ impl Peekable { ///# ], ///# ) /// ``` - #[unstable(feature = "peekable_next_if_map", issue = "143702")] + #[stable(feature = "peekable_next_if_map", since = "1.94.0")] pub fn next_if_map(&mut self, f: impl FnOnce(I::Item) -> Result) -> Option { let unpeek = if let Some(item) = self.next() { match f(item) { @@ -419,6 +419,43 @@ impl Peekable { self.peeked = Some(unpeek); None } + + /// Gives a mutable reference to the next value of the iterator and applies a function `f` to it, + /// returning the result and advancing the iterator if `f` returns `Some`. + /// + /// Otherwise, if `f` returns `None`, the next value is kept for the next iteration. + /// + /// If `f` panics, the item that is consumed from the iterator as if `Some` was returned from `f`. + /// The value will be dropped. + /// + /// This is similar to [`next_if_map`](Self::next_if_map), except ownership of the item is not given to `f`. + /// This can be preferable if `f` would copy the item anyway. + /// + /// # Examples + /// + /// Parse the leading decimal number from an iterator of characters. + /// ``` + /// let mut iter = "125 GOTO 10".chars().peekable(); + /// let mut line_num = 0_u32; + /// while let Some(digit) = iter.next_if_map_mut(|c| c.to_digit(10)) { + /// line_num = line_num * 10 + digit; + /// } + /// assert_eq!(line_num, 125); + /// assert_eq!(iter.collect::(), " GOTO 10"); + /// ``` + #[stable(feature = "peekable_next_if_map", since = "1.94.0")] + pub fn next_if_map_mut(&mut self, f: impl FnOnce(&mut I::Item) -> Option) -> Option { + let unpeek = if let Some(mut item) = self.next() { + match f(&mut item) { + Some(result) => return Some(result), + None => Some(item), + } + } else { + None + }; + self.peeked = Some(unpeek); + None + } } #[unstable(feature = "trusted_len", issue = "37572")] diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index c7e1c4ef767ba..d532f1e568071 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -382,7 +382,7 @@ macro_rules! impl_fold_via_try_fold { }; } -#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +#[unstable(feature = "iter_array_chunks", issue = "100450")] pub use self::adapters::ArrayChunks; #[unstable(feature = "std_internals", issue = "none")] pub use self::adapters::ByRefSized; @@ -394,7 +394,7 @@ pub use self::adapters::Copied; pub use self::adapters::Flatten; #[stable(feature = "iter_map_while", since = "1.57.0")] pub use self::adapters::MapWhile; -#[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] +#[unstable(feature = "iter_map_windows", issue = "87155")] pub use self::adapters::MapWindows; #[unstable(feature = "inplace_iteration", issue = "none")] pub use self::adapters::SourceIter; @@ -414,7 +414,7 @@ pub use self::adapters::{ Chain, Cycle, Enumerate, Filter, FilterMap, FlatMap, Fuse, Inspect, Map, Peekable, Rev, Scan, Skip, SkipWhile, Take, TakeWhile, Zip, }; -#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] +#[unstable(feature = "iter_intersperse", issue = "79524")] pub use self::adapters::{Intersperse, IntersperseWith}; #[unstable( feature = "step_trait", diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 79cc6873adc7d..8e71150aef552 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -25,6 +25,13 @@ unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u6 /// The *successor* operation moves towards values that compare greater. /// The *predecessor* operation moves towards values that compare lesser. #[rustc_diagnostic_item = "range_step"] +#[rustc_on_unimplemented( + message = "`std::ops::Range<{Self}>` is not an iterator", + label = "`Range<{Self}>` is not an iterator", + note = "`Range` only implements `Iterator` for select types in the standard library, \ + particularly integers; to see the full list of types, see the documentation for the \ + unstable `Step` trait" +)] #[unstable(feature = "step_trait", issue = "42168")] pub trait Step: Clone + PartialOrd + Sized { /// Returns the bounds on the number of *successor* steps required to get from `start` to `end` @@ -262,7 +269,7 @@ macro_rules! step_integer_impls { } => { $( #[allow(unreachable_patterns)] - #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] + #[unstable(feature = "step_trait", issue = "42168")] impl Step for $u_narrower { step_identical_methods!(); step_unsigned_methods!(); @@ -296,7 +303,7 @@ macro_rules! step_integer_impls { } #[allow(unreachable_patterns)] - #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] + #[unstable(feature = "step_trait", issue = "42168")] impl Step for $i_narrower { step_identical_methods!(); step_signed_methods!($u_narrower); @@ -362,7 +369,7 @@ macro_rules! step_integer_impls { $( #[allow(unreachable_patterns)] - #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] + #[unstable(feature = "step_trait", issue = "42168")] impl Step for $u_wider { step_identical_methods!(); step_unsigned_methods!(); @@ -392,7 +399,7 @@ macro_rules! step_integer_impls { } #[allow(unreachable_patterns)] - #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] + #[unstable(feature = "step_trait", issue = "42168")] impl Step for $i_wider { step_identical_methods!(); step_signed_methods!($u_wider); @@ -449,7 +456,7 @@ step_integer_impls! { wider than usize: [u32 i32], [u64 i64], [u128 i128]; } -#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +#[unstable(feature = "step_trait", issue = "42168")] impl Step for char { #[inline] fn steps_between(&start: &char, &end: &char) -> (usize, Option) { @@ -550,7 +557,7 @@ impl Step for char { } } -#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +#[unstable(feature = "step_trait", issue = "42168")] impl Step for AsciiChar { #[inline] fn steps_between(&start: &AsciiChar, &end: &AsciiChar) -> (usize, Option) { @@ -594,7 +601,7 @@ impl Step for AsciiChar { } } -#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +#[unstable(feature = "step_trait", issue = "42168")] impl Step for Ipv4Addr { #[inline] fn steps_between(&start: &Ipv4Addr, &end: &Ipv4Addr) -> (usize, Option) { @@ -628,7 +635,7 @@ impl Step for Ipv4Addr { } } -#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +#[unstable(feature = "step_trait", issue = "42168")] impl Step for Ipv6Addr { #[inline] fn steps_between(&start: &Ipv6Addr, &end: &Ipv6Addr) -> (usize, Option) { diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index cdf81385bdafb..9c3edfd4192d5 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -279,8 +279,10 @@ pub trait FromIterator
: Sized { )] #[rustc_skip_during_method_dispatch(array, boxed_slice)] #[stable(feature = "rust1", since = "1.0.0")] -pub trait IntoIterator { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +pub const trait IntoIterator { /// The type of the elements being iterated over. + #[rustc_diagnostic_item = "IntoIteratorItem"] #[stable(feature = "rust1", since = "1.0.0")] type Item; @@ -311,7 +313,8 @@ pub trait IntoIterator { } #[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for I { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +impl const IntoIterator for I { type Item = I::Item; type IntoIter = I; diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index 7dabaece95561..9f7ac7da2dbda 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -134,7 +134,7 @@ pub trait DoubleEndedIterator: Iterator { /// [`Ok(())`]: Ok /// [`Err(k)`]: Err #[inline] - #[unstable(feature = "iter_advance_by", reason = "recently added", issue = "77404")] + #[unstable(feature = "iter_advance_by", issue = "77404")] fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { for i in 0..n { if self.next_back().is_none() { @@ -334,8 +334,18 @@ pub trait DoubleEndedIterator: Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// assert_eq!(a.iter().rfind(|&&x| x == 2), Some(&2)); + /// assert_eq!(a.into_iter().rfind(|&x| x == 2), Some(2)); + /// assert_eq!(a.into_iter().rfind(|&x| x == 5), None); + /// ``` /// + /// Iterating over references: + /// + /// ``` + /// let a = [1, 2, 3]; + /// + /// // `iter()` yields references i.e. `&i32` and `rfind()` takes a + /// // reference to each element. + /// assert_eq!(a.iter().rfind(|&&x| x == 2), Some(&2)); /// assert_eq!(a.iter().rfind(|&&x| x == 5), None); /// ``` /// diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 0ce2b2abfbd68..13202e6cb5888 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -41,7 +41,8 @@ fn _assert_is_dyn_compatible(_: &dyn Iterator) {} #[lang = "iterator"] #[rustc_diagnostic_item = "Iterator"] #[must_use = "iterators are lazy and do nothing unless consumed"] -pub trait Iterator { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +pub const trait Iterator { /// The type of the elements being iterated over. #[rustc_diagnostic_item = "IteratorItem"] #[stable(feature = "rust1", since = "1.0.0")] @@ -110,7 +111,8 @@ pub trait Iterator { /// assert_eq!(third, "those"); /// ``` #[inline] - #[unstable(feature = "iter_next_chunk", reason = "recently added", issue = "98326")] + #[unstable(feature = "iter_next_chunk", issue = "98326")] + #[rustc_non_const_trait_method] fn next_chunk( &mut self, ) -> Result<[Self::Item; N], array::IntoIter> @@ -223,6 +225,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn count(self) -> usize where Self: Sized, @@ -240,6 +243,10 @@ pub trait Iterator { /// doing so, it keeps track of the current element. After [`None`] is /// returned, `last()` will then return the last element it saw. /// + /// # Panics + /// + /// This function might panic if the iterator is infinite. + /// /// # Examples /// /// ``` @@ -251,6 +258,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn last(self) -> Option where Self: Sized, @@ -297,7 +305,8 @@ pub trait Iterator { /// assert_eq!(iter.advance_by(100), Err(NonZero::new(99).unwrap())); // only `4` was skipped /// ``` #[inline] - #[unstable(feature = "iter_advance_by", reason = "recently added", issue = "77404")] + #[unstable(feature = "iter_advance_by", issue = "77404")] + #[rustc_non_const_trait_method] fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { /// Helper trait to specialize `advance_by` via `try_fold` for `Sized` iterators. trait SpecAdvanceBy { @@ -375,6 +384,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn nth(&mut self, n: usize) -> Option { self.advance_by(n).ok()?; self.next() @@ -425,6 +435,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_step_by", since = "1.28.0")] + #[rustc_non_const_trait_method] fn step_by(self, step: usize) -> StepBy where Self: Sized, @@ -496,6 +507,7 @@ pub trait Iterator { /// [`OsStr`]: ../../std/ffi/struct.OsStr.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn chain(self, other: U) -> Chain where Self: Sized, @@ -614,6 +626,7 @@ pub trait Iterator { /// [`zip`]: crate::iter::zip #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn zip(self, other: U) -> Zip where Self: Sized, @@ -656,7 +669,8 @@ pub trait Iterator { /// [`Clone`]: crate::clone::Clone /// [`intersperse_with`]: Iterator::intersperse_with #[inline] - #[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] + #[unstable(feature = "iter_intersperse", issue = "79524")] + #[rustc_non_const_trait_method] fn intersperse(self, separator: Self::Item) -> Intersperse where Self: Sized, @@ -714,7 +728,8 @@ pub trait Iterator { /// [`Clone`]: crate::clone::Clone /// [`intersperse`]: Iterator::intersperse #[inline] - #[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")] + #[unstable(feature = "iter_intersperse", issue = "79524")] + #[rustc_non_const_trait_method] fn intersperse_with(self, separator: G) -> IntersperseWith where Self: Sized, @@ -774,6 +789,7 @@ pub trait Iterator { #[rustc_diagnostic_item = "IteratorMap"] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn map(self, f: F) -> Map where Self: Sized, @@ -819,6 +835,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_for_each", since = "1.21.0")] + #[rustc_non_const_trait_method] fn for_each(self, f: F) where Self: Sized, @@ -894,6 +911,7 @@ pub trait Iterator { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "iter_filter"] + #[rustc_non_const_trait_method] fn filter

(self, predicate: P) -> Filter where Self: Sized, @@ -939,6 +957,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn filter_map(self, f: F) -> FilterMap where Self: Sized, @@ -986,6 +1005,7 @@ pub trait Iterator { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "enumerate_method"] + #[rustc_non_const_trait_method] fn enumerate(self) -> Enumerate where Self: Sized, @@ -1057,6 +1077,7 @@ pub trait Iterator { /// [`next`]: Iterator::next #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn peekable(self) -> Peekable where Self: Sized, @@ -1122,6 +1143,7 @@ pub trait Iterator { #[inline] #[doc(alias = "drop_while")] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn skip_while

(self, predicate: P) -> SkipWhile where Self: Sized, @@ -1200,6 +1222,7 @@ pub trait Iterator { /// the iteration should stop, but wasn't placed back into the iterator. #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn take_while

(self, predicate: P) -> TakeWhile where Self: Sized, @@ -1288,6 +1311,7 @@ pub trait Iterator { /// [`fuse`]: Iterator::fuse #[inline] #[stable(feature = "iter_map_while", since = "1.57.0")] + #[rustc_non_const_trait_method] fn map_while(self, predicate: P) -> MapWhile where Self: Sized, @@ -1317,6 +1341,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn skip(self, n: usize) -> Skip where Self: Sized, @@ -1389,6 +1414,7 @@ pub trait Iterator { #[doc(alias = "limit")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn take(self, n: usize) -> Take where Self: Sized, @@ -1436,6 +1462,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn scan(self, initial_state: St, f: F) -> Scan where Self: Sized, @@ -1474,6 +1501,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn flat_map(self, f: F) -> FlatMap where Self: Sized, @@ -1558,6 +1586,7 @@ pub trait Iterator { /// [`flat_map()`]: Iterator::flat_map #[inline] #[stable(feature = "iterator_flatten", since = "1.29.0")] + #[rustc_non_const_trait_method] fn flatten(self) -> Flatten where Self: Sized, @@ -1713,7 +1742,8 @@ pub trait Iterator { /// assert_eq!(iter.next(), None); /// ``` #[inline] - #[unstable(feature = "iter_map_windows", reason = "recently added", issue = "87155")] + #[unstable(feature = "iter_map_windows", issue = "87155")] + #[rustc_non_const_trait_method] #[requires(N > 0)] fn map_windows(self, f: F) -> MapWindows where @@ -1777,6 +1807,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn fuse(self) -> Fuse where Self: Sized, @@ -1861,6 +1892,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn inspect(self, f: F) -> Inspect where Self: Sized, @@ -1907,9 +1939,12 @@ pub trait Iterator { /// Transforms an iterator into a collection. /// - /// `collect()` can take anything iterable, and turn it into a relevant - /// collection. This is one of the more powerful methods in the standard - /// library, used in a variety of contexts. + /// `collect()` takes ownership of an iterator and produces whichever + /// collection type you request. The iterator itself carries no knowledge of + /// the eventual container; the target collection is chosen entirely by the + /// type you ask `collect()` to return. This makes `collect()` one of the + /// more powerful methods in the standard library, and it shows up in a wide + /// variety of contexts. /// /// The most basic pattern in which `collect()` is used is to turn one /// collection into another. You take a collection, call [`iter`] on it, @@ -2017,6 +2052,7 @@ pub trait Iterator { #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead"] #[rustc_diagnostic_item = "iterator_collect_fn"] + #[rustc_non_const_trait_method] fn collect>(self) -> B where Self: Sized, @@ -2104,6 +2140,7 @@ pub trait Iterator { /// [`collect`]: Iterator::collect #[inline] #[unstable(feature = "iterator_try_collect", issue = "94047")] + #[rustc_non_const_trait_method] fn try_collect(&mut self) -> ChangeOutputType where Self: Sized, @@ -2175,7 +2212,8 @@ pub trait Iterator { /// assert_eq!(vec, vec![1, 2, 3, 1, 2, 3]); /// ``` #[inline] - #[unstable(feature = "iter_collect_into", reason = "new API", issue = "94780")] + #[unstable(feature = "iter_collect_into", issue = "94780")] + #[rustc_non_const_trait_method] fn collect_into>(self, collection: &mut E) -> &mut E where Self: Sized, @@ -2208,6 +2246,7 @@ pub trait Iterator { /// assert_eq!(odd, [1, 3]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn partition(self, f: F) -> (B, B) where Self: Sized, @@ -2269,7 +2308,8 @@ pub trait Iterator { /// assert!(a[..i].iter().all(|n| n % 2 == 0)); // evens /// assert!(a[i..].iter().all(|n| n % 2 == 1)); // odds /// ``` - #[unstable(feature = "iter_partition_in_place", reason = "new API", issue = "62543")] + #[unstable(feature = "iter_partition_in_place", issue = "62543")] + #[rustc_non_const_trait_method] fn partition_in_place<'a, T: 'a, P>(mut self, ref mut predicate: P) -> usize where Self: Sized + DoubleEndedIterator, @@ -2326,7 +2366,8 @@ pub trait Iterator { /// assert!("Iterator".chars().is_partitioned(char::is_uppercase)); /// assert!(!"IntoIterator".chars().is_partitioned(char::is_uppercase)); /// ``` - #[unstable(feature = "iter_is_partitioned", reason = "new API", issue = "62544")] + #[unstable(feature = "iter_is_partitioned", issue = "62544")] + #[rustc_non_const_trait_method] fn is_partitioned

(mut self, mut predicate: P) -> bool where Self: Sized, @@ -2421,6 +2462,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_try_fold", since = "1.27.0")] + #[rustc_non_const_trait_method] fn try_fold(&mut self, init: B, mut f: F) -> R where Self: Sized, @@ -2479,6 +2521,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_try_fold", since = "1.27.0")] + #[rustc_non_const_trait_method] fn try_for_each(&mut self, f: F) -> R where Self: Sized, @@ -2598,6 +2641,7 @@ pub trait Iterator { #[doc(alias = "inject", alias = "foldl")] #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn fold(mut self, init: B, mut f: F) -> B where Self: Sized, @@ -2635,6 +2679,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_fold_self", since = "1.51.0")] + #[rustc_non_const_trait_method] fn reduce(mut self, f: F) -> Option where Self: Sized, @@ -2705,7 +2750,8 @@ pub trait Iterator { /// assert_eq!(max, Ok(Some("5"))); /// ``` #[inline] - #[unstable(feature = "iterator_try_reduce", reason = "new API", issue = "87053")] + #[unstable(feature = "iterator_try_reduce", issue = "87053")] + #[rustc_non_const_trait_method] fn try_reduce( &mut self, f: impl FnMut(Self::Item, Self::Item) -> R, @@ -2764,6 +2810,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn all(&mut self, f: F) -> bool where Self: Sized, @@ -2817,6 +2864,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn any(&mut self, f: F) -> bool where Self: Sized, @@ -2863,6 +2911,17 @@ pub trait Iterator { /// assert_eq!(a.into_iter().find(|&x| x == 5), None); /// ``` /// + /// Iterating over references: + /// + /// ``` + /// let a = [1, 2, 3]; + /// + /// // `iter()` yields references i.e. `&i32` and `find()` takes a + /// // reference to each element. + /// assert_eq!(a.iter().find(|&&x| x == 2), Some(&2)); + /// assert_eq!(a.iter().find(|&&x| x == 5), None); + /// ``` + /// /// Stopping at the first `true`: /// /// ``` @@ -2879,6 +2938,7 @@ pub trait Iterator { /// Note that `iter.find(f)` is equivalent to `iter.filter(f).next()`. #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn find

(&mut self, predicate: P) -> Option where Self: Sized, @@ -2910,6 +2970,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iterator_find_map", since = "1.30.0")] + #[rustc_non_const_trait_method] fn find_map(&mut self, f: F) -> Option where Self: Sized, @@ -2967,7 +3028,8 @@ pub trait Iterator { /// assert_eq!(result, None); /// ``` #[inline] - #[unstable(feature = "try_find", reason = "new API", issue = "63178")] + #[unstable(feature = "try_find", issue = "63178")] + #[rustc_non_const_trait_method] fn try_find( &mut self, f: impl FnMut(&Self::Item) -> R, @@ -3051,6 +3113,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn position

(&mut self, predicate: P) -> Option where Self: Sized, @@ -3116,6 +3179,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn rposition

(self) -> P where Self: Sized, @@ -3631,6 +3709,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().cmp([1].iter()), Ordering::Greater); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn cmp(self, other: I) -> Ordering where I: IntoIterator, @@ -3658,6 +3737,7 @@ pub trait Iterator { /// assert_eq!(xs.into_iter().cmp_by(ys, |x, y| (2 * x).cmp(&y)), Ordering::Greater); /// ``` #[unstable(feature = "iter_order_by", issue = "64295")] + #[rustc_non_const_trait_method] fn cmp_by(self, other: I, cmp: F) -> Ordering where Self: Sized, @@ -3714,6 +3794,7 @@ pub trait Iterator { /// ``` /// #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn partial_cmp(self, other: I) -> Option where I: IntoIterator, @@ -3750,6 +3831,7 @@ pub trait Iterator { /// ); /// ``` #[unstable(feature = "iter_order_by", issue = "64295")] + #[rustc_non_const_trait_method] fn partial_cmp_by(self, other: I, partial_cmp: F) -> Option where Self: Sized, @@ -3783,6 +3865,7 @@ pub trait Iterator { /// assert_eq!([1].iter().eq([1, 2].iter()), false); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn eq(self, other: I) -> bool where I: IntoIterator, @@ -3806,6 +3889,7 @@ pub trait Iterator { /// assert!(xs.iter().eq_by(ys, |x, y| x * x == y)); /// ``` #[unstable(feature = "iter_order_by", issue = "64295")] + #[rustc_non_const_trait_method] fn eq_by(self, other: I, eq: F) -> bool where Self: Sized, @@ -3835,6 +3919,7 @@ pub trait Iterator { /// assert_eq!([1].iter().ne([1, 2].iter()), true); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn ne(self, other: I) -> bool where I: IntoIterator, @@ -3856,6 +3941,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().lt([1, 2].iter()), false); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn lt(self, other: I) -> bool where I: IntoIterator, @@ -3877,6 +3963,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().le([1, 2].iter()), true); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn le(self, other: I) -> bool where I: IntoIterator, @@ -3898,6 +3985,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().gt([1, 2].iter()), false); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn gt(self, other: I) -> bool where I: IntoIterator, @@ -3919,6 +4007,7 @@ pub trait Iterator { /// assert_eq!([1, 2].iter().ge([1, 2].iter()), true); /// ``` #[stable(feature = "iter_order", since = "1.5.0")] + #[rustc_non_const_trait_method] fn ge(self, other: I) -> bool where I: IntoIterator, @@ -3948,6 +4037,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "is_sorted", since = "1.82.0")] + #[rustc_non_const_trait_method] fn is_sorted(self) -> bool where Self: Sized, @@ -3974,6 +4064,7 @@ pub trait Iterator { /// assert!(std::iter::empty::().is_sorted_by(|a, b| true)); /// ``` #[stable(feature = "is_sorted", since = "1.82.0")] + #[rustc_non_const_trait_method] fn is_sorted_by(mut self, compare: F) -> bool where Self: Sized, @@ -4018,6 +4109,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "is_sorted", since = "1.82.0")] + #[rustc_non_const_trait_method] fn is_sorted_by_key(self, f: F) -> bool where Self: Sized, @@ -4033,6 +4125,7 @@ pub trait Iterator { #[inline] #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] + #[rustc_non_const_trait_method] unsafe fn __iterator_get_unchecked(&mut self, _idx: usize) -> Self::Item where Self: TrustedRandomAccessNoCoerce, diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 3619e2f544947..f63afa5aac4d3 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -97,7 +97,6 @@ // tidy-alphabetical-start #![feature(array_ptr_get)] #![feature(asm_experimental_arch)] -#![feature(bigint_helper_methods)] #![feature(bstr)] #![feature(bstr_internals)] #![feature(cfg_select)] @@ -107,13 +106,13 @@ #![feature(const_destruct)] #![feature(const_eval_select)] #![feature(const_select_unpredictable)] +#![feature(const_unsigned_bigint_helpers)] #![feature(core_intrinsics)] #![feature(coverage_attribute)] #![feature(disjoint_bitor)] #![feature(internal_impls_macro)] #![feature(ip)] #![feature(is_ascii_octdigit)] -#![feature(lazy_get)] #![feature(link_cfg)] #![feature(offset_of_enum)] #![feature(panic_internals)] @@ -121,16 +120,17 @@ #![feature(ptr_alignment_type)] #![feature(ptr_metadata)] #![feature(set_ptr_value)] +#![feature(signed_bigint_helpers)] #![feature(slice_ptr_get)] #![feature(str_internals)] #![feature(str_split_inclusive_remainder)] #![feature(str_split_remainder)] +#![feature(type_info)] #![feature(ub_checks)] -#![feature(unchecked_neg)] -#![feature(unchecked_shifts)] #![feature(unsafe_pinned)] #![feature(utf16_extra)] #![feature(variant_count)] +#![feature(widening_mul)] // tidy-alphabetical-end // // Language features: @@ -149,6 +149,7 @@ #![feature(decl_macro)] #![feature(deprecated_suggestion)] #![feature(derive_const)] +#![feature(diagnostic_on_const)] #![feature(doc_cfg)] #![feature(doc_notable_trait)] #![feature(extern_types)] @@ -183,6 +184,7 @@ #![feature(staged_api)] #![feature(stmt_expr_attributes)] #![feature(strict_provenance_lints)] +#![feature(target_feature_inline_always)] #![feature(trait_alias)] #![feature(transparent_unions)] #![feature(try_blocks)] @@ -226,11 +228,7 @@ use prelude::rust_2024::*; mod macros; #[unstable(feature = "assert_matches", issue = "82775")] -/// Unstable module containing the unstable `assert_matches` macro. -pub mod assert_matches { - #[unstable(feature = "assert_matches", issue = "82775")] - pub use crate::macros::{assert_matches, debug_assert_matches}; -} +pub use crate::macros::{assert_matches, debug_assert_matches}; #[unstable(feature = "derive_from", issue = "144889")] /// Unstable module containing the unstable `From` derive macro. diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index f3386985bdf1b..3176f3c067092 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -124,6 +124,8 @@ macro_rules! assert_ne { }; } +// FIXME add back debug_assert_matches doc link after bootstrap. + /// Asserts that an expression matches the provided pattern. /// /// This macro is generally preferable to `assert!(matches!(value, pattern))`, because it can print @@ -135,11 +137,9 @@ macro_rules! assert_ne { /// otherwise this macro will panic. /// /// Assertions are always checked in both debug and release builds, and cannot -/// be disabled. See [`debug_assert_matches!`] for assertions that are disabled in +/// be disabled. See `debug_assert_matches!` for assertions that are disabled in /// release builds by default. /// -/// [`debug_assert_matches!`]: crate::assert_matches::debug_assert_matches -/// /// On panic, this macro will print the value of the expression with its debug representation. /// /// Like [`assert!`], this macro has a second form, where a custom panic message can be provided. @@ -149,7 +149,7 @@ macro_rules! assert_ne { /// ``` /// #![feature(assert_matches)] /// -/// use std::assert_matches::assert_matches; +/// use std::assert_matches; /// /// let a = Some(345); /// let b = Some(56); @@ -168,7 +168,7 @@ macro_rules! assert_ne { /// ``` #[unstable(feature = "assert_matches", issue = "82775")] #[allow_internal_unstable(panic_internals)] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro assert_matches { ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => { match $left { @@ -382,7 +382,7 @@ macro_rules! debug_assert_ne { /// ``` /// #![feature(assert_matches)] /// -/// use std::assert_matches::debug_assert_matches; +/// use std::debug_assert_matches; /// /// let a = Some(345); /// let b = Some(56); @@ -401,10 +401,10 @@ macro_rules! debug_assert_ne { /// ``` #[unstable(feature = "assert_matches", issue = "82775")] #[allow_internal_unstable(assert_matches)] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro debug_assert_matches($($arg:tt)*) { if $crate::cfg!(debug_assertions) { - $crate::assert_matches::assert_matches!($($arg)*); + $crate::assert_matches!($($arg)*); } } @@ -1890,4 +1890,29 @@ pub(crate) mod builtin { pub macro From($item: item) { /* compiler built-in */ } + + /// Externally Implementable Item: Defines an attribute macro that can override the item + /// this is applied to. + #[unstable(feature = "extern_item_impls", issue = "125418")] + #[rustc_builtin_macro] + #[allow_internal_unstable(eii_internals, decl_macro, rustc_attrs)] + pub macro eii($item:item) { + /* compiler built-in */ + } + + /// Unsafely Externally Implementable Item: Defines an unsafe attribute macro that can override + /// the item this is applied to. + #[unstable(feature = "extern_item_impls", issue = "125418")] + #[rustc_builtin_macro] + #[allow_internal_unstable(eii_internals, decl_macro, rustc_attrs)] + pub macro unsafe_eii($item:item) { + /* compiler built-in */ + } + + /// Impl detail of EII + #[unstable(feature = "eii_internals", issue = "none")] + #[rustc_builtin_macro] + pub macro eii_declaration($item:item) { + /* compiler built-in */ + } } diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index e945cd77a75f7..718ca6340c8ef 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -153,17 +153,19 @@ unsafe impl Send for &T {} #[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable #[rustc_specialization_trait] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] // `Sized` being coinductive, despite having supertraits, is okay as there are no user-written impls, // and we know that the supertraits are always implemented if the subtrait is just by looking at // the builtin impls. #[rustc_coinductive] +#[cfg_attr(flux, flux::assoc(fn size_of() -> int))] +#[cfg_attr(flux, flux::assoc(fn align_of() -> int))] pub trait Sized: MetaSized { // Empty. } /// Types with a size that can be determined from pointer metadata. -#[unstable(feature = "sized_hierarchy", issue = "none")] +#[unstable(feature = "sized_hierarchy", issue = "144404")] #[lang = "meta_sized"] #[diagnostic::on_unimplemented( message = "the size for values of type `{Self}` cannot be known", @@ -172,7 +174,6 @@ pub trait Sized: MetaSized { #[fundamental] #[rustc_specialization_trait] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] // `MetaSized` being coinductive, despite having supertraits, is okay for the same reasons as // `Sized` above. #[rustc_coinductive] @@ -181,7 +182,7 @@ pub trait MetaSized: PointeeSized { } /// Types that may or may not have a size. -#[unstable(feature = "sized_hierarchy", issue = "none")] +#[unstable(feature = "sized_hierarchy", issue = "144404")] #[lang = "pointee_sized"] #[diagnostic::on_unimplemented( message = "values of type `{Self}` may or may not have a size", @@ -190,7 +191,6 @@ pub trait MetaSized: PointeeSized { #[fundamental] #[rustc_specialization_trait] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] #[rustc_coinductive] pub trait PointeeSized { // Empty @@ -236,7 +236,7 @@ pub trait PointeeSized { #[unstable(feature = "unsize", issue = "18598")] #[lang = "unsize"] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] pub trait Unsize: PointeeSized { // Empty. } @@ -455,9 +455,6 @@ marker_impls! { /// [impls]: #implementors #[stable(feature = "rust1", since = "1.0.0")] #[lang = "copy"] -// This is unsound, but required by `hashbrown` -// FIXME(joboet): change `hashbrown` to use `TrivialClone` -#[rustc_unsafe_specialization_marker] #[rustc_diagnostic_item = "Copy"] pub trait Copy: Clone { // Empty. @@ -466,7 +463,7 @@ pub trait Copy: Clone { /// Derive macro generating an impl of the trait `Copy`. #[rustc_builtin_macro] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow_internal_unstable(core_intrinsics, derive_clone_copy)] +#[allow_internal_unstable(core_intrinsics, derive_clone_copy_internals)] pub macro Copy($item:item) { /* compiler built-in */ } @@ -512,7 +509,7 @@ impl Copy for &T {} #[unstable(feature = "bikeshed_guaranteed_no_drop", issue = "none")] #[lang = "bikeshed_guaranteed_no_drop"] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] #[doc(hidden)] pub trait BikeshedGuaranteedNoDrop {} @@ -860,7 +857,7 @@ impl Clone for PhantomData { #[doc(hidden)] #[unstable(feature = "trivial_clone", issue = "none")] -unsafe impl TrivialClone for PhantomData {} +unsafe impl TrivialClone for PhantomData {} #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_default", issue = "143894")] @@ -887,7 +884,7 @@ impl StructuralPartialEq for PhantomData {} )] #[lang = "discriminant_kind"] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] pub trait DiscriminantKind { /// The type of the discriminant, which must satisfy the trait /// bounds required by `mem::Discriminant`. @@ -1057,7 +1054,7 @@ marker_impls! { #[lang = "destruct"] #[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] pub const trait Destruct: PointeeSized {} /// A marker for tuple types. @@ -1068,7 +1065,7 @@ pub const trait Destruct: PointeeSized {} #[lang = "tuple_trait"] #[diagnostic::on_unimplemented(message = "`{Self}` is not a tuple")] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] pub trait Tuple {} /// A marker for types which can be used as types of `const` generic parameters. @@ -1126,7 +1123,7 @@ marker_impls! { )] #[lang = "fn_ptr_trait"] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] pub trait FnPtr: Copy + Clone { /// Returns the address of the function pointer. #[lang = "fn_ptr_addr"] diff --git a/library/core/src/mem/drop_guard.rs b/library/core/src/mem/drop_guard.rs index fecc94b815e97..74bf353907455 100644 --- a/library/core/src/mem/drop_guard.rs +++ b/library/core/src/mem/drop_guard.rs @@ -1,4 +1,5 @@ use crate::fmt::{self, Debug}; +use crate::marker::Destruct; use crate::mem::ManuallyDrop; use crate::ops::{Deref, DerefMut}; @@ -63,13 +64,10 @@ where /// Consumes the `DropGuard`, returning the wrapped value. /// - /// This will not execute the closure. This is implemented as an associated - /// function to prevent any potential conflicts with any other methods called - /// `into_inner` from the `Deref` and `DerefMut` impls. - /// - /// It is typically preferred to call this function instead of `mem::forget` - /// because it will return the stored value and drop variables captured - /// by the closure instead of leaking their owned resources. + /// This will not execute the closure. It is typically preferred to call + /// this function instead of `mem::forget` because it will return the stored + /// value and drop variables captured by the closure instead of leaking their + /// owned resources. /// /// # Example /// @@ -81,11 +79,15 @@ where /// /// let value = String::from("Nori likes chicken"); /// let guard = DropGuard::new(value, |s| println!("{s}")); - /// assert_eq!(DropGuard::into_inner(guard), "Nori likes chicken"); + /// assert_eq!(DropGuard::dismiss(guard), "Nori likes chicken"); /// ``` #[unstable(feature = "drop_guard", issue = "144426")] + #[rustc_const_unstable(feature = "const_drop_guard", issue = "none")] #[inline] - pub fn into_inner(guard: Self) -> T { + pub const fn dismiss(guard: Self) -> T + where + F: [const] Destruct, + { // First we ensure that dropping the guard will not trigger // its destructor let mut guard = ManuallyDrop::new(guard); @@ -106,7 +108,8 @@ where } #[unstable(feature = "drop_guard", issue = "144426")] -impl Deref for DropGuard +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const Deref for DropGuard where F: FnOnce(T), { @@ -118,7 +121,8 @@ where } #[unstable(feature = "drop_guard", issue = "144426")] -impl DerefMut for DropGuard +#[rustc_const_unstable(feature = "const_convert", issue = "143773")] +impl const DerefMut for DropGuard where F: FnOnce(T), { @@ -128,9 +132,10 @@ where } #[unstable(feature = "drop_guard", issue = "144426")] -impl Drop for DropGuard +#[rustc_const_unstable(feature = "const_drop_guard", issue = "none")] +impl const Drop for DropGuard where - F: FnOnce(T), + F: [const] FnOnce(T), { fn drop(&mut self) { // SAFETY: `DropGuard` is in the process of being dropped. diff --git a/library/core/src/mem/manually_drop.rs b/library/core/src/mem/manually_drop.rs index 7d49da8509577..ca008a82ee6f0 100644 --- a/library/core/src/mem/manually_drop.rs +++ b/library/core/src/mem/manually_drop.rs @@ -1,4 +1,7 @@ -use crate::marker::Destruct; +use crate::cmp::Ordering; +use crate::hash::{Hash, Hasher}; +use crate::marker::{Destruct, StructuralPartialEq}; +use crate::mem::MaybeDangling; use crate::ops::{Deref, DerefMut, DerefPure}; use crate::ptr; @@ -152,11 +155,11 @@ use crate::ptr; /// [`MaybeUninit`]: crate::mem::MaybeUninit #[stable(feature = "manually_drop", since = "1.20.0")] #[lang = "manually_drop"] -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Copy, Clone, Debug, Default)] #[repr(transparent)] #[rustc_pub_transparent] pub struct ManuallyDrop { - value: T, + value: MaybeDangling, } impl ManuallyDrop { @@ -179,7 +182,7 @@ impl ManuallyDrop { #[rustc_const_stable(feature = "const_manually_drop", since = "1.32.0")] #[inline(always)] pub const fn new(value: T) -> ManuallyDrop { - ManuallyDrop { value } + ManuallyDrop { value: MaybeDangling::new(value) } } /// Extracts the value from the `ManuallyDrop` container. @@ -197,7 +200,9 @@ impl ManuallyDrop { #[rustc_const_stable(feature = "const_manually_drop", since = "1.32.0")] #[inline(always)] pub const fn into_inner(slot: ManuallyDrop) -> T { - slot.value + // Cannot use `MaybeDangling::into_inner` as that does not yet have the desired semantics. + // SAFETY: We know this is a valid `T`. `slot` will not be dropped. + unsafe { (&raw const slot).cast::().read() } } /// Takes the value from the `ManuallyDrop` container out. @@ -222,7 +227,7 @@ impl ManuallyDrop { pub const unsafe fn take(slot: &mut ManuallyDrop) -> T { // SAFETY: we are reading from a reference, which is guaranteed // to be valid for reads. - unsafe { ptr::read(&slot.value) } + unsafe { ptr::read(slot.value.as_ref()) } } } @@ -259,7 +264,7 @@ impl ManuallyDrop { // SAFETY: we are dropping the value pointed to by a mutable reference // which is guaranteed to be valid for writes. // It is up to the caller to make sure that `slot` isn't dropped again. - unsafe { ptr::drop_in_place(&mut slot.value) } + unsafe { ptr::drop_in_place(slot.value.as_mut()) } } } @@ -269,7 +274,7 @@ impl const Deref for ManuallyDrop { type Target = T; #[inline(always)] fn deref(&self) -> &T { - &self.value + self.value.as_ref() } } @@ -278,9 +283,43 @@ impl const Deref for ManuallyDrop { impl const DerefMut for ManuallyDrop { #[inline(always)] fn deref_mut(&mut self) -> &mut T { - &mut self.value + self.value.as_mut() } } #[unstable(feature = "deref_pure_trait", issue = "87121")] unsafe impl DerefPure for ManuallyDrop {} + +#[stable(feature = "manually_drop", since = "1.20.0")] +impl Eq for ManuallyDrop {} + +#[stable(feature = "manually_drop", since = "1.20.0")] +impl PartialEq for ManuallyDrop { + fn eq(&self, other: &Self) -> bool { + self.value.as_ref().eq(other.value.as_ref()) + } +} + +#[stable(feature = "manually_drop", since = "1.20.0")] +impl StructuralPartialEq for ManuallyDrop {} + +#[stable(feature = "manually_drop", since = "1.20.0")] +impl Ord for ManuallyDrop { + fn cmp(&self, other: &Self) -> Ordering { + self.value.as_ref().cmp(other.value.as_ref()) + } +} + +#[stable(feature = "manually_drop", since = "1.20.0")] +impl PartialOrd for ManuallyDrop { + fn partial_cmp(&self, other: &Self) -> Option { + self.value.as_ref().partial_cmp(other.value.as_ref()) + } +} + +#[stable(feature = "manually_drop", since = "1.20.0")] +impl Hash for ManuallyDrop { + fn hash(&self, state: &mut H) { + self.value.as_ref().hash(state); + } +} diff --git a/library/core/src/mem/maybe_dangling.rs b/library/core/src/mem/maybe_dangling.rs new file mode 100644 index 0000000000000..a5f77e667f975 --- /dev/null +++ b/library/core/src/mem/maybe_dangling.rs @@ -0,0 +1,114 @@ +#![unstable(feature = "maybe_dangling", issue = "118166")] + +use crate::{mem, ptr}; + +/// Allows wrapped [references] and [boxes] to dangle. +/// +///

+/// This type is not properly implemented yet, and the documentation below is thus not accurate. +///
+/// +/// That is, if a reference (or a `Box`) is wrapped in `MaybeDangling` (including when in a +/// (nested) field of a compound type wrapped in `MaybeDangling`), it does not have to follow +/// pointer aliasing rules or be dereferenceable. +/// +/// This can be useful when the value can become dangling while the function holding it is still +/// executing (particularly in concurrent code). As a somewhat absurd example, consider this code: +/// +/// ```rust,no_run +/// #![feature(box_as_ptr)] +/// # use std::alloc::{dealloc, Layout}; +/// # use std::mem; +/// +/// let mut boxed = Box::new(0_u32); +/// let ptr = Box::as_mut_ptr(&mut boxed); +/// +/// // Safety: the pointer comes from a box and thus was allocated before; `box` is not used afterwards +/// unsafe { dealloc(ptr.cast(), Layout::new::()) }; +/// +/// mem::forget(boxed); // <-- this is UB! +/// ``` +/// +/// Even though the `Box`'s destructor is not run (and thus we don't have a double free bug), this +/// code is still UB. This is because when moving `boxed` into `forget`, its validity invariants +/// are asserted, causing UB since the `Box` is dangling. The safety comment is as such wrong, as +/// moving the `boxed` variable as part of the `forget` call *is* a use. +/// +/// To fix this we could use `MaybeDangling`: +/// +// FIXME: remove `no_run` once the semantics are actually implemented +/// ```rust,no_run +/// #![feature(maybe_dangling, box_as_ptr)] +/// # use std::alloc::{dealloc, Layout}; +/// # use std::mem::{self, MaybeDangling}; +/// +/// let mut boxed = MaybeDangling::new(Box::new(0_u32)); +/// let ptr = Box::as_mut_ptr(boxed.as_mut()); +/// +/// // Safety: the pointer comes from a box and thus was allocated before; `box` is not used afterwards +/// unsafe { dealloc(ptr.cast(), Layout::new::()) }; +/// +/// mem::forget(boxed); // <-- this is OK! +/// ``` +/// +/// Note that the bit pattern must still be valid for the wrapped type. That is, [references] +/// (and [boxes]) still must be aligned and non-null. +/// +/// Additionally note that safe code can still assume that the inner value in a `MaybeDangling` is +/// **not** dangling -- functions like [`as_ref`] and [`into_inner`] are safe. It is not sound to +/// return a dangling reference in a `MaybeDangling` to safe code. However, it *is* sound +/// to hold such values internally inside your code -- and there's no way to do that without +/// this type. Note that other types can use this type and thus get the same effect; in particular, +/// [`ManuallyDrop`] will use `MaybeDangling`. +/// +/// Note that `MaybeDangling` doesn't prevent drops from being run, which can lead to UB if the +/// drop observes a dangling value. If you need to prevent drops from being run use [`ManuallyDrop`] +/// instead. +/// +/// [references]: prim@reference +/// [boxes]: ../../std/boxed/struct.Box.html +/// [`into_inner`]: MaybeDangling::into_inner +/// [`as_ref`]: MaybeDangling::as_ref +/// [`ManuallyDrop`]: crate::mem::ManuallyDrop +#[repr(transparent)] +#[rustc_pub_transparent] +#[derive(Debug, Copy, Clone, Default)] +pub struct MaybeDangling(P); + +impl MaybeDangling

(path: P) -> io::Result + where + P: AsRef, + { + sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len: len as _ }) + } + fn address(&self) -> AddressKind<'_> { + let len = self.len as usize - SUN_PATH_OFFSET; + let path = unsafe { mem::transmute::<&[i8], &[u8]>(&self.addr.sun_path) }; + + if len == 0 { + AddressKind::Unnamed + } else if self.addr.sun_path[0] == 0 { + AddressKind::Abstract(ByteStr::from_bytes(&path[1..len])) + } else { + AddressKind::Pathname(unsafe { + OsStr::from_encoded_bytes_unchecked(&path[..len - 1]).as_ref() + }) + } + } + + /// Returns `true` if the address is unnamed. + /// + /// # Examples + /// + /// A named address: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixListener::bind("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.is_unnamed(), false); + /// Ok(()) + /// } + /// ``` + pub fn is_unnamed(&self) -> bool { + matches!(self.address(), AddressKind::Unnamed) + } +} +enum AddressKind<'a> { + Unnamed, + Pathname(&'a Path), + Abstract(&'a ByteStr), +} diff --git a/library/std/src/os/windows/net/listener.rs b/library/std/src/os/windows/net/listener.rs new file mode 100644 index 0000000000000..332b116ee1a39 --- /dev/null +++ b/library/std/src/os/windows/net/listener.rs @@ -0,0 +1,342 @@ +#![unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +use crate::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; +use crate::os::windows::net::{SocketAddr, UnixStream}; +use crate::path::Path; +#[cfg(not(doc))] +use crate::sys::c::{AF_UNIX, SOCK_STREAM, SOCKADDR_UN, bind, getsockname, listen}; +use crate::sys::net::Socket; +#[cfg(not(doc))] +use crate::sys::winsock::startup; +use crate::sys::{AsInner, cvt_nz}; +use crate::{fmt, io}; + +/// A structure representing a Unix domain socket server. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(windows_unix_domain_sockets)] +/// use std::thread; +/// use std::os::windows::net::{UnixStream, UnixListener}; +/// +/// fn handle_client(stream: UnixStream) { +/// // ... +/// } +/// +/// fn main() -> std::io::Result<()> { +/// let listener = UnixListener::bind("/path/to/the/socket")?; +/// +/// // accept connections and process them, spawning a new thread for each one +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// /* connection succeeded */ +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// /* connection failed */ +/// break; +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +pub struct UnixListener(Socket); + +impl fmt::Debug for UnixListener { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("UnixListener"); + builder.field("sock", self.0.as_inner()); + if let Ok(addr) = self.local_addr() { + builder.field("local", &addr); + } + builder.finish() + } +} +impl UnixListener { + /// Creates a new `UnixListener` bound to the specified socket. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// let listener = match UnixListener::bind("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {e:?}"); + /// return + /// } + /// }; + /// ``` + pub fn bind>(path: P) -> io::Result { + let socket_addr = SocketAddr::from_pathname(path)?; + Self::bind_addr(&socket_addr) + } + + /// Creates a new `UnixListener` bound to the specified [`socket address`]. + /// + /// [`socket address`]: crate::os::windows::net::SocketAddr + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::{UnixListener}; + /// + /// fn main() -> std::io::Result<()> { + /// let listener1 = UnixListener::bind("path/to/socket")?; + /// let addr = listener1.local_addr()?; + /// + /// let listener2 = match UnixListener::bind_addr(&addr) { + /// Ok(sock) => sock, + /// Err(err) => { + /// println!("Couldn't bind: {err:?}"); + /// return Err(err); + /// } + /// }; + /// Ok(()) + /// } + /// ``` + pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result { + startup(); + let inner = Socket::new(AF_UNIX as _, SOCK_STREAM)?; + unsafe { + cvt_nz(bind(inner.as_raw(), &raw const socket_addr.addr as _, socket_addr.len as _))?; + cvt_nz(listen(inner.as_raw(), 128))?; + } + Ok(UnixListener(inner)) + } + + /// Accepts a new incoming connection to this listener. + /// + /// This function will block the calling thread until a new Unix connection + /// is established. When established, the corresponding [`UnixStream`] and + /// the remote peer's address will be returned. + /// + /// [`UnixStream`]: crate::os::windows::net::UnixStream + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// + /// match listener.accept() { + /// Ok((socket, addr)) => println!("Got a client: {addr:?}"), + /// Err(e) => println!("accept function failed: {e:?}"), + /// } + /// Ok(()) + /// } + /// ``` + pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { + let mut storage = SOCKADDR_UN::default(); + let mut len = size_of::() as _; + let inner = self.0.accept(&raw mut storage as *mut _, &raw mut len)?; + let addr = SocketAddr::from_parts(storage, len)?; + Ok((UnixStream(inner), addr)) + } + + /// Returns the local socket address of this listener. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let addr = listener.local_addr().expect("Couldn't get local address"); + /// Ok(()) + /// } + /// ``` + pub fn local_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { getsockname(self.0.as_raw(), addr, len) }) + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UnixListener` is a reference to the same socket that this + /// object references. Both handles can be used to accept incoming + /// connections and options set on one listener will affect the other. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let listener_copy = listener.try_clone().expect("try_clone failed"); + /// Ok(()) + /// } + /// ``` + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(UnixListener) + } + + /// Moves the socket into or out of nonblocking mode. + /// + /// This will result in the `accept` operation becoming nonblocking, + /// i.e., immediately returning from their calls. If the IO operation is + /// successful, `Ok` is returned and no further action is required. If the + /// IO operation could not be completed and needs to be retried, an error + /// with kind [`io::ErrorKind::WouldBlock`] is returned. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// listener.set_nonblocking(true).expect("Couldn't set non blocking"); + /// Ok(()) + /// } + /// ``` + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/tmp/sock")?; + /// + /// if let Ok(Some(err)) = listener.take_error() { + /// println!("Got error: {err:?}"); + /// } + /// Ok(()) + /// } + /// ``` + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Returns an iterator over incoming connections. + /// + /// The iterator will never return [`None`] and will also not yield the + /// peer's [`SocketAddr`] structure. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::thread; + /// use std::os::windows::net::{UnixStream, UnixListener}; + /// + /// fn handle_client(stream: UnixStream) { + /// // ... + /// } + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// + /// for stream in listener.incoming() { + /// match stream { + /// Ok(stream) => { + /// thread::spawn(|| handle_client(stream)); + /// } + /// Err(err) => { + /// break; + /// } + /// } + /// } + /// Ok(()) + /// } + /// ``` + pub fn incoming(&self) -> Incoming<'_> { + Incoming { listener: self } + } +} + +/// An iterator over incoming connections to a [`UnixListener`]. +/// +/// It will never return [`None`]. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(windows_unix_domain_sockets)] +/// use std::thread; +/// use std::os::windows::net::{UnixStream, UnixListener}; +/// +/// fn handle_client(stream: UnixStream) { +/// // ... +/// } +/// +/// fn main() -> std::io::Result<()> { +/// let listener = UnixListener::bind("/path/to/the/socket")?; +/// +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// break; +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +pub struct Incoming<'a> { + listener: &'a UnixListener, +} + +impl<'a> Iterator for Incoming<'a> { + type Item = io::Result; + + fn next(&mut self) -> Option> { + Some(self.listener.accept().map(|s| s.0)) + } + + fn size_hint(&self) -> (usize, Option) { + (usize::MAX, None) + } +} + +impl AsRawSocket for UnixListener { + #[inline] + fn as_raw_socket(&self) -> RawSocket { + self.0.as_raw_socket() + } +} + +impl FromRawSocket for UnixListener { + #[inline] + unsafe fn from_raw_socket(sock: RawSocket) -> Self { + UnixListener(unsafe { Socket::from_raw_socket(sock) }) + } +} + +impl IntoRawSocket for UnixListener { + #[inline] + fn into_raw_socket(self) -> RawSocket { + self.0.into_raw_socket() + } +} + +impl<'a> IntoIterator for &'a UnixListener { + type Item = io::Result; + type IntoIter = Incoming<'a>; + + fn into_iter(self) -> Incoming<'a> { + self.incoming() + } +} diff --git a/library/std/src/os/windows/net/mod.rs b/library/std/src/os/windows/net/mod.rs new file mode 100644 index 0000000000000..6b3f062b8ab41 --- /dev/null +++ b/library/std/src/os/windows/net/mod.rs @@ -0,0 +1,6 @@ +mod addr; +mod listener; +mod stream; +pub use addr::*; +pub use listener::*; +pub use stream::*; diff --git a/library/std/src/os/windows/net/stream.rs b/library/std/src/os/windows/net/stream.rs new file mode 100644 index 0000000000000..c31f03fdf53f8 --- /dev/null +++ b/library/std/src/os/windows/net/stream.rs @@ -0,0 +1,421 @@ +#![unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +use crate::net::Shutdown; +use crate::os::windows::io::{ + AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, RawSocket, +}; +use crate::os::windows::net::SocketAddr; +use crate::path::Path; +#[cfg(not(doc))] +use crate::sys::c::{ + AF_UNIX, SO_RCVTIMEO, SO_SNDTIMEO, SOCK_STREAM, connect, getpeername, getsockname, +}; +use crate::sys::net::Socket; +#[cfg(not(doc))] +use crate::sys::winsock::startup; +use crate::sys::{AsInner, cvt_nz}; +use crate::time::Duration; +use crate::{fmt, io}; +/// A Unix stream socket. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(windows_unix_domain_sockets)] +/// use std::os::windows::net::UnixStream; +/// use std::io::prelude::*; +/// +/// fn main() -> std::io::Result<()> { +/// let mut stream = UnixStream::connect("/path/to/my/socket")?; +/// stream.write_all(b"hello world")?; +/// let mut response = String::new(); +/// stream.read_to_string(&mut response)?; +/// println!("{response}"); +/// Ok(()) +/// } +/// ``` +pub struct UnixStream(pub(super) Socket); +impl fmt::Debug for UnixStream { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("UnixStream"); + builder.field("sock", self.0.as_inner()); + if let Ok(addr) = self.local_addr() { + builder.field("local", &addr); + } + if let Ok(addr) = self.peer_addr() { + builder.field("peer", &addr); + } + builder.finish() + } +} +impl UnixStream { + /// Connects to the socket named by `path`. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// let socket = match UnixStream::connect("/tmp/sock") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {e:?}"); + /// return + /// } + /// }; + /// ``` + pub fn connect>(path: P) -> io::Result { + let socket_addr = SocketAddr::from_pathname(path)?; + Self::connect_addr(&socket_addr) + } + + /// Connects to the socket specified by [`address`]. + /// + /// [`address`]: crate::os::windows::net::SocketAddr + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::{UnixListener, UnixStream}; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let addr = listener.local_addr()?; + /// + /// let sock = match UnixStream::connect_addr(&addr) { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {e:?}"); + /// return Err(e) + /// } + /// }; + /// Ok(()) + /// } + /// ```` + pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result { + startup(); + let inner = Socket::new(AF_UNIX as _, SOCK_STREAM)?; + unsafe { + cvt_nz(connect( + inner.as_raw(), + &raw const socket_addr.addr as *const _, + socket_addr.len as _, + ))?; + } + Ok(UnixStream(inner)) + } + + /// Returns the socket address of the local half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// Ok(()) + /// } + /// ``` + pub fn local_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { getsockname(self.0.as_raw(), addr, len) }) + } + + /// Returns the socket address of the remote half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let addr = socket.peer_addr().expect("Couldn't get peer address"); + /// Ok(()) + /// } + /// ``` + pub fn peer_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { getpeername(self.0.as_raw(), addr, len) }) + } + + /// Returns the read timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// assert_eq!(socket.read_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + pub fn read_timeout(&self) -> io::Result> { + self.0.timeout(SO_RCVTIMEO) + } + + /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_nonblocking(true).expect("Couldn't set nonblocking"); + /// Ok(()) + /// } + /// ``` + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + /// Sets the read timeout for the socket. + /// + /// If the provided value is [`None`], then [`read`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method. + /// + /// [`read`]: io::Read::read + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// Ok(()) + /// } + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::io; + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_timeout(dur, SO_RCVTIMEO) + } + + /// Sets the write timeout for the socket. + /// + /// If the provided value is [`None`], then [`write`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is + /// passed to this method. + /// + /// [`read`]: io::Read::read + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("Couldn't set write timeout"); + /// Ok(()) + /// } + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::io; + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_timeout(dur, SO_SNDTIMEO) + } + + /// Shuts down the read, write, or both halves of this connection. + /// + /// This function will cause all pending and future I/O calls on the + /// specified portions to immediately return with an appropriate value + /// (see the documentation of [`Shutdown`]). + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::net::Shutdown; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// Ok(()) + /// } + /// ``` + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.0.shutdown(how) + } + + /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// if let Ok(Some(err)) = socket.take_error() { + /// println!("Got error: {err:?}"); + /// } + /// Ok(()) + /// } + /// ``` + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UnixStream` is a reference to the same stream that this + /// object references. Both handles will read and write the same stream of + /// data, and options set on one stream will be propagated to the other + /// stream. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let sock_copy = socket.try_clone().expect("Couldn't clone socket"); + /// Ok(()) + /// } + /// ``` + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(UnixStream) + } + + /// Returns the write timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("Couldn't set write timeout"); + /// assert_eq!(socket.write_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + pub fn write_timeout(&self) -> io::Result> { + self.0.timeout(SO_SNDTIMEO) + } +} + +impl io::Read for UnixStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + io::Read::read(&mut &*self, buf) + } +} + +impl<'a> io::Read for &'a UnixStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } +} + +impl io::Write for UnixStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + io::Write::write(&mut &*self, buf) + } + + fn flush(&mut self) -> io::Result<()> { + io::Write::flush(&mut &*self) + } +} +impl<'a> io::Write for &'a UnixStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.write_vectored(&[io::IoSlice::new(buf)]) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +impl AsSocket for UnixStream { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + self.0.as_socket() + } +} + +impl AsRawSocket for UnixStream { + #[inline] + fn as_raw_socket(&self) -> RawSocket { + self.0.as_raw_socket() + } +} + +impl FromRawSocket for UnixStream { + #[inline] + unsafe fn from_raw_socket(sock: RawSocket) -> Self { + unsafe { UnixStream(Socket::from_raw_socket(sock)) } + } +} + +impl IntoRawSocket for UnixStream { + fn into_raw_socket(self) -> RawSocket { + self.0.into_raw_socket() + } +} diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index f21ed51606f6d..b32c6cd442ffa 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -10,7 +10,7 @@ use crate::os::windows::io::{ AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, }; use crate::sealed::Sealed; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::{io, marker, process, ptr, sys}; #[stable(feature = "process_extensions", since = "1.2.0")] @@ -117,7 +117,7 @@ impl IntoRawHandle for process::ChildStderr { impl From for process::ChildStdin { fn from(handle: OwnedHandle) -> process::ChildStdin { let handle = sys::handle::Handle::from_inner(handle); - let pipe = sys::pipe::AnonPipe::from_inner(handle); + let pipe = sys::process::ChildPipe::from_inner(handle); process::ChildStdin::from_inner(pipe) } } @@ -130,7 +130,7 @@ impl From for process::ChildStdin { impl From for process::ChildStdout { fn from(handle: OwnedHandle) -> process::ChildStdout { let handle = sys::handle::Handle::from_inner(handle); - let pipe = sys::pipe::AnonPipe::from_inner(handle); + let pipe = sys::process::ChildPipe::from_inner(handle); process::ChildStdout::from_inner(pipe) } } @@ -143,7 +143,7 @@ impl From for process::ChildStdout { impl From for process::ChildStderr { fn from(handle: OwnedHandle) -> process::ChildStderr { let handle = sys::handle::Handle::from_inner(handle); - let pipe = sys::pipe::AnonPipe::from_inner(handle); + let pipe = sys::process::ChildPipe::from_inner(handle); process::ChildStderr::from_inner(pipe) } } diff --git a/library/std/src/os/windows/thread.rs b/library/std/src/os/windows/thread.rs index d81d6d0ac28a9..1cdee44de4b72 100644 --- a/library/std/src/os/windows/thread.rs +++ b/library/std/src/os/windows/thread.rs @@ -5,7 +5,7 @@ #![stable(feature = "thread_extensions", since = "1.9.0")] use crate::os::windows::io::{AsRawHandle, IntoRawHandle, RawHandle}; -use crate::sys_common::{AsInner, IntoInner}; +use crate::sys::{AsInner, IntoInner}; use crate::thread; #[stable(feature = "thread_extensions", since = "1.9.0")] diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 1997785885d34..658026a8020f9 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -212,7 +212,7 @@ impl fmt::Display for PanicHookInfo<'_> { #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] #[allow_internal_unstable(libstd_sys_internals, const_format_args, panic_internals, rt)] #[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_2015_macro")] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro panic_2015 { () => ({ $crate::rt::begin_panic("explicit panic") @@ -523,7 +523,7 @@ pub fn get_backtrace_style() -> Option { Some(x) if &x == "0" => BacktraceStyle::Off, Some(x) if &x == "full" => BacktraceStyle::Full, Some(_) => BacktraceStyle::Short, - None if crate::sys::FULL_BACKTRACE_DEFAULT => BacktraceStyle::Full, + None if crate::sys::backtrace::FULL_BACKTRACE_DEFAULT => BacktraceStyle::Full, None => BacktraceStyle::Off, }; diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 7efb7ad8ee8b3..a4a974d0447b8 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -285,7 +285,6 @@ fn default_hook(info: &PanicHookInfo<'_>) { static FIRST_PANIC: Atomic = AtomicBool::new(true); match backtrace { - // SAFETY: we took out a lock just a second ago. Some(BacktraceStyle::Short) => { drop(lock.print(err, crate::backtrace_rs::PrintFmt::Short)) } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 114fcc796c525..25bd7005b9942 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1877,9 +1877,7 @@ impl From<&Path> for Box { /// /// This will allocate and clone `path` to it. fn from(path: &Path) -> Box { - let boxed: Box = path.inner.into(); - let rw = Box::into_raw(boxed) as *mut Path; - unsafe { Box::from_raw(rw) } + Box::clone_from_ref(path) } } @@ -2974,6 +2972,15 @@ impl Path { /// /// If `path` is absolute, it replaces the current path. /// + /// On Windows: + /// + /// * if `path` has a root but no prefix (e.g., `\windows`), it + /// replaces and returns everything except for the prefix (if any) of `self`. + /// * if `path` has a prefix but no root, `self` is ignored and `path` is returned. + /// * if `self` has a verbatim prefix (e.g. `\\?\C:\windows`) + /// and `path` is not empty, the new path is normalized: all references + /// to `.` and `..` are removed. + /// /// See [`PathBuf::push`] for more details on what it means to adjoin a path. /// /// # Examples @@ -3208,6 +3215,17 @@ impl Path { Display { inner: self.inner.display() } } + /// Returns the same path as `&Path`. + /// + /// This method is redundant when used directly on `&Path`, but + /// it helps dereferencing other `PathBuf`-like types to `Path`s, + /// for example references to `Box` or `Arc`. + #[inline] + #[unstable(feature = "str_as_str", issue = "130366")] + pub const fn as_path(&self) -> &Path { + self + } + /// Queries the file system to get information about a file, directory, etc. /// /// This function will traverse symbolic links to query information about the diff --git a/library/std/src/prelude/mod.rs b/library/std/src/prelude/mod.rs index 5f7097c26e228..78eb79ac666a2 100644 --- a/library/std/src/prelude/mod.rs +++ b/library/std/src/prelude/mod.rs @@ -54,9 +54,9 @@ //! * [std::convert]::{[AsRef], [AsMut], [Into], [From]}, generic //! conversions, used by savvy API authors to create overloaded methods. //! * [std::default]::[Default], types that have default values. -//! * [std::iter]::{[Iterator], [Extend], [IntoIterator], [DoubleEndedIterator], [ExactSizeIterator]}, -//! iterators of various -//! kinds. +//! * [std::iter]::{[Iterator], [Extend], [IntoIterator], [DoubleEndedIterator], +//! [ExactSizeIterator]}, iterators of various kinds. +//! * Most of the standard macros. //! * [std::option]::[Option]::{[self][Option], [Some], [None]}, a //! type which expresses the presence or absence of a value. This type is so //! commonly used, its variants are also exported. @@ -145,6 +145,11 @@ pub mod rust_2021 { #[stable(feature = "prelude_2021", since = "1.55.0")] #[doc(no_inline)] pub use core::prelude::rust_2021::*; + + // There are two different panic macros, one in `core` and one in `std`. They are slightly + // different. For `std` we explicitly want the one defined in `std`. + #[stable(feature = "prelude_2021", since = "1.55.0")] + pub use super::v1::panic; } /// The 2024 version of the prelude of The Rust Standard Library. @@ -159,6 +164,11 @@ pub mod rust_2024 { #[stable(feature = "prelude_2024", since = "1.85.0")] #[doc(no_inline)] pub use core::prelude::rust_2024::*; + + // There are two different panic macros, one in `core` and one in `std`. They are slightly + // different. For `std` we explicitly want the one defined in `std`. + #[stable(feature = "prelude_2024", since = "1.85.0")] + pub use super::v1::panic; } /// The Future version of the prelude of The Rust Standard Library. @@ -174,4 +184,9 @@ pub mod rust_future { #[unstable(feature = "prelude_next", issue = "none")] #[doc(no_inline)] pub use core::prelude::rust_future::*; + + // There are two different panic macros, one in `core` and one in `std`. They are slightly + // different. For `std` we explicitly want the one defined in `std`. + #[unstable(feature = "prelude_next", issue = "none")] + pub use super::v1::panic; } diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index 70c1113155656..af9d28ebad356 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -43,15 +43,46 @@ pub use crate::option::Option::{self, None, Some}; #[doc(no_inline)] pub use crate::result::Result::{self, Err, Ok}; -// Re-exported built-in macros +// Re-exported built-in macros and traits #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[doc(no_inline)] +#[expect(deprecated)] pub use core::prelude::v1::{ - assert, cfg, column, compile_error, concat, env, file, format_args, - format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, - stringify, trace_macros, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, + assert, assert_eq, assert_ne, cfg, column, compile_error, concat, debug_assert, debug_assert_eq, + debug_assert_ne, env, file, format_args, include, include_bytes, include_str, line, matches, + module_path, option_env, stringify, todo, r#try, unimplemented, unreachable, write, + writeln, Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, }; +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[doc(no_inline)] +pub use crate::{ + dbg, eprint, eprintln, format, is_x86_feature_detected, print, println, thread_local +}; + +// These macros need special handling, so that we don't export them *and* the modules of the same +// name. We only want the macros in the prelude so we shadow the original modules with private +// modules with the same names. +mod ambiguous_macros_only { + #[expect(hidden_glob_reexports)] + mod vec {} + #[expect(hidden_glob_reexports)] + mod panic {} + // Building std without the expect exported_private_dependencies will create warnings, but then + // clippy claims its a useless_attribute. So silence both. + #[expect(clippy::useless_attribute)] + #[expect(exported_private_dependencies)] + #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] + pub use crate::*; +} +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[doc(no_inline)] +pub use self::ambiguous_macros_only::{vec, panic}; + +#[unstable(feature = "cfg_select", issue = "115585")] +#[doc(no_inline)] +pub use core::prelude::v1::cfg_select; + #[unstable( feature = "concat_bytes", issue = "87555", @@ -60,6 +91,26 @@ pub use core::prelude::v1::{ #[doc(no_inline)] pub use core::prelude::v1::concat_bytes; +#[unstable(feature = "const_format_args", issue = "none")] +#[doc(no_inline)] +pub use core::prelude::v1::const_format_args; + +#[unstable( + feature = "log_syntax", + issue = "29598", + reason = "`log_syntax!` is not stable enough for use and is subject to change" +)] +#[doc(no_inline)] +pub use core::prelude::v1::log_syntax; + +#[unstable( + feature = "trace_macros", + issue = "29598", + reason = "`trace_macros` is not stable enough for use and is subject to change" +)] +#[doc(no_inline)] +pub use core::prelude::v1::trace_macros; + // Do not `doc(no_inline)` so that they become doc items on their own // (no public module for them to be re-exported from). #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] @@ -110,6 +161,12 @@ pub use core::prelude::v1::deref; )] pub use core::prelude::v1::define_opaque; +#[unstable(feature = "extern_item_impls", issue = "125418")] +pub use core::prelude::v1::{eii, unsafe_eii}; + +#[unstable(feature = "eii_internals", issue = "none")] +pub use core::prelude::v1::eii_declaration; + // The file so far is equivalent to core/src/prelude/v1.rs. It is duplicated // rather than glob imported because we want docs to show these re-exports as // pointing to within `std`. diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 5c0ac526a36c9..6838bb422b0e0 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -166,10 +166,8 @@ use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::num::NonZero; use crate::path::Path; -use crate::sys::pipe::{AnonPipe, read2}; -use crate::sys::process as imp; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; -use crate::{fmt, fs, str}; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, process as imp}; +use crate::{fmt, format_args_nl, fs, str}; /// Representation of a running or exited child process. /// @@ -300,9 +298,9 @@ impl fmt::Debug for Child { /// /// Used to pass pipe handles between this module and [`imp`]. pub(crate) struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, } /// A handle to a child process's standard input (stdin). @@ -317,7 +315,7 @@ pub(crate) struct StdioPipes { /// [dropped]: Drop #[stable(feature = "process", since = "1.0.0")] pub struct ChildStdin { - inner: AnonPipe, + inner: imp::ChildPipe, } // In addition to the `impl`s here, `ChildStdin` also has `impl`s for @@ -366,21 +364,21 @@ impl Write for &ChildStdin { } } -impl AsInner for ChildStdin { +impl AsInner for ChildStdin { #[inline] - fn as_inner(&self) -> &AnonPipe { + fn as_inner(&self) -> &imp::ChildPipe { &self.inner } } -impl IntoInner for ChildStdin { - fn into_inner(self) -> AnonPipe { +impl IntoInner for ChildStdin { + fn into_inner(self) -> imp::ChildPipe { self.inner } } -impl FromInner for ChildStdin { - fn from_inner(pipe: AnonPipe) -> ChildStdin { +impl FromInner for ChildStdin { + fn from_inner(pipe: imp::ChildPipe) -> ChildStdin { ChildStdin { inner: pipe } } } @@ -403,7 +401,7 @@ impl fmt::Debug for ChildStdin { /// [dropped]: Drop #[stable(feature = "process", since = "1.0.0")] pub struct ChildStdout { - inner: AnonPipe, + inner: imp::ChildPipe, } // In addition to the `impl`s here, `ChildStdout` also has `impl`s for @@ -436,21 +434,21 @@ impl Read for ChildStdout { } } -impl AsInner for ChildStdout { +impl AsInner for ChildStdout { #[inline] - fn as_inner(&self) -> &AnonPipe { + fn as_inner(&self) -> &imp::ChildPipe { &self.inner } } -impl IntoInner for ChildStdout { - fn into_inner(self) -> AnonPipe { +impl IntoInner for ChildStdout { + fn into_inner(self) -> imp::ChildPipe { self.inner } } -impl FromInner for ChildStdout { - fn from_inner(pipe: AnonPipe) -> ChildStdout { +impl FromInner for ChildStdout { + fn from_inner(pipe: imp::ChildPipe) -> ChildStdout { ChildStdout { inner: pipe } } } @@ -473,7 +471,7 @@ impl fmt::Debug for ChildStdout { /// [dropped]: Drop #[stable(feature = "process", since = "1.0.0")] pub struct ChildStderr { - inner: AnonPipe, + inner: imp::ChildPipe, } // In addition to the `impl`s here, `ChildStderr` also has `impl`s for @@ -506,21 +504,21 @@ impl Read for ChildStderr { } } -impl AsInner for ChildStderr { +impl AsInner for ChildStderr { #[inline] - fn as_inner(&self) -> &AnonPipe { + fn as_inner(&self) -> &imp::ChildPipe { &self.inner } } -impl IntoInner for ChildStderr { - fn into_inner(self) -> AnonPipe { +impl IntoInner for ChildStderr { + fn into_inner(self) -> imp::ChildPipe { self.inner } } -impl FromInner for ChildStderr { - fn from_inner(pipe: AnonPipe) -> ChildStderr { +impl FromInner for ChildStderr { + fn from_inner(pipe: imp::ChildPipe) -> ChildStderr { ChildStderr { inner: pipe } } } @@ -1206,6 +1204,30 @@ impl Command { pub fn get_current_dir(&self) -> Option<&Path> { self.inner.get_current_dir() } + + /// Returns whether the environment will be cleared for the child process. + /// + /// This returns `true` if [`Command::env_clear`] was called, and `false` otherwise. + /// When `true`, the child process will not inherit any environment variables from + /// its parent process. + /// + /// # Examples + /// + /// ``` + /// #![feature(command_resolved_envs)] + /// use std::process::Command; + /// + /// let mut cmd = Command::new("ls"); + /// assert_eq!(cmd.get_env_clear(), false); + /// + /// cmd.env_clear(); + /// assert_eq!(cmd.get_env_clear(), true); + /// ``` + #[must_use] + #[unstable(feature = "command_resolved_envs", issue = "149070")] + pub fn get_env_clear(&self) -> bool { + self.inner.get_env_clear() + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -2356,7 +2378,7 @@ impl Child { res.unwrap(); } (Some(out), Some(err)) => { - let res = read2(out.inner, &mut stdout, err.inner, &mut stderr); + let res = imp::read_output(out.inner, &mut stdout, err.inner, &mut stderr); res.unwrap(); } } diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index 2717b7b469cee..11c0a0b9daf7b 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -109,14 +109,14 @@ fn handle_rt_panic(e: Box) -> T { // `compiler/rustc_session/src/config/sigpipe.rs`. #[cfg_attr(test, allow(dead_code))] unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { + // Remember the main thread ID to give it the correct name. + // SAFETY: this is the only time and place where we call this function. + unsafe { main_thread::set(thread::current_id()) }; + #[cfg_attr(target_os = "teeos", allow(unused_unsafe))] unsafe { sys::init(argc, argv, sigpipe) }; - - // Remember the main thread ID to give it the correct name. - // SAFETY: this is the only time and place where we call this function. - unsafe { main_thread::set(thread::current_id()) }; } /// Clean up the thread-local runtime state. This *should* be run after all other diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index c2c18889dde7d..6a5cc9b69f82c 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -65,8 +65,8 @@ impl fmt::Debug for Barrier { impl Barrier { /// Creates a new barrier that can block a given number of threads. /// - /// A barrier will block `n`-1 threads which call [`wait()`] and then wake - /// up all threads at once when the `n`th thread calls [`wait()`]. + /// A barrier will block all threads which call [`wait()`] until the `n`th thread calls [`wait()`], + /// and then wake up all threads at once. /// /// [`wait()`]: Barrier::wait /// diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index ef58b64d4967e..7274b5d10c75b 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -172,7 +172,6 @@ impl T> LazyLock { /// # Examples /// /// ``` - /// #![feature(lazy_get)] /// use std::sync::LazyLock; /// /// let mut lazy = LazyLock::new(|| 92); @@ -183,7 +182,7 @@ impl T> LazyLock { /// assert_eq!(*lazy, 44); /// ``` #[inline] - #[unstable(feature = "lazy_get", issue = "129333")] + #[stable(feature = "lazy_get", since = "1.94.0")] pub fn force_mut(this: &mut LazyLock) -> &mut T { #[cold] /// # Safety @@ -279,8 +278,6 @@ impl LazyLock { /// # Examples /// /// ``` - /// #![feature(lazy_get)] - /// /// use std::sync::LazyLock; /// /// let mut lazy = LazyLock::new(|| 92); @@ -291,7 +288,7 @@ impl LazyLock { /// assert_eq!(*lazy, 44); /// ``` #[inline] - #[unstable(feature = "lazy_get", issue = "129333")] + #[stable(feature = "lazy_get", since = "1.94.0")] pub fn get_mut(this: &mut LazyLock) -> Option<&mut T> { // `state()` does not perform an atomic load, so prefer it over `is_complete()`. let state = this.once.state(); @@ -309,8 +306,6 @@ impl LazyLock { /// # Examples /// /// ``` - /// #![feature(lazy_get)] - /// /// use std::sync::LazyLock; /// /// let lazy = LazyLock::new(|| 92); @@ -320,7 +315,7 @@ impl LazyLock { /// assert_eq!(LazyLock::get(&lazy), Some(&92)); /// ``` #[inline] - #[unstable(feature = "lazy_get", issue = "129333")] + #[stable(feature = "lazy_get", since = "1.94.0")] #[rustc_should_not_be_called_on_const_items] pub fn get(this: &LazyLock) -> Option<&T> { if this.once.is_completed() { diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index 19b3040dcb279..5da50480c7232 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -184,6 +184,8 @@ pub use alloc_crate::sync::{Arc, Weak}; #[unstable(feature = "mpmc_channel", issue = "126840")] pub mod mpmc; pub mod mpsc; +#[unstable(feature = "oneshot_channel", issue = "143674")] +pub mod oneshot; pub(crate) mod once; // `pub(crate)` for the `sys::sync::once` implementations and `LazyLock`. diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index 673033034eff5..8df81a580f7b8 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -6,9 +6,10 @@ //! * [`Sender`] //! * [`Receiver`] //! -//! [`Sender`]s are used to send data to a set of [`Receiver`]s. Both -//! sender and receiver are cloneable (multi-producer) such that many threads can send -//! simultaneously to receivers (multi-consumer). +//! [`Sender`]s are used to send data to a set of [`Receiver`]s where each item +//! sent is delivered to (at most) one receiver. Both sender and receiver are +//! cloneable (multi-producer) such that many threads can send simultaneously +//! to receivers (multi-consumer). //! //! These channels come in two flavors: //! @@ -653,7 +654,7 @@ impl Clone for Sender { #[unstable(feature = "mpmc_channel", issue = "126840")] impl fmt::Debug for Sender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Sender { .. }") + f.debug_struct("Sender").finish_non_exhaustive() } } @@ -1379,7 +1380,7 @@ impl Clone for Receiver { #[unstable(feature = "mpmc_channel", issue = "126840")] impl fmt::Debug for Receiver { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Receiver { .. }") + f.debug_struct("Receiver").finish_non_exhaustive() } } diff --git a/library/std/src/sync/mpsc.rs b/library/std/src/sync/mpsc.rs index f91c26aa22cf8..0ae23f6e13bf2 100644 --- a/library/std/src/sync/mpsc.rs +++ b/library/std/src/sync/mpsc.rs @@ -1114,8 +1114,10 @@ impl error::Error for SendError {} impl fmt::Debug for TrySendError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - TrySendError::Full(..) => "Full(..)".fmt(f), - TrySendError::Disconnected(..) => "Disconnected(..)".fmt(f), + TrySendError::Full(..) => f.debug_tuple("TrySendError::Full").finish_non_exhaustive(), + TrySendError::Disconnected(..) => { + f.debug_tuple("TrySendError::Disconnected").finish_non_exhaustive() + } } } } diff --git a/library/std/src/sync/nonpoison/mutex.rs b/library/std/src/sync/nonpoison/mutex.rs index ed3f8cfed821a..307bf8eaf512e 100644 --- a/library/std/src/sync/nonpoison/mutex.rs +++ b/library/std/src/sync/nonpoison/mutex.rs @@ -422,7 +422,7 @@ impl From for Mutex { } #[unstable(feature = "nonpoison_mutex", issue = "134645")] -impl Default for Mutex { +impl Default for Mutex { /// Creates a `Mutex`, with the `Default` value for T. fn default() -> Mutex { Mutex::new(Default::default()) diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index d1bbe0ff843cf..f6ea3c1a039b2 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -1,3 +1,4 @@ +use super::once::OnceExclusiveState; use crate::cell::UnsafeCell; use crate::fmt; use crate::marker::PhantomData; @@ -152,8 +153,8 @@ impl OnceLock { #[stable(feature = "once_cell", since = "1.70.0")] #[rustc_should_not_be_called_on_const_items] pub fn get(&self) -> Option<&T> { - if self.is_initialized() { - // Safe b/c checked is_initialized + if self.initialized() { + // Safe b/c checked initialized Some(unsafe { self.get_unchecked() }) } else { None @@ -170,8 +171,8 @@ impl OnceLock { #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get_mut(&mut self) -> Option<&mut T> { - if self.is_initialized() { - // Safe b/c checked is_initialized and we have a unique access + if self.initialized_mut() { + // Safe b/c checked initialized and we have a unique access Some(unsafe { self.get_unchecked_mut() }) } else { None @@ -402,14 +403,12 @@ impl OnceLock { // NOTE: We need to perform an acquire on the state in this method // in order to correctly synchronize `LazyLock::force`. This is // currently done by calling `self.get()`, which in turn calls - // `self.is_initialized()`, which in turn performs the acquire. + // `self.initialized()`, which in turn performs the acquire. if let Some(value) = self.get() { return Ok(value); } self.initialize(f)?; - debug_assert!(self.is_initialized()); - // SAFETY: The inner value has been initialized Ok(unsafe { self.get_unchecked() }) } @@ -451,10 +450,10 @@ impl OnceLock { where F: FnOnce() -> Result, { - if self.get().is_none() { + if self.get_mut().is_none() { self.initialize(f)?; } - debug_assert!(self.is_initialized()); + // SAFETY: The inner value has been initialized Ok(unsafe { self.get_unchecked_mut() }) } @@ -503,22 +502,32 @@ impl OnceLock { #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn take(&mut self) -> Option { - if self.is_initialized() { + if self.initialized_mut() { self.once = Once::new(); // SAFETY: `self.value` is initialized and contains a valid `T`. - // `self.once` is reset, so `is_initialized()` will be false again + // `self.once` is reset, so `initialized()` will be false again // which prevents the value from being read twice. - unsafe { Some((&mut *self.value.get()).assume_init_read()) } + unsafe { Some(self.value.get_mut().assume_init_read()) } } else { None } } #[inline] - fn is_initialized(&self) -> bool { + fn initialized(&self) -> bool { self.once.is_completed() } + #[inline] + fn initialized_mut(&mut self) -> bool { + // `state()` does not perform an atomic load, so prefer it over `is_complete()`. + let state = self.once.state(); + match state { + OnceExclusiveState::Complete => true, + _ => false, + } + } + #[cold] #[optimize(size)] fn initialize(&self, f: F) -> Result<(), E> @@ -552,7 +561,7 @@ impl OnceLock { /// The cell must be initialized #[inline] unsafe fn get_unchecked(&self) -> &T { - debug_assert!(self.is_initialized()); + debug_assert!(self.initialized()); unsafe { (&*self.value.get()).assume_init_ref() } } @@ -561,8 +570,8 @@ impl OnceLock { /// The cell must be initialized #[inline] unsafe fn get_unchecked_mut(&mut self) -> &mut T { - debug_assert!(self.is_initialized()); - unsafe { (&mut *self.value.get()).assume_init_mut() } + debug_assert!(self.initialized_mut()); + unsafe { self.value.get_mut().assume_init_mut() } } } @@ -582,7 +591,8 @@ impl RefUnwindSafe for OnceLock {} impl UnwindSafe for OnceLock {} #[stable(feature = "once_cell", since = "1.70.0")] -impl Default for OnceLock { +#[rustc_const_unstable(feature = "const_default", issue = "143894")] +impl const Default for OnceLock { /// Creates a new uninitialized cell. /// /// # Example @@ -689,11 +699,11 @@ impl Eq for OnceLock {} unsafe impl<#[may_dangle] T> Drop for OnceLock { #[inline] fn drop(&mut self) { - if self.is_initialized() { + if self.initialized_mut() { // SAFETY: The cell is initialized and being dropped, so it can't // be accessed again. We also don't touch the `T` other than // dropping it, which validates our usage of #[may_dangle]. - unsafe { (&mut *self.value.get()).assume_init_drop() }; + unsafe { self.value.get_mut().assume_init_drop() }; } } } diff --git a/library/std/src/sync/oneshot.rs b/library/std/src/sync/oneshot.rs new file mode 100644 index 0000000000000..b2c9ba34c0ff6 --- /dev/null +++ b/library/std/src/sync/oneshot.rs @@ -0,0 +1,466 @@ +//! A single-producer, single-consumer (oneshot) channel. +//! +//! This is an experimental module, so the API will likely change. + +use crate::sync::mpmc; +use crate::sync::mpsc::{RecvError, SendError}; +use crate::time::{Duration, Instant}; +use crate::{error, fmt}; + +/// Creates a new oneshot channel, returning the sender/receiver halves. +/// +/// # Examples +/// +/// ``` +/// #![feature(oneshot_channel)] +/// use std::sync::oneshot; +/// use std::thread; +/// +/// let (sender, receiver) = oneshot::channel(); +/// +/// // Spawn off an expensive computation. +/// thread::spawn(move || { +/// # fn expensive_computation() -> i32 { 42 } +/// sender.send(expensive_computation()).unwrap(); +/// // `sender` is consumed by `send`, so we cannot use it anymore. +/// }); +/// +/// # fn do_other_work() -> i32 { 42 } +/// do_other_work(); +/// +/// // Let's see what that answer was... +/// println!("{:?}", receiver.recv().unwrap()); +/// // `receiver` is consumed by `recv`, so we cannot use it anymore. +/// ``` +#[must_use] +#[unstable(feature = "oneshot_channel", issue = "143674")] +pub fn channel() -> (Sender, Receiver) { + // Using a `sync_channel` with capacity 1 means that the internal implementation will use the + // `Array`-flavored channel implementation. + let (sender, receiver) = mpmc::sync_channel(1); + (Sender { inner: sender }, Receiver { inner: receiver }) +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Sender +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/// The sending half of a oneshot channel. +/// +/// # Examples +/// +/// ``` +/// #![feature(oneshot_channel)] +/// use std::sync::oneshot; +/// use std::thread; +/// +/// let (sender, receiver) = oneshot::channel(); +/// +/// thread::spawn(move || { +/// sender.send("Hello from thread!").unwrap(); +/// }); +/// +/// assert_eq!(receiver.recv().unwrap(), "Hello from thread!"); +/// ``` +/// +/// `Sender` cannot be sent between threads if it is sending non-`Send` types. +/// +/// ```compile_fail +/// #![feature(oneshot_channel)] +/// use std::sync::oneshot; +/// use std::thread; +/// use std::ptr; +/// +/// let (sender, receiver) = oneshot::channel(); +/// +/// struct NotSend(*mut ()); +/// thread::spawn(move || { +/// sender.send(NotSend(ptr::null_mut())); +/// }); +/// +/// let reply = receiver.try_recv().unwrap(); +/// ``` +#[unstable(feature = "oneshot_channel", issue = "143674")] +pub struct Sender { + /// The `oneshot` channel is simply a wrapper around a `mpmc` channel. + inner: mpmc::Sender, +} + +// SAFETY: Since the only methods in which synchronization must occur take full ownership of the +// [`Sender`], it is perfectly safe to share a `&Sender` between threads (as it is effectively +// useless without ownership). +#[unstable(feature = "oneshot_channel", issue = "143674")] +unsafe impl Sync for Sender {} + +impl Sender { + /// Attempts to send a value through this channel. This can only fail if the corresponding + /// [`Receiver`] has been dropped. + /// + /// This method is non-blocking (wait-free). + /// + /// # Examples + /// + /// ``` + /// #![feature(oneshot_channel)] + /// use std::sync::oneshot; + /// use std::thread; + /// + /// let (tx, rx) = oneshot::channel(); + /// + /// thread::spawn(move || { + /// // Perform some computation. + /// let result = 2 + 2; + /// tx.send(result).unwrap(); + /// }); + /// + /// assert_eq!(rx.recv().unwrap(), 4); + /// ``` + #[unstable(feature = "oneshot_channel", issue = "143674")] + pub fn send(self, t: T) -> Result<(), SendError> { + self.inner.send(t) + } +} + +#[unstable(feature = "oneshot_channel", issue = "143674")] +impl fmt::Debug for Sender { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Sender").finish_non_exhaustive() + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Receiver +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/// The receiving half of a oneshot channel. +/// +/// # Examples +/// +/// ``` +/// #![feature(oneshot_channel)] +/// use std::sync::oneshot; +/// use std::thread; +/// use std::time::Duration; +/// +/// let (sender, receiver) = oneshot::channel(); +/// +/// thread::spawn(move || { +/// thread::sleep(Duration::from_millis(100)); +/// sender.send("Hello after delay!").unwrap(); +/// }); +/// +/// println!("Waiting for message..."); +/// println!("{}", receiver.recv().unwrap()); +/// ``` +/// +/// `Receiver` cannot be sent between threads if it is receiving non-`Send` types. +/// +/// ```compile_fail +/// # #![feature(oneshot_channel)] +/// # use std::sync::oneshot; +/// # use std::thread; +/// # use std::ptr; +/// # +/// let (sender, receiver) = oneshot::channel(); +/// +/// struct NotSend(*mut ()); +/// sender.send(NotSend(ptr::null_mut())); +/// +/// thread::spawn(move || { +/// let reply = receiver.try_recv().unwrap(); +/// }); +/// ``` +#[unstable(feature = "oneshot_channel", issue = "143674")] +pub struct Receiver { + /// The `oneshot` channel is simply a wrapper around a `mpmc` channel. + inner: mpmc::Receiver, +} + +// SAFETY: Since the only methods in which synchronization must occur take full ownership of the +// [`Receiver`], it is perfectly safe to share a `&Receiver` between threads (as it is unable to +// receive any values without ownership). +#[unstable(feature = "oneshot_channel", issue = "143674")] +unsafe impl Sync for Receiver {} + +impl Receiver { + /// Receives the value from the sending end, blocking the calling thread until it gets it. + /// + /// Can only fail if the corresponding [`Sender`] has been dropped. + /// + /// # Examples + /// + /// ``` + /// #![feature(oneshot_channel)] + /// use std::sync::oneshot; + /// use std::thread; + /// use std::time::Duration; + /// + /// let (tx, rx) = oneshot::channel(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(500)); + /// tx.send("Done!").unwrap(); + /// }); + /// + /// // This will block until the message arrives. + /// println!("{}", rx.recv().unwrap()); + /// ``` + #[unstable(feature = "oneshot_channel", issue = "143674")] + pub fn recv(self) -> Result { + self.inner.recv() + } + + // Fallible methods. + + /// Attempts to return a pending value on this receiver without blocking. + /// + /// # Examples + /// + /// ``` + /// #![feature(oneshot_channel)] + /// use std::sync::oneshot; + /// use std::thread; + /// use std::time::Duration; + /// + /// let (sender, mut receiver) = oneshot::channel(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(100)); + /// sender.send(42).unwrap(); + /// }); + /// + /// // Keep trying until we get the message, doing other work in the process. + /// loop { + /// match receiver.try_recv() { + /// Ok(value) => { + /// assert_eq!(value, 42); + /// break; + /// } + /// Err(oneshot::TryRecvError::Empty(rx)) => { + /// // Retake ownership of the receiver. + /// receiver = rx; + /// # fn do_other_work() { thread::sleep(Duration::from_millis(25)); } + /// do_other_work(); + /// } + /// Err(oneshot::TryRecvError::Disconnected) => panic!("Sender disconnected"), + /// } + /// } + /// ``` + #[unstable(feature = "oneshot_channel", issue = "143674")] + pub fn try_recv(self) -> Result> { + self.inner.try_recv().map_err(|err| match err { + mpmc::TryRecvError::Empty => TryRecvError::Empty(self), + mpmc::TryRecvError::Disconnected => TryRecvError::Disconnected, + }) + } + + /// Attempts to wait for a value on this receiver, returning an error if the corresponding + /// [`Sender`] half of this channel has been dropped, or if it waits more than `timeout`. + /// + /// # Examples + /// + /// ``` + /// #![feature(oneshot_channel)] + /// use std::sync::oneshot; + /// use std::thread; + /// use std::time::Duration; + /// + /// let (sender, receiver) = oneshot::channel(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(500)); + /// sender.send("Success!").unwrap(); + /// }); + /// + /// // Wait up to 1 second for the message + /// match receiver.recv_timeout(Duration::from_secs(1)) { + /// Ok(msg) => println!("Received: {}", msg), + /// Err(oneshot::RecvTimeoutError::Timeout(_)) => println!("Timed out!"), + /// Err(oneshot::RecvTimeoutError::Disconnected) => println!("Sender dropped!"), + /// } + /// ``` + #[unstable(feature = "oneshot_channel", issue = "143674")] + pub fn recv_timeout(self, timeout: Duration) -> Result> { + self.inner.recv_timeout(timeout).map_err(|err| match err { + mpmc::RecvTimeoutError::Timeout => RecvTimeoutError::Timeout(self), + mpmc::RecvTimeoutError::Disconnected => RecvTimeoutError::Disconnected, + }) + } + + /// Attempts to wait for a value on this receiver, returning an error if the corresponding + /// [`Sender`] half of this channel has been dropped, or if `deadline` is reached. + /// + /// # Examples + /// + /// ``` + /// #![feature(oneshot_channel)] + /// use std::sync::oneshot; + /// use std::thread; + /// use std::time::{Duration, Instant}; + /// + /// let (sender, receiver) = oneshot::channel(); + /// + /// thread::spawn(move || { + /// thread::sleep(Duration::from_millis(100)); + /// sender.send("Just in time!").unwrap(); + /// }); + /// + /// let deadline = Instant::now() + Duration::from_millis(500); + /// match receiver.recv_deadline(deadline) { + /// Ok(msg) => println!("Received: {}", msg), + /// Err(oneshot::RecvTimeoutError::Timeout(_)) => println!("Missed deadline!"), + /// Err(oneshot::RecvTimeoutError::Disconnected) => println!("Sender dropped!"), + /// } + /// ``` + #[unstable(feature = "oneshot_channel", issue = "143674")] + pub fn recv_deadline(self, deadline: Instant) -> Result> { + self.inner.recv_deadline(deadline).map_err(|err| match err { + mpmc::RecvTimeoutError::Timeout => RecvTimeoutError::Timeout(self), + mpmc::RecvTimeoutError::Disconnected => RecvTimeoutError::Disconnected, + }) + } +} + +#[unstable(feature = "oneshot_channel", issue = "143674")] +impl fmt::Debug for Receiver { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Receiver").finish_non_exhaustive() + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Receiver Errors +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/// An error returned from the [`try_recv`](Receiver::try_recv) method. +/// +/// See the documentation for [`try_recv`] for more information on how to use this error. +/// +/// [`try_recv`]: Receiver::try_recv +#[unstable(feature = "oneshot_channel", issue = "143674")] +pub enum TryRecvError { + /// The [`Sender`] has not sent a message yet, but it might in the future (as it has not yet + /// disconnected). This variant contains the [`Receiver`] that [`try_recv`](Receiver::try_recv) + /// took ownership over. + Empty(Receiver), + /// The corresponding [`Sender`] half of this channel has become disconnected, and there will + /// never be any more data sent over the channel. + Disconnected, +} + +/// An error returned from the [`recv_timeout`](Receiver::recv_timeout) or +/// [`recv_deadline`](Receiver::recv_deadline) methods. +/// +/// # Examples +/// +/// Usage of this error is similar to [`TryRecvError`]. +/// +/// ``` +/// #![feature(oneshot_channel)] +/// use std::sync::oneshot::{self, RecvTimeoutError}; +/// use std::thread; +/// use std::time::Duration; +/// +/// let (sender, receiver) = oneshot::channel(); +/// +/// let send_failure = thread::spawn(move || { +/// // Simulate a long computation that takes longer than our timeout. +/// thread::sleep(Duration::from_millis(250)); +/// +/// // This will likely fail to send because we drop the receiver in the main thread. +/// sender.send("Goodbye!".to_string()).unwrap(); +/// }); +/// +/// // Try to receive the message with a short timeout. +/// match receiver.recv_timeout(Duration::from_millis(10)) { +/// Ok(msg) => println!("Received: {}", msg), +/// Err(RecvTimeoutError::Timeout(rx)) => { +/// println!("Timed out waiting for message!"); +/// +/// // Note that you can reuse the receiver without dropping it. +/// drop(rx); +/// }, +/// Err(RecvTimeoutError::Disconnected) => println!("Sender dropped!"), +/// } +/// +/// send_failure.join().unwrap_err(); +/// ``` +#[unstable(feature = "oneshot_channel", issue = "143674")] +pub enum RecvTimeoutError { + /// The [`Sender`] has not sent a message yet, but it might in the future (as it has not yet + /// disconnected). This variant contains the [`Receiver`] that either + /// [`recv_timeout`](Receiver::recv_timeout) or [`recv_deadline`](Receiver::recv_deadline) took + /// ownership over. + Timeout(Receiver), + /// The corresponding [`Sender`] half of this channel has become disconnected, and there will + /// never be any more data sent over the channel. + Disconnected, +} + +#[unstable(feature = "oneshot_channel", issue = "143674")] +impl fmt::Debug for TryRecvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("TryRecvError").finish_non_exhaustive() + } +} + +#[unstable(feature = "oneshot_channel", issue = "143674")] +impl fmt::Display for TryRecvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + TryRecvError::Empty(..) => "receiving on an empty oneshot channel".fmt(f), + TryRecvError::Disconnected => "receiving on a closed oneshot channel".fmt(f), + } + } +} + +#[unstable(feature = "oneshot_channel", issue = "143674")] +impl error::Error for TryRecvError {} + +#[unstable(feature = "oneshot_channel", issue = "143674")] +impl From for TryRecvError { + /// Converts a `RecvError` into a `TryRecvError`. + /// + /// This conversion always returns `TryRecvError::Disconnected`. + /// + /// No data is allocated on the heap. + fn from(err: RecvError) -> TryRecvError { + match err { + RecvError => TryRecvError::Disconnected, + } + } +} + +#[unstable(feature = "oneshot_channel", issue = "143674")] +impl fmt::Debug for RecvTimeoutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("RecvTimeoutError").finish_non_exhaustive() + } +} + +#[unstable(feature = "oneshot_channel", issue = "143674")] +impl fmt::Display for RecvTimeoutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + RecvTimeoutError::Timeout(..) => "timed out waiting on oneshot channel".fmt(f), + RecvTimeoutError::Disconnected => "receiving on a closed oneshot channel".fmt(f), + } + } +} + +#[unstable(feature = "oneshot_channel", issue = "143674")] +impl error::Error for RecvTimeoutError {} + +#[unstable(feature = "oneshot_channel", issue = "143674")] +impl From for RecvTimeoutError { + /// Converts a `RecvError` into a `RecvTimeoutError`. + /// + /// This conversion always returns `RecvTimeoutError::Disconnected`. + /// + /// No data is allocated on the heap. + fn from(err: RecvError) -> RecvTimeoutError { + match err { + RecvError => RecvTimeoutError::Disconnected, + } + } +} diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 7f9e5fe62516f..6eccd8a875edd 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -688,7 +688,7 @@ impl From for Mutex { } #[stable(feature = "mutex_default", since = "1.10.0")] -impl Default for Mutex { +impl Default for Mutex { /// Creates a `Mutex`, with the `Default` value for T. fn default() -> Mutex { Mutex::new(Default::default()) diff --git a/library/std/src/sys/alloc/windows.rs b/library/std/src/sys/alloc/windows.rs index 7e2402afab972..90da0b7e9965c 100644 --- a/library/std/src/sys/alloc/windows.rs +++ b/library/std/src/sys/alloc/windows.rs @@ -20,7 +20,7 @@ const HEAP_ZERO_MEMORY: u32 = 0x00000008; // always return the same handle, which remains valid for the entire lifetime of the process. // // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-getprocessheap -windows_targets::link!("kernel32.dll" "system" fn GetProcessHeap() -> c::HANDLE); +windows_link::link!("kernel32.dll" "system" fn GetProcessHeap() -> c::HANDLE); // Allocate a block of `dwBytes` bytes of memory from a given heap `hHeap`. // The allocated memory may be uninitialized, or zeroed if `dwFlags` is @@ -36,7 +36,7 @@ windows_targets::link!("kernel32.dll" "system" fn GetProcessHeap() -> c::HANDLE) // Note that `dwBytes` is allowed to be zero, contrary to some other allocators. // // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapalloc -windows_targets::link!("kernel32.dll" "system" fn HeapAlloc(hheap: c::HANDLE, dwflags: u32, dwbytes: usize) -> *mut c_void); +windows_link::link!("kernel32.dll" "system" fn HeapAlloc(hheap: c::HANDLE, dwflags: u32, dwbytes: usize) -> *mut c_void); // Reallocate a block of memory behind a given pointer `lpMem` from a given heap `hHeap`, // to a block of at least `dwBytes` bytes, either shrinking the block in place, @@ -57,7 +57,7 @@ windows_targets::link!("kernel32.dll" "system" fn HeapAlloc(hheap: c::HANDLE, dw // Note that `dwBytes` is allowed to be zero, contrary to some other allocators. // // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heaprealloc -windows_targets::link!("kernel32.dll" "system" fn HeapReAlloc( +windows_link::link!("kernel32.dll" "system" fn HeapReAlloc( hheap: c::HANDLE, dwflags : u32, lpmem: *const c_void, @@ -78,7 +78,7 @@ windows_targets::link!("kernel32.dll" "system" fn HeapReAlloc( // Note that `lpMem` is allowed to be null, which will not cause the operation to fail. // // See https://docs.microsoft.com/windows/win32/api/heapapi/nf-heapapi-heapfree -windows_targets::link!("kernel32.dll" "system" fn HeapFree(hheap: c::HANDLE, dwflags: u32, lpmem: *const c_void) -> c::BOOL); +windows_link::link!("kernel32.dll" "system" fn HeapFree(hheap: c::HANDLE, dwflags: u32, lpmem: *const c_void) -> c::BOOL); fn get_process_heap() -> *mut c_void { // SAFETY: GetProcessHeap simply returns a valid handle or NULL so is always safe to call. diff --git a/library/std/src/sys/anonymous_pipe/motor.rs b/library/std/src/sys/anonymous_pipe/motor.rs deleted file mode 100644 index dfe10f7fafe49..0000000000000 --- a/library/std/src/sys/anonymous_pipe/motor.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::io; -use crate::sys::fd::FileDesc; -use crate::sys::pipe::anon_pipe; -use crate::sys_common::IntoInner; - -pub type AnonPipe = FileDesc; - -#[inline] -pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { - anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) -} diff --git a/library/std/src/sys/anonymous_pipe/unix.rs b/library/std/src/sys/anonymous_pipe/unix.rs deleted file mode 100644 index dfe10f7fafe49..0000000000000 --- a/library/std/src/sys/anonymous_pipe/unix.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::io; -use crate::sys::fd::FileDesc; -use crate::sys::pipe::anon_pipe; -use crate::sys_common::IntoInner; - -pub type AnonPipe = FileDesc; - -#[inline] -pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { - anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner())) -} diff --git a/library/std/src/sys/anonymous_pipe/unsupported.rs b/library/std/src/sys/anonymous_pipe/unsupported.rs deleted file mode 100644 index a0805ba9540e0..0000000000000 --- a/library/std/src/sys/anonymous_pipe/unsupported.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::io; -pub use crate::sys::pipe::AnonPipe; - -#[inline] -pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> { - Err(io::Error::UNSUPPORTED_PLATFORM) -} diff --git a/library/std/src/sys/args/sgx.rs b/library/std/src/sys/args/sgx.rs index f800500c22a8a..6ff94f5681b6f 100644 --- a/library/std/src/sys/args/sgx.rs +++ b/library/std/src/sys/args/sgx.rs @@ -4,10 +4,10 @@ use crate::ffi::OsString; use crate::num::NonZero; use crate::ops::Try; use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; +use crate::sys::FromInner; use crate::sys::os_str::Buf; use crate::sys::pal::abi::usercalls::alloc; use crate::sys::pal::abi::usercalls::raw::ByteBuffer; -use crate::sys_common::FromInner; use crate::{fmt, slice}; // Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests diff --git a/library/std/src/sys/args/windows.rs b/library/std/src/sys/args/windows.rs index 81c44fabdcc67..c1988657ff1b1 100644 --- a/library/std/src/sys/args/windows.rs +++ b/library/std/src/sys/args/windows.rs @@ -11,12 +11,11 @@ use crate::ffi::{OsStr, OsString}; use crate::num::NonZero; use crate::os::windows::prelude::*; use crate::path::{Path, PathBuf}; +use crate::sys::helpers::WStrUnits; use crate::sys::pal::os::current_exe; use crate::sys::pal::{ensure_no_nuls, fill_utf16_buf}; use crate::sys::path::get_long_path; -use crate::sys::{c, to_u16s}; -use crate::sys_common::AsInner; -use crate::sys_common::wstr::WStrUnits; +use crate::sys::{AsInner, c, to_u16s}; use crate::{io, iter, ptr}; pub fn args() -> Args { diff --git a/library/std/src/sys/backtrace.rs b/library/std/src/sys/backtrace.rs index 57682207e078e..858a95882b39f 100644 --- a/library/std/src/sys/backtrace.rs +++ b/library/std/src/sys/backtrace.rs @@ -11,6 +11,12 @@ use crate::{env, fmt, io}; /// Max number of frames to print. const MAX_NB_FRAMES: usize = 100; +pub(crate) const FULL_BACKTRACE_DEFAULT: bool = cfg_select! { + // Fuchsia components default to full backtrace. + target_os = "fuchsia" => true, + _ => false, +}; + pub(crate) struct BacktraceLock<'a>(#[allow(dead_code)] MutexGuard<'a, ()>); pub(crate) fn lock<'a>() -> BacktraceLock<'a> { @@ -20,8 +26,6 @@ pub(crate) fn lock<'a>() -> BacktraceLock<'a> { impl BacktraceLock<'_> { /// Prints the current backtrace. - /// - /// NOTE: this function is not Sync. The caller must hold a mutex lock, or there must be only one thread in the program. pub(crate) fn print(&mut self, w: &mut dyn Write, format: PrintFmt) -> io::Result<()> { // There are issues currently linking libbacktrace into tests, and in // general during std's own unit tests we're not testing this path. In @@ -36,6 +40,7 @@ impl BacktraceLock<'_> { } impl fmt::Display for DisplayBacktrace { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + // SAFETY: the backtrace lock is held unsafe { _print_fmt(fmt, self.format) } } } @@ -43,6 +48,9 @@ impl BacktraceLock<'_> { } } +/// # Safety +/// +/// This function is not Sync. The caller must hold a mutex lock, or there must be only one thread in the program. unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result { // Always 'fail' to get the cwd when running under Miri - // this allows Miri to display backtraces in isolation mode diff --git a/library/std/src/sys/configure_builtins.rs b/library/std/src/sys/configure_builtins.rs index 9d776b778dcbe..7cdf04ebb8899 100644 --- a/library/std/src/sys/configure_builtins.rs +++ b/library/std/src/sys/configure_builtins.rs @@ -1,13 +1,53 @@ -/// Hook into .init_array to enable LSE atomic operations at startup, if -/// supported. -#[cfg(all(target_arch = "aarch64", target_os = "linux", not(feature = "compiler-builtins-c")))] +//! The configure builtins provides runtime support compiler-builtin features +//! which require dynamic initialization to work as expected, e.g. aarch64 +//! outline-atomics. + +/// Enable LSE atomic operations at startup, if supported. +/// +/// Linker sections are based on what [`ctor`] does, with priorities to run slightly before user +/// code: +/// +/// - Apple uses the section `__mod_init_func`, `mod_init_funcs` is needed to set +/// `S_MOD_INIT_FUNC_POINTERS`. There doesn't seem to be a way to indicate priorities. +/// - Windows uses `.CRT$XCT`, which is run before user constructors (these should use `.CRT$XCU`). +/// - ELF uses `.init_array` with a priority of 90, which runs before our `ARGV_INIT_ARRAY` +/// initializer (priority 99). Both are within the 0-100 implementation-reserved range, per docs +/// for the [`prio-ctor-dtor`] warning, and this matches compiler-rt's `CONSTRUCTOR_PRIORITY`. +/// +/// To save startup time, the initializer is only run if outline atomic routines from +/// compiler-builtins may be used. If LSE is known to be available then the calls are never +/// emitted, and if we build the C intrinsics then it has its own initializer using the symbol +/// `__aarch64_have_lse_atomics`. +/// +/// Initialization is done in a global constructor to so we get the same behavior regardless of +/// whether Rust's `init` is used, or if we are in a `dylib` or `no_main` situation (as opposed +/// to doing it as part of pre-main startup). This also matches C implementations. +/// +/// Ideally `core` would have something similar, but detecting the CPU features requires the +/// auxiliary vector from the OS. We do the initialization in `std` rather than as part of +/// `compiler-builtins` because a builtins->std dependency isn't possible, and inlining parts of +/// `std-detect` would be much messier. +/// +/// [`ctor`]: https://github.com/mmastrac/rust-ctor/blob/63382b833ddcbfb8b064f4e86bfa1ed4026ff356/shared/src/macros/mod.rs#L522-L534 +/// [`prio-ctor-dtor`]: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html +#[cfg(all( + target_arch = "aarch64", + target_feature = "outline-atomics", + not(target_feature = "lse"), + not(feature = "compiler-builtins-c"), +))] #[used] -#[unsafe(link_section = ".init_array.90")] +#[cfg_attr(target_vendor = "apple", unsafe(link_section = "__DATA,__mod_init_func,mod_init_funcs"))] +#[cfg_attr(target_os = "windows", unsafe(link_section = ".CRT$XCT"))] +#[cfg_attr( + not(any(target_vendor = "apple", target_os = "windows")), + unsafe(link_section = ".init_array.90") +)] static RUST_LSE_INIT: extern "C" fn() = { extern "C" fn init_lse() { use crate::arch; - // This is provided by compiler-builtins::aarch64_linux. + // This is provided by compiler-builtins::aarch64_outline_atomics. unsafe extern "C" { fn __rust_enable_lse(); } diff --git a/library/std/src/sys/env/solid.rs b/library/std/src/sys/env/solid.rs index ea77fc3c11930..0ce5a22b42561 100644 --- a/library/std/src/sys/env/solid.rs +++ b/library/std/src/sys/env/solid.rs @@ -6,7 +6,7 @@ use crate::io; use crate::os::raw::{c_char, c_int}; use crate::os::solid::ffi::{OsStrExt, OsStringExt}; use crate::sync::{PoisonError, RwLock}; -use crate::sys::common::small_c_string::run_with_cstr; +use crate::sys::helpers::run_with_cstr; static ENV_LOCK: RwLock<()> = RwLock::new(()); diff --git a/library/std/src/sys/env/uefi.rs b/library/std/src/sys/env/uefi.rs index 1561df41cac3f..af16a02642a4c 100644 --- a/library/std/src/sys/env/uefi.rs +++ b/library/std/src/sys/env/uefi.rs @@ -24,7 +24,7 @@ mod uefi_env { use crate::io; use crate::os::uefi::ffi::OsStringExt; use crate::ptr::NonNull; - use crate::sys::{helpers, unsupported_err}; + use crate::sys::pal::{helpers, unsupported_err}; pub(crate) fn get(key: &OsStr) -> Option { let shell = helpers::open_shell()?; @@ -95,8 +95,8 @@ mod uefi_env { val_ptr: *mut r_efi::efi::Char16, ) -> io::Result<()> { let shell = helpers::open_shell().ok_or(unsupported_err())?; - let r = - unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, r_efi::efi::Boolean::FALSE) }; + let volatile = r_efi::efi::Boolean::TRUE; + let r = unsafe { ((*shell.as_ptr()).set_env)(key_ptr, val_ptr, volatile) }; if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } } diff --git a/library/std/src/sys/env/unix.rs b/library/std/src/sys/env/unix.rs index 78c7af65f9e38..66805ce3fa71e 100644 --- a/library/std/src/sys/env/unix.rs +++ b/library/std/src/sys/env/unix.rs @@ -7,8 +7,8 @@ use crate::ffi::{CStr, OsStr, OsString}; use crate::io; use crate::os::unix::prelude::*; use crate::sync::{PoisonError, RwLock}; -use crate::sys::common::small_c_string::run_with_cstr; use crate::sys::cvt; +use crate::sys::helpers::run_with_cstr; // Use `_NSGetEnviron` on Apple platforms. // diff --git a/library/std/src/sys/env/wasi.rs b/library/std/src/sys/env/wasi.rs index 1327cbc3263b8..c970aac182604 100644 --- a/library/std/src/sys/env/wasi.rs +++ b/library/std/src/sys/env/wasi.rs @@ -4,7 +4,7 @@ pub use super::common::Env; use crate::ffi::{CStr, OsStr, OsString}; use crate::io; use crate::os::wasi::prelude::*; -use crate::sys::common::small_c_string::run_with_cstr; +use crate::sys::helpers::run_with_cstr; use crate::sys::pal::os::{cvt, libc}; cfg_select! { diff --git a/library/std/src/sys/env/zkvm.rs b/library/std/src/sys/env/zkvm.rs index 2eb7005ba1289..b672a03bf0ba7 100644 --- a/library/std/src/sys/env/zkvm.rs +++ b/library/std/src/sys/env/zkvm.rs @@ -4,9 +4,8 @@ mod unsupported_env; pub use unsupported_env::{Env, env, setenv, unsetenv}; use crate::ffi::{OsStr, OsString}; -use crate::sys::os_str; use crate::sys::pal::{WORD_SIZE, abi}; -use crate::sys_common::FromInner; +use crate::sys::{FromInner, os_str}; pub fn getenv(varname: &OsStr) -> Option { let varname = varname.as_encoded_bytes(); diff --git a/library/std/src/sys/exit_guard.rs b/library/std/src/sys/exit_guard.rs index 00b91842e9dba..e7d7a478a5baa 100644 --- a/library/std/src/sys/exit_guard.rs +++ b/library/std/src/sys/exit_guard.rs @@ -34,7 +34,7 @@ cfg_select! { // lifetime of the thread. Additionally, accesses to `errno` are // async-signal-safe, so this function is available in all imaginable // circumstances. - let this_thread_id = crate::sys::os::errno_location(); + let this_thread_id = crate::sys::io::errno_location(); match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { Ok(_) => { // This is the first thread to call `unique_thread_exit`, diff --git a/library/std/src/sys/fd/hermit.rs b/library/std/src/sys/fd/hermit.rs index 7e8ba065f1b96..2666da16420c4 100644 --- a/library/std/src/sys/fd/hermit.rs +++ b/library/std/src/sys/fd/hermit.rs @@ -4,8 +4,7 @@ use crate::cmp; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, SeekFrom}; use crate::os::hermit::hermit_abi; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::{cvt, unsupported}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, cvt, unsupported}; const fn max_iov() -> usize { hermit_abi::IOV_MAX diff --git a/library/std/src/sys/fd/mod.rs b/library/std/src/sys/fd/mod.rs index 330499ecc18f6..02d61a62f4e6b 100644 --- a/library/std/src/sys/fd/mod.rs +++ b/library/std/src/sys/fd/mod.rs @@ -3,7 +3,7 @@ #![forbid(unsafe_op_in_unsafe_fn)] cfg_select! { - target_family = "unix" => { + any(target_family = "unix", target_os = "wasi") => { mod unix; pub use unix::*; } @@ -19,9 +19,5 @@ cfg_select! { mod sgx; pub use sgx::*; } - target_os = "wasi" => { - mod wasi; - pub use wasi::*; - } _ => {} } diff --git a/library/std/src/sys/fd/motor.rs b/library/std/src/sys/fd/motor.rs index 4211fef8007ab..edeb99ccf8efe 100644 --- a/library/std/src/sys/fd/motor.rs +++ b/library/std/src/sys/fd/motor.rs @@ -2,8 +2,7 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::map_motor_error; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, map_motor_error}; #[derive(Debug)] pub struct FileDesc(OwnedFd); diff --git a/library/std/src/sys/fd/unix.rs b/library/std/src/sys/fd/unix.rs index 2b2dfe48e89e2..bb6c0ac9e18e6 100644 --- a/library/std/src/sys/fd/unix.rs +++ b/library/std/src/sys/fd/unix.rs @@ -35,13 +35,12 @@ cfg_select! { use crate::cmp; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read}; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::cvt; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; #[cfg(all(target_os = "android", target_pointer_width = "64"))] use crate::sys::pal::weak::syscall; #[cfg(any(all(target_os = "android", target_pointer_width = "32"), target_vendor = "apple"))] use crate::sys::pal::weak::weak; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, cvt}; #[derive(Debug)] pub struct FileDesc(OwnedFd); @@ -152,7 +151,8 @@ impl FileDesc { target_os = "espidf", target_os = "horizon", target_os = "vita", - target_os = "nuttx" + target_os = "nuttx", + target_os = "wasi", ))) } @@ -385,7 +385,8 @@ impl FileDesc { target_os = "espidf", target_os = "horizon", target_os = "vita", - target_os = "nuttx" + target_os = "nuttx", + target_os = "wasi", ))) } @@ -560,6 +561,7 @@ impl FileDesc { target_os = "redox", target_os = "vxworks", target_os = "nto", + target_os = "wasi", )))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { @@ -583,6 +585,7 @@ impl FileDesc { target_os = "redox", target_os = "vxworks", target_os = "nto", + target_os = "wasi", ))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { diff --git a/library/std/src/sys/fd/wasi.rs b/library/std/src/sys/fd/wasi.rs deleted file mode 100644 index 80a5143ff0b00..0000000000000 --- a/library/std/src/sys/fd/wasi.rs +++ /dev/null @@ -1,331 +0,0 @@ -#![expect(dead_code)] - -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem; -use crate::net::Shutdown; -use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::pal::err2io; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; - -#[derive(Debug)] -pub struct WasiFd { - fd: OwnedFd, -} - -fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::Iovec] { - assert_eq!(size_of::>(), size_of::()); - assert_eq!(align_of::>(), align_of::()); - // SAFETY: `IoSliceMut` and `IoVec` have exactly the same memory layout. - // We decorate our `IoSliceMut` with `repr(transparent)` (see `io.rs`), and - // `crate::io::IoSliceMut` is a `repr(transparent)` wrapper around our type, so this is - // guaranteed. - unsafe { mem::transmute(a) } -} - -fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] { - assert_eq!(size_of::>(), size_of::()); - assert_eq!(align_of::>(), align_of::()); - // SAFETY: `IoSlice` and `CIoVec` have exactly the same memory layout. - // We decorate our `IoSlice` with `repr(transparent)` (see `io.rs`), and - // `crate::io::IoSlice` is a `repr(transparent)` wrapper around our type, so this is - // guaranteed. - unsafe { mem::transmute(a) } -} - -impl WasiFd { - pub fn datasync(&self) -> io::Result<()> { - unsafe { wasi::fd_datasync(self.as_raw_fd() as wasi::Fd).map_err(err2io) } - } - - pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - unsafe { wasi::fd_pread(self.as_raw_fd() as wasi::Fd, iovec(bufs), offset).map_err(err2io) } - } - - pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - unsafe { - wasi::fd_pwrite(self.as_raw_fd() as wasi::Fd, ciovec(bufs), offset).map_err(err2io) - } - } - - pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - unsafe { wasi::fd_read(self.as_raw_fd() as wasi::Fd, iovec(bufs)).map_err(err2io) } - } - - pub fn read_buf(&self, mut buf: BorrowedCursor<'_>) -> io::Result<()> { - unsafe { - let bufs = [wasi::Iovec { - buf: buf.as_mut().as_mut_ptr() as *mut u8, - buf_len: buf.capacity(), - }]; - match wasi::fd_read(self.as_raw_fd() as wasi::Fd, &bufs) { - Ok(n) => { - buf.advance_unchecked(n); - Ok(()) - } - Err(e) => Err(err2io(e)), - } - } - } - - pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result { - unsafe { wasi::fd_write(self.as_raw_fd() as wasi::Fd, ciovec(bufs)).map_err(err2io) } - } - - pub fn seek(&self, pos: SeekFrom) -> io::Result { - let (whence, offset) = match pos { - SeekFrom::Start(pos) => (wasi::WHENCE_SET, pos as i64), - SeekFrom::End(pos) => (wasi::WHENCE_END, pos), - SeekFrom::Current(pos) => (wasi::WHENCE_CUR, pos), - }; - unsafe { wasi::fd_seek(self.as_raw_fd() as wasi::Fd, offset, whence).map_err(err2io) } - } - - pub fn tell(&self) -> io::Result { - unsafe { wasi::fd_tell(self.as_raw_fd() as wasi::Fd).map_err(err2io) } - } - - // FIXME: __wasi_fd_fdstat_get - - pub fn set_flags(&self, flags: wasi::Fdflags) -> io::Result<()> { - unsafe { wasi::fd_fdstat_set_flags(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) } - } - - pub fn set_rights(&self, base: wasi::Rights, inheriting: wasi::Rights) -> io::Result<()> { - unsafe { - wasi::fd_fdstat_set_rights(self.as_raw_fd() as wasi::Fd, base, inheriting) - .map_err(err2io) - } - } - - pub fn sync(&self) -> io::Result<()> { - unsafe { wasi::fd_sync(self.as_raw_fd() as wasi::Fd).map_err(err2io) } - } - - pub(crate) fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> { - unsafe { - wasi::fd_advise(self.as_raw_fd() as wasi::Fd, offset, len, advice).map_err(err2io) - } - } - - pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { - unsafe { wasi::fd_allocate(self.as_raw_fd() as wasi::Fd, offset, len).map_err(err2io) } - } - - pub fn create_directory(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_create_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } - } - - pub fn link( - &self, - old_flags: wasi::Lookupflags, - old_path: &str, - new_fd: &WasiFd, - new_path: &str, - ) -> io::Result<()> { - unsafe { - wasi::path_link( - self.as_raw_fd() as wasi::Fd, - old_flags, - old_path, - new_fd.as_raw_fd() as wasi::Fd, - new_path, - ) - .map_err(err2io) - } - } - - pub fn open( - &self, - dirflags: wasi::Lookupflags, - path: &str, - oflags: wasi::Oflags, - fs_rights_base: wasi::Rights, - fs_rights_inheriting: wasi::Rights, - fs_flags: wasi::Fdflags, - ) -> io::Result { - unsafe { - wasi::path_open( - self.as_raw_fd() as wasi::Fd, - dirflags, - path, - oflags, - fs_rights_base, - fs_rights_inheriting, - fs_flags, - ) - .map(|fd| WasiFd::from_raw_fd(fd as RawFd)) - .map_err(err2io) - } - } - - pub fn readdir(&self, buf: &mut [u8], cookie: wasi::Dircookie) -> io::Result { - unsafe { - wasi::fd_readdir(self.as_raw_fd() as wasi::Fd, buf.as_mut_ptr(), buf.len(), cookie) - .map_err(err2io) - } - } - - pub fn readlink(&self, path: &str, buf: &mut [u8]) -> io::Result { - unsafe { - wasi::path_readlink(self.as_raw_fd() as wasi::Fd, path, buf.as_mut_ptr(), buf.len()) - .map_err(err2io) - } - } - - pub fn rename(&self, old_path: &str, new_fd: &WasiFd, new_path: &str) -> io::Result<()> { - unsafe { - wasi::path_rename( - self.as_raw_fd() as wasi::Fd, - old_path, - new_fd.as_raw_fd() as wasi::Fd, - new_path, - ) - .map_err(err2io) - } - } - - pub(crate) fn filestat_get(&self) -> io::Result { - unsafe { wasi::fd_filestat_get(self.as_raw_fd() as wasi::Fd).map_err(err2io) } - } - - pub fn filestat_set_times( - &self, - atim: wasi::Timestamp, - mtim: wasi::Timestamp, - fstflags: wasi::Fstflags, - ) -> io::Result<()> { - unsafe { - wasi::fd_filestat_set_times(self.as_raw_fd() as wasi::Fd, atim, mtim, fstflags) - .map_err(err2io) - } - } - - pub fn filestat_set_size(&self, size: u64) -> io::Result<()> { - unsafe { wasi::fd_filestat_set_size(self.as_raw_fd() as wasi::Fd, size).map_err(err2io) } - } - - pub(crate) fn path_filestat_get( - &self, - flags: wasi::Lookupflags, - path: &str, - ) -> io::Result { - unsafe { - wasi::path_filestat_get(self.as_raw_fd() as wasi::Fd, flags, path).map_err(err2io) - } - } - - pub fn path_filestat_set_times( - &self, - flags: wasi::Lookupflags, - path: &str, - atim: wasi::Timestamp, - mtim: wasi::Timestamp, - fstflags: wasi::Fstflags, - ) -> io::Result<()> { - unsafe { - wasi::path_filestat_set_times( - self.as_raw_fd() as wasi::Fd, - flags, - path, - atim, - mtim, - fstflags, - ) - .map_err(err2io) - } - } - - pub fn symlink(&self, old_path: &str, new_path: &str) -> io::Result<()> { - unsafe { - wasi::path_symlink(old_path, self.as_raw_fd() as wasi::Fd, new_path).map_err(err2io) - } - } - - pub fn unlink_file(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_unlink_file(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } - } - - pub fn remove_directory(&self, path: &str) -> io::Result<()> { - unsafe { wasi::path_remove_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } - } - - pub fn sock_accept(&self, flags: wasi::Fdflags) -> io::Result { - unsafe { wasi::sock_accept(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) } - } - - pub fn sock_recv( - &self, - ri_data: &mut [IoSliceMut<'_>], - ri_flags: wasi::Riflags, - ) -> io::Result<(usize, wasi::Roflags)> { - unsafe { - wasi::sock_recv(self.as_raw_fd() as wasi::Fd, iovec(ri_data), ri_flags).map_err(err2io) - } - } - - pub fn sock_send(&self, si_data: &[IoSlice<'_>], si_flags: wasi::Siflags) -> io::Result { - unsafe { - wasi::sock_send(self.as_raw_fd() as wasi::Fd, ciovec(si_data), si_flags).map_err(err2io) - } - } - - pub fn sock_shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Read => wasi::SDFLAGS_RD, - Shutdown::Write => wasi::SDFLAGS_WR, - Shutdown::Both => wasi::SDFLAGS_WR | wasi::SDFLAGS_RD, - }; - unsafe { wasi::sock_shutdown(self.as_raw_fd() as wasi::Fd, how).map_err(err2io) } - } -} - -impl AsInner for WasiFd { - #[inline] - fn as_inner(&self) -> &OwnedFd { - &self.fd - } -} - -impl AsInnerMut for WasiFd { - #[inline] - fn as_inner_mut(&mut self) -> &mut OwnedFd { - &mut self.fd - } -} - -impl IntoInner for WasiFd { - fn into_inner(self) -> OwnedFd { - self.fd - } -} - -impl FromInner for WasiFd { - fn from_inner(owned_fd: OwnedFd) -> Self { - Self { fd: owned_fd } - } -} - -impl AsFd for WasiFd { - fn as_fd(&self) -> BorrowedFd<'_> { - self.fd.as_fd() - } -} - -impl AsRawFd for WasiFd { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.fd.as_raw_fd() - } -} - -impl IntoRawFd for WasiFd { - fn into_raw_fd(self) -> RawFd { - self.fd.into_raw_fd() - } -} - -impl FromRawFd for WasiFd { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } } - } -} diff --git a/library/std/src/sys/fs/common.rs b/library/std/src/sys/fs/common.rs index bfd684d295b89..4d47d392a8268 100644 --- a/library/std/src/sys/fs/common.rs +++ b/library/std/src/sys/fs/common.rs @@ -1,9 +1,10 @@ #![allow(dead_code)] // not used on all platforms -use crate::fs; use crate::io::{self, Error, ErrorKind}; -use crate::path::Path; -use crate::sys_common::ignore_notfound; +use crate::path::{Path, PathBuf}; +use crate::sys::fs::{File, OpenOptions}; +use crate::sys::helpers::ignore_notfound; +use crate::{fmt, fs}; pub(crate) const NOT_FILE_ERROR: Error = io::const_error!( ErrorKind::InvalidInput, @@ -58,3 +59,23 @@ pub fn exists(path: &Path) -> io::Result { Err(error) => Err(error), } } + +pub struct Dir { + path: PathBuf, +} + +impl Dir { + pub fn open(path: &Path, _opts: &OpenOptions) -> io::Result { + path.canonicalize().map(|path| Self { path }) + } + + pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { + File::open(&self.path.join(path), &opts) + } +} + +impl fmt::Debug for Dir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Dir").field("path", &self.path).finish() + } +} diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs index 21235bcfbd8c5..1e281eb0d9d18 100644 --- a/library/std/src/sys/fs/hermit.rs +++ b/library/std/src/sys/fs/hermit.rs @@ -9,16 +9,16 @@ use crate::os::hermit::hermit_abi::{ use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; use crate::sync::Arc; -use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::fd::FileDesc; -pub use crate::sys::fs::common::{copy, exists}; +pub use crate::sys::fs::common::{Dir, copy, exists}; +use crate::sys::helpers::run_path_with_cstr; use crate::sys::time::SystemTime; -use crate::sys::{cvt, unsupported, unsupported_err}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, cvt, unsupported, unsupported_err}; use crate::{fmt, mem}; #[derive(Debug)] pub struct File(FileDesc); + #[derive(Clone)] pub struct FileAttr { stat_val: stat_struct, diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index eaea28871241a..0c297c5766b82 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -6,16 +6,18 @@ use crate::path::{Path, PathBuf}; pub mod common; cfg_select! { - target_family = "unix" => { + any(target_family = "unix", target_os = "wasi") => { mod unix; use unix as imp; + #[cfg(not(target_os = "wasi"))] pub use unix::{chown, fchown, lchown, mkfifo}; - #[cfg(not(target_os = "fuchsia"))] + #[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] pub use unix::chroot; + #[cfg(not(target_os = "wasi"))] pub(crate) use unix::debug_assert_fd_is_open; #[cfg(any(target_os = "linux", target_os = "android"))] pub(super) use unix::CachedFileMetadata; - use crate::sys::common::small_c_string::run_path_with_cstr as with_native_path; + use crate::sys::helpers::run_path_with_cstr as with_native_path; } target_os = "windows" => { mod windows; @@ -43,10 +45,6 @@ cfg_select! { mod vexos; use vexos as imp; } - target_os = "wasi" => { - mod wasi; - use wasi as imp; - } _ => { mod unsupported; use unsupported as imp; @@ -54,14 +52,14 @@ cfg_select! { } // FIXME: Replace this with platform-specific path conversion functions. -#[cfg(not(any(target_family = "unix", target_os = "windows")))] +#[cfg(not(any(target_family = "unix", target_os = "windows", target_os = "wasi")))] #[inline] pub fn with_native_path(path: &Path, f: &dyn Fn(&Path) -> io::Result) -> io::Result { f(path) } pub use imp::{ - DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions, + Dir, DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions, ReadDir, }; diff --git a/library/std/src/sys/fs/motor.rs b/library/std/src/sys/fs/motor.rs index f723a23bd5dff..2ae01db24be5c 100644 --- a/library/std/src/sys/fs/motor.rs +++ b/library/std/src/sys/fs/motor.rs @@ -4,10 +4,9 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; use crate::sys::fd::FileDesc; -pub use crate::sys::fs::common::exists; +pub use crate::sys::fs::common::{Dir, exists}; use crate::sys::time::SystemTime; -use crate::sys::{map_motor_error, unsupported}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, map_motor_error, unsupported}; #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct FileType { diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index f6d5d3b784d3b..114ffda57bc52 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -9,11 +9,11 @@ use crate::os::raw::{c_int, c_short}; use crate::os::solid::ffi::OsStrExt; use crate::path::{Path, PathBuf}; use crate::sync::Arc; -pub use crate::sys::fs::common::exists; +pub use crate::sys::fs::common::{Dir, exists}; +use crate::sys::helpers::ignore_notfound; use crate::sys::pal::{abi, error}; use crate::sys::time::SystemTime; use crate::sys::{unsupported, unsupported_err}; -use crate::sys_common::ignore_notfound; type CIntNotMinusOne = core::num::niche_types::NotAllOnes; diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index 7625409007a46..8135519317a02 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -6,26 +6,29 @@ use crate::fs::TryLockError; use crate::hash::Hash; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::path::{Path, PathBuf}; +pub use crate::sys::fs::common::{Dir, copy, remove_dir_all}; +use crate::sys::pal::{helpers, unsupported}; use crate::sys::time::SystemTime; -use crate::sys::{helpers, unsupported}; -#[expect(dead_code)] const FILE_PERMISSIONS_MASK: u64 = r_efi::protocols::file::READ_ONLY; -pub struct File(!); +pub struct File(uefi_fs::File); #[derive(Clone)] pub struct FileAttr { attr: u64, size: u64, - accessed: SystemTime, - modified: SystemTime, - created: SystemTime, + file_time: FileTimes, + created: Option, } -pub struct ReadDir(!); +pub struct ReadDir(uefi_fs::File); -pub struct DirEntry(!); +pub struct DirEntry { + attr: FileAttr, + file_name: OsString, + path: PathBuf, +} #[derive(Clone, Debug)] pub struct OpenOptions { @@ -66,15 +69,20 @@ impl FileAttr { } pub fn modified(&self) -> io::Result { - Ok(self.modified) + self.file_time + .modified + .ok_or(io::const_error!(io::ErrorKind::InvalidData, "modification time is not valid")) } pub fn accessed(&self) -> io::Result { - Ok(self.accessed) + self.file_time + .accessed + .ok_or(io::const_error!(io::ErrorKind::InvalidData, "last access time is not valid")) } pub fn created(&self) -> io::Result { - Ok(self.created) + self.created + .ok_or(io::const_error!(io::ErrorKind::InvalidData, "creation time is not valid")) } fn from_uefi(info: helpers::UefiBox) -> Self { @@ -82,8 +90,10 @@ impl FileAttr { Self { attr: (*info.as_ptr()).attribute, size: (*info.as_ptr()).file_size, - modified: uefi_fs::uefi_to_systemtime((*info.as_ptr()).modification_time), - accessed: uefi_fs::uefi_to_systemtime((*info.as_ptr()).last_access_time), + file_time: FileTimes { + modified: uefi_fs::uefi_to_systemtime((*info.as_ptr()).modification_time), + accessed: uefi_fs::uefi_to_systemtime((*info.as_ptr()).last_access_time), + }, created: uefi_fs::uefi_to_systemtime((*info.as_ptr()).create_time), } } @@ -103,7 +113,6 @@ impl FilePermissions { Self(attr & r_efi::protocols::file::READ_ONLY != 0) } - #[expect(dead_code)] const fn to_attr(&self) -> u64 { if self.0 { r_efi::protocols::file::READ_ONLY } else { 0 } } @@ -139,8 +148,10 @@ impl FileType { } impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut b = f.debug_struct("ReadDir"); + b.field("path", &self.0.path()); + b.finish() } } @@ -148,25 +159,43 @@ impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option> { - self.0 + match self.0.read_dir_entry() { + Ok(None) => None, + Ok(Some(x)) => { + let temp = DirEntry::from_uefi(x, self.0.path()); + // Ignore "." and "..". This is how ReadDir behaves in Unix. + if temp.file_name == "." || temp.file_name == ".." { + self.next() + } else { + Some(Ok(temp)) + } + } + Err(e) => Some(Err(e)), + } } } impl DirEntry { pub fn path(&self) -> PathBuf { - self.0 + self.path.clone() } pub fn file_name(&self) -> OsString { - self.0 + self.file_name.clone() } pub fn metadata(&self) -> io::Result { - self.0 + Ok(self.attr.clone()) } pub fn file_type(&self) -> io::Result { - self.0 + Ok(self.attr.file_type()) + } + + fn from_uefi(info: helpers::UefiBox, parent: &Path) -> Self { + let file_name = uefi_fs::file_name_from_uefi(&info); + let path = parent.join(&file_name); + Self { file_name, path, attr: FileAttr::from_uefi(info) } } } @@ -215,9 +244,11 @@ impl OpenOptions { pub fn create_new(&mut self, create_new: bool) { self.create_new = create_new; + if create_new { + self.create(true); + } } - #[expect(dead_code)] const fn is_mode_valid(&self) -> bool { // Valid Combinations: Read, Read/Write, Read/Write/Create self.mode == file::MODE_READ @@ -227,100 +258,142 @@ impl OpenOptions { } impl File { - pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { - unsupported() + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + if !opts.is_mode_valid() { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "Invalid open options")); + } + + if opts.create_new && exists(path)? { + return Err(io::const_error!(io::ErrorKind::AlreadyExists, "File already exists")); + } + + let f = uefi_fs::File::from_path(path, opts.mode, 0).map(Self)?; + + if opts.truncate { + f.truncate(0)?; + } + + if opts.append { + f.seek(io::SeekFrom::End(0))?; + } + + Ok(f) } pub fn file_attr(&self) -> io::Result { - self.0 + self.0.file_info().map(FileAttr::from_uefi) } pub fn fsync(&self) -> io::Result<()> { - self.0 + self.datasync() } pub fn datasync(&self) -> io::Result<()> { - self.0 + self.0.flush() } pub fn lock(&self) -> io::Result<()> { - self.0 + unsupported() } pub fn lock_shared(&self) -> io::Result<()> { - self.0 + unsupported() } pub fn try_lock(&self) -> Result<(), TryLockError> { - self.0 + unsupported().map_err(TryLockError::Error) } pub fn try_lock_shared(&self) -> Result<(), TryLockError> { - self.0 + unsupported().map_err(TryLockError::Error) } pub fn unlock(&self) -> io::Result<()> { - self.0 + unsupported() } - pub fn truncate(&self, _size: u64) -> io::Result<()> { - self.0 + pub fn truncate(&self, size: u64) -> io::Result<()> { + let mut file_info = self.0.file_info()?; + + unsafe { (*file_info.as_mut_ptr()).file_size = size }; + + self.0.set_file_info(file_info) } - pub fn read(&self, _buf: &mut [u8]) -> io::Result { - self.0 + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) } - pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + crate::io::default_read_vectored(|b| self.read(b), bufs) } pub fn is_read_vectored(&self) -> bool { - self.0 + false } - pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.0 + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), cursor) } - pub fn write(&self, _buf: &[u8]) -> io::Result { - self.0 + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) } - pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { - self.0 + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + crate::io::default_write_vectored(|b| self.write(b), bufs) } pub fn is_write_vectored(&self) -> bool { - self.0 + false } + // Write::flush is only meant for buffered writers. So should be noop for unbuffered files. pub fn flush(&self) -> io::Result<()> { - self.0 + Ok(()) } - pub fn seek(&self, _pos: SeekFrom) -> io::Result { - self.0 + pub fn seek(&self, pos: SeekFrom) -> io::Result { + const NEG_OFF_ERR: io::Error = + io::const_error!(io::ErrorKind::InvalidInput, "cannot seek to negative offset."); + + let off = match pos { + SeekFrom::Start(p) => p, + SeekFrom::End(p) => { + // Seeking to position 0xFFFFFFFFFFFFFFFF causes the current position to be set to the end of the file. + if p == 0 { + 0xFFFFFFFFFFFFFFFF + } else { + self.file_attr()?.size().checked_add_signed(p).ok_or(NEG_OFF_ERR)? + } + } + SeekFrom::Current(p) => self.tell()?.checked_add_signed(p).ok_or(NEG_OFF_ERR)?, + }; + + self.0.set_position(off).map(|_| off) } pub fn size(&self) -> Option> { - self.0 + match self.file_attr() { + Ok(x) => Some(Ok(x.size())), + Err(e) => Some(Err(e)), + } } pub fn tell(&self) -> io::Result { - self.0 + self.0.position() } pub fn duplicate(&self) -> io::Result { - self.0 + unsupported() } - pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - self.0 + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + set_perm_inner(&self.0, perm) } - pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { - self.0 + pub fn set_times(&self, times: FileTimes) -> io::Result<()> { + set_times_inner(&self.0, times) } } @@ -335,41 +408,102 @@ impl DirBuilder { } impl fmt::Debug for File { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut b = f.debug_struct("File"); + b.field("path", &self.0.path()); + b.finish() } } -pub fn readdir(_p: &Path) -> io::Result { - unsupported() -} +pub fn readdir(p: &Path) -> io::Result { + let path = crate::path::absolute(p)?; + let f = uefi_fs::File::from_path(&path, file::MODE_READ, 0)?; + let file_info = f.file_info()?; + let file_attr = FileAttr::from_uefi(file_info); -pub fn unlink(_p: &Path) -> io::Result<()> { - unsupported() + if file_attr.file_type().is_dir() { + Ok(ReadDir(f)) + } else { + Err(io::const_error!(io::ErrorKind::NotADirectory, "expected a directory but got a file")) + } } -pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { - unsupported() +pub fn unlink(p: &Path) -> io::Result<()> { + let f = uefi_fs::File::from_path(p, file::MODE_READ | file::MODE_WRITE, 0)?; + let file_info = f.file_info()?; + let file_attr = FileAttr::from_uefi(file_info); + + if file_attr.file_type().is_file() { + f.delete() + } else { + Err(io::const_error!(io::ErrorKind::IsADirectory, "expected a file but got a directory")) + } } -pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { - unsupported() +/// The implementation mirrors `mv` implementation in UEFI shell: +/// https://github.com/tianocore/edk2/blob/66346d5edeac2a00d3cf2f2f3b5f66d423c07b3e/ShellPkg/Library/UefiShellLevel2CommandsLib/Mv.c#L455 +/// +/// In a nutshell we do the following: +/// 1. Convert both old and new paths to absolute paths. +/// 2. Check that both lie in the same disk. +/// 3. Construct the target path relative to the current disk root. +/// 4. Set this target path as the file_name in the file_info structure. +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + let old_absolute = crate::path::absolute(old)?; + let new_absolute = crate::path::absolute(new)?; + + let mut old_components = old_absolute.components(); + let mut new_components = new_absolute.components(); + + let Some(old_disk) = old_components.next() else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "Old path is not valid")); + }; + let Some(new_disk) = new_components.next() else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "New path is not valid")); + }; + + // Ensure that paths are on the same device. + if old_disk != new_disk { + return Err(io::const_error!(io::ErrorKind::CrossesDevices, "Cannot rename across device")); + } + + // Construct an path relative the current disk root. + let new_relative = + [crate::path::Component::RootDir].into_iter().chain(new_components).collect::(); + + let f = uefi_fs::File::from_path(old, file::MODE_READ | file::MODE_WRITE, 0)?; + let file_info = f.file_info()?; + + let new_info = file_info.with_file_name(new_relative.as_os_str())?; + + f.set_file_info(new_info) } -pub fn set_times(_p: &Path, _times: FileTimes) -> io::Result<()> { - unsupported() +pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + let f = uefi_fs::File::from_path(p, file::MODE_READ | file::MODE_WRITE, 0)?; + set_perm_inner(&f, perm) } -pub fn set_times_nofollow(_p: &Path, _times: FileTimes) -> io::Result<()> { - unsupported() +pub fn set_times(p: &Path, times: FileTimes) -> io::Result<()> { + // UEFI does not support symlinks + set_times_nofollow(p, times) } -pub fn rmdir(_p: &Path) -> io::Result<()> { - unsupported() +pub fn set_times_nofollow(p: &Path, times: FileTimes) -> io::Result<()> { + let f = uefi_fs::File::from_path(p, file::MODE_READ | file::MODE_WRITE, 0)?; + set_times_inner(&f, times) } -pub fn remove_dir_all(_path: &Path) -> io::Result<()> { - unsupported() +pub fn rmdir(p: &Path) -> io::Result<()> { + let f = uefi_fs::File::from_path(p, file::MODE_READ | file::MODE_WRITE, 0)?; + let file_info = f.file_info()?; + let file_attr = FileAttr::from_uefi(file_info); + + if file_attr.file_type().is_dir() { + f.delete() + } else { + Err(io::const_error!(io::ErrorKind::NotADirectory, "expected a directory but got a file")) + } } pub fn exists(path: &Path) -> io::Result { @@ -407,21 +541,52 @@ pub fn canonicalize(p: &Path) -> io::Result { crate::path::absolute(p) } -pub fn copy(_from: &Path, _to: &Path) -> io::Result { - unsupported() +fn set_perm_inner(f: &uefi_fs::File, perm: FilePermissions) -> io::Result<()> { + let mut file_info = f.file_info()?; + + unsafe { + (*file_info.as_mut_ptr()).attribute = + ((*file_info.as_ptr()).attribute & !FILE_PERMISSIONS_MASK) | perm.to_attr() + }; + + f.set_file_info(file_info) +} + +fn set_times_inner(f: &uefi_fs::File, times: FileTimes) -> io::Result<()> { + let mut file_info = f.file_info()?; + + if let Some(x) = times.accessed { + unsafe { + (*file_info.as_mut_ptr()).last_access_time = uefi_fs::systemtime_to_uefi(x); + } + } + + if let Some(x) = times.modified { + unsafe { + (*file_info.as_mut_ptr()).modification_time = uefi_fs::systemtime_to_uefi(x); + } + } + + f.set_file_info(file_info) } mod uefi_fs { use r_efi::protocols::{device_path, file, simple_file_system}; use crate::boxed::Box; + use crate::ffi::OsString; use crate::io; + use crate::os::uefi::ffi::OsStringExt; use crate::path::Path; use crate::ptr::NonNull; - use crate::sys::helpers::{self, UefiBox}; - use crate::sys::time::{self, SystemTime}; + use crate::sys::pal::helpers::{self, UefiBox}; + use crate::sys::pal::system_time; + use crate::sys::time::SystemTime; - pub(crate) struct File(NonNull); + pub(crate) struct File { + protocol: NonNull, + path: crate::path::PathBuf, + } impl File { pub(crate) fn from_path(path: &Path, open_mode: u64, attr: u64) -> io::Result { @@ -430,7 +595,8 @@ mod uefi_fs { let p = helpers::OwnedDevicePath::from_text(absolute.as_os_str())?; let (vol, mut path_remaining) = Self::open_volume_from_device_path(p.borrow())?; - vol.open(&mut path_remaining, open_mode, attr) + let protocol = Self::open(vol, &mut path_remaining, open_mode, attr)?; + Ok(Self { protocol, path: absolute }) } /// Open Filesystem volume given a devicepath to the volume, or a file/directory in the @@ -446,7 +612,7 @@ mod uefi_fs { /// and return the remaining file path "\abc\run.efi". fn open_volume_from_device_path( path: helpers::BorrowedDevicePath<'_>, - ) -> io::Result<(Self, Box<[u16]>)> { + ) -> io::Result<(NonNull, Box<[u16]>)> { let handles = match helpers::locate_handles(simple_file_system::PROTOCOL_GUID) { Ok(x) => x, Err(e) => return Err(e), @@ -468,7 +634,9 @@ mod uefi_fs { } // Open volume on device_handle using SIMPLE_FILE_SYSTEM_PROTOCOL - fn open_volume(device_handle: NonNull) -> io::Result { + fn open_volume( + device_handle: NonNull, + ) -> io::Result> { let simple_file_system_protocol = helpers::open_protocol::( device_handle, simple_file_system::PROTOCOL_GUID, @@ -487,11 +655,16 @@ mod uefi_fs { // Since no error was returned, file protocol should be non-NULL. let p = NonNull::new(file_protocol).unwrap(); - Ok(Self(p)) + Ok(p) } - fn open(&self, path: &mut [u16], open_mode: u64, attr: u64) -> io::Result { - let file_ptr = self.0.as_ptr(); + fn open( + protocol: NonNull, + path: &mut [u16], + open_mode: u64, + attr: u64, + ) -> io::Result> { + let file_ptr = protocol.as_ptr(); let mut file_opened = crate::ptr::null_mut(); let r = unsafe { @@ -504,11 +677,69 @@ mod uefi_fs { // Since no error was returned, file protocol should be non-NULL. let p = NonNull::new(file_opened).unwrap(); - Ok(File(p)) + Ok(p) + } + + pub(crate) fn read(&self, buf: &mut [u8]) -> io::Result { + let file_ptr = self.protocol.as_ptr(); + let mut buf_size = buf.len(); + + let r = unsafe { ((*file_ptr).read)(file_ptr, &mut buf_size, buf.as_mut_ptr().cast()) }; + + if buf_size == 0 && r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + Ok(buf_size) + } + } + + pub(crate) fn read_dir_entry(&self) -> io::Result>> { + let file_ptr = self.protocol.as_ptr(); + let mut buf_size = 0; + + let r = unsafe { ((*file_ptr).read)(file_ptr, &mut buf_size, crate::ptr::null_mut()) }; + + if buf_size == 0 { + return Ok(None); + } + + assert!(r.is_error()); + if r != r_efi::efi::Status::BUFFER_TOO_SMALL { + return Err(io::Error::from_raw_os_error(r.as_usize())); + } + + let mut info: UefiBox = UefiBox::new(buf_size)?; + let r = + unsafe { ((*file_ptr).read)(file_ptr, &mut buf_size, info.as_mut_ptr().cast()) }; + + if r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + Ok(Some(info)) + } + } + + pub(crate) fn write(&self, buf: &[u8]) -> io::Result { + let file_ptr = self.protocol.as_ptr(); + let mut buf_size = buf.len(); + + let r = unsafe { + ((*file_ptr).write)( + file_ptr, + &mut buf_size, + buf.as_ptr().cast::().cast_mut(), + ) + }; + + if buf_size == 0 && r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + Ok(buf_size) + } } pub(crate) fn file_info(&self) -> io::Result> { - let file_ptr = self.0.as_ptr(); + let file_ptr = self.protocol.as_ptr(); let mut info_id = file::INFO_ID; let mut buf_size = 0; @@ -537,12 +768,57 @@ mod uefi_fs { if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(info) } } + + pub(crate) fn set_file_info(&self, mut info: UefiBox) -> io::Result<()> { + let file_ptr = self.protocol.as_ptr(); + let mut info_id = file::INFO_ID; + + let r = unsafe { + ((*file_ptr).set_info)(file_ptr, &mut info_id, info.len(), info.as_mut_ptr().cast()) + }; + + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } + + pub(crate) fn position(&self) -> io::Result { + let file_ptr = self.protocol.as_ptr(); + let mut pos = 0; + + let r = unsafe { ((*file_ptr).get_position)(file_ptr, &mut pos) }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(pos) } + } + + pub(crate) fn set_position(&self, pos: u64) -> io::Result<()> { + let file_ptr = self.protocol.as_ptr(); + let r = unsafe { ((*file_ptr).set_position)(file_ptr, pos) }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } + + pub(crate) fn delete(self) -> io::Result<()> { + let file_ptr = self.protocol.as_ptr(); + let r = unsafe { ((*file_ptr).delete)(file_ptr) }; + + // Spec states that even in case of failure, the file handle will be closed. + crate::mem::forget(self); + + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } + + pub(crate) fn flush(&self) -> io::Result<()> { + let file_ptr = self.protocol.as_ptr(); + let r = unsafe { ((*file_ptr).flush)(file_ptr) }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } + + pub(crate) fn path(&self) -> &Path { + &self.path + } } impl Drop for File { fn drop(&mut self) { - let file_ptr = self.0.as_ptr(); - let _ = unsafe { ((*self.0.as_ptr()).close)(file_ptr) }; + let file_ptr = self.protocol.as_ptr(); + let _ = unsafe { ((*file_ptr).close)(file_ptr) }; } } @@ -582,7 +858,7 @@ mod uefi_fs { let (vol, mut path_remaining) = File::open_volume_from_device_path(p.borrow())?; // Check if file exists - match vol.open(&mut path_remaining, file::MODE_READ, 0) { + match File::open(vol, &mut path_remaining, file::MODE_READ, 0) { Ok(_) => { return Err(io::Error::new(io::ErrorKind::AlreadyExists, "Path already exists")); } @@ -590,7 +866,8 @@ mod uefi_fs { Err(e) => return Err(e), } - let _ = vol.open( + let _ = File::open( + vol, &mut path_remaining, file::MODE_READ | file::MODE_WRITE | file::MODE_CREATE, file::DIRECTORY, @@ -601,9 +878,9 @@ mod uefi_fs { /// EDK2 FAT driver uses EFI_UNSPECIFIED_TIMEZONE to represent localtime. So for proper /// conversion to SystemTime, we use the current time to get the timezone in such cases. - pub(crate) fn uefi_to_systemtime(mut time: r_efi::efi::Time) -> SystemTime { + pub(crate) fn uefi_to_systemtime(mut time: r_efi::efi::Time) -> Option { time.timezone = if time.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE { - time::system_time_internal::now().unwrap().timezone + system_time::now().timezone } else { time.timezone }; @@ -611,9 +888,13 @@ mod uefi_fs { } /// Convert to UEFI Time with the current timezone. - #[expect(dead_code)] - fn systemtime_to_uefi(time: SystemTime) -> r_efi::efi::Time { - let now = time::system_time_internal::now().unwrap(); + pub(crate) fn systemtime_to_uefi(time: SystemTime) -> r_efi::efi::Time { + let now = system_time::now(); time.to_uefi_loose(now.timezone, now.daylight) } + + pub(crate) fn file_name_from_uefi(info: &UefiBox) -> OsString { + let fname = info.file_name(); + OsString::from_wide(&fname[..fname.len() - 1]) + } } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 47d9ee226653e..3ca84db0f47fc 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -31,6 +31,7 @@ use libc::fstatat64; target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", all(target_os = "linux", target_env = "musl"), ))] use libc::readdir as readdir64; @@ -47,6 +48,7 @@ use libc::readdir as readdir64; target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", )))] use libc::readdir_r as readdir64_r; #[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] @@ -80,20 +82,22 @@ use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt::{self, Write as _}; use crate::fs::TryLockError; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; +#[cfg(target_family = "unix")] use crate::os::unix::prelude::*; +#[cfg(target_os = "wasi")] +use crate::os::wasi::prelude::*; use crate::path::{Path, PathBuf}; use crate::sync::Arc; -use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::fd::FileDesc; pub use crate::sys::fs::common::exists; +use crate::sys::helpers::run_path_with_cstr; use crate::sys::time::SystemTime; #[cfg(all(target_os = "linux", target_env = "gnu"))] use crate::sys::weak::syscall; #[cfg(target_os = "android")] use crate::sys::weak::weak; -use crate::sys::{cvt, cvt_r}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, cvt, cvt_r}; use crate::{mem, ptr}; pub struct File(FileDesc); @@ -253,7 +257,7 @@ cfg_has_statx! {{ // all DirEntry's will have a reference to this struct struct InnerReadDir { - dirp: Dir, + dirp: DirStream, root: PathBuf, } @@ -268,10 +272,134 @@ impl ReadDir { } } -struct Dir(*mut libc::DIR); +struct DirStream(*mut libc::DIR); -unsafe impl Send for Dir {} -unsafe impl Sync for Dir {} +// dir::Dir requires openat support +cfg_select! { + any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nto", + target_os = "vxworks", + ) => { + pub use crate::sys::fs::common::Dir; + } + _ => { + mod dir; + pub use dir::Dir; + } +} + +fn debug_path_fd<'a, 'b>( + fd: c_int, + f: &'a mut fmt::Formatter<'b>, + name: &str, +) -> fmt::DebugStruct<'a, 'b> { + let mut b = f.debug_struct(name); + + fn get_mode(fd: c_int) -> Option<(bool, bool)> { + let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) }; + if mode == -1 { + return None; + } + match mode & libc::O_ACCMODE { + libc::O_RDONLY => Some((true, false)), + libc::O_RDWR => Some((true, true)), + libc::O_WRONLY => Some((false, true)), + _ => None, + } + } + + b.field("fd", &fd); + if let Some(path) = get_path_from_fd(fd) { + b.field("path", &path); + } + if let Some((read, write)) = get_mode(fd) { + b.field("read", &read).field("write", &write); + } + + b +} + +fn get_path_from_fd(fd: c_int) -> Option { + #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris"))] + fn get_path(fd: c_int) -> Option { + let mut p = PathBuf::from("/proc/self/fd"); + p.push(&fd.to_string()); + run_path_with_cstr(&p, &readlink).ok() + } + + #[cfg(any(target_vendor = "apple", target_os = "netbsd"))] + fn get_path(fd: c_int) -> Option { + // FIXME: The use of PATH_MAX is generally not encouraged, but it + // is inevitable in this case because Apple targets and NetBSD define `fcntl` + // with `F_GETPATH` in terms of `MAXPATHLEN`, and there are no + // alternatives. If a better method is invented, it should be used + // instead. + let mut buf = vec![0; libc::PATH_MAX as usize]; + let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }; + if n == -1 { + cfg_select! { + target_os = "netbsd" => { + // fallback to procfs as last resort + let mut p = PathBuf::from("/proc/self/fd"); + p.push(&fd.to_string()); + return run_path_with_cstr(&p, &readlink).ok() + } + _ => { + return None; + } + } + } + let l = buf.iter().position(|&c| c == 0).unwrap(); + buf.truncate(l as usize); + buf.shrink_to_fit(); + Some(PathBuf::from(OsString::from_vec(buf))) + } + + #[cfg(target_os = "freebsd")] + fn get_path(fd: c_int) -> Option { + let info = Box::::new_zeroed(); + let mut info = unsafe { info.assume_init() }; + info.kf_structsize = size_of::() as libc::c_int; + let n = unsafe { libc::fcntl(fd, libc::F_KINFO, &mut *info) }; + if n == -1 { + return None; + } + let buf = unsafe { CStr::from_ptr(info.kf_path.as_mut_ptr()).to_bytes().to_vec() }; + Some(PathBuf::from(OsString::from_vec(buf))) + } + + #[cfg(target_os = "vxworks")] + fn get_path(fd: c_int) -> Option { + let mut buf = vec![0; libc::PATH_MAX as usize]; + let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) }; + if n == -1 { + return None; + } + let l = buf.iter().position(|&c| c == 0).unwrap(); + buf.truncate(l as usize); + Some(PathBuf::from(OsString::from_vec(buf))) + } + + #[cfg(not(any( + target_os = "linux", + target_os = "vxworks", + target_os = "freebsd", + target_os = "netbsd", + target_os = "illumos", + target_os = "solaris", + target_vendor = "apple", + )))] + fn get_path(_fd: c_int) -> Option { + // FIXME(#24570): implement this for other Unix platforms + None + } + + get_path(fd) +} #[cfg(any( target_os = "aix", @@ -285,6 +413,7 @@ unsafe impl Sync for Dir {} target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", ))] pub struct DirEntry { dir: Arc, @@ -310,6 +439,7 @@ pub struct DirEntry { target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", ))] struct dirent64_min { d_ino: u64, @@ -335,6 +465,7 @@ struct dirent64_min { target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", )))] pub struct DirEntry { dir: Arc, @@ -480,7 +611,7 @@ impl FileAttr { } } -#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix")))] +#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix", target_os = "wasi")))] impl FileAttr { #[cfg(not(any( target_os = "vxworks", @@ -595,18 +726,18 @@ impl FileAttr { } } -#[cfg(target_os = "nto")] +#[cfg(any(target_os = "nto", target_os = "wasi"))] impl FileAttr { pub fn modified(&self) -> io::Result { - SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec) + SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec.into()) } pub fn accessed(&self) -> io::Result { - SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec) + SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec.into()) } pub fn created(&self) -> io::Result { - SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec) + SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec.into()) } } @@ -632,6 +763,7 @@ impl FilePermissions { self.mode |= 0o222; } } + #[cfg(not(target_os = "wasi"))] pub fn mode(&self) -> u32 { self.mode as u32 } @@ -715,9 +847,10 @@ impl Iterator for ReadDir { target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", ))] fn next(&mut self) -> Option> { - use crate::sys::os::{errno, set_errno}; + use crate::sys::io::{errno, set_errno}; if self.end_of_stream { return None; @@ -812,6 +945,7 @@ impl Iterator for ReadDir { target_os = "redox", target_os = "solaris", target_os = "vita", + target_os = "wasi", )))] fn next(&mut self) -> Option> { if self.end_of_stream { @@ -854,7 +988,7 @@ impl Iterator for ReadDir { /// The downside is that it costs an extra syscall, so we only do it for debug. #[inline] pub(crate) fn debug_assert_fd_is_open(fd: RawFd) { - use crate::sys::os::errno; + use crate::sys::io::errno; // this is similar to assert_unsafe_precondition!() but it doesn't require const if core::ub_checks::check_library_ub() { @@ -864,7 +998,7 @@ pub(crate) fn debug_assert_fd_is_open(fd: RawFd) { } } -impl Drop for Dir { +impl Drop for DirStream { fn drop(&mut self) { // dirfd isn't supported everywhere #[cfg(not(any( @@ -892,6 +1026,11 @@ impl Drop for Dir { } } +// SAFETY: `int dirfd (DIR *dirstream)` is MT-safe, implying that the pointer +// may be safely sent among threads. +unsafe impl Send for DirStream {} +unsafe impl Sync for DirStream {} + impl DirEntry { pub fn path(&self) -> PathBuf { self.dir.root.join(self.file_name_os_str()) @@ -1002,6 +1141,7 @@ impl DirEntry { target_os = "solaris", target_os = "vita", target_os = "vxworks", + target_os = "wasi", target_vendor = "apple", ))] pub fn ino(&self) -> u64 { @@ -1057,6 +1197,7 @@ impl DirEntry { target_os = "nto", target_os = "vita", target_os = "hurd", + target_os = "wasi", )))] fn name_cstr(&self) -> &CStr { unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) } @@ -1073,6 +1214,7 @@ impl DirEntry { target_os = "nto", target_os = "vita", target_os = "hurd", + target_os = "wasi", ))] fn name_cstr(&self) -> &CStr { &self.name @@ -1121,6 +1263,7 @@ impl OpenOptions { pub fn custom_flags(&mut self, flags: i32) { self.custom_flags = flags; } + #[cfg(not(target_os = "wasi"))] pub fn mode(&mut self, mode: u32) { self.mode = mode as mode_t; } @@ -1291,6 +1434,7 @@ impl File { #[cfg(any( target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", @@ -1316,6 +1460,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", @@ -1332,6 +1477,7 @@ impl File { #[cfg(any( target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", @@ -1357,6 +1503,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", @@ -1373,6 +1520,7 @@ impl File { #[cfg(any( target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", @@ -1414,6 +1562,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", @@ -1433,6 +1582,7 @@ impl File { #[cfg(any( target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", @@ -1474,6 +1624,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", @@ -1493,6 +1644,7 @@ impl File { #[cfg(any( target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", @@ -1518,6 +1670,7 @@ impl File { #[cfg(not(any( target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "linux", target_os = "netbsd", target_os = "openbsd", @@ -1669,7 +1822,7 @@ impl File { _ => { #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))] { - use crate::sys::{time::__timespec64, weak::weak}; + use crate::sys::pal::{time::__timespec64, weak::weak}; // Added in glibc 2.34 weak!( @@ -1769,6 +1922,7 @@ impl DirBuilder { run_path_with_cstr(p, &|p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ())) } + #[cfg(not(target_os = "wasi"))] pub fn set_mode(&mut self, mode: u32) { self.mode = mode as mode_t; } @@ -1835,102 +1989,8 @@ impl FromRawFd for File { impl fmt::Debug for File { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris"))] - fn get_path(fd: c_int) -> Option { - let mut p = PathBuf::from("/proc/self/fd"); - p.push(&fd.to_string()); - run_path_with_cstr(&p, &readlink).ok() - } - - #[cfg(any(target_vendor = "apple", target_os = "netbsd"))] - fn get_path(fd: c_int) -> Option { - // FIXME: The use of PATH_MAX is generally not encouraged, but it - // is inevitable in this case because Apple targets and NetBSD define `fcntl` - // with `F_GETPATH` in terms of `MAXPATHLEN`, and there are no - // alternatives. If a better method is invented, it should be used - // instead. - let mut buf = vec![0; libc::PATH_MAX as usize]; - let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) }; - if n == -1 { - cfg_select! { - target_os = "netbsd" => { - // fallback to procfs as last resort - let mut p = PathBuf::from("/proc/self/fd"); - p.push(&fd.to_string()); - return run_path_with_cstr(&p, &readlink).ok() - } - _ => { - return None; - } - } - } - let l = buf.iter().position(|&c| c == 0).unwrap(); - buf.truncate(l as usize); - buf.shrink_to_fit(); - Some(PathBuf::from(OsString::from_vec(buf))) - } - - #[cfg(target_os = "freebsd")] - fn get_path(fd: c_int) -> Option { - let info = Box::::new_zeroed(); - let mut info = unsafe { info.assume_init() }; - info.kf_structsize = size_of::() as libc::c_int; - let n = unsafe { libc::fcntl(fd, libc::F_KINFO, &mut *info) }; - if n == -1 { - return None; - } - let buf = unsafe { CStr::from_ptr(info.kf_path.as_mut_ptr()).to_bytes().to_vec() }; - Some(PathBuf::from(OsString::from_vec(buf))) - } - - #[cfg(target_os = "vxworks")] - fn get_path(fd: c_int) -> Option { - let mut buf = vec![0; libc::PATH_MAX as usize]; - let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) }; - if n == -1 { - return None; - } - let l = buf.iter().position(|&c| c == 0).unwrap(); - buf.truncate(l as usize); - Some(PathBuf::from(OsString::from_vec(buf))) - } - - #[cfg(not(any( - target_os = "linux", - target_os = "vxworks", - target_os = "freebsd", - target_os = "netbsd", - target_os = "illumos", - target_os = "solaris", - target_vendor = "apple", - )))] - fn get_path(_fd: c_int) -> Option { - // FIXME(#24570): implement this for other Unix platforms - None - } - - fn get_mode(fd: c_int) -> Option<(bool, bool)> { - let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) }; - if mode == -1 { - return None; - } - match mode & libc::O_ACCMODE { - libc::O_RDONLY => Some((true, false)), - libc::O_RDWR => Some((true, true)), - libc::O_WRONLY => Some((false, true)), - _ => None, - } - } - let fd = self.as_raw_fd(); - let mut b = f.debug_struct("File"); - b.field("fd", &fd); - if let Some(path) = get_path(fd) { - b.field("path", &path); - } - if let Some((read, write)) = get_mode(fd) { - b.field("read", &read).field("write", &write); - } + let mut b = debug_path_fd(fd, f, "File"); b.finish() } } @@ -2008,7 +2068,7 @@ pub fn readdir(path: &Path) -> io::Result { Err(Error::last_os_error()) } else { let root = path.to_path_buf(); - let inner = InnerReadDir { dirp: Dir(ptr), root }; + let inner = InnerReadDir { dirp: DirStream(ptr), root }; Ok(ReadDir::new(inner)) } } @@ -2061,12 +2121,25 @@ pub fn symlink(original: &CStr, link: &CStr) -> io::Result<()> { pub fn link(original: &CStr, link: &CStr) -> io::Result<()> { cfg_select! { - any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita", target_env = "nto70") => { - // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves - // it implementation-defined whether `link` follows symlinks, so rely on the - // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior. - // Android has `linkat` on newer versions, but we happen to know `link` - // always has the correct behavior, so it's here as well. + any( + // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. + // POSIX leaves it implementation-defined whether `link` follows + // symlinks, so rely on the `symlink_hard_link` test in + // library/std/src/fs/tests.rs to check the behavior. + target_os = "vxworks", + target_os = "redox", + target_os = "espidf", + // Android has `linkat` on newer versions, but we happen to know + // `link` always has the correct behavior, so it's here as well. + target_os = "android", + // wasi-sdk-29-and-prior have a buggy `linkat` so use `link` instead + // until wasi-sdk is updated (see WebAssembly/wasi-libc#690) + target_os = "wasi", + // Other misc platforms + target_os = "horizon", + target_os = "vita", + target_env = "nto70", + ) => { cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?; } _ => { @@ -2138,7 +2211,7 @@ fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> fn set_times_impl(p: &CStr, times: FileTimes, follow_symlinks: bool) -> io::Result<()> { cfg_select! { - any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx") => { + any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx", target_os = "vita", target_os = "rtems") => { let _ = (p, times, follow_symlinks); Err(io::const_error!( io::ErrorKind::Unsupported, @@ -2185,7 +2258,7 @@ fn set_times_impl(p: &CStr, times: FileTimes, follow_symlinks: bool) -> io::Resu let flags = if follow_symlinks { 0 } else { libc::AT_SYMLINK_NOFOLLOW }; #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))] { - use crate::sys::{time::__timespec64, weak::weak}; + use crate::sys::pal::{time::__timespec64, weak::weak}; // Added in glibc 2.34 weak!( @@ -2217,18 +2290,18 @@ pub fn set_times_nofollow(p: &CStr, times: FileTimes) -> io::Result<()> { set_times_impl(p, times, false) } -#[cfg(target_os = "espidf")] +#[cfg(any(target_os = "espidf", target_os = "wasi"))] fn open_to_and_set_permissions( to: &Path, _reader_metadata: &crate::fs::Metadata, ) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { use crate::fs::OpenOptions; - let writer = OpenOptions::new().open(to)?; + let writer = OpenOptions::new().write(true).create(true).truncate(true).open(to)?; let writer_metadata = writer.metadata()?; Ok((writer, writer_metadata)) } -#[cfg(not(target_os = "espidf"))] +#[cfg(not(any(target_os = "espidf", target_os = "wasi")))] fn open_to_and_set_permissions( to: &Path, reader_metadata: &crate::fs::Metadata, @@ -2376,6 +2449,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result { Ok(bytes_copied as u64) } +#[cfg(not(target_os = "wasi"))] pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { run_path_with_cstr(path, &|path| { cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) @@ -2383,12 +2457,13 @@ pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { }) } +#[cfg(not(target_os = "wasi"))] pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> { cvt(unsafe { libc::fchown(fd, uid as libc::uid_t, gid as libc::gid_t) })?; Ok(()) } -#[cfg(not(target_os = "vxworks"))] +#[cfg(not(any(target_os = "vxworks", target_os = "wasi")))] pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { run_path_with_cstr(path, &|path| { cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) }) @@ -2402,7 +2477,7 @@ pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> { Err(io::const_error!(io::ErrorKind::Unsupported, "lchown not supported by vxworks")) } -#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] +#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks", target_os = "wasi")))] pub fn chroot(dir: &Path) -> io::Result<()> { run_path_with_cstr(dir, &|dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ())) } @@ -2413,6 +2488,7 @@ pub fn chroot(dir: &Path) -> io::Result<()> { Err(io::const_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks")) } +#[cfg(not(target_os = "wasi"))] pub fn mkfifo(path: &Path, mode: u32) -> io::Result<()> { run_path_with_cstr(path, &|path| { cvt(unsafe { libc::mkfifo(path.as_ptr(), mode.try_into().unwrap()) }).map(|_| ()) @@ -2451,15 +2527,15 @@ mod remove_dir_impl { #[cfg(all(target_os = "linux", target_env = "gnu"))] use libc::{fdopendir, openat64 as openat, unlinkat}; - use super::{Dir, DirEntry, InnerReadDir, ReadDir, lstat}; + use super::{ + AsRawFd, DirEntry, DirStream, FromRawFd, InnerReadDir, IntoRawFd, OwnedFd, RawFd, ReadDir, + lstat, + }; use crate::ffi::CStr; use crate::io; - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}; - use crate::os::unix::prelude::{OwnedFd, RawFd}; use crate::path::{Path, PathBuf}; - use crate::sys::common::small_c_string::run_path_with_cstr; + use crate::sys::helpers::{ignore_notfound, run_path_with_cstr}; use crate::sys::{cvt, cvt_r}; - use crate::sys_common::ignore_notfound; pub fn openat_nofollow_dironly(parent_fd: Option, p: &CStr) -> io::Result { let fd = cvt_r(|| unsafe { @@ -2477,7 +2553,7 @@ mod remove_dir_impl { if ptr.is_null() { return Err(io::Error::last_os_error()); } - let dirp = Dir(ptr); + let dirp = DirStream(ptr); // file descriptor is automatically closed by libc::closedir() now, so give up ownership let new_parent_fd = dir_fd.into_raw_fd(); // a valid root is not needed because we do not call any functions involving the full path @@ -2543,6 +2619,16 @@ mod remove_dir_impl { // open the directory passing ownership of the fd let (dir, fd) = fdreaddir(fd)?; + + // For WASI all directory entries for this directory are read first + // before any removal is done. This works around the fact that the + // WASIp1 API for reading directories is not well-designed for handling + // mutations between invocations of reading a directory. By reading all + // the entries at once this ensures that, at least without concurrent + // modifications, it should be possible to delete everything. + #[cfg(target_os = "wasi")] + let dir = dir.collect::>(); + for child in dir { let child = child?; let child_name = child.name_cstr(); diff --git a/library/std/src/sys/fs/unix/dir.rs b/library/std/src/sys/fs/unix/dir.rs new file mode 100644 index 0000000000000..094d1bfd31685 --- /dev/null +++ b/library/std/src/sys/fs/unix/dir.rs @@ -0,0 +1,114 @@ +use libc::c_int; + +cfg_select! { + not( + any( + all(target_os = "linux", not(target_env = "musl")), + target_os = "l4re", + target_os = "android", + target_os = "hurd", + ) + ) => { + use libc::{open as open64, openat as openat64}; + } + _ => { + use libc::{open64, openat64}; + } +} + +use crate::ffi::CStr; +use crate::os::fd::{AsFd, BorrowedFd, IntoRawFd, OwnedFd, RawFd}; +#[cfg(target_family = "unix")] +use crate::os::unix::io::{AsRawFd, FromRawFd}; +#[cfg(target_os = "wasi")] +use crate::os::wasi::io::{AsRawFd, FromRawFd}; +use crate::path::Path; +use crate::sys::fd::FileDesc; +use crate::sys::fs::OpenOptions; +use crate::sys::fs::unix::{File, debug_path_fd}; +use crate::sys::helpers::run_path_with_cstr; +use crate::sys::{AsInner, FromInner, IntoInner, cvt_r}; +use crate::{fmt, fs, io}; + +pub struct Dir(OwnedFd); + +impl Dir { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + run_path_with_cstr(path, &|path| Self::open_with_c(path, opts)) + } + + pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { + run_path_with_cstr(path.as_ref(), &|path| self.open_file_c(path, &opts)) + } + + pub fn open_with_c(path: &CStr, opts: &OpenOptions) -> io::Result { + let flags = libc::O_CLOEXEC + | libc::O_DIRECTORY + | opts.get_access_mode()? + | opts.get_creation_mode()? + | (opts.custom_flags as c_int & !libc::O_ACCMODE); + let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?; + Ok(Self(unsafe { OwnedFd::from_raw_fd(fd) })) + } + + fn open_file_c(&self, path: &CStr, opts: &OpenOptions) -> io::Result { + let flags = libc::O_CLOEXEC + | opts.get_access_mode()? + | opts.get_creation_mode()? + | (opts.custom_flags as c_int & !libc::O_ACCMODE); + let fd = cvt_r(|| unsafe { + openat64(self.0.as_raw_fd(), path.as_ptr(), flags, opts.mode as c_int) + })?; + Ok(File(unsafe { FileDesc::from_raw_fd(fd) })) + } +} + +impl fmt::Debug for Dir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let fd = self.0.as_raw_fd(); + let mut b = debug_path_fd(fd, f, "Dir"); + b.finish() + } +} + +#[unstable(feature = "dirfd", issue = "120426")] +impl AsRawFd for fs::Dir { + fn as_raw_fd(&self) -> RawFd { + self.as_inner().0.as_raw_fd() + } +} + +#[unstable(feature = "dirfd", issue = "120426")] +impl IntoRawFd for fs::Dir { + fn into_raw_fd(self) -> RawFd { + self.into_inner().0.into_raw_fd() + } +} + +#[unstable(feature = "dirfd", issue = "120426")] +impl FromRawFd for fs::Dir { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + Self::from_inner(Dir(unsafe { FromRawFd::from_raw_fd(fd) })) + } +} + +#[unstable(feature = "dirfd", issue = "120426")] +impl AsFd for fs::Dir { + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().0.as_fd() + } +} + +#[unstable(feature = "dirfd", issue = "120426")] +impl From for OwnedFd { + fn from(value: fs::Dir) -> Self { + value.into_inner().0 + } +} + +#[unstable(feature = "dirfd", issue = "120426")] +impl From for fs::Dir { + fn from(value: OwnedFd) -> Self { + Self::from_inner(Dir(value)) + } +} diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index f222151d18e25..069b4fb8a29ce 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -4,6 +4,7 @@ use crate::fs::TryLockError; use crate::hash::{Hash, Hasher}; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::path::{Path, PathBuf}; +pub use crate::sys::fs::common::Dir; use crate::sys::time::SystemTime; use crate::sys::unsupported; diff --git a/library/std/src/sys/fs/vexos.rs b/library/std/src/sys/fs/vexos.rs index 381c87c62c688..81083a4fa81d3 100644 --- a/library/std/src/sys/fs/vexos.rs +++ b/library/std/src/sys/fs/vexos.rs @@ -4,7 +4,7 @@ use crate::fs::TryLockError; use crate::hash::Hash; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::path::{Path, PathBuf}; -use crate::sys::common::small_c_string::run_path_with_cstr; +use crate::sys::helpers::run_path_with_cstr; use crate::sys::time::SystemTime; use crate::sys::{unsupported, unsupported_err}; diff --git a/library/std/src/sys/fs/wasi.rs b/library/std/src/sys/fs/wasi.rs deleted file mode 100644 index 92eb35317415f..0000000000000 --- a/library/std/src/sys/fs/wasi.rs +++ /dev/null @@ -1,913 +0,0 @@ -use crate::ffi::{CStr, OsStr, OsString}; -use crate::fs::TryLockError; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem::{self, ManuallyDrop}; -use crate::os::raw::c_int; -use crate::os::wasi::ffi::{OsStrExt, OsStringExt}; -use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::path::{Path, PathBuf}; -use crate::sync::Arc; -use crate::sys::common::small_c_string::run_path_with_cstr; -use crate::sys::fd::WasiFd; -pub use crate::sys::fs::common::exists; -use crate::sys::time::SystemTime; -use crate::sys::{unsupported, unsupported_err}; -use crate::sys_common::{AsInner, FromInner, IntoInner, ignore_notfound}; -use crate::{fmt, iter, ptr}; - -pub struct File { - fd: WasiFd, -} - -#[derive(Clone)] -pub struct FileAttr { - meta: wasi::Filestat, -} - -pub struct ReadDir { - inner: Arc, - state: ReadDirState, -} - -enum ReadDirState { - /// Fill `buf` with `buf.len()` bytes starting from `next_read_offset`. - FillBuffer { - next_read_offset: wasi::Dircookie, - buf: Vec, - }, - ProcessEntry { - buf: Vec, - next_read_offset: Option, - offset: usize, - }, - /// There is no more data to get in [`Self::FillBuffer`]; keep returning - /// entries via ProcessEntry until `buf` is exhausted. - RunUntilExhaustion { - buf: Vec, - offset: usize, - }, - Done, -} - -struct ReadDirInner { - root: PathBuf, - dir: File, -} - -pub struct DirEntry { - meta: wasi::Dirent, - name: Vec, - inner: Arc, -} - -#[derive(Clone, Debug, Default)] -pub struct OpenOptions { - read: bool, - write: bool, - append: bool, - dirflags: wasi::Lookupflags, - fdflags: wasi::Fdflags, - oflags: wasi::Oflags, - rights_base: Option, - rights_inheriting: Option, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct FilePermissions { - readonly: bool, -} - -#[derive(Copy, Clone, Debug, Default)] -pub struct FileTimes { - accessed: Option, - modified: Option, -} - -#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] -pub struct FileType { - bits: wasi::Filetype, -} - -#[derive(Debug)] -pub struct DirBuilder {} - -impl FileAttr { - pub fn size(&self) -> u64 { - self.meta.size - } - - pub fn perm(&self) -> FilePermissions { - // not currently implemented in wasi yet - FilePermissions { readonly: false } - } - - pub fn file_type(&self) -> FileType { - FileType { bits: self.meta.filetype } - } - - pub fn modified(&self) -> io::Result { - Ok(SystemTime::from_wasi_timestamp(self.meta.mtim)) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from_wasi_timestamp(self.meta.atim)) - } - - pub fn created(&self) -> io::Result { - Ok(SystemTime::from_wasi_timestamp(self.meta.ctim)) - } - - pub(crate) fn as_wasi(&self) -> &wasi::Filestat { - &self.meta - } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - self.readonly - } - - pub fn set_readonly(&mut self, readonly: bool) { - self.readonly = readonly; - } -} - -impl FileTimes { - pub fn set_accessed(&mut self, t: SystemTime) { - self.accessed = Some(t); - } - - pub fn set_modified(&mut self, t: SystemTime) { - self.modified = Some(t); - } -} - -impl FileType { - pub fn is_dir(&self) -> bool { - self.bits == wasi::FILETYPE_DIRECTORY - } - - pub fn is_file(&self) -> bool { - self.bits == wasi::FILETYPE_REGULAR_FILE - } - - pub fn is_symlink(&self) -> bool { - self.bits == wasi::FILETYPE_SYMBOLIC_LINK - } - - pub(crate) fn bits(&self) -> wasi::Filetype { - self.bits - } -} - -impl ReadDir { - fn new(dir: File, root: PathBuf) -> ReadDir { - ReadDir { - inner: Arc::new(ReadDirInner { dir, root }), - state: ReadDirState::FillBuffer { next_read_offset: 0, buf: vec![0; 128] }, - } - } -} - -impl fmt::Debug for ReadDir { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ReadDir").finish_non_exhaustive() - } -} - -impl core::iter::FusedIterator for ReadDir {} - -impl Iterator for ReadDir { - type Item = io::Result; - - fn next(&mut self) -> Option> { - match &mut self.state { - ReadDirState::FillBuffer { next_read_offset, buf } => { - let result = self.inner.dir.fd.readdir(buf, *next_read_offset); - match result { - Ok(read_bytes) => { - if read_bytes < buf.len() { - buf.truncate(read_bytes); - self.state = - ReadDirState::RunUntilExhaustion { buf: mem::take(buf), offset: 0 }; - } else { - debug_assert_eq!(read_bytes, buf.len()); - self.state = ReadDirState::ProcessEntry { - buf: mem::take(buf), - offset: 0, - next_read_offset: Some(*next_read_offset), - }; - } - self.next() - } - Err(e) => { - self.state = ReadDirState::Done; - return Some(Err(e)); - } - } - } - ReadDirState::ProcessEntry { buf, next_read_offset, offset } => { - let contents = &buf[*offset..]; - const DIRENT_SIZE: usize = size_of::(); - if contents.len() >= DIRENT_SIZE { - let (dirent, data) = contents.split_at(DIRENT_SIZE); - let dirent = - unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) }; - // If the file name was truncated, then we need to reinvoke - // `readdir` so we truncate our buffer to start over and reread this - // descriptor. - if data.len() < dirent.d_namlen as usize { - if buf.len() < dirent.d_namlen as usize + DIRENT_SIZE { - buf.resize(dirent.d_namlen as usize + DIRENT_SIZE, 0); - } - if let Some(next_read_offset) = *next_read_offset { - self.state = - ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) }; - } else { - self.state = ReadDirState::Done; - } - - return self.next(); - } - next_read_offset.as_mut().map(|cookie| { - *cookie = dirent.d_next; - }); - *offset = *offset + DIRENT_SIZE + dirent.d_namlen as usize; - - let name = &data[..(dirent.d_namlen as usize)]; - - // These names are skipped on all other platforms, so let's skip - // them here too - if name == b"." || name == b".." { - return self.next(); - } - - return Some(Ok(DirEntry { - meta: dirent, - name: name.to_vec(), - inner: self.inner.clone(), - })); - } else if let Some(next_read_offset) = *next_read_offset { - self.state = ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) }; - } else { - self.state = ReadDirState::Done; - } - self.next() - } - ReadDirState::RunUntilExhaustion { buf, offset } => { - if *offset >= buf.len() { - self.state = ReadDirState::Done; - } else { - self.state = ReadDirState::ProcessEntry { - buf: mem::take(buf), - offset: *offset, - next_read_offset: None, - }; - } - - self.next() - } - ReadDirState::Done => None, - } - } -} - -impl DirEntry { - pub fn path(&self) -> PathBuf { - let name = OsStr::from_bytes(&self.name); - self.inner.root.join(name) - } - - pub fn file_name(&self) -> OsString { - OsString::from_vec(self.name.clone()) - } - - pub fn metadata(&self) -> io::Result { - metadata_at(&self.inner.dir.fd, 0, OsStr::from_bytes(&self.name).as_ref()) - } - - pub fn file_type(&self) -> io::Result { - Ok(FileType { bits: self.meta.d_type }) - } - - pub fn ino(&self) -> wasi::Inode { - self.meta.d_ino - } -} - -impl OpenOptions { - pub fn new() -> OpenOptions { - let mut base = OpenOptions::default(); - base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW; - base - } - - pub fn read(&mut self, read: bool) { - self.read = read; - } - - pub fn write(&mut self, write: bool) { - self.write = write; - } - - pub fn truncate(&mut self, truncate: bool) { - self.oflag(wasi::OFLAGS_TRUNC, truncate); - } - - pub fn create(&mut self, create: bool) { - self.oflag(wasi::OFLAGS_CREAT, create); - } - - pub fn create_new(&mut self, create_new: bool) { - self.oflag(wasi::OFLAGS_EXCL, create_new); - self.oflag(wasi::OFLAGS_CREAT, create_new); - } - - pub fn directory(&mut self, directory: bool) { - self.oflag(wasi::OFLAGS_DIRECTORY, directory); - } - - fn oflag(&mut self, bit: wasi::Oflags, set: bool) { - if set { - self.oflags |= bit; - } else { - self.oflags &= !bit; - } - } - - pub fn append(&mut self, append: bool) { - self.append = append; - self.fdflag(wasi::FDFLAGS_APPEND, append); - } - - pub fn dsync(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_DSYNC, set); - } - - pub fn nonblock(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_NONBLOCK, set); - } - - pub fn rsync(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_RSYNC, set); - } - - pub fn sync(&mut self, set: bool) { - self.fdflag(wasi::FDFLAGS_SYNC, set); - } - - fn fdflag(&mut self, bit: wasi::Fdflags, set: bool) { - if set { - self.fdflags |= bit; - } else { - self.fdflags &= !bit; - } - } - - pub fn fs_rights_base(&mut self, rights: wasi::Rights) { - self.rights_base = Some(rights); - } - - pub fn fs_rights_inheriting(&mut self, rights: wasi::Rights) { - self.rights_inheriting = Some(rights); - } - - fn rights_base(&self) -> wasi::Rights { - if let Some(rights) = self.rights_base { - return rights; - } - - // If rights haven't otherwise been specified try to pick a reasonable - // set. This can always be overridden by users via extension traits, and - // implementations may give us fewer rights silently than we ask for. So - // given that, just look at `read` and `write` and bucket permissions - // based on that. - let mut base = 0; - if self.read { - base |= wasi::RIGHTS_FD_READ; - base |= wasi::RIGHTS_FD_READDIR; - } - if self.write || self.append { - base |= wasi::RIGHTS_FD_WRITE; - base |= wasi::RIGHTS_FD_DATASYNC; - base |= wasi::RIGHTS_FD_ALLOCATE; - base |= wasi::RIGHTS_FD_FILESTAT_SET_SIZE; - } - - // FIXME: some of these should probably be read-only or write-only... - base |= wasi::RIGHTS_FD_ADVISE; - base |= wasi::RIGHTS_FD_FDSTAT_SET_FLAGS; - base |= wasi::RIGHTS_FD_FILESTAT_GET; - base |= wasi::RIGHTS_FD_FILESTAT_SET_TIMES; - base |= wasi::RIGHTS_FD_SEEK; - base |= wasi::RIGHTS_FD_SYNC; - base |= wasi::RIGHTS_FD_TELL; - base |= wasi::RIGHTS_PATH_CREATE_DIRECTORY; - base |= wasi::RIGHTS_PATH_CREATE_FILE; - base |= wasi::RIGHTS_PATH_FILESTAT_GET; - base |= wasi::RIGHTS_PATH_LINK_SOURCE; - base |= wasi::RIGHTS_PATH_LINK_TARGET; - base |= wasi::RIGHTS_PATH_OPEN; - base |= wasi::RIGHTS_PATH_READLINK; - base |= wasi::RIGHTS_PATH_REMOVE_DIRECTORY; - base |= wasi::RIGHTS_PATH_RENAME_SOURCE; - base |= wasi::RIGHTS_PATH_RENAME_TARGET; - base |= wasi::RIGHTS_PATH_SYMLINK; - base |= wasi::RIGHTS_PATH_UNLINK_FILE; - base |= wasi::RIGHTS_POLL_FD_READWRITE; - - base - } - - fn rights_inheriting(&self) -> wasi::Rights { - self.rights_inheriting.unwrap_or_else(|| self.rights_base()) - } - - pub fn lookup_flags(&mut self, flags: wasi::Lookupflags) { - self.dirflags = flags; - } -} - -impl File { - pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { - let (dir, file) = open_parent(path)?; - open_at(&dir, &file, opts) - } - - pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result { - open_at(&self.fd, path, opts) - } - - pub fn file_attr(&self) -> io::Result { - self.fd.filestat_get().map(|meta| FileAttr { meta }) - } - - pub fn metadata_at(&self, flags: wasi::Lookupflags, path: &Path) -> io::Result { - metadata_at(&self.fd, flags, path) - } - - pub fn fsync(&self) -> io::Result<()> { - self.fd.sync() - } - - pub fn datasync(&self) -> io::Result<()> { - self.fd.datasync() - } - - pub fn lock(&self) -> io::Result<()> { - unsupported() - } - - pub fn lock_shared(&self) -> io::Result<()> { - unsupported() - } - - pub fn try_lock(&self) -> Result<(), TryLockError> { - Err(TryLockError::Error(unsupported_err())) - } - - pub fn try_lock_shared(&self) -> Result<(), TryLockError> { - Err(TryLockError::Error(unsupported_err())) - } - - pub fn unlock(&self) -> io::Result<()> { - unsupported() - } - - pub fn truncate(&self, size: u64) -> io::Result<()> { - self.fd.filestat_set_size(size) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.read_vectored(&mut [IoSliceMut::new(buf)]) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.fd.read(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.fd.read_buf(cursor) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.write_vectored(&[IoSlice::new(buf)]) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.fd.write(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } - - pub fn seek(&self, pos: SeekFrom) -> io::Result { - self.fd.seek(pos) - } - - pub fn size(&self) -> Option> { - None - } - - pub fn tell(&self) -> io::Result { - self.fd.tell() - } - - pub fn duplicate(&self) -> io::Result { - // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-rationale.md#why-no-dup - unsupported() - } - - pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - // Permissions haven't been fully figured out in wasi yet, so this is - // likely temporary - unsupported() - } - - pub fn set_times(&self, times: FileTimes) -> io::Result<()> { - self.fd.filestat_set_times( - to_wasi_timestamp_or_now(times.accessed)?, - to_wasi_timestamp_or_now(times.modified)?, - times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM) - | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM), - ) - } - - pub fn read_link(&self, file: &Path) -> io::Result { - read_link(&self.fd, file) - } -} - -impl AsInner for File { - #[inline] - fn as_inner(&self) -> &WasiFd { - &self.fd - } -} - -impl IntoInner for File { - fn into_inner(self) -> WasiFd { - self.fd - } -} - -impl FromInner for File { - fn from_inner(fd: WasiFd) -> File { - File { fd } - } -} - -impl AsFd for File { - fn as_fd(&self) -> BorrowedFd<'_> { - self.fd.as_fd() - } -} - -impl AsRawFd for File { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.fd.as_raw_fd() - } -} - -impl IntoRawFd for File { - fn into_raw_fd(self) -> RawFd { - self.fd.into_raw_fd() - } -} - -impl FromRawFd for File { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } } - } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder {} - } - - pub fn mkdir(&self, p: &Path) -> io::Result<()> { - let (dir, file) = open_parent(p)?; - dir.create_directory(osstr2str(file.as_ref())?) - } -} - -impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("File").field("fd", &self.as_raw_fd()).finish() - } -} - -pub fn readdir(p: &Path) -> io::Result { - let mut opts = OpenOptions::new(); - opts.directory(true); - opts.read(true); - let dir = File::open(p, &opts)?; - Ok(ReadDir::new(dir, p.to_path_buf())) -} - -pub fn unlink(p: &Path) -> io::Result<()> { - let (dir, file) = open_parent(p)?; - dir.unlink_file(osstr2str(file.as_ref())?) -} - -pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - let (old, old_file) = open_parent(old)?; - let (new, new_file) = open_parent(new)?; - old.rename(osstr2str(old_file.as_ref())?, &new, osstr2str(new_file.as_ref())?) -} - -pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { - // Permissions haven't been fully figured out in wasi yet, so this is - // likely temporary - unsupported() -} - -#[inline(always)] -pub fn set_times(p: &Path, times: FileTimes) -> io::Result<()> { - let (dir, file) = open_parent(p)?; - set_times_impl(&dir, &file, times, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW) -} - -#[inline(always)] -pub fn set_times_nofollow(p: &Path, times: FileTimes) -> io::Result<()> { - let (dir, file) = open_parent(p)?; - set_times_impl(&dir, &file, times, 0) -} - -fn to_wasi_timestamp_or_now(time: Option) -> io::Result { - match time { - Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts), - Some(_) => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too large to set as a file time", - )), - None => Ok(0), - } -} - -fn set_times_impl( - fd: &WasiFd, - path: &Path, - times: FileTimes, - flags: wasi::Lookupflags, -) -> io::Result<()> { - fd.path_filestat_set_times( - flags, - osstr2str(path.as_ref())?, - to_wasi_timestamp_or_now(times.accessed)?, - to_wasi_timestamp_or_now(times.modified)?, - times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM) - | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM), - ) -} - -pub fn rmdir(p: &Path) -> io::Result<()> { - let (dir, file) = open_parent(p)?; - dir.remove_directory(osstr2str(file.as_ref())?) -} - -pub fn readlink(p: &Path) -> io::Result { - let (dir, file) = open_parent(p)?; - read_link(&dir, &file) -} - -fn read_link(fd: &WasiFd, file: &Path) -> io::Result { - // Try to get a best effort initial capacity for the vector we're going to - // fill. Note that if it's not a symlink we don't use a file to avoid - // allocating gigabytes if you read_link a huge movie file by accident. - // Additionally we add 1 to the initial size so if it doesn't change until - // when we call `readlink` the returned length will be less than the - // capacity, guaranteeing that we got all the data. - let meta = metadata_at(fd, 0, file)?; - let initial_size = if meta.file_type().is_symlink() { - (meta.size() as usize).saturating_add(1) - } else { - 1 // this'll fail in just a moment - }; - - // Now that we have an initial guess of how big to make our buffer, call - // `readlink` in a loop until it fails or reports it filled fewer bytes than - // we asked for, indicating we got everything. - let file = osstr2str(file.as_ref())?; - let mut destination = vec![0u8; initial_size]; - loop { - let len = fd.readlink(file, &mut destination)?; - if len < destination.len() { - destination.truncate(len); - destination.shrink_to_fit(); - return Ok(PathBuf::from(OsString::from_vec(destination))); - } - let amt_to_add = destination.len(); - destination.extend(iter::repeat(0).take(amt_to_add)); - } -} - -pub fn symlink(original: &Path, link: &Path) -> io::Result<()> { - let (link, link_file) = open_parent(link)?; - link.symlink(osstr2str(original.as_ref())?, osstr2str(link_file.as_ref())?) -} - -pub fn link(original: &Path, link: &Path) -> io::Result<()> { - let (original, original_file) = open_parent(original)?; - let (link, link_file) = open_parent(link)?; - // Pass 0 as the flags argument, meaning don't follow symlinks. - original.link(0, osstr2str(original_file.as_ref())?, &link, osstr2str(link_file.as_ref())?) -} - -pub fn stat(p: &Path) -> io::Result { - let (dir, file) = open_parent(p)?; - metadata_at(&dir, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, &file) -} - -pub fn lstat(p: &Path) -> io::Result { - let (dir, file) = open_parent(p)?; - metadata_at(&dir, 0, &file) -} - -fn metadata_at(fd: &WasiFd, flags: wasi::Lookupflags, path: &Path) -> io::Result { - let meta = fd.path_filestat_get(flags, osstr2str(path.as_ref())?)?; - Ok(FileAttr { meta }) -} - -pub fn canonicalize(_p: &Path) -> io::Result { - // This seems to not be in wasi's API yet, and we may need to end up - // emulating it ourselves. For now just return an error. - unsupported() -} - -fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result { - let fd = fd.open( - opts.dirflags, - osstr2str(path.as_ref())?, - opts.oflags, - opts.rights_base(), - opts.rights_inheriting(), - opts.fdflags, - )?; - Ok(File { fd }) -} - -/// Attempts to open a bare path `p`. -/// -/// WASI has no fundamental capability to do this. All syscalls and operations -/// are relative to already-open file descriptors. The C library, however, -/// manages a map of pre-opened file descriptors to their path, and then the C -/// library provides an API to look at this. In other words, when you want to -/// open a path `p`, you have to find a previously opened file descriptor in a -/// global table and then see if `p` is relative to that file descriptor. -/// -/// This function, if successful, will return two items: -/// -/// * The first is a `ManuallyDrop`. This represents a pre-opened file -/// descriptor which we don't have ownership of, but we can use. You shouldn't -/// actually drop the `fd`. -/// -/// * The second is a path that should be a part of `p` and represents a -/// relative traversal from the file descriptor specified to the desired -/// location `p`. -/// -/// If successful you can use the returned file descriptor to perform -/// file-descriptor-relative operations on the path returned as well. The -/// `rights` argument indicates what operations are desired on the returned file -/// descriptor, and if successful the returned file descriptor should have the -/// appropriate rights for performing `rights` actions. -/// -/// Note that this can fail if `p` doesn't look like it can be opened relative -/// to any pre-opened file descriptor. -fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { - run_path_with_cstr(p, &|p| { - let mut buf = Vec::::with_capacity(512); - loop { - unsafe { - let mut relative_path = buf.as_ptr().cast(); - let mut abs_prefix = ptr::null(); - let fd = __wasilibc_find_relpath( - p.as_ptr(), - &mut abs_prefix, - &mut relative_path, - buf.capacity(), - ); - if fd == -1 { - if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) { - // Trigger the internal buffer resizing logic of `Vec` by requiring - // more space than the current capacity. - let cap = buf.capacity(); - buf.set_len(cap); - buf.reserve(1); - continue; - } - let msg = format!( - "failed to find a pre-opened file descriptor \ - through which {p:?} could be opened", - ); - return Err(io::Error::new(io::ErrorKind::Uncategorized, msg)); - } - let relative = CStr::from_ptr(relative_path).to_bytes().to_vec(); - - return Ok(( - ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)), - PathBuf::from(OsString::from_vec(relative)), - )); - } - } - - unsafe extern "C" { - pub fn __wasilibc_find_relpath( - path: *const libc::c_char, - abs_prefix: *mut *const libc::c_char, - relative_path: *mut *const libc::c_char, - relative_path_len: libc::size_t, - ) -> libc::c_int; - } - }) -} - -pub fn osstr2str(f: &OsStr) -> io::Result<&str> { - f.to_str().ok_or_else(|| io::const_error!(io::ErrorKind::Uncategorized, "input must be utf-8")) -} - -pub fn copy(from: &Path, to: &Path) -> io::Result { - use crate::fs::File; - - let mut reader = File::open(from)?; - let mut writer = File::create(to)?; - - io::copy(&mut reader, &mut writer) -} - -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - let (parent, path) = open_parent(path)?; - remove_dir_all_recursive(&parent, &path) -} - -fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { - // Open up a file descriptor for the directory itself. Note that we don't - // follow symlinks here and we specifically open directories. - // - // At the root invocation of this function this will correctly handle - // symlinks passed to the top-level `remove_dir_all`. At the recursive - // level this will double-check that after the `readdir` call deduced this - // was a directory it's still a directory by the time we open it up. - // - // If the opened file was actually a symlink then the symlink is deleted, - // not the directory recursively. - let mut opts = OpenOptions::new(); - opts.lookup_flags(0); - opts.directory(true); - opts.read(true); - let fd = open_at(parent, path, &opts)?; - if fd.file_attr()?.file_type().is_symlink() { - return parent.unlink_file(osstr2str(path.as_ref())?); - } - - // this "root" is only used by `DirEntry::path` which we don't use below so - // it's ok for this to be a bogus value - let dummy_root = PathBuf::new(); - - // Iterate over all the entries in this directory, and travel recursively if - // necessary - // - // Note that all directory entries for this directory are read first before - // any removal is done. This works around the fact that the WASIp1 API for - // reading directories is not well-designed for handling mutations between - // invocations of reading a directory. By reading all the entries at once - // this ensures that, at least without concurrent modifications, it should - // be possible to delete everything. - for entry in ReadDir::new(fd, dummy_root).collect::>() { - let entry = entry?; - let path = crate::str::from_utf8(&entry.name).map_err(|_| { - io::const_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found") - })?; - - let result: io::Result<()> = try { - if entry.file_type()?.is_dir() { - remove_dir_all_recursive(&entry.inner.dir.fd, path.as_ref())?; - } else { - entry.inner.dir.fd.unlink_file(path)?; - } - }; - // ignore internal NotFound errors - if let Err(err) = &result - && err.kind() != io::ErrorKind::NotFound - { - return result; - } - } - - // Once all this directory's contents are deleted it should be safe to - // delete the directory tiself. - ignore_notfound(parent.remove_directory(osstr2str(path.as_ref())?)) -} diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index f2d325da35c7d..74854cdeb498d 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -15,10 +15,11 @@ use crate::sys::pal::api::{self, WinError, set_file_information_by_handle}; use crate::sys::pal::{IoResult, fill_utf16_buf, to_u16s, truncate_utf16_at_nul}; use crate::sys::path::{WCStr, maybe_verbatim}; use crate::sys::time::SystemTime; -use crate::sys::{Align8, c, cvt}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{Align8, AsInner, FromInner, IntoInner, c, cvt}; use crate::{fmt, ptr, slice}; +mod dir; +pub use dir::Dir; mod remove_dir_all; use remove_dir_all::remove_dir_all_iterative; @@ -81,6 +82,8 @@ pub struct OpenOptions { share_mode: u32, security_qos_flags: u32, inherit_handle: bool, + freeze_last_access_time: bool, + freeze_last_write_time: bool, } #[derive(Clone, PartialEq, Eq, Debug)] @@ -204,6 +207,8 @@ impl OpenOptions { attributes: 0, security_qos_flags: 0, inherit_handle: false, + freeze_last_access_time: false, + freeze_last_write_time: false, } } @@ -246,6 +251,12 @@ impl OpenOptions { pub fn inherit_handle(&mut self, inherit: bool) { self.inherit_handle = inherit; } + pub fn freeze_last_access_time(&mut self, freeze: bool) { + self.freeze_last_access_time = freeze; + } + pub fn freeze_last_write_time(&mut self, freeze: bool) { + self.freeze_last_write_time = freeze; + } fn get_access_mode(&self) -> io::Result { match (self.read, self.write, self.append, self.access_mode) { @@ -275,7 +286,7 @@ impl OpenOptions { } } - fn get_creation_mode(&self) -> io::Result { + fn get_cmode_disposition(&self) -> io::Result<(u32, u32)> { match (self.write, self.append) { (true, false) => {} (false, false) => { @@ -297,16 +308,24 @@ impl OpenOptions { } Ok(match (self.create, self.truncate, self.create_new) { - (false, false, false) => c::OPEN_EXISTING, - (true, false, false) => c::OPEN_ALWAYS, - (false, true, false) => c::TRUNCATE_EXISTING, + (false, false, false) => (c::OPEN_EXISTING, c::FILE_OPEN), + (true, false, false) => (c::OPEN_ALWAYS, c::FILE_OPEN_IF), + (false, true, false) => (c::TRUNCATE_EXISTING, c::FILE_OVERWRITE), // `CREATE_ALWAYS` has weird semantics so we emulate it using // `OPEN_ALWAYS` and a manual truncation step. See #115745. - (true, true, false) => c::OPEN_ALWAYS, - (_, _, true) => c::CREATE_NEW, + (true, true, false) => (c::OPEN_ALWAYS, c::FILE_OVERWRITE_IF), + (_, _, true) => (c::CREATE_NEW, c::FILE_CREATE), }) } + fn get_creation_mode(&self) -> io::Result { + self.get_cmode_disposition().map(|(mode, _)| mode) + } + + fn get_disposition(&self) -> io::Result { + self.get_cmode_disposition().map(|(_, mode)| mode) + } + fn get_flags_and_attributes(&self) -> u32 { self.custom_flags | self.attributes @@ -343,6 +362,18 @@ impl File { }; let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) }; if let Ok(handle) = OwnedHandle::try_from(handle) { + if opts.freeze_last_access_time || opts.freeze_last_write_time { + let file_time = + c::FILETIME { dwLowDateTime: 0xFFFFFFFF, dwHighDateTime: 0xFFFFFFFF }; + cvt(unsafe { + c::SetFileTime( + handle.as_raw_handle(), + core::ptr::null(), + if opts.freeze_last_access_time { &file_time } else { core::ptr::null() }, + if opts.freeze_last_write_time { &file_time } else { core::ptr::null() }, + ) + })?; + } // Manual truncation. See #115745. if opts.truncate && creation == c::OPEN_ALWAYS @@ -1020,14 +1051,23 @@ impl FromRawHandle for File { } } +fn debug_path_handle<'a, 'b>( + handle: BorrowedHandle<'a>, + f: &'a mut fmt::Formatter<'b>, + name: &str, +) -> fmt::DebugStruct<'a, 'b> { + // FIXME(#24570): add more info here (e.g., mode) + let mut b = f.debug_struct(name); + b.field("handle", &handle.as_raw_handle()); + if let Ok(path) = get_path(handle) { + b.field("path", &path); + } + b +} + impl fmt::Debug for File { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // FIXME(#24570): add more info here (e.g., mode) - let mut b = f.debug_struct("File"); - b.field("handle", &self.handle.as_raw_handle()); - if let Ok(path) = get_path(self) { - b.field("path", &path); - } + let mut b = debug_path_handle(self.handle.as_handle(), f, "File"); b.finish() } } @@ -1293,8 +1333,8 @@ pub fn rename(old: &WCStr, new: &WCStr) -> io::Result<()> { Layout::from_size_align(struct_size as usize, align_of::()) .unwrap(); - // SAFETY: We allocate enough memory for a full FILE_RENAME_INFO struct and a filename. let file_rename_info; + // SAFETY: We allocate enough memory for a full FILE_RENAME_INFO struct and a filename. unsafe { file_rename_info = alloc(layout).cast::(); if file_rename_info.is_null() { @@ -1516,7 +1556,7 @@ pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> { pub fn set_times(p: &WCStr, times: FileTimes) -> io::Result<()> { let mut opts = OpenOptions::new(); - opts.write(true); + opts.access_mode(c::FILE_WRITE_ATTRIBUTES); opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); let file = File::open_native(p, &opts)?; file.set_times(times) @@ -1524,17 +1564,17 @@ pub fn set_times(p: &WCStr, times: FileTimes) -> io::Result<()> { pub fn set_times_nofollow(p: &WCStr, times: FileTimes) -> io::Result<()> { let mut opts = OpenOptions::new(); - opts.write(true); + opts.access_mode(c::FILE_WRITE_ATTRIBUTES); // `FILE_FLAG_OPEN_REPARSE_POINT` for no_follow behavior opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); let file = File::open_native(p, &opts)?; file.set_times(times) } -fn get_path(f: &File) -> io::Result { +fn get_path(f: impl AsRawHandle) -> io::Result { fill_utf16_buf( |buf, sz| unsafe { - c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS) + c::GetFinalPathNameByHandleW(f.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS) }, |buf| PathBuf::from(OsString::from_wide(buf)), ) @@ -1547,7 +1587,7 @@ pub fn canonicalize(p: &WCStr) -> io::Result { // This flag is so we can open directories too opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); let f = File::open_native(p, &opts)?; - get_path(&f) + get_path(f.handle) } pub fn copy(from: &WCStr, to: &WCStr) -> io::Result { diff --git a/library/std/src/sys/fs/windows/dir.rs b/library/std/src/sys/fs/windows/dir.rs new file mode 100644 index 0000000000000..3f617806c6c36 --- /dev/null +++ b/library/std/src/sys/fs/windows/dir.rs @@ -0,0 +1,153 @@ +use crate::os::windows::io::{ + AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, HandleOrInvalid, IntoRawHandle, + OwnedHandle, RawHandle, +}; +use crate::path::Path; +use crate::sys::api::{UnicodeStrRef, WinError}; +use crate::sys::fs::windows::debug_path_handle; +use crate::sys::fs::{File, OpenOptions}; +use crate::sys::handle::Handle; +use crate::sys::path::{WCStr, with_native_path}; +use crate::sys::{AsInner, FromInner, IntoInner, IoResult, c, to_u16s}; +use crate::{fmt, fs, io, ptr}; + +pub struct Dir { + handle: Handle, +} + +/// A wrapper around a raw NtCreateFile call. +/// +/// This isn't completely safe because `OBJECT_ATTRIBUTES` contains raw pointers. +unsafe fn nt_create_file( + opts: &OpenOptions, + object_attributes: &c::OBJECT_ATTRIBUTES, + create_options: c::NTCREATEFILE_CREATE_OPTIONS, +) -> io::Result { + let mut handle = ptr::null_mut(); + let mut io_status = c::IO_STATUS_BLOCK::PENDING; + // SYNCHRONIZE is included in FILE_GENERIC_READ, but not GENERIC_READ, so we add it manually + let access = opts.get_access_mode()? | c::SYNCHRONIZE; + // one of FILE_SYNCHRONOUS_IO_{,NON}ALERT is required for later operations to succeed. + let options = create_options | c::FILE_SYNCHRONOUS_IO_NONALERT; + let status = unsafe { + c::NtCreateFile( + &mut handle, + access, + object_attributes, + &mut io_status, + ptr::null(), + c::FILE_ATTRIBUTE_NORMAL, + opts.share_mode, + opts.get_disposition()?, + options, + ptr::null(), + 0, + ) + }; + if c::nt_success(status) { + // SAFETY: nt_success guarantees that handle is no longer null + unsafe { Ok(Handle::from_raw_handle(handle)) } + } else { + Err(WinError::new(unsafe { c::RtlNtStatusToDosError(status) })).io_result() + } +} + +impl Dir { + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + with_native_path(path, &|path| Self::open_with_native(path, opts)) + } + + pub fn open_file(&self, path: &Path, opts: &OpenOptions) -> io::Result { + // NtCreateFile will fail if given an absolute path and a non-null RootDirectory + if path.is_absolute() { + return File::open(path, opts); + } + let path = to_u16s(path)?; + let path = &path[..path.len() - 1]; // trim 0 byte + self.open_file_native(&path, opts).map(|handle| File { handle }) + } + + fn open_with_native(path: &WCStr, opts: &OpenOptions) -> io::Result { + let creation = opts.get_creation_mode()?; + let sa = c::SECURITY_ATTRIBUTES { + nLength: size_of::() as u32, + lpSecurityDescriptor: ptr::null_mut(), + bInheritHandle: opts.inherit_handle as c::BOOL, + }; + let handle = unsafe { + c::CreateFileW( + path.as_ptr(), + opts.get_access_mode()?, + opts.share_mode, + &raw const sa, + creation, + // FILE_FLAG_BACKUP_SEMANTICS is required to open a directory + opts.get_flags_and_attributes() | c::FILE_FLAG_BACKUP_SEMANTICS, + ptr::null_mut(), + ) + }; + match OwnedHandle::try_from(unsafe { HandleOrInvalid::from_raw_handle(handle) }) { + Ok(handle) => Ok(Self { handle: Handle::from_inner(handle) }), + Err(_) => Err(io::Error::last_os_error()), + } + } + + fn open_file_native(&self, path: &[u16], opts: &OpenOptions) -> io::Result { + let name = UnicodeStrRef::from_slice(path); + let object_attributes = c::OBJECT_ATTRIBUTES { + RootDirectory: self.handle.as_raw_handle(), + ObjectName: name.as_ptr(), + ..c::OBJECT_ATTRIBUTES::with_length() + }; + unsafe { nt_create_file(opts, &object_attributes, c::FILE_NON_DIRECTORY_FILE) } + } +} + +impl fmt::Debug for Dir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut b = debug_path_handle(self.handle.as_handle(), f, "Dir"); + b.finish() + } +} + +#[unstable(feature = "dirfd", issue = "120426")] +impl AsRawHandle for fs::Dir { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().handle.as_raw_handle() + } +} + +#[unstable(feature = "dirhandle", issue = "120426")] +impl IntoRawHandle for fs::Dir { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().handle.into_raw_handle() + } +} + +#[unstable(feature = "dirhandle", issue = "120426")] +impl FromRawHandle for fs::Dir { + unsafe fn from_raw_handle(handle: RawHandle) -> Self { + Self::from_inner(Dir { handle: unsafe { FromRawHandle::from_raw_handle(handle) } }) + } +} + +#[unstable(feature = "dirhandle", issue = "120426")] +impl AsHandle for fs::Dir { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.as_inner().handle.as_handle() + } +} + +#[unstable(feature = "dirhandle", issue = "120426")] +impl From for OwnedHandle { + fn from(value: fs::Dir) -> Self { + value.into_inner().handle.into_inner() + } +} + +#[unstable(feature = "dirhandle", issue = "120426")] +impl From for fs::Dir { + fn from(value: OwnedHandle) -> Self { + Self::from_inner(Dir { handle: Handle::from_inner(value) }) + } +} diff --git a/library/std/src/sys/helpers/mod.rs b/library/std/src/sys/helpers/mod.rs new file mode 100644 index 0000000000000..a6236799caea6 --- /dev/null +++ b/library/std/src/sys/helpers/mod.rs @@ -0,0 +1,39 @@ +//! Small helper functions used inside `sys`. +//! +//! If any of these have uses outside of `sys`, please move them to a different +//! module. + +#[cfg_attr(not(target_os = "linux"), allow(unused))] // Not used on all platforms. +mod small_c_string; +#[cfg_attr(not(target_os = "windows"), allow(unused))] // Not used on all platforms. +mod wstr; + +#[cfg(test)] +mod tests; + +#[cfg_attr(not(target_os = "linux"), allow(unused))] // Not used on all platforms. +pub use small_c_string::{run_path_with_cstr, run_with_cstr}; +#[cfg_attr(not(target_os = "windows"), allow(unused))] // Not used on all platforms. +pub use wstr::WStrUnits; + +/// Computes `(value*numerator)/denom` without overflow, as long as both +/// `numerator*denom` and the overall result fit into `u64` (which is the case +/// for our time conversions). +#[cfg_attr(not(target_os = "windows"), allow(unused))] // Not used on all platforms. +pub fn mul_div_u64(value: u64, numerator: u64, denom: u64) -> u64 { + let q = value / denom; + let r = value % denom; + // Decompose value as (value/denom*denom + value%denom), + // substitute into (value*numerator)/denom and simplify. + // r < denom, so (denom*numerator) is the upper bound of (r*numerator) + q * numerator + r * numerator / denom +} + +#[cfg_attr(not(target_os = "linux"), allow(unused))] // Not used on all platforms. +pub fn ignore_notfound(result: crate::io::Result) -> crate::io::Result<()> { + match result { + Err(err) if err.kind() == crate::io::ErrorKind::NotFound => Ok(()), + Ok(_) => Ok(()), + Err(err) => Err(err), + } +} diff --git a/library/std/src/sys/pal/common/small_c_string.rs b/library/std/src/sys/helpers/small_c_string.rs similarity index 100% rename from library/std/src/sys/pal/common/small_c_string.rs rename to library/std/src/sys/helpers/small_c_string.rs diff --git a/library/std/src/sys/pal/common/tests.rs b/library/std/src/sys/helpers/tests.rs similarity index 89% rename from library/std/src/sys/pal/common/tests.rs rename to library/std/src/sys/helpers/tests.rs index b7698907070c7..b67fdb9408d63 100644 --- a/library/std/src/sys/pal/common/tests.rs +++ b/library/std/src/sys/helpers/tests.rs @@ -1,9 +1,10 @@ use core::iter::repeat; +use super::mul_div_u64; +use super::small_c_string::run_path_with_cstr; use crate::ffi::CString; use crate::hint::black_box; use crate::path::Path; -use crate::sys::common::small_c_string::run_path_with_cstr; #[test] fn stack_allocation_works() { @@ -65,3 +66,8 @@ fn bench_heap_path_alloc(b: &mut test::Bencher) { .unwrap(); }); } + +#[test] +fn test_muldiv() { + assert_eq!(mul_div_u64(1_000_000_000_001, 1_000_000_000, 1_000_000), 1_000_000_000_001_000); +} diff --git a/library/std/src/sys_common/wstr.rs b/library/std/src/sys/helpers/wstr.rs similarity index 98% rename from library/std/src/sys_common/wstr.rs rename to library/std/src/sys/helpers/wstr.rs index f9a171fb7d8f5..9c4b8e09e463b 100644 --- a/library/std/src/sys_common/wstr.rs +++ b/library/std/src/sys/helpers/wstr.rs @@ -1,5 +1,4 @@ //! This module contains constructs to work with 16-bit characters (UCS-2 or UTF-16) -#![allow(dead_code)] use crate::marker::PhantomData; use crate::num::NonZero; diff --git a/library/std/src/sys/io/error/generic.rs b/library/std/src/sys/io/error/generic.rs new file mode 100644 index 0000000000000..fc70fbaba7e8c --- /dev/null +++ b/library/std/src/sys/io/error/generic.rs @@ -0,0 +1,15 @@ +pub fn errno() -> i32 { + 0 +} + +pub fn is_interrupted(_code: i32) -> bool { + false +} + +pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { + crate::io::ErrorKind::Uncategorized +} + +pub fn error_string(_errno: i32) -> String { + "operation successful".to_string() +} diff --git a/library/std/src/sys/io/error/hermit.rs b/library/std/src/sys/io/error/hermit.rs new file mode 100644 index 0000000000000..5f42144bb7cfb --- /dev/null +++ b/library/std/src/sys/io/error/hermit.rs @@ -0,0 +1,35 @@ +use crate::io; + +pub fn errno() -> i32 { + unsafe { hermit_abi::get_errno() } +} + +#[inline] +pub fn is_interrupted(errno: i32) -> bool { + errno == hermit_abi::errno::EINTR +} + +pub fn decode_error_kind(errno: i32) -> io::ErrorKind { + match errno { + hermit_abi::errno::EACCES => io::ErrorKind::PermissionDenied, + hermit_abi::errno::EADDRINUSE => io::ErrorKind::AddrInUse, + hermit_abi::errno::EADDRNOTAVAIL => io::ErrorKind::AddrNotAvailable, + hermit_abi::errno::EAGAIN => io::ErrorKind::WouldBlock, + hermit_abi::errno::ECONNABORTED => io::ErrorKind::ConnectionAborted, + hermit_abi::errno::ECONNREFUSED => io::ErrorKind::ConnectionRefused, + hermit_abi::errno::ECONNRESET => io::ErrorKind::ConnectionReset, + hermit_abi::errno::EEXIST => io::ErrorKind::AlreadyExists, + hermit_abi::errno::EINTR => io::ErrorKind::Interrupted, + hermit_abi::errno::EINVAL => io::ErrorKind::InvalidInput, + hermit_abi::errno::ENOENT => io::ErrorKind::NotFound, + hermit_abi::errno::ENOTCONN => io::ErrorKind::NotConnected, + hermit_abi::errno::EPERM => io::ErrorKind::PermissionDenied, + hermit_abi::errno::EPIPE => io::ErrorKind::BrokenPipe, + hermit_abi::errno::ETIMEDOUT => io::ErrorKind::TimedOut, + _ => io::ErrorKind::Uncategorized, + } +} + +pub fn error_string(errno: i32) -> String { + hermit_abi::error_string(errno).to_string() +} diff --git a/library/std/src/sys/io/error/mod.rs b/library/std/src/sys/io/error/mod.rs new file mode 100644 index 0000000000000..d7a0b9b4b301d --- /dev/null +++ b/library/std/src/sys/io/error/mod.rs @@ -0,0 +1,55 @@ +cfg_select! { + target_os = "hermit" => { + mod hermit; + pub use hermit::*; + } + target_os = "motor" => { + mod motor; + pub use motor::*; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + pub use sgx::*; + } + target_os = "solid_asp3" => { + mod solid; + pub use solid::*; + } + target_os = "teeos" => { + mod teeos; + pub use teeos::*; + } + target_os = "uefi" => { + mod uefi; + pub use uefi::*; + } + target_family = "unix" => { + mod unix; + pub use unix::*; + } + target_os = "wasi" => { + mod wasi; + pub use wasi::*; + } + target_os = "windows" => { + mod windows; + pub use windows::*; + } + target_os = "xous" => { + mod xous; + pub use xous::*; + } + any( + target_os = "vexos", + target_family = "wasm", + target_os = "zkvm", + ) => { + mod generic; + pub use generic::*; + } +} + +pub type RawOsError = cfg_select! { + target_os = "uefi" => usize, + _ => i32, +}; diff --git a/library/std/src/sys/io/error/motor.rs b/library/std/src/sys/io/error/motor.rs new file mode 100644 index 0000000000000..7d612d817cdd7 --- /dev/null +++ b/library/std/src/sys/io/error/motor.rs @@ -0,0 +1,67 @@ +use crate::io; +use crate::sys::io::RawOsError; + +pub fn errno() -> RawOsError { + // Not used in Motor OS because it is ambiguous: Motor OS + // is micro-kernel-based, and I/O happens via a shared-memory + // ring buffer, so an I/O operation that on a unix is a syscall + // may involve no sycalls on Motor OS at all, or a syscall + // that e.g. waits for a notification from the I/O driver + // (sys-io); and the wait syscall may succeed, but the + // driver may report an I/O error; or a bunch of results + // for several I/O operations, some successful and some + // not. + // + // Also I/O operations in a Motor OS process are handled by a + // separate runtime background/I/O thread, so it is really hard + // to define what "last system error in the current thread" + // actually means. + let error_code: moto_rt::ErrorCode = moto_rt::Error::Unknown.into(); + error_code.into() +} + +pub fn is_interrupted(_code: io::RawOsError) -> bool { + false // Motor OS doesn't have signals. +} + +pub fn decode_error_kind(code: io::RawOsError) -> io::ErrorKind { + if code < 0 || code > u16::MAX.into() { + return io::ErrorKind::Uncategorized; + } + + let error = moto_rt::Error::from(code as moto_rt::ErrorCode); + + match error { + moto_rt::Error::Unspecified => io::ErrorKind::Uncategorized, + moto_rt::Error::Unknown => io::ErrorKind::Uncategorized, + moto_rt::Error::NotReady => io::ErrorKind::WouldBlock, + moto_rt::Error::NotImplemented => io::ErrorKind::Unsupported, + moto_rt::Error::VersionTooHigh => io::ErrorKind::Unsupported, + moto_rt::Error::VersionTooLow => io::ErrorKind::Unsupported, + moto_rt::Error::InvalidArgument => io::ErrorKind::InvalidInput, + moto_rt::Error::OutOfMemory => io::ErrorKind::OutOfMemory, + moto_rt::Error::NotAllowed => io::ErrorKind::PermissionDenied, + moto_rt::Error::NotFound => io::ErrorKind::NotFound, + moto_rt::Error::InternalError => io::ErrorKind::Other, + moto_rt::Error::TimedOut => io::ErrorKind::TimedOut, + moto_rt::Error::AlreadyInUse => io::ErrorKind::AlreadyExists, + moto_rt::Error::UnexpectedEof => io::ErrorKind::UnexpectedEof, + moto_rt::Error::InvalidFilename => io::ErrorKind::InvalidFilename, + moto_rt::Error::NotADirectory => io::ErrorKind::NotADirectory, + moto_rt::Error::BadHandle => io::ErrorKind::InvalidInput, + moto_rt::Error::FileTooLarge => io::ErrorKind::FileTooLarge, + moto_rt::Error::NotConnected => io::ErrorKind::NotConnected, + moto_rt::Error::StorageFull => io::ErrorKind::StorageFull, + moto_rt::Error::InvalidData => io::ErrorKind::InvalidData, + _ => io::ErrorKind::Uncategorized, + } +} + +pub fn error_string(errno: RawOsError) -> String { + let error: moto_rt::Error = match errno { + x if x < 0 => moto_rt::Error::Unknown, + x if x > u16::MAX.into() => moto_rt::Error::Unknown, + x => (x as moto_rt::ErrorCode).into(), /* u16 */ + }; + format!("{}", error) +} diff --git a/library/std/src/sys/io/error/sgx.rs b/library/std/src/sys/io/error/sgx.rs new file mode 100644 index 0000000000000..8b3e08b0b661b --- /dev/null +++ b/library/std/src/sys/io/error/sgx.rs @@ -0,0 +1,65 @@ +use fortanix_sgx_abi::{Error, RESULT_SUCCESS}; + +use crate::io; + +pub fn errno() -> i32 { + RESULT_SUCCESS +} + +#[inline] +pub fn is_interrupted(code: i32) -> bool { + code == fortanix_sgx_abi::Error::Interrupted as _ +} + +pub fn decode_error_kind(code: i32) -> io::ErrorKind { + // FIXME: not sure how to make sure all variants of Error are covered + if code == Error::NotFound as _ { + io::ErrorKind::NotFound + } else if code == Error::PermissionDenied as _ { + io::ErrorKind::PermissionDenied + } else if code == Error::ConnectionRefused as _ { + io::ErrorKind::ConnectionRefused + } else if code == Error::ConnectionReset as _ { + io::ErrorKind::ConnectionReset + } else if code == Error::ConnectionAborted as _ { + io::ErrorKind::ConnectionAborted + } else if code == Error::NotConnected as _ { + io::ErrorKind::NotConnected + } else if code == Error::AddrInUse as _ { + io::ErrorKind::AddrInUse + } else if code == Error::AddrNotAvailable as _ { + io::ErrorKind::AddrNotAvailable + } else if code == Error::BrokenPipe as _ { + io::ErrorKind::BrokenPipe + } else if code == Error::AlreadyExists as _ { + io::ErrorKind::AlreadyExists + } else if code == Error::WouldBlock as _ { + io::ErrorKind::WouldBlock + } else if code == Error::InvalidInput as _ { + io::ErrorKind::InvalidInput + } else if code == Error::InvalidData as _ { + io::ErrorKind::InvalidData + } else if code == Error::TimedOut as _ { + io::ErrorKind::TimedOut + } else if code == Error::WriteZero as _ { + io::ErrorKind::WriteZero + } else if code == Error::Interrupted as _ { + io::ErrorKind::Interrupted + } else if code == Error::Other as _ { + io::ErrorKind::Uncategorized + } else if code == Error::UnexpectedEof as _ { + io::ErrorKind::UnexpectedEof + } else { + io::ErrorKind::Uncategorized + } +} + +pub fn error_string(errno: i32) -> String { + if errno == RESULT_SUCCESS { + "operation successful".into() + } else if ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&errno) { + format!("user-specified error {errno:08x}") + } else { + decode_error_kind(errno).as_str().into() + } +} diff --git a/library/std/src/sys/io/error/solid.rs b/library/std/src/sys/io/error/solid.rs new file mode 100644 index 0000000000000..8e9503272abbc --- /dev/null +++ b/library/std/src/sys/io/error/solid.rs @@ -0,0 +1,19 @@ +use crate::io; +use crate::sys::pal::error; + +pub fn errno() -> i32 { + 0 +} + +#[inline] +pub fn is_interrupted(code: i32) -> bool { + crate::sys::net::is_interrupted(code) +} + +pub fn decode_error_kind(code: i32) -> io::ErrorKind { + error::decode_error_kind(code) +} + +pub fn error_string(errno: i32) -> String { + if let Some(name) = error::error_name(errno) { name.to_owned() } else { format!("{errno}") } +} diff --git a/library/std/src/sys/io/error/teeos.rs b/library/std/src/sys/io/error/teeos.rs new file mode 100644 index 0000000000000..18826ffc3894f --- /dev/null +++ b/library/std/src/sys/io/error/teeos.rs @@ -0,0 +1,64 @@ +use crate::io; + +pub fn errno() -> i32 { + unsafe { (*libc::__errno_location()) as i32 } +} + +#[inline] +pub fn is_interrupted(errno: i32) -> bool { + errno == libc::EINTR +} + +// Note: code below is 1:1 copied from unix/mod.rs +pub fn decode_error_kind(errno: i32) -> io::ErrorKind { + use io::ErrorKind::*; + match errno as libc::c_int { + libc::E2BIG => ArgumentListTooLong, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::EBUSY => ResourceBusy, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::EDEADLK => Deadlock, + libc::EDQUOT => QuotaExceeded, + libc::EEXIST => AlreadyExists, + libc::EFBIG => FileTooLarge, + libc::EHOSTUNREACH => HostUnreachable, + libc::EINTR => Interrupted, + libc::EINVAL => InvalidInput, + libc::EISDIR => IsADirectory, + libc::ELOOP => FilesystemLoop, + libc::ENOENT => NotFound, + libc::ENOMEM => OutOfMemory, + libc::ENOSPC => StorageFull, + libc::ENOSYS => Unsupported, + libc::EMLINK => TooManyLinks, + libc::ENAMETOOLONG => InvalidFilename, + libc::ENETDOWN => NetworkDown, + libc::ENETUNREACH => NetworkUnreachable, + libc::ENOTCONN => NotConnected, + libc::ENOTDIR => NotADirectory, + libc::ENOTEMPTY => DirectoryNotEmpty, + libc::EPIPE => BrokenPipe, + libc::EROFS => ReadOnlyFilesystem, + libc::ESPIPE => NotSeekable, + libc::ESTALE => StaleNetworkFileHandle, + libc::ETIMEDOUT => TimedOut, + libc::ETXTBSY => ExecutableFileBusy, + libc::EXDEV => CrossesDevices, + + libc::EACCES | libc::EPERM => PermissionDenied, + + // These two constants can have the same value on some systems, + // but different values on others, so we can't use a match + // clause + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, + + _ => Uncategorized, + } +} + +pub fn error_string(_errno: i32) -> String { + "error string unimplemented".to_string() +} diff --git a/library/std/src/sys/io/error/uefi.rs b/library/std/src/sys/io/error/uefi.rs new file mode 100644 index 0000000000000..bedea240d523b --- /dev/null +++ b/library/std/src/sys/io/error/uefi.rs @@ -0,0 +1,104 @@ +use r_efi::efi::Status; + +use crate::io; + +pub fn errno() -> io::RawOsError { + 0 +} + +pub fn is_interrupted(_code: io::RawOsError) -> bool { + false +} + +pub fn decode_error_kind(code: io::RawOsError) -> io::ErrorKind { + match Status::from_usize(code) { + Status::ALREADY_STARTED + | Status::COMPROMISED_DATA + | Status::CONNECTION_FIN + | Status::CRC_ERROR + | Status::DEVICE_ERROR + | Status::END_OF_MEDIA + | Status::HTTP_ERROR + | Status::ICMP_ERROR + | Status::INCOMPATIBLE_VERSION + | Status::LOAD_ERROR + | Status::MEDIA_CHANGED + | Status::NO_MAPPING + | Status::NO_MEDIA + | Status::NOT_STARTED + | Status::PROTOCOL_ERROR + | Status::PROTOCOL_UNREACHABLE + | Status::TFTP_ERROR + | Status::VOLUME_CORRUPTED => io::ErrorKind::Other, + Status::BAD_BUFFER_SIZE | Status::INVALID_LANGUAGE => io::ErrorKind::InvalidData, + Status::ABORTED => io::ErrorKind::ConnectionAborted, + Status::ACCESS_DENIED => io::ErrorKind::PermissionDenied, + Status::BUFFER_TOO_SMALL => io::ErrorKind::FileTooLarge, + Status::CONNECTION_REFUSED => io::ErrorKind::ConnectionRefused, + Status::CONNECTION_RESET => io::ErrorKind::ConnectionReset, + Status::END_OF_FILE => io::ErrorKind::UnexpectedEof, + Status::HOST_UNREACHABLE => io::ErrorKind::HostUnreachable, + Status::INVALID_PARAMETER => io::ErrorKind::InvalidInput, + Status::IP_ADDRESS_CONFLICT => io::ErrorKind::AddrInUse, + Status::NETWORK_UNREACHABLE => io::ErrorKind::NetworkUnreachable, + Status::NO_RESPONSE => io::ErrorKind::HostUnreachable, + Status::NOT_FOUND => io::ErrorKind::NotFound, + Status::NOT_READY => io::ErrorKind::ResourceBusy, + Status::OUT_OF_RESOURCES => io::ErrorKind::OutOfMemory, + Status::SECURITY_VIOLATION => io::ErrorKind::PermissionDenied, + Status::TIMEOUT => io::ErrorKind::TimedOut, + Status::UNSUPPORTED => io::ErrorKind::Unsupported, + Status::VOLUME_FULL => io::ErrorKind::StorageFull, + Status::WRITE_PROTECTED => io::ErrorKind::ReadOnlyFilesystem, + _ => io::ErrorKind::Uncategorized, + } +} + +pub fn error_string(errno: io::RawOsError) -> String { + // Keep the List in Alphabetical Order + // The Messages are taken from UEFI Specification Appendix D - Status Codes + #[rustfmt::skip] + let msg = match Status::from_usize(errno) { + Status::ABORTED => "The operation was aborted.", + Status::ACCESS_DENIED => "Access was denied.", + Status::ALREADY_STARTED => "The protocol has already been started.", + Status::BAD_BUFFER_SIZE => "The buffer was not the proper size for the request.", + Status::BUFFER_TOO_SMALL => "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs.", + Status::COMPROMISED_DATA => "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status.", + Status::CONNECTION_FIN => "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance.", + Status::CONNECTION_REFUSED => "The receiving or transmission operation fails because this connection is refused.", + Status::CONNECTION_RESET => "The connect fails because the connection is reset either by instance itself or the communication peer.", + Status::CRC_ERROR => "A CRC error was detected.", + Status::DEVICE_ERROR => "The physical device reported an error while attempting the operation.", + Status::END_OF_FILE => "The end of the file was reached.", + Status::END_OF_MEDIA => "Beginning or end of media was reached", + Status::HOST_UNREACHABLE => "The remote host is not reachable.", + Status::HTTP_ERROR => "A HTTP error occurred during the network operation.", + Status::ICMP_ERROR => "An ICMP error occurred during the network operation.", + Status::INCOMPATIBLE_VERSION => "The function encountered an internal version that was incompatible with a version requested by the caller.", + Status::INVALID_LANGUAGE => "The language specified was invalid.", + Status::INVALID_PARAMETER => "A parameter was incorrect.", + Status::IP_ADDRESS_CONFLICT => "There is an address conflict address allocation", + Status::LOAD_ERROR => "The image failed to load.", + Status::MEDIA_CHANGED => "The medium in the device has changed since the last access.", + Status::NETWORK_UNREACHABLE => "The network containing the remote host is not reachable.", + Status::NO_MAPPING => "A mapping to a device does not exist.", + Status::NO_MEDIA => "The device does not contain any medium to perform the operation.", + Status::NO_RESPONSE => "The server was not found or did not respond to the request.", + Status::NOT_FOUND => "The item was not found.", + Status::NOT_READY => "There is no data pending upon return.", + Status::NOT_STARTED => "The protocol has not been started.", + Status::OUT_OF_RESOURCES => "A resource has run out.", + Status::PROTOCOL_ERROR => "A protocol error occurred during the network operation.", + Status::PROTOCOL_UNREACHABLE => "An ICMP protocol unreachable error is received.", + Status::SECURITY_VIOLATION => "The function was not performed due to a security violation.", + Status::TFTP_ERROR => "A TFTP error occurred during the network operation.", + Status::TIMEOUT => "The timeout time expired.", + Status::UNSUPPORTED => "The operation is not supported.", + Status::VOLUME_FULL => "There is no more space on the file system.", + Status::VOLUME_CORRUPTED => "An inconstancy was detected on the file system causing the operating to fail.", + Status::WRITE_PROTECTED => "The device cannot be written to.", + _ => return format!("Status: {errno}"), + }; + msg.to_owned() +} diff --git a/library/std/src/sys/io/error/unix.rs b/library/std/src/sys/io/error/unix.rs new file mode 100644 index 0000000000000..b10343b2752c2 --- /dev/null +++ b/library/std/src/sys/io/error/unix.rs @@ -0,0 +1,186 @@ +use crate::ffi::{CStr, c_char, c_int}; +use crate::io; + +unsafe extern "C" { + #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] + #[cfg_attr( + any( + target_os = "linux", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "l4re", + target_os = "hurd", + ), + link_name = "__errno_location" + )] + #[cfg_attr( + any( + target_os = "netbsd", + target_os = "openbsd", + target_os = "cygwin", + target_os = "android", + target_os = "redox", + target_os = "nuttx", + target_env = "newlib" + ), + link_name = "__errno" + )] + #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")] + #[cfg_attr(target_os = "nto", link_name = "__get_errno_ptr")] + #[cfg_attr(any(target_os = "freebsd", target_vendor = "apple"), link_name = "__error")] + #[cfg_attr(target_os = "haiku", link_name = "_errnop")] + #[cfg_attr(target_os = "aix", link_name = "_Errno")] + // SAFETY: this will always return the same pointer on a given thread. + #[unsafe(ffi_const)] + pub safe fn errno_location() -> *mut c_int; +} + +/// Returns the platform-specific value of errno +#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] +#[inline] +pub fn errno() -> i32 { + unsafe { (*errno_location()) as i32 } +} + +/// Sets the platform-specific value of errno +// needed for readdir and syscall! +#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks"), not(target_os = "rtems")))] +#[allow(dead_code)] // but not all target cfgs actually end up using it +#[inline] +pub fn set_errno(e: i32) { + unsafe { *errno_location() = e as c_int } +} + +#[cfg(target_os = "vxworks")] +#[inline] +pub fn errno() -> i32 { + unsafe { libc::errnoGet() } +} + +#[cfg(target_os = "rtems")] +#[inline] +pub fn errno() -> i32 { + unsafe extern "C" { + #[thread_local] + static _tls_errno: c_int; + } + + unsafe { _tls_errno as i32 } +} + +#[cfg(target_os = "dragonfly")] +#[inline] +pub fn errno() -> i32 { + unsafe extern "C" { + #[thread_local] + static errno: c_int; + } + + unsafe { errno as i32 } +} + +#[cfg(target_os = "dragonfly")] +#[allow(dead_code)] +#[inline] +pub fn set_errno(e: i32) { + unsafe extern "C" { + #[thread_local] + static mut errno: c_int; + } + + unsafe { + errno = e; + } +} + +#[inline] +pub fn is_interrupted(errno: i32) -> bool { + errno == libc::EINTR +} + +pub fn decode_error_kind(errno: i32) -> io::ErrorKind { + use io::ErrorKind::*; + match errno as libc::c_int { + libc::E2BIG => ArgumentListTooLong, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::EBUSY => ResourceBusy, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::EDEADLK => Deadlock, + libc::EDQUOT => QuotaExceeded, + libc::EEXIST => AlreadyExists, + libc::EFBIG => FileTooLarge, + libc::EHOSTUNREACH => HostUnreachable, + libc::EINTR => Interrupted, + libc::EINVAL => InvalidInput, + libc::EISDIR => IsADirectory, + libc::ELOOP => FilesystemLoop, + libc::ENOENT => NotFound, + libc::ENOMEM => OutOfMemory, + libc::ENOSPC => StorageFull, + libc::ENOSYS => Unsupported, + libc::EMLINK => TooManyLinks, + libc::ENAMETOOLONG => InvalidFilename, + libc::ENETDOWN => NetworkDown, + libc::ENETUNREACH => NetworkUnreachable, + libc::ENOTCONN => NotConnected, + libc::ENOTDIR => NotADirectory, + #[cfg(not(target_os = "aix"))] + libc::ENOTEMPTY => DirectoryNotEmpty, + libc::EPIPE => BrokenPipe, + libc::EROFS => ReadOnlyFilesystem, + libc::ESPIPE => NotSeekable, + libc::ESTALE => StaleNetworkFileHandle, + libc::ETIMEDOUT => TimedOut, + libc::ETXTBSY => ExecutableFileBusy, + libc::EXDEV => CrossesDevices, + libc::EINPROGRESS => InProgress, + libc::EOPNOTSUPP => Unsupported, + + libc::EACCES | libc::EPERM => PermissionDenied, + + // These two constants can have the same value on some systems, + // but different values on others, so we can't use a match + // clause + x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, + + _ => Uncategorized, + } +} + +/// Gets a detailed string description for the given error number. +pub fn error_string(errno: i32) -> String { + const TMPBUF_SZ: usize = 128; + + unsafe extern "C" { + #[cfg_attr( + all( + any( + target_os = "linux", + target_os = "hurd", + target_env = "newlib", + target_os = "cygwin" + ), + not(target_env = "ohos") + ), + link_name = "__xpg_strerror_r" + )] + fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int; + } + + let mut buf = [0 as c_char; TMPBUF_SZ]; + + let p = buf.as_mut_ptr(); + unsafe { + if strerror_r(errno as c_int, p, buf.len()) < 0 { + panic!("strerror_r failure"); + } + + let p = p as *const _; + // We can't always expect a UTF-8 environment. When we don't get that luxury, + // it's better to give a low-quality error message than none at all. + String::from_utf8_lossy(CStr::from_ptr(p).to_bytes()).into() + } +} diff --git a/library/std/src/sys/io/error/wasi.rs b/library/std/src/sys/io/error/wasi.rs new file mode 100644 index 0000000000000..719fc0c900adc --- /dev/null +++ b/library/std/src/sys/io/error/wasi.rs @@ -0,0 +1,80 @@ +use crate::ffi::CStr; +use crate::io as std_io; + +unsafe extern "C" { + #[thread_local] + #[link_name = "errno"] + static mut libc_errno: libc::c_int; +} + +pub fn errno() -> i32 { + unsafe { libc_errno as i32 } +} + +pub fn set_errno(val: i32) { + unsafe { + libc_errno = val; + } +} + +#[inline] +pub fn is_interrupted(errno: i32) -> bool { + errno == libc::EINTR +} + +pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { + use std_io::ErrorKind::*; + match errno as libc::c_int { + libc::E2BIG => ArgumentListTooLong, + libc::EADDRINUSE => AddrInUse, + libc::EADDRNOTAVAIL => AddrNotAvailable, + libc::EBUSY => ResourceBusy, + libc::ECONNABORTED => ConnectionAborted, + libc::ECONNREFUSED => ConnectionRefused, + libc::ECONNRESET => ConnectionReset, + libc::EDEADLK => Deadlock, + libc::EDQUOT => QuotaExceeded, + libc::EEXIST => AlreadyExists, + libc::EFBIG => FileTooLarge, + libc::EHOSTUNREACH => HostUnreachable, + libc::EINTR => Interrupted, + libc::EINVAL => InvalidInput, + libc::EISDIR => IsADirectory, + libc::ELOOP => FilesystemLoop, + libc::ENOENT => NotFound, + libc::ENOMEM => OutOfMemory, + libc::ENOSPC => StorageFull, + libc::ENOSYS => Unsupported, + libc::EMLINK => TooManyLinks, + libc::ENAMETOOLONG => InvalidFilename, + libc::ENETDOWN => NetworkDown, + libc::ENETUNREACH => NetworkUnreachable, + libc::ENOTCONN => NotConnected, + libc::ENOTDIR => NotADirectory, + libc::EPIPE => BrokenPipe, + libc::EROFS => ReadOnlyFilesystem, + libc::ESPIPE => NotSeekable, + libc::ESTALE => StaleNetworkFileHandle, + libc::ETIMEDOUT => TimedOut, + libc::ETXTBSY => ExecutableFileBusy, + libc::EXDEV => CrossesDevices, + libc::EINPROGRESS => InProgress, + libc::EOPNOTSUPP => Unsupported, + libc::EACCES | libc::EPERM => PermissionDenied, + libc::EWOULDBLOCK => WouldBlock, + + _ => Uncategorized, + } +} + +pub fn error_string(errno: i32) -> String { + let mut buf = [0 as libc::c_char; 1024]; + + let p = buf.as_mut_ptr(); + unsafe { + if libc::strerror_r(errno as libc::c_int, p, buf.len()) < 0 { + panic!("strerror_r failure"); + } + str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() + } +} diff --git a/library/std/src/sys/io/error/windows.rs b/library/std/src/sys/io/error/windows.rs new file mode 100644 index 0000000000000..d7607082a30a0 --- /dev/null +++ b/library/std/src/sys/io/error/windows.rs @@ -0,0 +1,140 @@ +use crate::sys::pal::{api, c}; +use crate::{io, ptr}; + +pub fn errno() -> i32 { + api::get_last_error().code as i32 +} + +#[inline] +pub fn is_interrupted(_errno: i32) -> bool { + false +} + +pub fn decode_error_kind(errno: i32) -> io::ErrorKind { + use io::ErrorKind::*; + + match errno as u32 { + c::ERROR_ACCESS_DENIED => return PermissionDenied, + c::ERROR_ALREADY_EXISTS => return AlreadyExists, + c::ERROR_FILE_EXISTS => return AlreadyExists, + c::ERROR_BROKEN_PIPE => return BrokenPipe, + c::ERROR_FILE_NOT_FOUND + | c::ERROR_PATH_NOT_FOUND + | c::ERROR_INVALID_DRIVE + | c::ERROR_BAD_NETPATH + | c::ERROR_BAD_NET_NAME => return NotFound, + c::ERROR_NO_DATA => return BrokenPipe, + c::ERROR_INVALID_NAME | c::ERROR_BAD_PATHNAME => return InvalidFilename, + c::ERROR_INVALID_PARAMETER => return InvalidInput, + c::ERROR_NOT_ENOUGH_MEMORY | c::ERROR_OUTOFMEMORY => return OutOfMemory, + c::ERROR_SEM_TIMEOUT + | c::WAIT_TIMEOUT + | c::ERROR_DRIVER_CANCEL_TIMEOUT + | c::ERROR_OPERATION_ABORTED + | c::ERROR_SERVICE_REQUEST_TIMEOUT + | c::ERROR_COUNTER_TIMEOUT + | c::ERROR_TIMEOUT + | c::ERROR_RESOURCE_CALL_TIMED_OUT + | c::ERROR_CTX_MODEM_RESPONSE_TIMEOUT + | c::ERROR_CTX_CLIENT_QUERY_TIMEOUT + | c::FRS_ERR_SYSVOL_POPULATE_TIMEOUT + | c::ERROR_DS_TIMELIMIT_EXCEEDED + | c::DNS_ERROR_RECORD_TIMED_OUT + | c::ERROR_IPSEC_IKE_TIMED_OUT + | c::ERROR_RUNLEVEL_SWITCH_TIMEOUT + | c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return TimedOut, + c::ERROR_CALL_NOT_IMPLEMENTED => return Unsupported, + c::ERROR_HOST_UNREACHABLE => return HostUnreachable, + c::ERROR_NETWORK_UNREACHABLE => return NetworkUnreachable, + c::ERROR_DIRECTORY => return NotADirectory, + c::ERROR_DIRECTORY_NOT_SUPPORTED => return IsADirectory, + c::ERROR_DIR_NOT_EMPTY => return DirectoryNotEmpty, + c::ERROR_WRITE_PROTECT => return ReadOnlyFilesystem, + c::ERROR_DISK_FULL | c::ERROR_HANDLE_DISK_FULL => return StorageFull, + c::ERROR_SEEK_ON_DEVICE => return NotSeekable, + c::ERROR_DISK_QUOTA_EXCEEDED => return QuotaExceeded, + c::ERROR_FILE_TOO_LARGE => return FileTooLarge, + c::ERROR_BUSY => return ResourceBusy, + c::ERROR_POSSIBLE_DEADLOCK => return Deadlock, + c::ERROR_NOT_SAME_DEVICE => return CrossesDevices, + c::ERROR_TOO_MANY_LINKS => return TooManyLinks, + c::ERROR_FILENAME_EXCED_RANGE => return InvalidFilename, + c::ERROR_CANT_RESOLVE_FILENAME => return FilesystemLoop, + _ => {} + } + + match errno { + c::WSAEACCES => PermissionDenied, + c::WSAEADDRINUSE => AddrInUse, + c::WSAEADDRNOTAVAIL => AddrNotAvailable, + c::WSAECONNABORTED => ConnectionAborted, + c::WSAECONNREFUSED => ConnectionRefused, + c::WSAECONNRESET => ConnectionReset, + c::WSAEINVAL => InvalidInput, + c::WSAENOTCONN => NotConnected, + c::WSAEWOULDBLOCK => WouldBlock, + c::WSAETIMEDOUT => TimedOut, + c::WSAEHOSTUNREACH => HostUnreachable, + c::WSAENETDOWN => NetworkDown, + c::WSAENETUNREACH => NetworkUnreachable, + c::WSAEDQUOT => QuotaExceeded, + + _ => Uncategorized, + } +} + +/// Gets a detailed string description for the given error number. +pub fn error_string(mut errnum: i32) -> String { + let mut buf = [0 as c::WCHAR; 2048]; + + unsafe { + let mut module = ptr::null_mut(); + let mut flags = 0; + + // NTSTATUS errors may be encoded as HRESULT, which may returned from + // GetLastError. For more information about Windows error codes, see + // `[MS-ERREF]`: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a + if (errnum & c::FACILITY_NT_BIT as i32) != 0 { + // format according to https://support.microsoft.com/en-us/help/259693 + const NTDLL_DLL: &[u16] = &[ + 'N' as _, 'T' as _, 'D' as _, 'L' as _, 'L' as _, '.' as _, 'D' as _, 'L' as _, + 'L' as _, 0, + ]; + module = c::GetModuleHandleW(NTDLL_DLL.as_ptr()); + + if !module.is_null() { + errnum ^= c::FACILITY_NT_BIT as i32; + flags = c::FORMAT_MESSAGE_FROM_HMODULE; + } + } + + let res = c::FormatMessageW( + flags | c::FORMAT_MESSAGE_FROM_SYSTEM | c::FORMAT_MESSAGE_IGNORE_INSERTS, + module, + errnum as u32, + 0, + buf.as_mut_ptr(), + buf.len() as u32, + ptr::null(), + ) as usize; + if res == 0 { + // Sometimes FormatMessageW can fail e.g., system doesn't like 0 as langId, + let fm_err = errno(); + return format!("OS Error {errnum} (FormatMessageW() returned error {fm_err})"); + } + + match String::from_utf16(&buf[..res]) { + Ok(mut msg) => { + // Trim trailing CRLF inserted by FormatMessageW + let len = msg.trim_ascii_end().len(); + msg.truncate(len); + msg + } + Err(..) => format!( + "OS Error {} (FormatMessageW() returned \ + invalid UTF-16)", + errnum + ), + } + } +} diff --git a/library/std/src/sys/io/error/xous.rs b/library/std/src/sys/io/error/xous.rs new file mode 100644 index 0000000000000..2e9ea8e4f0928 --- /dev/null +++ b/library/std/src/sys/io/error/xous.rs @@ -0,0 +1,17 @@ +use crate::os::xous::ffi::Error as XousError; + +pub fn errno() -> i32 { + 0 +} + +pub fn is_interrupted(_code: i32) -> bool { + false +} + +pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind { + crate::io::ErrorKind::Uncategorized +} + +pub fn error_string(errno: i32) -> String { + Into::::into(errno).to_string() +} diff --git a/library/std/src/sys/io/io_slice/iovec.rs b/library/std/src/sys/io/io_slice/iovec.rs index df56358969a39..d549aca250d5f 100644 --- a/library/std/src/sys/io/io_slice/iovec.rs +++ b/library/std/src/sys/io/io_slice/iovec.rs @@ -1,6 +1,6 @@ #[cfg(target_os = "hermit")] use hermit_abi::iovec; -#[cfg(any(target_family = "unix", target_os = "trusty"))] +#[cfg(any(target_family = "unix", target_os = "trusty", target_os = "wasi"))] use libc::iovec; use crate::ffi::c_void; diff --git a/library/std/src/sys/io/io_slice/wasi.rs b/library/std/src/sys/io/io_slice/wasi.rs deleted file mode 100644 index 87acbbd924e56..0000000000000 --- a/library/std/src/sys/io/io_slice/wasi.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::marker::PhantomData; -use crate::slice; - -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: wasi::Ciovec, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice { vec: wasi::Ciovec { buf: buf.as_ptr(), buf_len: buf.len() }, _p: PhantomData } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.buf_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.buf_len -= n; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub const fn as_slice(&self) -> &'a [u8] { - unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) } - } -} - -#[repr(transparent)] -pub struct IoSliceMut<'a> { - vec: wasi::Iovec, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut { - vec: wasi::Iovec { buf: buf.as_mut_ptr(), buf_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.buf_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.buf_len -= n; - self.vec.buf = self.vec.buf.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) } - } - - #[inline] - pub const fn into_slice(self) -> &'a mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) } - } -} diff --git a/library/std/src/sys/io/kernel_copy/linux/tests.rs b/library/std/src/sys/io/kernel_copy/linux/tests.rs index 15dee768d928b..9b2d9cbef9902 100644 --- a/library/std/src/sys/io/kernel_copy/linux/tests.rs +++ b/library/std/src/sys/io/kernel_copy/linux/tests.rs @@ -88,13 +88,8 @@ fn dont_splice_pipes_from_files() -> Result<()> { use crate::io::SeekFrom; use crate::os::unix::fs::FileExt; - use crate::process::{ChildStdin, ChildStdout}; - use crate::sys_common::FromInner; - let (read_end, write_end) = crate::sys::pipe::anon_pipe()?; - - let mut read_end = ChildStdout::from_inner(read_end); - let mut write_end = ChildStdin::from_inner(write_end); + let (mut read_end, mut write_end) = crate::io::pipe()?; let tmp_path = tmpdir(); let file = tmp_path.join("to_be_modified"); @@ -220,13 +215,8 @@ fn bench_file_to_uds_copy(b: &mut test::Bencher) { fn bench_socket_pipe_socket_copy(b: &mut test::Bencher) { use super::CopyResult; use crate::io::ErrorKind; - use crate::process::{ChildStdin, ChildStdout}; - use crate::sys_common::FromInner; - - let (read_end, write_end) = crate::sys::pipe::anon_pipe().unwrap(); - let mut read_end = ChildStdout::from_inner(read_end); - let write_end = ChildStdin::from_inner(write_end); + let (mut read_end, write_end) = crate::io::pipe().unwrap(); let acceptor = crate::net::TcpListener::bind("localhost:0").unwrap(); let mut remote_end = crate::net::TcpStream::connect(acceptor.local_addr().unwrap()).unwrap(); diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index e2c5e7f88d492..b3587ab63696a 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -1,8 +1,10 @@ #![forbid(unsafe_op_in_unsafe_fn)] +mod error; + mod io_slice { cfg_select! { - any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty") => { + any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty", target_os = "wasi") => { mod iovec; pub use iovec::*; } @@ -10,10 +12,6 @@ mod io_slice { mod windows; pub use windows::*; } - target_os = "wasi" => { - mod wasi; - pub use wasi::*; - } target_os = "uefi" => { mod uefi; pub use uefi::*; @@ -52,6 +50,19 @@ mod is_terminal { mod kernel_copy; +#[cfg_attr(not(target_os = "linux"), allow(unused_imports))] +#[cfg(all( + target_family = "unix", + not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")) +))] +pub use error::errno_location; +#[cfg_attr(not(target_os = "linux"), allow(unused_imports))] +#[cfg(any( + all(target_family = "unix", not(any(target_os = "vxworks", target_os = "rtems"))), + target_os = "wasi", +))] +pub use error::set_errno; +pub use error::{RawOsError, decode_error_kind, errno, error_string, is_interrupted}; pub use io_slice::{IoSlice, IoSliceMut}; pub use is_terminal::is_terminal; pub use kernel_copy::{CopyState, kernel_copy}; diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 2dbdc8a4e026e..5436c144d3330 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -1,19 +1,11 @@ #![allow(unsafe_op_in_unsafe_fn)] -/// The configure builtins provides runtime support compiler-builtin features -/// which require dynamic initialization to work as expected, e.g. aarch64 -/// outline-atomics. +mod alloc; mod configure_builtins; - -/// The PAL (platform abstraction layer) contains platform-specific abstractions -/// for implementing the features in the other submodules, e.g. UNIX file -/// descriptors. +mod helpers; mod pal; - -mod alloc; mod personality; -pub mod anonymous_pipe; pub mod args; pub mod backtrace; pub mod cmath; @@ -26,6 +18,7 @@ pub mod io; pub mod net; pub mod os_str; pub mod path; +pub mod pipe; pub mod platform_version; pub mod process; pub mod random; @@ -33,7 +26,30 @@ pub mod stdio; pub mod sync; pub mod thread; pub mod thread_local; +pub mod time; // FIXME(117276): remove this, move feature implementations into individual // submodules. pub use pal::*; + +/// A trait for viewing representations from std types. +#[cfg_attr(not(target_os = "linux"), allow(unused))] +pub(crate) trait AsInner { + fn as_inner(&self) -> &Inner; +} + +/// A trait for viewing representations from std types. +#[cfg_attr(not(target_os = "linux"), allow(unused))] +pub(crate) trait AsInnerMut { + fn as_inner_mut(&mut self) -> &mut Inner; +} + +/// A trait for extracting representations from std types. +pub(crate) trait IntoInner { + fn into_inner(self) -> Inner; +} + +/// A trait for creating std types from internal representations. +pub(crate) trait FromInner { + fn from_inner(inner: Inner) -> Self; +} diff --git a/library/std/src/sys/net/connection/motor.rs b/library/std/src/sys/net/connection/motor.rs index e9bf29e34f90c..79a528792106c 100644 --- a/library/std/src/sys/net/connection/motor.rs +++ b/library/std/src/sys/net/connection/motor.rs @@ -5,8 +5,7 @@ use crate::net::SocketAddr::{V4, V6}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; -use crate::sys::map_motor_error; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, map_motor_error}; use crate::time::Duration; // We want to re-use as much of Rust's stdlib code as possible, @@ -145,8 +144,9 @@ impl TcpStream { } pub fn take_error(&self) -> io::Result> { - let e = moto_rt::net::take_error(self.inner.as_raw_fd()).map_err(map_motor_error)?; - if e == moto_rt::E_OK { Ok(None) } else { Ok(Some(map_motor_error(e))) } + moto_rt::net::take_error(self.inner.as_raw_fd()) + .map(|e| e.map(map_motor_error)) + .map_err(map_motor_error) } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { @@ -213,8 +213,9 @@ impl TcpListener { } pub fn take_error(&self) -> io::Result> { - let e = moto_rt::net::take_error(self.inner.as_raw_fd()).map_err(map_motor_error)?; - if e == moto_rt::E_OK { Ok(None) } else { Ok(Some(map_motor_error(e))) } + moto_rt::net::take_error(self.inner.as_raw_fd()) + .map(|e| e.map(map_motor_error)) + .map_err(map_motor_error) } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { @@ -364,10 +365,7 @@ impl UdpSocket { pub fn take_error(&self) -> io::Result> { moto_rt::net::take_error(self.inner.as_raw_fd()) - .map(|e| match e { - moto_rt::E_OK => None, - e => Some(map_motor_error(e)), - }) + .map(|e| e.map(map_motor_error)) .map_err(map_motor_error) } @@ -415,10 +413,12 @@ impl TryFrom<&str> for LookupHost { fn try_from(host_port: &str) -> io::Result { let (host, port_str) = host_port .rsplit_once(':') - .ok_or(moto_rt::E_INVALID_ARGUMENT) + .ok_or(moto_rt::Error::InvalidArgument) + .map_err(map_motor_error)?; + let port: u16 = port_str + .parse() + .map_err(|_| moto_rt::Error::InvalidArgument) .map_err(map_motor_error)?; - let port: u16 = - port_str.parse().map_err(|_| moto_rt::E_INVALID_ARGUMENT).map_err(map_motor_error)?; (host, port).try_into() } } diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs index 2f5c6fa31d407..59f515d31aff9 100644 --- a/library/std/src/sys/net/connection/socket/hermit.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -9,10 +9,9 @@ use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; use crate::sys::fd::FileDesc; -use crate::sys::time::Instant; +use crate::sys::{AsInner, FromInner, IntoInner}; pub use crate::sys::{cvt, cvt_r}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::Duration; +use crate::time::{Duration, Instant}; use crate::{cmp, mem}; #[expect(non_camel_case_types)] @@ -300,7 +299,6 @@ impl Socket { if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } - // This is used by sys_common code to abstract over Windows and Unix. pub fn as_raw(&self) -> RawFd { self.0.as_raw_fd() } diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 1d941dec1b792..256b99dfa9874 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -7,9 +7,9 @@ use crate::mem::MaybeUninit; use crate::net::{ Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs, }; -use crate::sys::common::small_c_string::run_with_cstr; +use crate::sys::helpers::run_with_cstr; use crate::sys::net::connection::each_addr; -use crate::sys_common::{AsInner, FromInner}; +use crate::sys::{AsInner, FromInner}; use crate::time::Duration; use crate::{cmp, fmt, mem, ptr}; @@ -22,14 +22,10 @@ cfg_select! { mod solid; pub use solid::*; } - target_family = "unix" => { + any(target_family = "unix", target_os = "wasi") => { mod unix; pub use unix::*; } - all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { - mod wasip2; - pub use wasip2::*; - } target_os = "windows" => { mod windows; pub use windows::*; diff --git a/library/std/src/sys/net/connection/socket/solid.rs b/library/std/src/sys/net/connection/socket/solid.rs index 14cf75adcc06f..e20a3fbb76b72 100644 --- a/library/std/src/sys/net/connection/socket/solid.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -6,8 +6,7 @@ use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; -use crate::sys::abi; -use crate::sys_common::{FromInner, IntoInner}; +use crate::sys::{FromInner, IntoInner, abi}; use crate::time::Duration; use crate::{cmp, mem, ptr, str}; @@ -354,7 +353,6 @@ impl Socket { if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } - // This method is used by sys_common code to abstract over targets. pub fn as_raw(&self) -> c_int { self.as_raw_fd() } diff --git a/library/std/src/sys/net/connection/socket/unix.rs b/library/std/src/sys/net/connection/socket/unix.rs index 559e27604a9d3..5e20c0ffdfa6a 100644 --- a/library/std/src/sys/net/connection/socket/unix.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -4,11 +4,11 @@ use libc::{MSG_PEEK, c_int, c_void, size_t, sockaddr, socklen_t}; use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; -use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; use crate::sys::net::{getsockopt, setsockopt}; use crate::sys::pal::IsMinusOne; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; use crate::{cmp, mem}; @@ -107,7 +107,7 @@ impl Socket { } } - #[cfg(not(target_os = "vxworks"))] + #[cfg(not(any(target_os = "vxworks", target_os = "wasi")))] pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { unsafe { let mut fds = [0, 0]; @@ -151,7 +151,7 @@ impl Socket { loop { let result = unsafe { libc::connect(self.as_raw_fd(), addr.as_ptr(), len) }; if result.is_minus_one() { - let err = crate::sys::os::errno(); + let err = crate::sys::io::errno(); match err { libc::EINTR => continue, libc::EISCONN => return Ok(()), @@ -275,6 +275,7 @@ impl Socket { self.0.duplicate().map(Socket) } + #[cfg(not(target_os = "wasi"))] pub fn send_with_flags(&self, buf: &[u8], flags: c_int) -> io::Result { let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; let ret = cvt(unsafe { @@ -361,6 +362,7 @@ impl Socket { self.recv_from_with_flags(buf, MSG_PEEK) } + #[cfg(not(target_os = "wasi"))] pub fn write(&self, buf: &[u8]) -> io::Result { self.0.write(buf) } @@ -612,7 +614,6 @@ impl Socket { if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } - // This is used by sys_common code to abstract over Windows and Unix. pub fn as_raw(&self) -> RawFd { self.as_raw_fd() } diff --git a/library/std/src/sys/net/connection/socket/wasip2.rs b/library/std/src/sys/net/connection/socket/wasip2.rs deleted file mode 100644 index a1b08609eb024..0000000000000 --- a/library/std/src/sys/net/connection/socket/wasip2.rs +++ /dev/null @@ -1,408 +0,0 @@ -#![deny(unsafe_op_in_unsafe_fn)] - -pub(super) use libc as netc; -use libc::{c_int, c_void, size_t}; - -use super::{getsockopt, setsockopt, socket_addr_from_c, socket_addr_to_c}; -use crate::ffi::CStr; -use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Shutdown, SocketAddr}; -use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::unsupported; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::{Duration, Instant}; -use crate::{cmp, mem, str}; - -#[allow(non_camel_case_types)] -pub type wrlen_t = size_t; - -#[doc(hidden)] -pub trait IsMinusOne { - fn is_minus_one(&self) -> bool; -} - -macro_rules! impl_is_minus_one { - ($($t:ident)*) => ($(impl IsMinusOne for $t { - fn is_minus_one(&self) -> bool { - *self == -1 - } - })*) -} - -impl_is_minus_one! { i8 i16 i32 i64 isize } - -pub fn cvt(t: T) -> crate::io::Result { - if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } -} - -pub fn cvt_r(mut f: F) -> crate::io::Result -where - T: IsMinusOne, - F: FnMut() -> T, -{ - loop { - match cvt(f()) { - Err(ref e) if e.is_interrupted() => {} - other => return other, - } - } -} - -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { - return Ok(()); - } - - if err == netc::EAI_SYSTEM { - return Err(io::Error::last_os_error()); - } - - let detail = unsafe { - str::from_utf8(CStr::from_ptr(netc::gai_strerror(err)).to_bytes()).unwrap().to_owned() - }; - - Err(io::Error::new( - io::ErrorKind::Uncategorized, - &format!("failed to lookup address information: {detail}")[..], - )) -} - -pub fn init() {} - -pub struct WasiSocket(OwnedFd); - -pub struct Socket(WasiSocket); - -impl Socket { - pub fn new(family: c_int, ty: c_int) -> io::Result { - let fd = cvt(unsafe { netc::socket(family, ty, 0) })?; - Ok(unsafe { Self::from_raw_fd(fd) }) - } - - pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { - let (addr, len) = socket_addr_to_c(addr); - cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; - Ok(()) - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let r = self.connect(addr); - self.set_nonblocking(false)?; - - match r { - Ok(_) => return Ok(()), - // there's no ErrorKind for EINPROGRESS - Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {} - Err(e) => return Err(e), - } - - let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 }; - - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let start = Instant::now(); - - loop { - let elapsed = start.elapsed(); - if elapsed >= timeout { - return Err(io::const_error!(io::ErrorKind::TimedOut, "connection timed out")); - } - - let timeout = timeout - elapsed; - let mut timeout = timeout - .as_secs() - .saturating_mul(1_000) - .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); - if timeout == 0 { - timeout = 1; - } - - let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; - - match unsafe { netc::poll(&mut pollfd, 1, timeout) } { - -1 => { - let err = io::Error::last_os_error(); - if !err.is_interrupted() { - return Err(err); - } - } - 0 => {} - _ => { - // WASI poll does not return POLLHUP or POLLERR in revents. Check if the - // connection actually succeeded and return ok only when the socket is - // ready and no errors were found. - if let Some(e) = self.take_error()? { - return Err(e); - } - - return Ok(()); - } - } - } - } - - pub fn accept( - &self, - storage: *mut netc::sockaddr, - len: *mut netc::socklen_t, - ) -> io::Result { - let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?; - Ok(unsafe { Self::from_raw_fd(fd) }) - } - - pub fn duplicate(&self) -> io::Result { - unsupported() - } - - fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { - let ret = cvt(unsafe { - netc::recv( - self.as_raw_fd(), - buf.as_mut().as_mut_ptr() as *mut c_void, - buf.capacity(), - flags, - ) - })?; - unsafe { - buf.advance_unchecked(ret as usize); - } - Ok(()) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), 0)?; - Ok(buf.len()) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - let mut buf = BorrowedBuf::from(buf); - self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?; - Ok(buf.len()) - } - - pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { - self.recv_with_flags(buf, 0) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - io::default_read_vectored(|b| self.read(b), bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - false - } - - fn recv_from_with_flags( - &self, - buf: &mut [u8], - flags: c_int, - ) -> io::Result<(usize, SocketAddr)> { - let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = size_of_val(&storage) as netc::socklen_t; - - let n = cvt(unsafe { - netc::recvfrom( - self.as_raw_fd(), - buf.as_mut_ptr() as *mut c_void, - buf.len(), - flags, - core::ptr::addr_of_mut!(storage) as *mut _, - &mut addrlen, - ) - })?; - Ok((n as usize, unsafe { socket_addr_from_c(&storage, addrlen as usize)? })) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, netc::MSG_PEEK) - } - - fn write(&self, buf: &[u8]) -> io::Result { - let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; - let ret = cvt(unsafe { - netc::send(self.as_raw(), buf.as_ptr() as *const c_void, len, netc::MSG_NOSIGNAL) - })?; - Ok(ret as usize) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - io::default_write_vectored(|b| self.write(b), bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn set_timeout(&self, dur: Option, kind: c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::ZERO_TIMEOUT); - } - - let secs = dur.as_secs().try_into().unwrap_or(netc::time_t::MAX); - let mut timeout = netc::timeval { - tv_sec: secs, - tv_usec: dur.subsec_micros() as netc::suseconds_t, - }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - timeout - } - None => netc::timeval { tv_sec: 0, tv_usec: 0 }, - }; - unsafe { setsockopt(self, netc::SOL_SOCKET, kind, timeout) } - } - - pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: netc::timeval = unsafe { getsockopt(self, netc::SOL_SOCKET, kind)? }; - if raw.tv_sec == 0 && raw.tv_usec == 0 { - Ok(None) - } else { - let sec = raw.tv_sec as u64; - let nsec = (raw.tv_usec as u32) * 1000; - Ok(Some(Duration::new(sec, nsec))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => netc::SHUT_WR, - Shutdown::Read => netc::SHUT_RD, - Shutdown::Both => netc::SHUT_RDWR, - }; - cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; - Ok(()) - } - - pub fn set_linger(&self, _linger: Option) -> io::Result<()> { - unsupported() - } - - pub fn linger(&self) -> io::Result> { - unsupported() - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - unsafe { setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) } - } - - pub fn nodelay(&self) -> io::Result { - let raw: c_int = unsafe { getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)? }; - Ok(raw != 0) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = nonblocking as c_int; - cvt(unsafe { netc::ioctl(self.as_raw_fd(), netc::FIONBIO, &mut nonblocking) }).map(drop) - } - - pub fn take_error(&self) -> io::Result> { - let raw: c_int = unsafe { getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)? }; - if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.as_raw_fd() - } -} - -impl AsInner for WasiSocket { - #[inline] - fn as_inner(&self) -> &OwnedFd { - &self.0 - } -} - -impl IntoInner for WasiSocket { - fn into_inner(self) -> OwnedFd { - self.0 - } -} - -impl FromInner for WasiSocket { - fn from_inner(owned_fd: OwnedFd) -> Self { - Self(owned_fd) - } -} - -impl AsFd for WasiSocket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for WasiSocket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for WasiSocket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for WasiSocket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } - } -} - -impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &WasiSocket { - &self.0 - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> WasiSocket { - self.0 - } -} - -impl FromInner for Socket { - fn from_inner(sock: WasiSocket) -> Socket { - Socket(sock) - } -} - -impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } - } -} diff --git a/library/std/src/sys/net/connection/socket/windows.rs b/library/std/src/sys/net/connection/socket/windows.rs index 6dbebc5e276ec..b23fb9c09f871 100644 --- a/library/std/src/sys/net/connection/socket/windows.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -8,9 +8,8 @@ use crate::net::{Shutdown, SocketAddr}; use crate::os::windows::io::{ AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, }; -use crate::sys::c; use crate::sys::pal::winsock::last_error; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, c}; use crate::time::Duration; use crate::{cmp, mem, ptr, sys}; @@ -440,7 +439,6 @@ impl Socket { if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } - // This is used by sys_common code to abstract over Windows and Unix. pub fn as_raw(&self) -> c::SOCKET { debug_assert_eq!(size_of::(), size_of::()); debug_assert_eq!(align_of::(), align_of::()); @@ -453,7 +451,6 @@ impl Socket { } } -#[unstable(reason = "not public", issue = "none", feature = "fd_read")] impl<'a> Read for &'a Socket { fn read(&mut self, buf: &mut [u8]) -> io::Result { (**self).read(buf) diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index db2d18646d026..107a3e23733de 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -16,26 +16,26 @@ pub struct TcpStream { } impl TcpStream { + fn new(inner: tcp::Tcp) -> Self { + Self { + inner, + read_timeout: Arc::new(Mutex::new(None)), + write_timeout: Arc::new(Mutex::new(None)), + } + } + pub fn connect(addr: A) -> io::Result { return each_addr(addr, inner); fn inner(addr: &SocketAddr) -> io::Result { let inner = tcp::Tcp::connect(addr, None)?; - Ok(TcpStream { - inner, - read_timeout: Arc::new(Mutex::new(None)), - write_timeout: Arc::new(Mutex::new(None)), - }) + Ok(TcpStream::new(inner)) } } pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { let inner = tcp::Tcp::connect(addr, Some(timeout))?; - Ok(Self { - inner, - read_timeout: Arc::new(Mutex::new(None)), - write_timeout: Arc::new(Mutex::new(None)), - }) + Ok(Self::new(inner)) } pub fn set_read_timeout(&self, t: Option) -> io::Result<()> { @@ -148,16 +148,23 @@ pub struct TcpListener { } impl TcpListener { - pub fn bind(_: A) -> io::Result { - unsupported() + pub fn bind(addr: A) -> io::Result { + return each_addr(addr, inner); + + fn inner(addr: &SocketAddr) -> io::Result { + let inner = tcp::Tcp::bind(addr)?; + Ok(TcpListener { inner }) + } } pub fn socket_addr(&self) -> io::Result { - unsupported() + self.inner.socket_addr() } pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - unsupported() + let tcp = self.inner.accept()?; + let addr = tcp.peer_addr()?; + Ok((TcpStream::new(tcp), addr)) } pub fn duplicate(&self) -> io::Result { diff --git a/library/std/src/sys/net/connection/uefi/tcp.rs b/library/std/src/sys/net/connection/uefi/tcp.rs index 1e7e829c85f35..1e58db0d70468 100644 --- a/library/std/src/sys/net/connection/uefi/tcp.rs +++ b/library/std/src/sys/net/connection/uefi/tcp.rs @@ -2,13 +2,16 @@ use super::tcp4; use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::SocketAddr; use crate::ptr::NonNull; -use crate::sys::{helpers, unsupported}; +use crate::sys::pal::{helpers, unsupported}; use crate::time::Duration; pub(crate) enum Tcp { V4(tcp4::Tcp4), } +// SAFETY: UEFI has no threads. +unsafe impl Send for Tcp {} + impl Tcp { pub(crate) fn connect(addr: &SocketAddr, timeout: Option) -> io::Result { match addr { @@ -18,7 +21,24 @@ impl Tcp { temp.connect(timeout)?; Ok(Tcp::V4(temp)) } - SocketAddr::V6(_) => todo!(), + SocketAddr::V6(_) => unsupported(), + } + } + + pub(crate) fn bind(addr: &SocketAddr) -> io::Result { + match addr { + SocketAddr::V4(x) => { + let temp = tcp4::Tcp4::new()?; + temp.configure(false, None, Some(x))?; + Ok(Tcp::V4(temp)) + } + SocketAddr::V6(_) => unsupported(), + } + } + + pub(crate) fn accept(&self) -> io::Result { + match self { + Self::V4(client) => client.accept().map(Tcp::V4), } } diff --git a/library/std/src/sys/net/connection/uefi/tcp4.rs b/library/std/src/sys/net/connection/uefi/tcp4.rs index 0409997f02721..ac38dd901e4d3 100644 --- a/library/std/src/sys/net/connection/uefi/tcp4.rs +++ b/library/std/src/sys/net/connection/uefi/tcp4.rs @@ -3,7 +3,7 @@ use r_efi::protocols::tcp4; use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::SocketAddrV4; -use crate::ptr::NonNull; +use crate::ptr::{self, NonNull}; use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sys::pal::helpers; use crate::time::{Duration, Instant}; @@ -12,9 +12,9 @@ const TYPE_OF_SERVICE: u8 = 8; const TIME_TO_LIVE: u8 = 255; pub(crate) struct Tcp4 { + handle: NonNull, protocol: NonNull, flag: AtomicBool, - #[expect(dead_code)] service_binding: helpers::ServiceProtocol, } @@ -22,10 +22,11 @@ const DEFAULT_ADDR: efi::Ipv4Address = efi::Ipv4Address { addr: [0u8; 4] }; impl Tcp4 { pub(crate) fn new() -> io::Result { - let service_binding = helpers::ServiceProtocol::open(tcp4::SERVICE_BINDING_PROTOCOL_GUID)?; - let protocol = helpers::open_protocol(service_binding.child_handle(), tcp4::PROTOCOL_GUID)?; + let (service_binding, handle) = + helpers::ServiceProtocol::open(tcp4::SERVICE_BINDING_PROTOCOL_GUID)?; + let protocol = helpers::open_protocol(handle, tcp4::PROTOCOL_GUID)?; - Ok(Self { service_binding, protocol, flag: AtomicBool::new(false) }) + Ok(Self { service_binding, handle, protocol, flag: AtomicBool::new(false) }) } pub(crate) fn configure( @@ -42,11 +43,14 @@ impl Tcp4 { (DEFAULT_ADDR, 0) }; - // FIXME: Remove when passive connections with proper subnet handling are added - assert!(station_address.is_none()); - let use_default_address = efi::Boolean::TRUE; - let (station_address, station_port) = (DEFAULT_ADDR, 0); - let subnet_mask = helpers::ipv4_to_r_efi(crate::net::Ipv4Addr::new(0, 0, 0, 0)); + let use_default_address: r_efi::efi::Boolean = + station_address.is_none_or(|addr| addr.ip().is_unspecified()).into(); + let (station_address, station_port) = if let Some(x) = station_address { + (helpers::ipv4_to_r_efi(*x.ip()), x.port()) + } else { + (DEFAULT_ADDR, 0) + }; + let subnet_mask = helpers::ipv4_to_r_efi(crate::net::Ipv4Addr::new(255, 255, 255, 0)); let mut config_data = tcp4::ConfigData { type_of_service: TYPE_OF_SERVICE, @@ -60,7 +64,7 @@ impl Tcp4 { station_port, subnet_mask, }, - control_option: crate::ptr::null_mut(), + control_option: ptr::null_mut(), }; let r = unsafe { ((*protocol).configure)(protocol, &mut config_data) }; @@ -74,17 +78,55 @@ impl Tcp4 { let r = unsafe { ((*protocol).get_mode_data)( protocol, - crate::ptr::null_mut(), + ptr::null_mut(), &mut config_data, - crate::ptr::null_mut(), - crate::ptr::null_mut(), - crate::ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), ) }; if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(config_data) } } + pub(crate) fn accept(&self) -> io::Result { + let evt = unsafe { self.create_evt() }?; + let completion_token = + tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS }; + let mut listen_token = + tcp4::ListenToken { completion_token, new_child_handle: ptr::null_mut() }; + + let protocol = self.protocol.as_ptr(); + let r = unsafe { ((*protocol).accept)(protocol, &mut listen_token) }; + if r.is_error() { + return Err(io::Error::from_raw_os_error(r.as_usize())); + } + + unsafe { self.wait_or_cancel(None, &mut listen_token.completion_token) }?; + + if completion_token.status.is_error() { + Err(io::Error::from_raw_os_error(completion_token.status.as_usize())) + } else { + // EDK2 internals seem to assume a single ServiceBinding Protocol for TCP4 and TCP6, and + // thus does not use any service binding protocol data in destroying child sockets. It + // does seem to suggest that we need to cleanup even the protocols created by accept. To + // be on the safe side with other implementations, we will be using the same service + // binding protocol as the parent TCP4 handle. + // + // https://github.com/tianocore/edk2/blob/f80580f56b267c96f16f985dbf707b2f96947da4/NetworkPkg/TcpDxe/TcpDriver.c#L938 + + let handle = NonNull::new(listen_token.new_child_handle).unwrap(); + let protocol = helpers::open_protocol(handle, tcp4::PROTOCOL_GUID)?; + + Ok(Self { + handle, + service_binding: self.service_binding, + protocol, + flag: AtomicBool::new(false), + }) + } + } + pub(crate) fn connect(&self, timeout: Option) -> io::Result<()> { let evt = unsafe { self.create_evt() }?; let completion_token = @@ -206,7 +248,7 @@ impl Tcp4 { fragment_table: [fragment], }; - self.read_inner((&raw mut rx_data).cast(), timeout).map(|_| data_len as usize) + self.read_inner((&raw mut rx_data).cast(), timeout) } pub(crate) fn read_vectored( @@ -246,14 +288,14 @@ impl Tcp4 { ); }; - self.read_inner(rx_data.as_mut_ptr(), timeout).map(|_| data_length as usize) + self.read_inner(rx_data.as_mut_ptr(), timeout) } pub(crate) fn read_inner( &self, rx_data: *mut tcp4::ReceiveData, timeout: Option, - ) -> io::Result<()> { + ) -> io::Result { let evt = unsafe { self.create_evt() }?; let completion_token = tcp4::CompletionToken { event: evt.as_ptr(), status: Status::SUCCESS }; @@ -271,7 +313,8 @@ impl Tcp4 { if completion_token.status.is_error() { Err(io::Error::from_raw_os_error(completion_token.status.as_usize())) } else { - Ok(()) + let data_length = unsafe { (*rx_data).data_length }; + Ok(data_length as usize) } } @@ -352,6 +395,12 @@ impl Tcp4 { } } +impl Drop for Tcp4 { + fn drop(&mut self) { + let _ = unsafe { self.service_binding.destroy_child(self.handle) }; + } +} + extern "efiapi" fn toggle_atomic_flag(_: r_efi::efi::Event, ctx: *mut crate::ffi::c_void) { let flag = unsafe { AtomicBool::from_ptr(ctx.cast()) }; flag.store(true, Ordering::Relaxed); diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs index 048dafdcd7f7c..95a4ab2fbf0fd 100644 --- a/library/std/src/sys/net/connection/wasip1.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -4,32 +4,31 @@ use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys::fd::WasiFd; -use crate::sys::{err2io, unsupported}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::fd::FileDesc; +use crate::sys::{AsInner, FromInner, IntoInner, err2io, unsupported}; use crate::time::Duration; -pub struct Socket(WasiFd); +pub struct Socket(FileDesc); pub struct TcpStream { inner: Socket, } -impl AsInner for Socket { +impl AsInner for Socket { #[inline] - fn as_inner(&self) -> &WasiFd { + fn as_inner(&self) -> &FileDesc { &self.0 } } -impl IntoInner for Socket { - fn into_inner(self) -> WasiFd { +impl IntoInner for Socket { + fn into_inner(self) -> FileDesc { self.0 } } -impl FromInner for Socket { - fn from_inner(inner: WasiFd) -> Socket { +impl FromInner for Socket { + fn from_inner(inner: FileDesc) -> Socket { Socket(inner) } } @@ -97,7 +96,7 @@ impl TcpStream { } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.socket().as_inner().read(bufs) + self.socket().as_inner().read_vectored(bufs) } pub fn is_read_vectored(&self) -> bool { @@ -109,7 +108,7 @@ impl TcpStream { } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.socket().as_inner().write(bufs) + self.socket().as_inner().write_vectored(bufs) } pub fn is_write_vectored(&self) -> bool { diff --git a/library/std/src/sys/net/hostname/mod.rs b/library/std/src/sys/net/hostname/mod.rs index 8ffe4894d7181..65fcd6bfb00d7 100644 --- a/library/std/src/sys/net/hostname/mod.rs +++ b/library/std/src/sys/net/hostname/mod.rs @@ -3,7 +3,8 @@ cfg_select! { mod unix; pub use unix::hostname; } - target_os = "windows" => { + // `GetHostNameW` is only available starting with Windows 8. + all(target_os = "windows", not(target_vendor = "win7")) => { mod windows; pub use windows::hostname; } diff --git a/library/std/src/sys/net/hostname/unix.rs b/library/std/src/sys/net/hostname/unix.rs index bc6fa82a38f0d..d444182f3fde6 100644 --- a/library/std/src/sys/net/hostname/unix.rs +++ b/library/std/src/sys/net/hostname/unix.rs @@ -1,7 +1,7 @@ use crate::ffi::OsString; use crate::io; use crate::os::unix::ffi::OsStringExt; -use crate::sys::pal::os::errno; +use crate::sys::io::errno; pub fn hostname() -> io::Result { // Query the system for the maximum host name length. diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index 9373982c455fa..5482663ef0079 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -4,11 +4,11 @@ use core::clone::CloneToUninit; use crate::borrow::Cow; +use crate::bstr::ByteStr; use crate::collections::TryReserveError; -use crate::fmt::Write; use crate::rc::Rc; use crate::sync::Arc; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{fmt, mem, str}; #[cfg(test)] @@ -64,25 +64,7 @@ impl fmt::Debug for Slice { impl fmt::Display for Slice { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // If we're the empty string then our iterator won't actually yield - // anything, so perform the formatting manually - if self.inner.is_empty() { - return "".fmt(f); - } - - for chunk in self.inner.utf8_chunks() { - let valid = chunk.valid(); - // If we successfully decoded the whole chunk as a valid string then - // we can return a direct formatting of the string which will also - // respect various formatting flags if possible. - if chunk.invalid().is_empty() { - return valid.fmt(f); - } - - f.write_str(valid)?; - f.write_char(char::REPLACEMENT_CHARACTER)?; - } - Ok(()) + fmt::Display::fmt(ByteStr::new(&self.inner), f) } } @@ -321,12 +303,6 @@ impl Slice { self.inner.clone_into(&mut buf.inner) } - #[inline] - pub fn into_box(&self) -> Box { - let boxed: Box<[u8]> = self.inner.into(); - unsafe { mem::transmute(boxed) } - } - #[inline] pub fn empty_box() -> Box { let boxed: Box<[u8]> = Default::default(); diff --git a/library/std/src/sys/os_str/utf8.rs b/library/std/src/sys/os_str/utf8.rs index 5dd24f67d3039..a324a478325e6 100644 --- a/library/std/src/sys/os_str/utf8.rs +++ b/library/std/src/sys/os_str/utf8.rs @@ -6,7 +6,7 @@ use crate::borrow::Cow; use crate::collections::TryReserveError; use crate::rc::Rc; use crate::sync::Arc; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{fmt, mem}; #[derive(Hash)] diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index 208755cd5b9c7..1f130d91cf393 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -8,7 +8,7 @@ use crate::borrow::Cow; use crate::collections::TryReserveError; use crate::rc::Rc; use crate::sync::Arc; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{fmt, mem}; #[derive(Hash)] @@ -271,11 +271,6 @@ impl Slice { self.inner.clone_into(&mut buf.inner) } - #[inline] - pub fn into_box(&self) -> Box { - unsafe { mem::transmute(self.inner.into_box()) } - } - #[inline] pub fn empty_box() -> Box { unsafe { mem::transmute(Wtf8::empty_box()) } diff --git a/library/std/src/sys/pal/common/mod.rs b/library/std/src/sys/pal/common/mod.rs deleted file mode 100644 index 9af4dee401cf3..0000000000000 --- a/library/std/src/sys/pal/common/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -// This module contains code that is shared between all platforms, mostly utility or fallback code. -// This explicitly does not include code that is shared between only a few platforms, -// such as when reusing an implementation from `unix` or `unsupported`. -// In those cases the desired code should be included directly using the #[path] attribute, -// not moved to this module. -// -// Currently `sys_common` contains a lot of code that should live in this module, -// ideally `sys_common` would only contain platform-independent abstractions on top of `sys`. -// Progress on this is tracked in #84187. - -#![allow(dead_code)] - -pub mod small_c_string; - -#[cfg(test)] -mod tests; diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index 3ddf6e5acb02e..db64f8d882e24 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -16,26 +16,21 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(missing_docs, nonstandard_style)] -use crate::io::ErrorKind; +use crate::io; use crate::os::hermit::hermit_abi; use crate::os::raw::c_char; use crate::sys::env; pub mod futex; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub mod time; -pub fn unsupported() -> crate::io::Result { +pub fn unsupported() -> io::Result { Err(unsupported_err()) } -pub fn unsupported_err() -> crate::io::Error { - crate::io::const_error!( - crate::io::ErrorKind::Unsupported, - "operation not supported on HermitCore yet", - ) +pub fn unsupported_err() -> io::Error { + io::const_error!(io::ErrorKind::Unsupported, "operation not supported on HermitCore yet") } pub fn abort_internal() -> ! { @@ -80,32 +75,6 @@ pub unsafe extern "C" fn runtime_entry( } } -#[inline] -pub(crate) fn is_interrupted(errno: i32) -> bool { - errno == hermit_abi::errno::EINTR -} - -pub fn decode_error_kind(errno: i32) -> ErrorKind { - match errno { - hermit_abi::errno::EACCES => ErrorKind::PermissionDenied, - hermit_abi::errno::EADDRINUSE => ErrorKind::AddrInUse, - hermit_abi::errno::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, - hermit_abi::errno::EAGAIN => ErrorKind::WouldBlock, - hermit_abi::errno::ECONNABORTED => ErrorKind::ConnectionAborted, - hermit_abi::errno::ECONNREFUSED => ErrorKind::ConnectionRefused, - hermit_abi::errno::ECONNRESET => ErrorKind::ConnectionReset, - hermit_abi::errno::EEXIST => ErrorKind::AlreadyExists, - hermit_abi::errno::EINTR => ErrorKind::Interrupted, - hermit_abi::errno::EINVAL => ErrorKind::InvalidInput, - hermit_abi::errno::ENOENT => ErrorKind::NotFound, - hermit_abi::errno::ENOTCONN => ErrorKind::NotConnected, - hermit_abi::errno::EPERM => ErrorKind::PermissionDenied, - hermit_abi::errno::EPIPE => ErrorKind::BrokenPipe, - hermit_abi::errno::ETIMEDOUT => ErrorKind::TimedOut, - _ => ErrorKind::Uncategorized, - } -} - #[doc(hidden)] pub trait IsNegative { fn is_negative(&self) -> bool; @@ -135,16 +104,11 @@ impl IsNegative for i32 { } impl_is_negative! { i8 i16 i64 isize } -pub fn cvt(t: T) -> crate::io::Result { - if t.is_negative() { - let e = decode_error_kind(t.negate()); - Err(crate::io::Error::from(e)) - } else { - Ok(t) - } +pub fn cvt(t: T) -> io::Result { + if t.is_negative() { Err(io::Error::from_raw_os_error(t.negate())) } else { Ok(t) } } -pub fn cvt_r(mut f: F) -> crate::io::Result +pub fn cvt_r(mut f: F) -> io::Result where T: IsNegative, F: FnMut() -> T, diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index 9681964ed9b2f..48a7cdcd2f763 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -5,14 +5,6 @@ use crate::path::{self, PathBuf}; use crate::sys::unsupported; use crate::{fmt, io}; -pub fn errno() -> i32 { - unsafe { hermit_abi::get_errno() } -} - -pub fn error_string(errno: i32) -> String { - hermit_abi::error_string(errno).to_string() -} - pub fn getcwd() -> io::Result { Ok(PathBuf::from("/")) } diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index bd6fd5a3de428..b9851eb6754d6 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -1,31 +1,32 @@ -#![allow(dead_code)] +use hermit_abi::{self, timespec}; -use core::hash::{Hash, Hasher}; - -use super::hermit_abi::{self, CLOCK_MONOTONIC, CLOCK_REALTIME, timespec}; use crate::cmp::Ordering; -use crate::ops::{Add, AddAssign, Sub, SubAssign}; +use crate::hash::{Hash, Hasher}; use crate::time::Duration; const NSEC_PER_SEC: i32 = 1_000_000_000; #[derive(Copy, Clone, Debug)] -struct Timespec { - t: timespec, +pub struct Timespec { + pub t: timespec, } impl Timespec { - const fn zero() -> Timespec { + pub const MAX: Timespec = Self::new(i64::MAX, 1_000_000_000 - 1); + + pub const MIN: Timespec = Self::new(i64::MIN, 0); + + pub const fn zero() -> Timespec { Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } } } - const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec { + pub const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec { assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC); // SAFETY: The assert above checks tv_nsec is within the valid range Timespec { t: timespec { tv_sec, tv_nsec } } } - fn sub_timespec(&self, other: &Timespec) -> Result { + pub fn sub_timespec(&self, other: &Timespec) -> Result { fn sub_ge_to_unsigned(a: i64, b: i64) -> u64 { debug_assert!(a >= b); a.wrapping_sub(b).cast_unsigned() @@ -53,7 +54,7 @@ impl Timespec { } } - fn checked_add_duration(&self, other: &Duration) -> Option { + pub fn checked_add_duration(&self, other: &Duration) -> Option { let mut secs = self.t.tv_sec.checked_add_unsigned(other.as_secs())?; // Nano calculations can't overflow because nanos are <1B which fit @@ -66,7 +67,7 @@ impl Timespec { Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } }) } - fn checked_sub_duration(&self, other: &Duration) -> Option { + pub fn checked_sub_duration(&self, other: &Duration) -> Option { let mut secs = self.t.tv_sec.checked_sub_unsigned(other.as_secs())?; // Similar to above, nanos can't overflow. @@ -107,128 +108,3 @@ impl Hash for Timespec { self.t.tv_nsec.hash(state); } } - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub struct Instant(Timespec); - -impl Instant { - pub fn now() -> Instant { - let mut time: Timespec = Timespec::zero(); - let _ = unsafe { hermit_abi::clock_gettime(CLOCK_MONOTONIC, &raw mut time.t) }; - - Instant(time) - } - - #[stable(feature = "time2", since = "1.8.0")] - pub fn elapsed(&self) -> Duration { - Instant::now() - *self - } - - pub fn duration_since(&self, earlier: Instant) -> Duration { - self.checked_duration_since(earlier).unwrap_or_default() - } - - pub fn checked_duration_since(&self, earlier: Instant) -> Option { - self.checked_sub_instant(&earlier) - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option { - self.0.sub_timespec(&other.0).ok() - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_add_duration(other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(Instant(self.0.checked_sub_duration(other)?)) - } - - pub fn checked_add(&self, duration: Duration) -> Option { - self.0.checked_add_duration(&duration).map(Instant) - } - - pub fn checked_sub(&self, duration: Duration) -> Option { - self.0.checked_sub_duration(&duration).map(Instant) - } -} - -impl Add for Instant { - type Output = Instant; - - /// # Panics - /// - /// This function may panic if the resulting point in time cannot be represented by the - /// underlying data structure. See [`Instant::checked_add`] for a version without panic. - fn add(self, other: Duration) -> Instant { - self.checked_add(other).expect("overflow when adding duration to instant") - } -} - -impl AddAssign for Instant { - fn add_assign(&mut self, other: Duration) { - *self = *self + other; - } -} - -impl Sub for Instant { - type Output = Instant; - - fn sub(self, other: Duration) -> Instant { - self.checked_sub(other).expect("overflow when subtracting duration from instant") - } -} - -impl SubAssign for Instant { - fn sub_assign(&mut self, other: Duration) { - *self = *self - other; - } -} - -impl Sub for Instant { - type Output = Duration; - - /// Returns the amount of time elapsed from another instant to this one, - /// or zero duration if that instant is later than this one. - /// - /// # Panics - /// - /// Previous Rust versions panicked when `other` was later than `self`. Currently this - /// method saturates. Future versions may reintroduce the panic in some circumstances. - /// See [Monotonicity]. - /// - /// [Monotonicity]: Instant#monotonicity - fn sub(self, other: Instant) -> Duration { - self.duration_since(other) - } -} - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct SystemTime(Timespec); - -pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero()); - -impl SystemTime { - pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime { - SystemTime(Timespec::new(tv_sec, tv_nsec)) - } - - pub fn now() -> SystemTime { - let mut time: Timespec = Timespec::zero(); - let _ = unsafe { hermit_abi::clock_gettime(CLOCK_REALTIME, &raw mut time.t) }; - - SystemTime(time) - } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.0.sub_timespec(&other.0) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_add_duration(other)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_sub_duration(other)?)) - } -} diff --git a/library/std/src/sys/pal/itron/error.rs b/library/std/src/sys/pal/itron/error.rs index 8ff3017c61471..87be7d5b3546e 100644 --- a/library/std/src/sys/pal/itron/error.rs +++ b/library/std/src/sys/pal/itron/error.rs @@ -1,6 +1,5 @@ use super::abi; -use crate::fmt; -use crate::io::ErrorKind; +use crate::{fmt, io}; /// Wraps a μITRON error code. #[derive(Debug, Copy, Clone)] @@ -84,39 +83,39 @@ pub fn is_interrupted(er: abi::ER) -> bool { er == abi::E_RLWAI } -pub fn decode_error_kind(er: abi::ER) -> ErrorKind { +pub fn decode_error_kind(er: abi::ER) -> io::ErrorKind { match er { // Success - er if er >= 0 => ErrorKind::Uncategorized, + er if er >= 0 => io::ErrorKind::Uncategorized, // μITRON 4.0 // abi::E_SYS - abi::E_NOSPT => ErrorKind::Unsupported, // Some("unsupported function"), - abi::E_RSFN => ErrorKind::InvalidInput, // Some("reserved function code"), - abi::E_RSATR => ErrorKind::InvalidInput, // Some("reserved attribute"), - abi::E_PAR => ErrorKind::InvalidInput, // Some("parameter error"), - abi::E_ID => ErrorKind::NotFound, // Some("invalid ID number"), + abi::E_NOSPT => io::ErrorKind::Unsupported, // Some("unsupported function"), + abi::E_RSFN => io::ErrorKind::InvalidInput, // Some("reserved function code"), + abi::E_RSATR => io::ErrorKind::InvalidInput, // Some("reserved attribute"), + abi::E_PAR => io::ErrorKind::InvalidInput, // Some("parameter error"), + abi::E_ID => io::ErrorKind::NotFound, // Some("invalid ID number"), // abi::E_CTX - abi::E_MACV => ErrorKind::PermissionDenied, // Some("memory access violation"), - abi::E_OACV => ErrorKind::PermissionDenied, // Some("object access violation"), + abi::E_MACV => io::ErrorKind::PermissionDenied, // Some("memory access violation"), + abi::E_OACV => io::ErrorKind::PermissionDenied, // Some("object access violation"), // abi::E_ILUSE - abi::E_NOMEM => ErrorKind::OutOfMemory, // Some("insufficient memory"), - abi::E_NOID => ErrorKind::OutOfMemory, // Some("no ID number available"), + abi::E_NOMEM => io::ErrorKind::OutOfMemory, // Some("insufficient memory"), + abi::E_NOID => io::ErrorKind::OutOfMemory, // Some("no ID number available"), // abi::E_OBJ - abi::E_NOEXS => ErrorKind::NotFound, // Some("non-existent object"), + abi::E_NOEXS => io::ErrorKind::NotFound, // Some("non-existent object"), // abi::E_QOVR - abi::E_RLWAI => ErrorKind::Interrupted, // Some("forced release from waiting"), - abi::E_TMOUT => ErrorKind::TimedOut, // Some("polling failure or timeout"), + abi::E_RLWAI => io::ErrorKind::Interrupted, // Some("forced release from waiting"), + abi::E_TMOUT => io::ErrorKind::TimedOut, // Some("polling failure or timeout"), // abi::E_DLT // abi::E_CLS // abi::E_WBLK // abi::E_BOVR // The TOPPERS third generation kernels - abi::E_NORES => ErrorKind::OutOfMemory, // Some("insufficient system resources"), + abi::E_NORES => io::ErrorKind::OutOfMemory, // Some("insufficient system resources"), // abi::E_RASTER // abi::E_COMM - _ => ErrorKind::Uncategorized, + _ => io::ErrorKind::Uncategorized, } } diff --git a/library/std/src/sys/pal/itron/time.rs b/library/std/src/sys/pal/itron/time.rs index 7976c27f4952b..ff3cffd2069e9 100644 --- a/library/std/src/sys/pal/itron/time.rs +++ b/library/std/src/sys/pal/itron/time.rs @@ -3,38 +3,16 @@ use super::error::expect_success; use crate::mem::MaybeUninit; use crate::time::Duration; -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -pub struct Instant(abi::SYSTIM); - -impl Instant { - pub fn now() -> Instant { - // Safety: The provided pointer is valid - unsafe { - let mut out = MaybeUninit::uninit(); - expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim"); - Instant(out.assume_init()) - } - } - - pub fn checked_sub_instant(&self, other: &Instant) -> Option { - self.0.checked_sub(other.0).map(|ticks| { - // `SYSTIM` is measured in microseconds - Duration::from_micros(ticks) - }) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - // `SYSTIM` is measured in microseconds - let ticks = other.as_micros(); - - Some(Instant(self.0.checked_add(ticks.try_into().ok()?)?)) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - // `SYSTIM` is measured in microseconds - let ticks = other.as_micros(); +#[cfg(test)] +mod tests; - Some(Instant(self.0.checked_sub(ticks.try_into().ok()?)?)) +#[inline] +pub fn get_tim() -> abi::SYSTIM { + // Safety: The provided pointer is valid + unsafe { + let mut out = MaybeUninit::uninit(); + expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim"); + out.assume_init() } } @@ -98,7 +76,7 @@ pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) - // a problem in practice. (`u64::MAX` μs ≈ 584942 years) let ticks = dur.as_micros().min(abi::SYSTIM::MAX as u128) as abi::SYSTIM; - let start = Instant::now().0; + let start = get_tim(); let mut elapsed = 0; let mut er = abi::E_TMOUT; while elapsed <= ticks { @@ -106,11 +84,8 @@ pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) - if er != abi::E_TMOUT { break; } - elapsed = Instant::now().0.wrapping_sub(start); + elapsed = get_tim().wrapping_sub(start); } er } - -#[cfg(test)] -mod tests; diff --git a/library/std/src/sys/pal/mod.rs b/library/std/src/sys/pal/mod.rs index e11df38a8ee68..88d9d42059900 100644 --- a/library/std/src/sys/pal/mod.rs +++ b/library/std/src/sys/pal/mod.rs @@ -1,29 +1,8 @@ -//! Platform-dependent platform abstraction. -//! -//! The `std::sys` module is the abstracted interface through which -//! `std` talks to the underlying operating system. It has different -//! implementations for different operating system families, today -//! just Unix and Windows, and initial support for Redox. -//! -//! The centralization of platform-specific code in this module is -//! enforced by the "platform abstraction layer" tidy script in -//! `tools/tidy/src/pal.rs`. -//! -//! This module is closely related to the platform-independent system -//! integration code in `std::sys_common`. See that module's -//! documentation for details. -//! -//! In the future it would be desirable for the independent -//! implementations of this module to be extracted to their own crates -//! that `std` can link to, thus enabling their implementation -//! out-of-tree via crate replacement. Though due to the complex -//! inter-dependencies within `std` that will be a challenging goal to -//! achieve. +//! The PAL (platform abstraction layer) contains platform-specific abstractions +//! for implementing the features in the other submodules, such as e.g. bindings. #![allow(missing_debug_implementations)] -pub mod common; - cfg_select! { unix => { mod unix; @@ -53,13 +32,9 @@ cfg_select! { mod vexos; pub use self::vexos::*; } - all(target_os = "wasi", any(target_env = "p2", target_env = "p3")) => { - mod wasip2; - pub use self::wasip2::*; - } - all(target_os = "wasi", target_env = "p1") => { - mod wasip1; - pub use self::wasip1::*; + target_os = "wasi" => { + mod wasi; + pub use self::wasi::*; } target_family = "wasm" => { mod wasm; @@ -90,12 +65,3 @@ cfg_select! { pub use self::unsupported::*; } } - -pub const FULL_BACKTRACE_DEFAULT: bool = cfg_select! { - // Fuchsia components default to full backtrace. - target_os = "fuchsia" => true, - _ => false, -}; - -#[cfg(not(target_os = "uefi"))] -pub type RawOsError = i32; diff --git a/library/std/src/sys/pal/motor/mod.rs b/library/std/src/sys/pal/motor/mod.rs index c64f8ff7a8a83..a520375a4bbff 100644 --- a/library/std/src/sys/pal/motor/mod.rs +++ b/library/std/src/sys/pal/motor/mod.rs @@ -1,16 +1,14 @@ #![allow(unsafe_op_in_unsafe_fn)] pub mod os; -pub mod pipe; -pub mod time; pub use moto_rt::futex; -use crate::io as std_io; -use crate::sys::RawOsError; +use crate::io; -pub(crate) fn map_motor_error(err: moto_rt::ErrorCode) -> crate::io::Error { - crate::io::Error::from_raw_os_error(err.into()) +pub(crate) fn map_motor_error(err: moto_rt::Error) -> io::Error { + let error_code: moto_rt::ErrorCode = err.into(); + io::Error::from_raw_os_error(error_code.into()) } #[cfg(not(test))] @@ -37,39 +35,12 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() {} -pub fn unsupported() -> std_io::Result { +pub fn unsupported() -> io::Result { Err(unsupported_err()) } -pub fn unsupported_err() -> std_io::Error { - std_io::Error::UNSUPPORTED_PLATFORM -} - -pub fn is_interrupted(_code: RawOsError) -> bool { - false // Motor OS doesn't have signals. -} - -pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { - use moto_rt::error::*; - use std_io::ErrorKind; - - if code < 0 || code > u16::MAX.into() { - return std_io::ErrorKind::Uncategorized; - } - - match code as moto_rt::ErrorCode /* u16 */ { - E_ALREADY_IN_USE => ErrorKind::AlreadyExists, - E_INVALID_FILENAME => ErrorKind::InvalidFilename, - E_NOT_FOUND => ErrorKind::NotFound, - E_TIMED_OUT => ErrorKind::TimedOut, - E_NOT_IMPLEMENTED => ErrorKind::Unsupported, - E_FILE_TOO_LARGE => ErrorKind::FileTooLarge, - E_UNEXPECTED_EOF => ErrorKind::UnexpectedEof, - E_INVALID_ARGUMENT => ErrorKind::InvalidInput, - E_NOT_READY => ErrorKind::WouldBlock, - E_NOT_CONNECTED => ErrorKind::NotConnected, - _ => crate::io::ErrorKind::Uncategorized, - } +pub fn unsupported_err() -> io::Error { + io::Error::UNSUPPORTED_PLATFORM } pub fn abort_internal() -> ! { diff --git a/library/std/src/sys/pal/motor/os.rs b/library/std/src/sys/pal/motor/os.rs index 052e3b238b6af..cdf66e3958dbe 100644 --- a/library/std/src/sys/pal/motor/os.rs +++ b/library/std/src/sys/pal/motor/os.rs @@ -4,36 +4,8 @@ use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::os::motor::ffi::OsStrExt; use crate::path::{self, PathBuf}; -use crate::sys::RawOsError; use crate::{fmt, io}; -pub fn errno() -> RawOsError { - // Not used in Motor OS because it is ambiguous: Motor OS - // is micro-kernel-based, and I/O happens via a shared-memory - // ring buffer, so an I/O operation that on a unix is a syscall - // may involve no sycalls on Motor OS at all, or a syscall - // that e.g. waits for a notification from the I/O driver - // (sys-io); and the wait syscall may succeed, but the - // driver may report an I/O error; or a bunch of results - // for several I/O operations, some successful and some - // not. - // - // Also I/O operations in a Motor OS process are handled by a - // separate runtime background/I/O thread, so it is really hard - // to define what "last system error in the current thread" - // actually means. - moto_rt::E_UNKNOWN.into() -} - -pub fn error_string(errno: RawOsError) -> String { - let error_code: moto_rt::ErrorCode = match errno { - x if x < 0 => moto_rt::E_UNKNOWN, - x if x > u16::MAX.into() => moto_rt::E_UNKNOWN, - x => x as moto_rt::ErrorCode, /* u16 */ - }; - format!("{}", moto_rt::Error::from(error_code)) -} - pub fn getcwd() -> io::Result { moto_rt::fs::getcwd().map(PathBuf::from).map_err(map_motor_error) } diff --git a/library/std/src/sys/pal/motor/pipe.rs b/library/std/src/sys/pal/motor/pipe.rs deleted file mode 100644 index d3be6ddf1573e..0000000000000 --- a/library/std/src/sys/pal/motor/pipe.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys::fd::FileDesc; -use crate::sys::map_motor_error; -use crate::sys_common::{FromInner, IntoInner}; - -#[derive(Debug)] -pub struct AnonPipe(FileDesc); - -impl From for AnonPipe { - fn from(rt_fd: moto_rt::RtFd) -> AnonPipe { - unsafe { AnonPipe::from_raw_fd(rt_fd) } - } -} - -impl AnonPipe { - pub fn read(&self, buf: &mut [u8]) -> io::Result { - moto_rt::fs::read(self.as_raw_fd(), buf).map_err(map_motor_error) - } - - pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { - crate::io::default_read_buf(|buf| self.read(buf), cursor) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - crate::io::default_read_vectored(|b| self.read(b), bufs) - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - moto_rt::fs::write(self.as_raw_fd(), buf).map_err(map_motor_error) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - crate::io::default_write_vectored(|b| self.write(b), bufs) - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { - let mut temp_vec = Vec::new(); - let mut size = 0_usize; - loop { - temp_vec.resize(256, 0_u8); - match self.read(&mut temp_vec[..]) { - Ok(sz) => { - if sz == 0 { - return Ok(size); - } - size += sz; - temp_vec.truncate(sz); - buf.append(&mut temp_vec); - } - Err(err) => { - if size != 0 { - return Ok(size); - } else { - return Err(err); - } - } - } - } - } -} - -impl AsRawFd for AnonPipe { - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } -} - -impl FromRawFd for AnonPipe { - unsafe fn from_raw_fd(fd: RawFd) -> Self { - let desc = FileDesc::from_raw_fd(fd); - Self(desc) - } -} - -impl IntoRawFd for AnonPipe { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } -} - -impl AsFd for AnonPipe { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } -} - -impl IntoInner for AnonPipe { - fn into_inner(self) -> OwnedFd { - self.0.into_inner() - } -} - -impl IntoInner for AnonPipe { - fn into_inner(self) -> FileDesc { - self.0 - } -} - -impl FromInner for AnonPipe { - fn from_inner(owned_fd: OwnedFd) -> Self { - Self(FileDesc::from_inner(owned_fd)) - } -} - -pub fn read2(_p1: AnonPipe, _v1: &mut Vec, _p2: AnonPipe, _v2: &mut Vec) -> io::Result<()> { - Err(io::Error::from_raw_os_error(moto_rt::E_NOT_IMPLEMENTED.into())) -} - -#[inline] -pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { - Err(io::Error::UNSUPPORTED_PLATFORM) -} diff --git a/library/std/src/sys/pal/motor/time.rs b/library/std/src/sys/pal/motor/time.rs deleted file mode 100644 index e917fd466c2e4..0000000000000 --- a/library/std/src/sys/pal/motor/time.rs +++ /dev/null @@ -1 +0,0 @@ -pub use moto_rt::time::{Instant, SystemTime, UNIX_EPOCH}; diff --git a/library/std/src/sys/pal/sgx/abi/mod.rs b/library/std/src/sys/pal/sgx/abi/mod.rs index b8c4d7740c4e1..1c6c681d4c179 100644 --- a/library/std/src/sys/pal/sgx/abi/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/mod.rs @@ -3,6 +3,7 @@ use core::arch::global_asm; use core::sync::atomic::{Atomic, AtomicUsize, Ordering}; +use crate::alloc::System; use crate::io::Write; // runtime features @@ -63,7 +64,9 @@ unsafe extern "C" fn tcs_init(secondary: bool) { #[unsafe(no_mangle)] extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> EntryReturn { // FIXME: how to support TLS in library mode? - let tls = Box::new(tls::Tls::new()); + // We use the System allocator here such that the global allocator may use + // thread-locals. + let tls = Box::new_in(tls::Tls::new(), System); let tls_guard = unsafe { tls.activate() }; if secondary { diff --git a/library/std/src/sys/pal/sgx/abi/tls/mod.rs b/library/std/src/sys/pal/sgx/abi/tls/mod.rs index 41e38b6961680..553814dcb5fda 100644 --- a/library/std/src/sys/pal/sgx/abi/tls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/tls/mod.rs @@ -89,13 +89,6 @@ impl Tls { ActiveTls { tls: self } } - #[allow(unused)] - pub unsafe fn activate_persistent(self: Box) { - // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition. - let ptr = Box::into_raw(self).cast_const().cast::(); - unsafe { set_tls_ptr(ptr) }; - } - unsafe fn current<'a>() -> &'a Tls { // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition. unsafe { &*(get_tls_ptr() as *const Tls) } diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs index fb410c2851604..aaf380d1bef47 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs @@ -773,7 +773,7 @@ where #[unstable(feature = "sgx_platform", issue = "56975")] impl, U> CoerceUnsized> for UserRef {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for UserRef {} #[unstable(feature = "sgx_platform", issue = "56975")] diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs index 5041770faf661..f49e940c5044c 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/mod.rs @@ -1,7 +1,5 @@ use crate::cmp; -use crate::io::{ - BorrowedCursor, Error as IoError, ErrorKind, IoSlice, IoSliceMut, Result as IoResult, -}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::random::random; use crate::time::{Duration, Instant}; @@ -18,7 +16,7 @@ use self::raw::*; /// This will do a single `read` usercall and scatter the read data among /// `bufs`. To read to a single buffer, just pass a slice of length one. #[unstable(feature = "sgx_platform", issue = "56975")] -pub fn read(fd: Fd, bufs: &mut [IoSliceMut<'_>]) -> IoResult { +pub fn read(fd: Fd, bufs: &mut [IoSliceMut<'_>]) -> io::Result { unsafe { let total_len = bufs.iter().fold(0usize, |sum, buf| sum.saturating_add(buf.len())); let mut userbuf = alloc::User::<[u8]>::uninitialized(total_len); @@ -41,7 +39,7 @@ pub fn read(fd: Fd, bufs: &mut [IoSliceMut<'_>]) -> IoResult { /// Usercall `read` with an uninitialized buffer. See the ABI documentation for /// more information. #[unstable(feature = "sgx_platform", issue = "56975")] -pub fn read_buf(fd: Fd, mut buf: BorrowedCursor<'_>) -> IoResult<()> { +pub fn read_buf(fd: Fd, mut buf: BorrowedCursor<'_>) -> io::Result<()> { unsafe { let mut userbuf = alloc::User::<[u8]>::uninitialized(buf.capacity()); let len = raw::read(fd, userbuf.as_mut_ptr().cast(), userbuf.len()).from_sgx_result()?; @@ -53,7 +51,7 @@ pub fn read_buf(fd: Fd, mut buf: BorrowedCursor<'_>) -> IoResult<()> { /// Usercall `read_alloc`. See the ABI documentation for more information. #[unstable(feature = "sgx_platform", issue = "56975")] -pub fn read_alloc(fd: Fd) -> IoResult> { +pub fn read_alloc(fd: Fd) -> io::Result> { unsafe { let userbuf = ByteBuffer { data: crate::ptr::null_mut(), len: 0 }; let mut userbuf = alloc::User::new_from_enclave(&userbuf); @@ -67,7 +65,7 @@ pub fn read_alloc(fd: Fd) -> IoResult> { /// This will do a single `write` usercall and gather the written data from /// `bufs`. To write from a single buffer, just pass a slice of length one. #[unstable(feature = "sgx_platform", issue = "56975")] -pub fn write(fd: Fd, bufs: &[IoSlice<'_>]) -> IoResult { +pub fn write(fd: Fd, bufs: &[IoSlice<'_>]) -> io::Result { unsafe { let total_len = bufs.iter().fold(0usize, |sum, buf| sum.saturating_add(buf.len())); let mut userbuf = alloc::User::<[u8]>::uninitialized(total_len); @@ -87,7 +85,7 @@ pub fn write(fd: Fd, bufs: &[IoSlice<'_>]) -> IoResult { /// Usercall `flush`. See the ABI documentation for more information. #[unstable(feature = "sgx_platform", issue = "56975")] -pub fn flush(fd: Fd) -> IoResult<()> { +pub fn flush(fd: Fd) -> io::Result<()> { unsafe { raw::flush(fd).from_sgx_result() } } @@ -104,7 +102,7 @@ fn string_from_bytebuffer(buf: &alloc::UserRef, usercall: &str, arg: /// Usercall `bind_stream`. See the ABI documentation for more information. #[unstable(feature = "sgx_platform", issue = "56975")] -pub fn bind_stream(addr: &str) -> IoResult<(Fd, String)> { +pub fn bind_stream(addr: &str) -> io::Result<(Fd, String)> { unsafe { let addr_user = alloc::User::new_from_enclave(addr.as_bytes()); let mut local = alloc::User::::uninitialized(); @@ -117,7 +115,7 @@ pub fn bind_stream(addr: &str) -> IoResult<(Fd, String)> { /// Usercall `accept_stream`. See the ABI documentation for more information. #[unstable(feature = "sgx_platform", issue = "56975")] -pub fn accept_stream(fd: Fd) -> IoResult<(Fd, String, String)> { +pub fn accept_stream(fd: Fd) -> io::Result<(Fd, String, String)> { unsafe { let mut bufs = alloc::User::<[ByteBuffer; 2]>::uninitialized(); let mut buf_it = alloc::UserRef::iter_mut(&mut *bufs); // FIXME: can this be done @@ -133,7 +131,7 @@ pub fn accept_stream(fd: Fd) -> IoResult<(Fd, String, String)> { /// Usercall `connect_stream`. See the ABI documentation for more information. #[unstable(feature = "sgx_platform", issue = "56975")] -pub fn connect_stream(addr: &str) -> IoResult<(Fd, String, String)> { +pub fn connect_stream(addr: &str) -> io::Result<(Fd, String, String)> { unsafe { let addr_user = alloc::User::new_from_enclave(addr.as_bytes()); let mut bufs = alloc::User::<[ByteBuffer; 2]>::uninitialized(); @@ -155,7 +153,7 @@ pub fn connect_stream(addr: &str) -> IoResult<(Fd, String, String)> { /// Usercall `launch_thread`. See the ABI documentation for more information. #[unstable(feature = "sgx_platform", issue = "56975")] -pub unsafe fn launch_thread() -> IoResult<()> { +pub unsafe fn launch_thread() -> io::Result<()> { // SAFETY: The caller must uphold the safety contract for `launch_thread`. unsafe { raw::launch_thread().from_sgx_result() } } @@ -168,7 +166,7 @@ pub fn exit(panic: bool) -> ! { /// Usercall `wait`. See the ABI documentation for more information. #[unstable(feature = "sgx_platform", issue = "56975")] -pub fn wait(event_mask: u64, mut timeout: u64) -> IoResult { +pub fn wait(event_mask: u64, mut timeout: u64) -> io::Result { if timeout != WAIT_NO && timeout != WAIT_INDEFINITE { // We don't want people to rely on accuracy of timeouts to make // security decisions in an SGX enclave. That's why we add a random @@ -216,7 +214,9 @@ where true } Err(e) => { - rtassert!(e.kind() == ErrorKind::TimedOut || e.kind() == ErrorKind::WouldBlock); + rtassert!( + e.kind() == io::ErrorKind::TimedOut || e.kind() == io::ErrorKind::WouldBlock + ); false } } @@ -260,7 +260,7 @@ where /// Usercall `send`. See the ABI documentation for more information. #[unstable(feature = "sgx_platform", issue = "56975")] -pub fn send(event_set: u64, tcs: Option) -> IoResult<()> { +pub fn send(event_set: u64, tcs: Option) -> io::Result<()> { unsafe { raw::send(event_set, tcs).from_sgx_result() } } @@ -273,7 +273,7 @@ pub fn insecure_time() -> Duration { /// Usercall `alloc`. See the ABI documentation for more information. #[unstable(feature = "sgx_platform", issue = "56975")] -pub fn alloc(size: usize, alignment: usize) -> IoResult<*mut u8> { +pub fn alloc(size: usize, alignment: usize) -> io::Result<*mut u8> { unsafe { raw::alloc(size, alignment).from_sgx_result() } } @@ -316,18 +316,18 @@ pub trait FromSgxResult { type Return; /// Translate the raw result of an SGX usercall. - fn from_sgx_result(self) -> IoResult; + fn from_sgx_result(self) -> io::Result; } #[unstable(feature = "sgx_platform", issue = "56975")] impl FromSgxResult for (Result, T) { type Return = T; - fn from_sgx_result(self) -> IoResult { + fn from_sgx_result(self) -> io::Result { if self.0 == RESULT_SUCCESS { Ok(self.1) } else { - Err(IoError::from_raw_os_error(check_os_error(self.0))) + Err(io::Error::from_raw_os_error(check_os_error(self.0))) } } } @@ -336,11 +336,11 @@ impl FromSgxResult for (Result, T) { impl FromSgxResult for Result { type Return = (); - fn from_sgx_result(self) -> IoResult { + fn from_sgx_result(self) -> io::Result { if self == RESULT_SUCCESS { Ok(()) } else { - Err(IoError::from_raw_os_error(check_os_error(self))) + Err(io::Error::from_raw_os_error(check_os_error(self))) } } } diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 9a33873af581f..1de3ca4a5d79c 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -5,16 +5,13 @@ #![deny(unsafe_op_in_unsafe_fn)] #![allow(fuzzy_provenance_casts)] // FIXME: this entire module systematically confuses pointers and integers -use crate::io::ErrorKind; +use crate::io; use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; pub mod abi; mod libunwind_integration; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub mod thread_parking; -pub mod time; pub mod waitqueue; // SAFETY: must be called only once during runtime initialization. @@ -31,12 +28,12 @@ pub unsafe fn cleanup() {} /// This function is used to implement functionality that simply doesn't exist. /// Programs relying on this functionality will need to deal with the error. -pub fn unsupported() -> crate::io::Result { +pub fn unsupported() -> io::Result { Err(unsupported_err()) } -pub fn unsupported_err() -> crate::io::Error { - crate::io::const_error!(ErrorKind::Unsupported, "operation not supported on SGX yet") +pub fn unsupported_err() -> io::Error { + io::const_error!(io::ErrorKind::Unsupported, "operation not supported on SGX yet") } /// This function is used to implement various functions that doesn't exist, @@ -44,11 +41,11 @@ pub fn unsupported_err() -> crate::io::Error { /// returned, the program might very well be able to function normally. This is /// what happens when `SGX_INEFFECTIVE_ERROR` is set to `true`. If it is /// `false`, the behavior is the same as `unsupported`. -pub fn sgx_ineffective(v: T) -> crate::io::Result { +pub fn sgx_ineffective(v: T) -> io::Result { static SGX_INEFFECTIVE_ERROR: Atomic = AtomicBool::new(false); if SGX_INEFFECTIVE_ERROR.load(Ordering::Relaxed) { - Err(crate::io::const_error!( - ErrorKind::Uncategorized, + Err(io::const_error!( + io::ErrorKind::Uncategorized, "operation can't be trusted to have any effect on SGX", )) } else { @@ -56,56 +53,6 @@ pub fn sgx_ineffective(v: T) -> crate::io::Result { } } -#[inline] -pub fn is_interrupted(code: i32) -> bool { - code == fortanix_sgx_abi::Error::Interrupted as _ -} - -pub fn decode_error_kind(code: i32) -> ErrorKind { - use fortanix_sgx_abi::Error; - - // FIXME: not sure how to make sure all variants of Error are covered - if code == Error::NotFound as _ { - ErrorKind::NotFound - } else if code == Error::PermissionDenied as _ { - ErrorKind::PermissionDenied - } else if code == Error::ConnectionRefused as _ { - ErrorKind::ConnectionRefused - } else if code == Error::ConnectionReset as _ { - ErrorKind::ConnectionReset - } else if code == Error::ConnectionAborted as _ { - ErrorKind::ConnectionAborted - } else if code == Error::NotConnected as _ { - ErrorKind::NotConnected - } else if code == Error::AddrInUse as _ { - ErrorKind::AddrInUse - } else if code == Error::AddrNotAvailable as _ { - ErrorKind::AddrNotAvailable - } else if code == Error::BrokenPipe as _ { - ErrorKind::BrokenPipe - } else if code == Error::AlreadyExists as _ { - ErrorKind::AlreadyExists - } else if code == Error::WouldBlock as _ { - ErrorKind::WouldBlock - } else if code == Error::InvalidInput as _ { - ErrorKind::InvalidInput - } else if code == Error::InvalidData as _ { - ErrorKind::InvalidData - } else if code == Error::TimedOut as _ { - ErrorKind::TimedOut - } else if code == Error::WriteZero as _ { - ErrorKind::WriteZero - } else if code == Error::Interrupted as _ { - ErrorKind::Interrupted - } else if code == Error::Other as _ { - ErrorKind::Uncategorized - } else if code == Error::UnexpectedEof as _ { - ErrorKind::UnexpectedEof - } else { - ErrorKind::Uncategorized - } -} - pub fn abort_internal() -> ! { abi::usercalls::exit(true) } @@ -122,8 +69,6 @@ pub extern "C" fn __rust_abort() { abort_internal(); } -pub use crate::sys_common::{AsInner, FromInner, IntoInner}; - pub trait TryIntoInner: Sized { fn try_into_inner(self) -> Result; } diff --git a/library/std/src/sys/pal/sgx/os.rs b/library/std/src/sys/pal/sgx/os.rs index 28d79963ac874..ba47af7ff88d7 100644 --- a/library/std/src/sys/pal/sgx/os.rs +++ b/library/std/src/sys/pal/sgx/os.rs @@ -1,25 +1,9 @@ -use fortanix_sgx_abi::{Error, RESULT_SUCCESS}; - use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; -use crate::sys::{decode_error_kind, sgx_ineffective, unsupported}; +use crate::sys::{sgx_ineffective, unsupported}; use crate::{fmt, io}; -pub fn errno() -> i32 { - RESULT_SUCCESS -} - -pub fn error_string(errno: i32) -> String { - if errno == RESULT_SUCCESS { - "operation successful".into() - } else if ((Error::UserRangeStart as _)..=(Error::UserRangeEnd as _)).contains(&errno) { - format!("user-specified error {errno:08x}") - } else { - decode_error_kind(errno).as_str().into() - } -} - pub fn getcwd() -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/sgx/thread_parking.rs b/library/std/src/sys/pal/sgx/thread_parking.rs index 660624ea9c3b8..6510a3ab211f1 100644 --- a/library/std/src/sys/pal/sgx/thread_parking.rs +++ b/library/std/src/sys/pal/sgx/thread_parking.rs @@ -1,7 +1,7 @@ use fortanix_sgx_abi::{EV_UNPARK, WAIT_INDEFINITE}; use super::abi::usercalls; -use crate::io::ErrorKind; +use crate::io; use crate::time::Duration; pub type ThreadId = fortanix_sgx_abi::Tcs; @@ -15,7 +15,7 @@ pub fn park(_hint: usize) { pub fn park_timeout(dur: Duration, _hint: usize) { let timeout = u128::min(dur.as_nanos(), WAIT_INDEFINITE as u128 - 1) as u64; if let Err(e) = usercalls::wait(EV_UNPARK, timeout) { - assert!(matches!(e.kind(), ErrorKind::TimedOut | ErrorKind::WouldBlock)) + assert!(matches!(e.kind(), io::ErrorKind::TimedOut | io::ErrorKind::WouldBlock)) } } diff --git a/library/std/src/sys/pal/solid/error.rs b/library/std/src/sys/pal/solid/error.rs index b399463c0c280..3e85cdb3c1d9f 100644 --- a/library/std/src/sys/pal/solid/error.rs +++ b/library/std/src/sys/pal/solid/error.rs @@ -1,6 +1,6 @@ pub use self::itron::error::{ItronError as SolidError, expect_success}; use super::{abi, itron}; -use crate::io::ErrorKind; +use crate::io; use crate::sys::net; /// Describe the specified SOLID error code. Returns `None` if it's an @@ -31,23 +31,23 @@ pub fn error_name(er: abi::ER) -> Option<&'static str> { } } -pub fn decode_error_kind(er: abi::ER) -> ErrorKind { +pub fn decode_error_kind(er: abi::ER) -> io::ErrorKind { match er { // Success - er if er >= 0 => ErrorKind::Uncategorized, + er if er >= 0 => io::ErrorKind::Uncategorized, er if er < abi::sockets::SOLID_NET_ERR_BASE => net::decode_error_kind(er), - abi::SOLID_ERR_NOTFOUND => ErrorKind::NotFound, - abi::SOLID_ERR_NOTSUPPORTED => ErrorKind::Unsupported, - abi::SOLID_ERR_EBADF => ErrorKind::InvalidInput, - abi::SOLID_ERR_INVALIDCONTENT => ErrorKind::InvalidData, + abi::SOLID_ERR_NOTFOUND => io::ErrorKind::NotFound, + abi::SOLID_ERR_NOTSUPPORTED => io::ErrorKind::Unsupported, + abi::SOLID_ERR_EBADF => io::ErrorKind::InvalidInput, + abi::SOLID_ERR_INVALIDCONTENT => io::ErrorKind::InvalidData, // abi::SOLID_ERR_NOTUSED // abi::SOLID_ERR_ALREADYUSED - abi::SOLID_ERR_OUTOFBOUND => ErrorKind::InvalidInput, + abi::SOLID_ERR_OUTOFBOUND => io::ErrorKind::InvalidInput, // abi::SOLID_ERR_BADSEQUENCE - abi::SOLID_ERR_UNKNOWNDEVICE => ErrorKind::NotFound, + abi::SOLID_ERR_UNKNOWNDEVICE => io::ErrorKind::NotFound, // abi::SOLID_ERR_BUSY - abi::SOLID_ERR_TIMEOUT => ErrorKind::TimedOut, + abi::SOLID_ERR_TIMEOUT => io::ErrorKind::TimedOut, // abi::SOLID_ERR_INVALIDACCESS // abi::SOLID_ERR_NOTREADY _ => itron::error::decode_error_kind(er), diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 9ca6dc581183d..1376af8304cf6 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -2,6 +2,8 @@ #![allow(missing_docs, nonstandard_style)] #![forbid(unsafe_op_in_unsafe_fn)] +use crate::io; + pub mod abi; #[path = "../itron"] @@ -18,10 +20,7 @@ pub mod itron { // `crate::sys::error` pub(crate) mod error; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; pub use self::itron::thread_parking; -pub mod time; // SAFETY: must be called only once during runtime initialization. // NOTE: this is not guaranteed to run, for example when Rust code is called externally. @@ -30,21 +29,12 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {} // SAFETY: must be called only once during runtime cleanup. pub unsafe fn cleanup() {} -pub fn unsupported() -> crate::io::Result { +pub fn unsupported() -> io::Result { Err(unsupported_err()) } -pub fn unsupported_err() -> crate::io::Error { - crate::io::Error::UNSUPPORTED_PLATFORM -} - -#[inline] -pub fn is_interrupted(code: i32) -> bool { - crate::sys::net::is_interrupted(code) -} - -pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { - error::decode_error_kind(code) +pub fn unsupported_err() -> io::Error { + io::Error::UNSUPPORTED_PLATFORM } #[inline] diff --git a/library/std/src/sys/pal/solid/os.rs b/library/std/src/sys/pal/solid/os.rs index cb6e2cbceae6c..c336a1042da40 100644 --- a/library/std/src/sys/pal/solid/os.rs +++ b/library/std/src/sys/pal/solid/os.rs @@ -1,4 +1,4 @@ -use super::{error, itron, unsupported}; +use super::{itron, unsupported}; use crate::ffi::{OsStr, OsString}; use crate::path::{self, PathBuf}; use crate::{fmt, io}; @@ -11,14 +11,6 @@ impl itron::error::ItronError { } } -pub fn errno() -> i32 { - 0 -} - -pub fn error_string(errno: i32) -> String { - if let Some(name) = error::error_name(errno) { name.to_owned() } else { format!("{errno}") } -} - pub fn getcwd() -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index dd0155265da63..f76c26d3c966c 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -7,9 +7,6 @@ #![allow(dead_code)] pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; -#[allow(non_upper_case_globals)] #[path = "../unix/time.rs"] pub mod time; @@ -21,7 +18,7 @@ pub mod sync { pub use mutex::Mutex; } -use crate::io::ErrorKind; +use crate::io; pub fn abort_internal() -> ! { unsafe { libc::abort() } @@ -40,61 +37,6 @@ pub unsafe fn cleanup() { // stack_overflow::cleanup(); } -#[inline] -pub(crate) fn is_interrupted(errno: i32) -> bool { - errno == libc::EINTR -} - -// Note: code below is 1:1 copied from unix/mod.rs -pub fn decode_error_kind(errno: i32) -> ErrorKind { - use ErrorKind::*; - match errno as libc::c_int { - libc::E2BIG => ArgumentListTooLong, - libc::EADDRINUSE => AddrInUse, - libc::EADDRNOTAVAIL => AddrNotAvailable, - libc::EBUSY => ResourceBusy, - libc::ECONNABORTED => ConnectionAborted, - libc::ECONNREFUSED => ConnectionRefused, - libc::ECONNRESET => ConnectionReset, - libc::EDEADLK => Deadlock, - libc::EDQUOT => QuotaExceeded, - libc::EEXIST => AlreadyExists, - libc::EFBIG => FileTooLarge, - libc::EHOSTUNREACH => HostUnreachable, - libc::EINTR => Interrupted, - libc::EINVAL => InvalidInput, - libc::EISDIR => IsADirectory, - libc::ELOOP => FilesystemLoop, - libc::ENOENT => NotFound, - libc::ENOMEM => OutOfMemory, - libc::ENOSPC => StorageFull, - libc::ENOSYS => Unsupported, - libc::EMLINK => TooManyLinks, - libc::ENAMETOOLONG => InvalidFilename, - libc::ENETDOWN => NetworkDown, - libc::ENETUNREACH => NetworkUnreachable, - libc::ENOTCONN => NotConnected, - libc::ENOTDIR => NotADirectory, - libc::ENOTEMPTY => DirectoryNotEmpty, - libc::EPIPE => BrokenPipe, - libc::EROFS => ReadOnlyFilesystem, - libc::ESPIPE => NotSeekable, - libc::ESTALE => StaleNetworkFileHandle, - libc::ETIMEDOUT => TimedOut, - libc::ETXTBSY => ExecutableFileBusy, - libc::EXDEV => CrossesDevices, - - libc::EACCES | libc::EPERM => PermissionDenied, - - // These two constants can have the same value on some systems, - // but different values on others, so we can't use a match - // clause - x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, - - _ => Uncategorized, - } -} - #[doc(hidden)] pub trait IsMinusOne { fn is_minus_one(&self) -> bool; @@ -110,32 +52,31 @@ macro_rules! impl_is_minus_one { impl_is_minus_one! { i8 i16 i32 i64 isize } -pub fn cvt(t: T) -> crate::io::Result { - if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } +pub fn cvt(t: T) -> io::Result { + if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) } } -pub fn cvt_r(mut f: F) -> crate::io::Result +pub fn cvt_r(mut f: F) -> io::Result where T: IsMinusOne, F: FnMut() -> T, { loop { match cvt(f()) { - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} other => return other, } } } -pub fn cvt_nz(error: libc::c_int) -> crate::io::Result<()> { - if error == 0 { Ok(()) } else { Err(crate::io::Error::from_raw_os_error(error)) } +pub fn cvt_nz(error: libc::c_int) -> io::Result<()> { + if error == 0 { Ok(()) } else { Err(io::Error::from_raw_os_error(error)) } } -use crate::io as std_io; -pub fn unsupported() -> std_io::Result { +pub fn unsupported() -> io::Result { Err(unsupported_err()) } -pub fn unsupported_err() -> std_io::Error { - std_io::Error::UNSUPPORTED_PLATFORM +pub fn unsupported_err() -> io::Error { + io::Error::UNSUPPORTED_PLATFORM } diff --git a/library/std/src/sys/pal/teeos/os.rs b/library/std/src/sys/pal/teeos/os.rs index 512b3e2885bf0..a4b1d3c6ae670 100644 --- a/library/std/src/sys/pal/teeos/os.rs +++ b/library/std/src/sys/pal/teeos/os.rs @@ -7,10 +7,6 @@ use crate::ffi::{OsStr, OsString}; use crate::path::PathBuf; use crate::{fmt, io, path}; -pub fn errno() -> i32 { - unsafe { (*libc::__errno_location()) as i32 } -} - // Hardcoded to return 4096, since `sysconf` is only implemented as a stub. pub fn page_size() -> usize { // unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }; @@ -19,10 +15,6 @@ pub fn page_size() -> usize { // Everything below are stubs and copied from unsupported.rs -pub fn error_string(_errno: i32) -> String { - "error string unimplemented".to_string() -} - pub fn getcwd() -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/trusty/mod.rs b/library/std/src/sys/pal/trusty/mod.rs index cf0c098f8a2f3..b785c2dbb7892 100644 --- a/library/std/src/sys/pal/trusty/mod.rs +++ b/library/std/src/sys/pal/trusty/mod.rs @@ -5,9 +5,5 @@ mod common; #[path = "../unsupported/os.rs"] pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; -#[path = "../unsupported/time.rs"] -pub mod time; pub use common::*; diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index bfad6491e3219..9db72db606779 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -10,7 +10,7 @@ //! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) use r_efi::efi::{self, Guid}; -use r_efi::protocols::{device_path, device_path_to_text, service_binding, shell}; +use r_efi::protocols::{device_path, device_path_to_text, file, service_binding, shell}; use crate::alloc::Layout; use crate::ffi::{OsStr, OsString}; @@ -24,7 +24,7 @@ use crate::path::Path; use crate::ptr::NonNull; use crate::slice; use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; -use crate::sys_common::wstr::WStrUnits; +use crate::sys::helpers::WStrUnits; type BootInstallMultipleProtocolInterfaces = unsafe extern "efiapi" fn(_: *mut r_efi::efi::Handle, _: ...) -> r_efi::efi::Status; @@ -651,34 +651,38 @@ pub(crate) fn get_device_path_from_map(map: &Path) -> io::Result, - child_handle: NonNull, } impl ServiceProtocol { - pub(crate) fn open(service_guid: r_efi::efi::Guid) -> io::Result { + /// Open a child handle on a service_binding protocol. + pub(crate) fn open( + service_guid: r_efi::efi::Guid, + ) -> io::Result<(Self, NonNull)> { let handles = locate_handles(service_guid)?; for handle in handles { if let Ok(protocol) = open_protocol::(handle, service_guid) { - let Ok(child_handle) = Self::create_child(protocol) else { - continue; - }; - - return Ok(Self { service_guid, handle, child_handle }); + if let Ok(child_handle) = unsafe { Self::create_child(protocol) } { + return Ok((Self { service_guid, handle }, child_handle)); + } } } Err(io::const_error!(io::ErrorKind::NotFound, "no service binding protocol found")) } - pub(crate) fn child_handle(&self) -> NonNull { - self.child_handle - } - - fn create_child( + // SAFETY: sbp must be a valid service binding protocol pointer + unsafe fn create_child( sbp: NonNull, ) -> io::Result> { let mut child_handle: r_efi::efi::Handle = crate::ptr::null_mut(); @@ -692,17 +696,17 @@ impl ServiceProtocol { .ok_or(const_error!(io::ErrorKind::Other, "null child handle")) } } -} -impl Drop for ServiceProtocol { - fn drop(&mut self) { - if let Ok(sbp) = open_protocol::(self.handle, self.service_guid) - { - // SAFETY: Child handle must be allocated by the current service binding protocol. - let _ = unsafe { - ((*sbp.as_ptr()).destroy_child)(sbp.as_ptr(), self.child_handle.as_ptr()) - }; - } + // SAFETY: Child handle must be allocated by the current service binding protocol and must be + // valid. + pub(crate) unsafe fn destroy_child( + &self, + handle: NonNull, + ) -> io::Result<()> { + let sbp = open_protocol::(self.handle, self.service_guid)?; + + let r = unsafe { ((*sbp.as_ptr()).destroy_child)(sbp.as_ptr(), handle.as_ptr()) }; + if r.is_error() { Err(crate::io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } } @@ -787,7 +791,7 @@ impl UefiBox { match NonNull::new(ptr.cast()) { Some(inner) => Ok(Self { inner, size: len }), - None => Err(io::Error::new(io::ErrorKind::OutOfMemory, "Allocation failed")), + None => Err(const_error!(io::ErrorKind::OutOfMemory, "Allocation failed")), } } @@ -802,6 +806,10 @@ impl UefiBox { pub(crate) fn as_ptr(&self) -> *const T { self.inner.as_ptr().cast() } + + pub(crate) const fn len(&self) -> usize { + self.size + } } impl Drop for UefiBox { @@ -810,3 +818,58 @@ impl Drop for UefiBox { unsafe { crate::alloc::dealloc(self.inner.as_ptr().cast(), layout) }; } } + +impl UefiBox { + fn size(&self) -> u64 { + unsafe { (*self.as_ptr()).size } + } + + fn set_size(&mut self, s: u64) { + unsafe { (*self.as_mut_ptr()).size = s } + } + + // Length of string (including NULL), not number of bytes. + fn file_name_len(&self) -> usize { + (self.size() as usize - size_of::>()) / size_of::() + } + + pub(crate) fn file_name(&self) -> &[u16] { + unsafe { + crate::slice::from_raw_parts((*self.as_ptr()).file_name.as_ptr(), self.file_name_len()) + } + } + + fn file_name_mut(&mut self) -> &mut [u16] { + unsafe { + crate::slice::from_raw_parts_mut( + (*self.as_mut_ptr()).file_name.as_mut_ptr(), + self.file_name_len(), + ) + } + } + + pub(crate) fn with_file_name(mut self, name: &OsStr) -> io::Result { + // os_string_to_raw returns NULL terminated string. So no need to handle it separately. + let fname = os_string_to_raw(name) + .ok_or(const_error!(io::ErrorKind::OutOfMemory, "Allocation failed"))?; + let new_size = size_of::>() + fname.len() * size_of::(); + + // Reuse the current structure if the new name can fit in it. + if self.size() >= new_size as u64 { + self.file_name_mut()[..fname.len()].copy_from_slice(&fname); + self.set_size(new_size as u64); + + return Ok(self); + } + + let mut new_box = UefiBox::new(new_size)?; + + unsafe { + crate::ptr::copy_nonoverlapping(self.as_ptr(), new_box.as_mut_ptr(), 1); + } + new_box.set_size(new_size as u64); + new_box.file_name_mut().copy_from_slice(&fname); + + Ok(new_box) + } +} diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index ebd311db1e12c..e4a8f50e4274d 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -15,16 +15,12 @@ pub mod helpers; pub mod os; -#[path = "../unsupported/pipe.rs"] -pub mod pipe; -pub mod time; +pub mod system_time; #[cfg(test)] mod tests; -pub type RawOsError = usize; - -use crate::io as std_io; +use crate::io; use crate::os::uefi; use crate::ptr::NonNull; use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; @@ -78,61 +74,13 @@ pub unsafe fn cleanup() { } #[inline] -pub const fn unsupported() -> std_io::Result { +pub const fn unsupported() -> io::Result { Err(unsupported_err()) } #[inline] -pub const fn unsupported_err() -> std_io::Error { - std_io::const_error!(std_io::ErrorKind::Unsupported, "operation not supported on UEFI") -} - -pub fn decode_error_kind(code: RawOsError) -> crate::io::ErrorKind { - use r_efi::efi::Status; - - use crate::io::ErrorKind; - - match r_efi::efi::Status::from_usize(code) { - Status::ALREADY_STARTED - | Status::COMPROMISED_DATA - | Status::CONNECTION_FIN - | Status::CRC_ERROR - | Status::DEVICE_ERROR - | Status::END_OF_MEDIA - | Status::HTTP_ERROR - | Status::ICMP_ERROR - | Status::INCOMPATIBLE_VERSION - | Status::LOAD_ERROR - | Status::MEDIA_CHANGED - | Status::NO_MAPPING - | Status::NO_MEDIA - | Status::NOT_STARTED - | Status::PROTOCOL_ERROR - | Status::PROTOCOL_UNREACHABLE - | Status::TFTP_ERROR - | Status::VOLUME_CORRUPTED => ErrorKind::Other, - Status::BAD_BUFFER_SIZE | Status::INVALID_LANGUAGE => ErrorKind::InvalidData, - Status::ABORTED => ErrorKind::ConnectionAborted, - Status::ACCESS_DENIED => ErrorKind::PermissionDenied, - Status::BUFFER_TOO_SMALL => ErrorKind::FileTooLarge, - Status::CONNECTION_REFUSED => ErrorKind::ConnectionRefused, - Status::CONNECTION_RESET => ErrorKind::ConnectionReset, - Status::END_OF_FILE => ErrorKind::UnexpectedEof, - Status::HOST_UNREACHABLE => ErrorKind::HostUnreachable, - Status::INVALID_PARAMETER => ErrorKind::InvalidInput, - Status::IP_ADDRESS_CONFLICT => ErrorKind::AddrInUse, - Status::NETWORK_UNREACHABLE => ErrorKind::NetworkUnreachable, - Status::NO_RESPONSE => ErrorKind::HostUnreachable, - Status::NOT_FOUND => ErrorKind::NotFound, - Status::NOT_READY => ErrorKind::ResourceBusy, - Status::OUT_OF_RESOURCES => ErrorKind::OutOfMemory, - Status::SECURITY_VIOLATION => ErrorKind::PermissionDenied, - Status::TIMEOUT => ErrorKind::TimedOut, - Status::UNSUPPORTED => ErrorKind::Unsupported, - Status::VOLUME_FULL => ErrorKind::StorageFull, - Status::WRITE_PROTECTED => ErrorKind::ReadOnlyFilesystem, - _ => ErrorKind::Uncategorized, - } +pub const fn unsupported_err() -> io::Error { + io::const_error!(io::ErrorKind::Unsupported, "operation not supported on UEFI") } pub fn abort_internal() -> ! { @@ -164,7 +112,3 @@ pub fn abort_internal() -> ! { extern "efiapi" fn exit_boot_service_handler(_e: r_efi::efi::Event, _ctx: *mut crate::ffi::c_void) { uefi::env::disable_boot_services(); } - -pub fn is_interrupted(_code: RawOsError) -> bool { - false -} diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index aae6cb9e06462..5b9785c8371e3 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -1,66 +1,16 @@ use r_efi::efi::Status; use r_efi::efi::protocols::{device_path, loaded_image_device_path}; -use super::{RawOsError, helpers, unsupported_err}; +use super::{helpers, unsupported_err}; use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::os::uefi; +use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::path::{self, PathBuf}; use crate::ptr::NonNull; use crate::{fmt, io}; -pub fn errno() -> RawOsError { - 0 -} - -pub fn error_string(errno: RawOsError) -> String { - // Keep the List in Alphabetical Order - // The Messages are taken from UEFI Specification Appendix D - Status Codes - #[rustfmt::skip] - let msg = match r_efi::efi::Status::from_usize(errno) { - Status::ABORTED => "The operation was aborted.", - Status::ACCESS_DENIED => "Access was denied.", - Status::ALREADY_STARTED => "The protocol has already been started.", - Status::BAD_BUFFER_SIZE => "The buffer was not the proper size for the request.", - Status::BUFFER_TOO_SMALL => "The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs.", - Status::COMPROMISED_DATA => "The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status.", - Status::CONNECTION_FIN => "The receiving operation fails because the communication peer has closed the connection and there is no more data in the receive buffer of the instance.", - Status::CONNECTION_REFUSED => "The receiving or transmission operation fails because this connection is refused.", - Status::CONNECTION_RESET => "The connect fails because the connection is reset either by instance itself or the communication peer.", - Status::CRC_ERROR => "A CRC error was detected.", - Status::DEVICE_ERROR => "The physical device reported an error while attempting the operation.", - Status::END_OF_FILE => "The end of the file was reached.", - Status::END_OF_MEDIA => "Beginning or end of media was reached", - Status::HOST_UNREACHABLE => "The remote host is not reachable.", - Status::HTTP_ERROR => "A HTTP error occurred during the network operation.", - Status::ICMP_ERROR => "An ICMP error occurred during the network operation.", - Status::INCOMPATIBLE_VERSION => "The function encountered an internal version that was incompatible with a version requested by the caller.", - Status::INVALID_LANGUAGE => "The language specified was invalid.", - Status::INVALID_PARAMETER => "A parameter was incorrect.", - Status::IP_ADDRESS_CONFLICT => "There is an address conflict address allocation", - Status::LOAD_ERROR => "The image failed to load.", - Status::MEDIA_CHANGED => "The medium in the device has changed since the last access.", - Status::NETWORK_UNREACHABLE => "The network containing the remote host is not reachable.", - Status::NO_MAPPING => "A mapping to a device does not exist.", - Status::NO_MEDIA => "The device does not contain any medium to perform the operation.", - Status::NO_RESPONSE => "The server was not found or did not respond to the request.", - Status::NOT_FOUND => "The item was not found.", - Status::NOT_READY => "There is no data pending upon return.", - Status::NOT_STARTED => "The protocol has not been started.", - Status::OUT_OF_RESOURCES => "A resource has run out.", - Status::PROTOCOL_ERROR => "A protocol error occurred during the network operation.", - Status::PROTOCOL_UNREACHABLE => "An ICMP protocol unreachable error is received.", - Status::SECURITY_VIOLATION => "The function was not performed due to a security violation.", - Status::TFTP_ERROR => "A TFTP error occurred during the network operation.", - Status::TIMEOUT => "The timeout time expired.", - Status::UNSUPPORTED => "The operation is not supported.", - Status::VOLUME_FULL => "There is no more space on the file system.", - Status::VOLUME_CORRUPTED => "An inconstancy was detected on the file system causing the operating to fail.", - Status::WRITE_PROTECTED => "The device cannot be written to.", - _ => return format!("Status: {errno}"), - }; - msg.to_owned() -} +const PATHS_SEP: u16 = b';' as u16; pub fn getcwd() -> io::Result { match helpers::open_shell() { @@ -107,17 +57,34 @@ impl<'a> Iterator for SplitPaths<'a> { #[derive(Debug)] pub struct JoinPathsError; -pub fn join_paths(_paths: I) -> Result +// UEFI Shell Path variable is defined in Section 3.6.1 +// [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_2_2.pdf). +pub fn join_paths(paths: I) -> Result where I: Iterator, T: AsRef, { - Err(JoinPathsError) + let mut joined = Vec::new(); + + for (i, path) in paths.enumerate() { + if i > 0 { + joined.push(PATHS_SEP) + } + + let v = path.as_ref().encode_wide().collect::>(); + if v.contains(&PATHS_SEP) { + return Err(JoinPathsError); + } + + joined.extend_from_slice(&v); + } + + Ok(OsString::from_wide(&joined)) } impl fmt::Display for JoinPathsError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "not supported on this platform yet".fmt(f) + "path segment contains `;`".fmt(f) } } diff --git a/library/std/src/sys/pal/uefi/system_time.rs b/library/std/src/sys/pal/uefi/system_time.rs new file mode 100644 index 0000000000000..557a49b27c2d1 --- /dev/null +++ b/library/std/src/sys/pal/uefi/system_time.rs @@ -0,0 +1,151 @@ +use r_efi::efi::{RuntimeServices, Time}; + +use super::helpers; +use crate::mem::MaybeUninit; +use crate::ptr::NonNull; +use crate::time::Duration; + +const SECS_IN_MINUTE: u64 = 60; +const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60; +const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24; +const SYSTEMTIME_TIMEZONE: i64 = -1440 * SECS_IN_MINUTE as i64; + +pub(crate) fn now() -> Time { + let runtime_services: NonNull = + helpers::runtime_services().expect("Runtime services are not available"); + let mut t: MaybeUninit

{ + /// Wraps a value in a `MaybeDangling`, allowing it to dangle. + pub const fn new(x: P) -> Self + where + P: Sized, + { + MaybeDangling(x) + } + + /// Returns a reference to the inner value. + /// + /// Note that this is UB if the inner value is currently dangling. + pub const fn as_ref(&self) -> &P { + &self.0 + } + + /// Returns a mutable reference to the inner value. + /// + /// Note that this is UB if the inner value is currently dangling. + pub const fn as_mut(&mut self) -> &mut P { + &mut self.0 + } + + /// Extracts the value from the `MaybeDangling` container. + /// + /// Note that this is UB if the inner value is currently dangling. + pub const fn into_inner(self) -> P + where + P: Sized, + { + // FIXME: replace this with `self.0` when const checker can figure out that `self` isn't actually dropped + // SAFETY: this is equivalent to `self.0` + let x = unsafe { ptr::read(&self.0) }; + mem::forget(self); + x + } +} diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 3507d1a0a9a8c..320eb97f83a43 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -160,11 +160,10 @@ use crate::{fmt, intrinsics, ptr, slice}; /// /// ## Initializing a struct field-by-field /// -/// You can use `MaybeUninit`, and the [`std::ptr::addr_of_mut`] macro, to initialize structs field by field: +/// You can use `MaybeUninit` and the [`&raw mut`] syntax to initialize structs field by field: /// /// ```rust /// use std::mem::MaybeUninit; -/// use std::ptr::addr_of_mut; /// /// #[derive(Debug, PartialEq)] /// pub struct Foo { @@ -179,11 +178,11 @@ use crate::{fmt, intrinsics, ptr, slice}; /// // Initializing the `name` field /// // Using `write` instead of assignment via `=` to not call `drop` on the /// // old, uninitialized value. -/// unsafe { addr_of_mut!((*ptr).name).write("Bob".to_string()); } +/// unsafe { (&raw mut (*ptr).name).write("Bob".to_string()); } /// /// // Initializing the `list` field /// // If there is a panic here, then the `String` in the `name` field leaks. -/// unsafe { addr_of_mut!((*ptr).list).write(vec![0, 1, 2]); } +/// unsafe { (&raw mut (*ptr).list).write(vec![0, 1, 2]); } /// /// // All the fields are initialized, so we call `assume_init` to get an initialized Foo. /// unsafe { uninit.assume_init() } @@ -197,7 +196,7 @@ use crate::{fmt, intrinsics, ptr, slice}; /// } /// ); /// ``` -/// [`std::ptr::addr_of_mut`]: crate::ptr::addr_of_mut +/// [`&raw mut`]: https://doc.rust-lang.org/reference/types/pointer.html#r-type.pointer.raw.constructor /// [ub]: ../../reference/behavior-considered-undefined.html /// /// # Layout @@ -257,7 +256,7 @@ use crate::{fmt, intrinsics, ptr, slice}; /// /// # Validity /// -/// `MaybeUninit` has no validity requirements –- any sequence of [bytes] of +/// `MaybeUninit` has no validity requirements – any sequence of [bytes] of /// the appropriate length, initialized or uninitialized, are a valid /// representation. /// @@ -1047,7 +1046,7 @@ impl MaybeUninit { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_as_bytes, maybe_uninit_slice)] + /// #![feature(maybe_uninit_as_bytes)] /// use std::mem::MaybeUninit; /// /// let val = 0x12345678_i32; @@ -1097,20 +1096,6 @@ impl MaybeUninit { ) } } - - /// Gets a pointer to the first element of the array. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] - pub const fn slice_as_ptr(this: &[MaybeUninit]) -> *const T { - this.as_ptr() as *const T - } - - /// Gets a mutable pointer to the first element of the array. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] - #[inline(always)] - pub const fn slice_as_mut_ptr(this: &mut [MaybeUninit]) -> *mut T { - this.as_mut_ptr() as *mut T - } } impl [MaybeUninit] { @@ -1128,7 +1113,6 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_write_slice)] /// use std::mem::MaybeUninit; /// /// let mut dst = [MaybeUninit::uninit(); 32]; @@ -1140,8 +1124,6 @@ impl [MaybeUninit] { /// ``` /// /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// /// let mut vec = Vec::with_capacity(32); /// let src = [0; 16]; /// @@ -1157,7 +1139,8 @@ impl [MaybeUninit] { /// ``` /// /// [`write_clone_of_slice`]: slice::write_clone_of_slice - #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[stable(feature = "maybe_uninit_write_slice", since = "1.93.0")] + #[rustc_const_stable(feature = "maybe_uninit_write_slice", since = "1.93.0")] pub const fn write_copy_of_slice(&mut self, src: &[T]) -> &mut [T] where T: Copy, @@ -1188,7 +1171,6 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_write_slice)] /// use std::mem::MaybeUninit; /// /// let mut dst = [const { MaybeUninit::uninit() }; 5]; @@ -1203,8 +1185,6 @@ impl [MaybeUninit] { /// ``` /// /// ``` - /// #![feature(maybe_uninit_write_slice)] - /// /// let mut vec = Vec::with_capacity(32); /// let src = ["rust", "is", "a", "pretty", "cool", "language"].map(|s| s.to_string()); /// @@ -1220,7 +1200,7 @@ impl [MaybeUninit] { /// ``` /// /// [`write_copy_of_slice`]: slice::write_copy_of_slice - #[unstable(feature = "maybe_uninit_write_slice", issue = "79995")] + #[stable(feature = "maybe_uninit_write_slice", since = "1.93.0")] pub fn write_clone_of_slice(&mut self, src: &[T]) -> &mut [T] where T: Clone, @@ -1415,7 +1395,7 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_as_bytes, maybe_uninit_write_slice, maybe_uninit_slice)] + /// #![feature(maybe_uninit_as_bytes)] /// use std::mem::MaybeUninit; /// /// let uninit = [MaybeUninit::new(0x1234u16), MaybeUninit::new(0x5678u16)]; @@ -1442,7 +1422,7 @@ impl [MaybeUninit] { /// # Examples /// /// ``` - /// #![feature(maybe_uninit_as_bytes, maybe_uninit_write_slice, maybe_uninit_slice)] + /// #![feature(maybe_uninit_as_bytes)] /// use std::mem::MaybeUninit; /// /// let mut uninit = [MaybeUninit::::uninit(), MaybeUninit::::uninit()]; @@ -1482,7 +1462,7 @@ impl [MaybeUninit] { /// requirement the compiler knows about it is that the data pointer must be /// non-null. Dropping such a `Vec` however will cause undefined /// behaviour. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[stable(feature = "maybe_uninit_slice", since = "1.93.0")] #[inline(always)] #[rustc_const_unstable(feature = "const_drop_in_place", issue = "109342")] pub const unsafe fn assume_init_drop(&mut self) @@ -1504,7 +1484,8 @@ impl [MaybeUninit] { /// Calling this when the content is not yet fully initialized causes undefined /// behavior: it is up to the caller to guarantee that every `MaybeUninit` in /// the slice really is in an initialized state. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[stable(feature = "maybe_uninit_slice", since = "1.93.0")] + #[rustc_const_stable(feature = "maybe_uninit_slice", since = "1.93.0")] #[inline(always)] pub const unsafe fn assume_init_ref(&self) -> &[T] { // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that @@ -1522,7 +1503,8 @@ impl [MaybeUninit] { /// behavior: it is up to the caller to guarantee that every `MaybeUninit` in the /// slice really is in an initialized state. For instance, `.assume_init_mut()` cannot /// be used to initialize a `MaybeUninit` slice. - #[unstable(feature = "maybe_uninit_slice", issue = "63569")] + #[stable(feature = "maybe_uninit_slice", since = "1.93.0")] + #[rustc_const_stable(feature = "maybe_uninit_slice", since = "1.93.0")] #[inline(always)] pub const unsafe fn assume_init_mut(&mut self) -> &mut [T] { // SAFETY: similar to safety notes for `slice_get_ref`, but we have a diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index eb235cbf10147..d38b51da02829 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -11,6 +11,7 @@ use crate::clone::TrivialClone; use crate::kani; use crate::marker::{Destruct, DiscriminantKind}; use crate::panic::const_assert; +use crate::ptr::Alignment; use crate::{clone, cmp, fmt, hash, intrinsics, ptr}; mod manually_drop; @@ -21,6 +22,10 @@ mod maybe_uninit; #[stable(feature = "maybe_uninit", since = "1.36.0")] pub use maybe_uninit::MaybeUninit; +mod maybe_dangling; +#[unstable(feature = "maybe_dangling", issue = "118166")] +pub use maybe_dangling::MaybeDangling; + mod transmutability; #[unstable(feature = "transmutability", issue = "99571")] pub use transmutability::{Assume, TransmuteFrom}; @@ -35,6 +40,9 @@ pub use drop_guard::DropGuard; #[doc(inline)] pub use crate::intrinsics::transmute; +#[unstable(feature = "type_info", issue = "146922")] +pub mod type_info; + /// Takes ownership and "forgets" about the value **without running its destructor**. /// /// Any resources the value manages, such as heap memory or a file handle, will linger @@ -902,8 +910,6 @@ pub const fn replace(dest: &mut T, src: T) -> T { /// Disposes of a value. /// -/// This does so by calling the argument's implementation of [`Drop`][drop]. -/// /// This effectively does nothing for types which implement `Copy`, e.g. /// integers. Such values are copied and _then_ moved into the function, so the /// value persists after this function call. @@ -914,7 +920,7 @@ pub const fn replace(dest: &mut T, src: T) -> T { /// pub fn drop(_x: T) {} /// ``` /// -/// Because `_x` is moved into the function, it is automatically dropped before +/// Because `_x` is moved into the function, it is automatically [dropped][drop] before /// the function returns. /// /// [drop]: Drop @@ -1256,6 +1262,10 @@ pub trait SizedTypeProperties: Sized { #[lang = "mem_align_const"] const ALIGN: usize = intrinsics::align_of::(); + #[doc(hidden)] + #[unstable(feature = "ptr_alignment_type", issue = "102070")] + const ALIGNMENT: Alignment = Alignment::of::(); + /// `true` if this type requires no storage. /// `false` if its [size](size_of) is greater than zero. /// diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index f36cb8cddb837..e26c1b8fa1e19 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -86,7 +86,7 @@ use crate::marker::ConstParamTy_; #[unstable_feature_bound(transmutability)] #[lang = "transmute_trait"] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] #[rustc_coinductive] pub unsafe trait TransmuteFrom where diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs new file mode 100644 index 0000000000000..8b30803c97c98 --- /dev/null +++ b/library/core/src/mem/type_info.rs @@ -0,0 +1,204 @@ +//! MVP for exposing compile-time information about types in a +//! runtime or const-eval processable way. + +use crate::any::TypeId; +use crate::intrinsics::type_of; + +/// Compile-time type information. +#[derive(Debug)] +#[non_exhaustive] +#[lang = "type_info"] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Type { + /// Per-type information + pub kind: TypeKind, + /// Size of the type. `None` if it is unsized + pub size: Option, +} + +impl TypeId { + /// Compute the type information of a concrete type. + /// It can only be called at compile time. + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + pub const fn info(self) -> Type { + type_of(self) + } +} + +impl Type { + /// Returns the type information of the generic type parameter. + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + // FIXME(reflection): don't require the 'static bound + pub const fn of() -> Self { + const { TypeId::of::().info() } + } +} + +/// Compile-time type information. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub enum TypeKind { + /// Tuples. + Tuple(Tuple), + /// Arrays. + Array(Array), + /// Slices. + Slice(Slice), + /// Dynamic Traits. + DynTrait(DynTrait), + /// Primitive boolean type. + Bool(Bool), + /// Primitive character type. + Char(Char), + /// Primitive signed and unsigned integer type. + Int(Int), + /// Primitive floating-point type. + Float(Float), + /// String slice type. + Str(Str), + /// References. + Reference(Reference), + /// Pointers. + Pointer(Pointer), + /// FIXME(#146922): add all the common types + Other, +} + +/// Compile-time type information about tuples. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Tuple { + /// All fields of a tuple. + pub fields: &'static [Field], +} + +/// Compile-time type information about fields of tuples, structs and enum variants. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Field { + /// The field's type. + pub ty: TypeId, + /// Offset in bytes from the parent type + pub offset: usize, +} + +/// Compile-time type information about arrays. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Array { + /// The type of each element in the array. + pub element_ty: TypeId, + /// The length of the array. + pub len: usize, +} + +/// Compile-time type information about slices. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Slice { + /// The type of each element in the slice. + pub element_ty: TypeId, +} + +/// Compile-time type information about dynamic traits. +/// FIXME(#146922): Add super traits and generics +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct DynTrait { + /// The predicates of a dynamic trait. + pub predicates: &'static [DynTraitPredicate], +} + +/// Compile-time type information about a dynamic trait predicate. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct DynTraitPredicate { + /// The type of the trait as a dynamic trait type. + pub trait_ty: Trait, +} + +/// Compile-time type information about a trait. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Trait { + /// The TypeId of the trait as a dynamic type + pub ty: TypeId, + /// Whether the trait is an auto trait + pub is_auto: bool, +} + +/// Compile-time type information about `bool`. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Bool { + // No additional information to provide for now. +} + +/// Compile-time type information about `char`. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Char { + // No additional information to provide for now. +} + +/// Compile-time type information about signed and unsigned integer types. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Int { + /// The bit width of the signed integer type. + pub bits: u32, + /// Whether the integer type is signed. + pub signed: bool, +} + +/// Compile-time type information about floating-point types. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Float { + /// The bit width of the floating-point type. + pub bits: u32, +} + +/// Compile-time type information about string slice types. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Str { + // No additional information to provide for now. +} + +/// Compile-time type information about references. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Reference { + /// The type of the value being referred to. + pub pointee: TypeId, + /// Whether this reference is mutable or not. + pub mutable: bool, +} + +/// Compile-time type information about pointers. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Pointer { + /// The type of the value being pointed to. + pub pointee: TypeId, + /// Whether this pointer is mutable or not. + pub mutable: bool, +} diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs index dd4eccd24de03..66e30e1c5f7f1 100644 --- a/library/core/src/num/dec2flt/mod.rs +++ b/library/core/src/num/dec2flt/mod.rs @@ -179,7 +179,7 @@ from_str_float_impl!(f16); from_str_float_impl!(f32); from_str_float_impl!(f64); -// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order +// FIXME(f16): A fallback is used when the backend+target does not support f16 well, in order // to avoid ICEs. #[cfg(not(target_has_reliable_f16))] @@ -255,11 +255,7 @@ fn biased_fp_to_float(x: BiasedFp) -> F { #[inline(always)] // Will be inlined into a function with `#[inline(never)]`, see above pub fn dec2flt(s: &str) -> Result { let mut s = s.as_bytes(); - let c = if let Some(&c) = s.first() { - c - } else { - return Err(pfe_empty()); - }; + let Some(&c) = s.first() else { return Err(pfe_empty()) }; let negative = c == b'-'; if c == b'-' || c == b'+' { s = &s[1..]; diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index f55797377f0c4..72df42c4de89f 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -40,13 +40,13 @@ pub mod consts { /// The golden ratio (φ) #[unstable(feature = "f128", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "146939")] - pub const PHI: f128 = 1.61803398874989484820458683436563811772030917980576286213545_f128; + pub const GOLDEN_RATIO: f128 = + 1.61803398874989484820458683436563811772030917980576286213545_f128; /// The Euler-Mascheroni constant (γ) #[unstable(feature = "f128", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "146939")] - pub const EGAMMA: f128 = 0.577215664901532860606512090082402431042159335939923598805767_f128; + pub const EULER_GAMMA: f128 = + 0.577215664901532860606512090082402431042159335939923598805767_f128; /// π/2 #[unstable(feature = "f128", issue = "116909")] @@ -114,6 +114,17 @@ pub mod consts { pub const FRAC_1_SQRT_3: f128 = 0.577350269189625764509148780501957455647601751270126876018602_f128; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f128", issue = "116909")] + pub const SQRT_5: f128 = 2.23606797749978969640917366873127623544061835961152572427089_f128; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f128", issue = "116909")] + pub const FRAC_1_SQRT_5: f128 = + 0.447213595499957939281834733746255247088123671922305144854179_f128; + /// Euler's number (e) #[unstable(feature = "f128", issue = "116909")] pub const E: f128 = 2.71828182845904523536028747135266249775724709369995957496697_f128; @@ -143,14 +154,17 @@ pub mod consts { pub const LN_10: f128 = 2.30258509299404568401799145468436420760110148862877297603333_f128; } +#[doc(test(attr(feature(cfg_target_has_reliable_f16_f128), allow(internal_features))))] impl f128 { - // FIXME(f16_f128): almost all methods in this `impl` are missing examples and a const - // implementation. Add these once we can run code on all platforms and have f16/f128 in CTFE. - /// The radix or base of the internal representation of `f128`. #[unstable(feature = "f128", issue = "116909")] pub const RADIX: u32 = 2; + /// The size of this float type in bits. + // #[unstable(feature = "f128", issue = "116909")] + #[unstable(feature = "float_bits_const", issue = "151073")] + pub const BITS: u32 = 128; + /// Number of significant digits in base 2. /// /// Note that the size of the mantissa in the bitwise representation is one @@ -283,8 +297,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `unordtf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let nan = f128::NAN; /// let f = 7.0_f128; @@ -306,8 +319,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `eqtf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let f = 7.0f128; /// let inf = f128::INFINITY; @@ -332,8 +344,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `lttf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let f = 7.0f128; /// let inf: f128 = f128::INFINITY; @@ -361,8 +372,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `eqtf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let min = f128::MIN_POSITIVE; // 3.362103143e-4932f128 /// let max = f128::MAX; @@ -392,8 +402,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `eqtf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let min = f128::MIN_POSITIVE; // 3.362103143e-4932f128 /// let max = f128::MAX; @@ -425,8 +434,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `eqtf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// use std::num::FpCategory; /// @@ -520,8 +528,7 @@ impl f128 { /// /// ```rust /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `eqtf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// // f128::EPSILON is the difference between 1.0 and the next number up. /// assert_eq!(1.0f128.next_up(), 1.0 + f128::EPSILON); @@ -575,8 +582,7 @@ impl f128 { /// /// ```rust /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `eqtf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let x = 1.0f128; /// // Clamp value into range [0, 1). @@ -619,8 +625,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `eqtf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let x = 2.0_f128; /// let abs_difference = (x.recip() - (1.0 / x)).abs(); @@ -646,8 +651,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `eqtf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let angle = std::f128::consts::PI; /// @@ -677,8 +681,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `eqtf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let angle = 180.0f128; /// @@ -700,21 +703,25 @@ impl f128 { /// Returns the maximum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. + /// + /// The handling of NaNs follows the IEEE 754-2019 semantics for `maximumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `maxNum`. /// /// ``` /// #![feature(f128)] - /// # // Using aarch64 because `reliable_f128_math` is needed - /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = 1.0f128; /// let y = 2.0f128; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f128::NAN), x); /// # } /// ``` #[inline] @@ -727,21 +734,25 @@ impl f128 { /// Returns the minimum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. + /// + /// The handling of NaNs follows the IEEE 754-2019 semantics for `minimumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `minNum`. /// /// ``` /// #![feature(f128)] - /// # // Using aarch64 because `reliable_f128_math` is needed - /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = 1.0f128; /// let y = 2.0f128; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f128::NAN), x); /// # } /// ``` #[inline] @@ -754,14 +765,20 @@ impl f128 { /// Returns the maximum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f128::max`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f128::max`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `maximum`. /// /// ``` /// #![feature(f128)] /// #![feature(float_minimum_maximum)] - /// # // Using aarch64 because `reliable_f128_math` is needed - /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = 1.0f128; /// let y = 2.0f128; @@ -770,13 +787,6 @@ impl f128 { /// assert!(x.maximum(f128::NAN).is_nan()); /// # } /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[inline] #[unstable(feature = "f128", issue = "116909")] // #[unstable(feature = "float_minimum_maximum", issue = "91079")] @@ -787,14 +797,20 @@ impl f128 { /// Returns the minimum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f128::min`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f128::min`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `minimum`. /// /// ``` /// #![feature(f128)] /// #![feature(float_minimum_maximum)] - /// # // Using aarch64 because `reliable_f128_math` is needed - /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128_math)] { /// /// let x = 1.0f128; /// let y = 2.0f128; @@ -803,13 +819,6 @@ impl f128 { /// assert!(x.minimum(f128::NAN).is_nan()); /// # } /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[inline] #[unstable(feature = "f128", issue = "116909")] // #[unstable(feature = "float_minimum_maximum", issue = "91079")] @@ -827,8 +836,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // Using aarch64 because `reliable_f128_math` is needed - /// # #[cfg(all(target_arch = "aarch64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// assert_eq!(1f128.midpoint(4.0), 2.5); /// assert_eq!((-5.5f128).midpoint(8.0), 1.25); @@ -858,8 +866,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `float*itf` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let value = 4.6_f128; /// let rounded = unsafe { value.to_int_unchecked::() }; @@ -904,10 +911,11 @@ impl f128 { /// /// ``` /// #![feature(f128)] + /// # #[cfg(target_has_reliable_f128)] { /// - /// # // FIXME(f16_f128): enable this once const casting works - /// # // assert_ne!((1f128).to_bits(), 1f128 as u128); // to_bits() is not casting! + /// assert_ne!((1f128).to_bits(), 1f128 as u128); // to_bits() is not casting! /// assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); + /// # } /// ``` #[inline] #[unstable(feature = "f128", issue = "116909")] @@ -950,8 +958,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `eqtf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let v = f128::from_bits(0x40029000000000000000000000000000); /// assert_eq!(v, 12.5); @@ -1062,8 +1069,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `eqtf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let value = f128::from_be_bytes( /// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1088,8 +1094,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `eqtf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let value = f128::from_le_bytes( /// [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1121,8 +1126,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `eqtf2` is available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let value = f128::from_ne_bytes(if cfg!(target_endian = "big") { /// [0x40, 0x02, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1244,7 +1248,8 @@ impl f128 { /// less than `min`. Otherwise this returns `self`. /// /// Note that this function returns NaN if the initial value was NaN as - /// well. + /// well. If the result is zero and among the three inputs `self`, `min`, and `max` there are + /// zeros with different sign, either `0.0` or `-0.0` is returned non-deterministically. /// /// # Panics /// @@ -1254,13 +1259,18 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # // FIXME(f16_f128): remove when `{eq,gt,unord}tf` are available - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// assert!((-3.0f128).clamp(-2.0, 1.0) == -2.0); /// assert!((0.0f128).clamp(-2.0, 1.0) == 0.0); /// assert!((2.0f128).clamp(-2.0, 1.0) == 1.0); /// assert!((f128::NAN).clamp(-2.0, 1.0).is_nan()); + /// + /// // These always returns zero, but the sign (which is ignored by `==`) is non-deterministic. + /// assert!((0.0f128).clamp(-0.0, -0.0) == 0.0); + /// assert!((1.0f128).clamp(-0.0, 0.0) == 0.0); + /// // This is definitely a negative zero. + /// assert!((-1.0f128).clamp(-0.0, 1.0).is_sign_negative()); /// # } /// ``` #[inline] @@ -1284,6 +1294,38 @@ impl f128 { self } + /// Clamps this number to a symmetric range centered around zero. + /// + /// The method clamps the number's magnitude (absolute value) to be at most `limit`. + /// + /// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more + /// explicit about the intent. + /// + /// # Panics + /// + /// Panics if `limit` is negative or NaN, as this indicates a logic error. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// #![feature(clamp_magnitude)] + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// assert_eq!(5.0f128.clamp_magnitude(3.0), 3.0); + /// assert_eq!((-5.0f128).clamp_magnitude(3.0), -3.0); + /// assert_eq!(2.0f128.clamp_magnitude(3.0), 2.0); + /// assert_eq!((-2.0f128).clamp_magnitude(3.0), -2.0); + /// # } + /// ``` + #[inline] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "this returns the clamped value and does not modify the original"] + pub fn clamp_magnitude(self, limit: f128) -> f128 { + assert!(limit >= 0.0, "limit must be non-negative"); + let limit = limit.abs(); // Canonicalises -0.0 to 0.0 + self.clamp(-limit, limit) + } + /// Computes the absolute value of `self`. /// /// This function always returns the precise result. @@ -1292,7 +1334,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let x = 3.5_f128; /// let y = -3.5_f128; @@ -1308,9 +1350,7 @@ impl f128 { #[rustc_const_unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn abs(self) -> Self { - // FIXME(f16_f128): replace with `intrinsics::fabsf128` when available - // We don't do this now because LLVM has lowering bugs for f128 math. - Self::from_bits(self.to_bits() & !(1 << 127)) + intrinsics::fabsf128(self) } /// Returns a number that represents the sign of `self`. @@ -1323,7 +1363,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let f = 3.5_f128; /// @@ -1359,7 +1399,7 @@ impl f128 { /// /// ``` /// #![feature(f128)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f128)] { /// /// let f = 3.5_f128; /// @@ -1436,8 +1476,6 @@ impl f128 { } // Functions in this module fall into `core_float_math` -// FIXME(f16_f128): all doctests must be gated to platforms that have `long double` === `_Float128` -// due to https://github.com/llvm/llvm-project/issues/44744. aarch64 linux matches this. // #[unstable(feature = "core_float_math", issue = "137578")] #[cfg(not(test))] #[doc(test(attr(feature(cfg_target_has_reliable_f16_f128), expect(internal_features))))] @@ -1668,7 +1706,6 @@ impl f128 { #[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")] #[unstable(feature = "f128", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] - #[rustc_const_unstable(feature = "const_mul_add", issue = "146724")] pub const fn mul_add(self, a: f128, b: f128) -> f128 { intrinsics::fmaf128(self, a, b) } @@ -1712,7 +1749,8 @@ impl f128 { q } - /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// Calculates the least nonnegative remainder of `self` when + /// divided by `rhs`. /// /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in /// most cases. However, due to a floating point round-off error it can @@ -1761,6 +1799,11 @@ impl f128 { /// It might have a different sequence of rounding operations than `powf`, /// so the results are not guaranteed to agree. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f128::powi(f128::NAN, 0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 4ad1eaa48f91b..7d05c3592ea2a 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -42,13 +42,11 @@ pub mod consts { /// The golden ratio (φ) #[unstable(feature = "f16", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "146939")] - pub const PHI: f16 = 1.618033988749894848204586834365638118_f16; + pub const GOLDEN_RATIO: f16 = 1.618033988749894848204586834365638118_f16; /// The Euler-Mascheroni constant (γ) #[unstable(feature = "f16", issue = "116909")] - // Also, #[unstable(feature = "more_float_constants", issue = "146939")] - pub const EGAMMA: f16 = 0.577215664901532860606512090082402431_f16; + pub const EULER_GAMMA: f16 = 0.577215664901532860606512090082402431_f16; /// π/2 #[unstable(feature = "f16", issue = "116909")] @@ -111,6 +109,16 @@ pub mod consts { // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f16 = 0.577350269189625764509148780501957456_f16; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f16", issue = "116909")] + pub const SQRT_5: f16 = 2.23606797749978969640917366873127623_f16; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f16", issue = "116909")] + pub const FRAC_1_SQRT_5: f16 = 0.44721359549995793928183473374625524_f16; + /// Euler's number (e) #[unstable(feature = "f16", issue = "116909")] pub const E: f16 = 2.71828182845904523536028747135266250_f16; @@ -140,14 +148,17 @@ pub mod consts { pub const LN_10: f16 = 2.30258509299404568401799145468436421_f16; } +#[doc(test(attr(feature(cfg_target_has_reliable_f16_f128), allow(internal_features))))] impl f16 { - // FIXME(f16_f128): almost all methods in this `impl` are missing examples and a const - // implementation. Add these once we can run code on all platforms and have f16/f128 in CTFE. - /// The radix or base of the internal representation of `f16`. #[unstable(feature = "f16", issue = "116909")] pub const RADIX: u32 = 2; + /// The size of this float type in bits. + // #[unstable(feature = "f16", issue = "116909")] + #[unstable(feature = "float_bits_const", issue = "151073")] + pub const BITS: u32 = 16; + /// Number of significant digits in base 2. /// /// Note that the size of the mantissa in the bitwise representation is one @@ -280,7 +291,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let nan = f16::NAN; /// let f = 7.0_f16; @@ -302,7 +313,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let f = 7.0f16; /// let inf = f16::INFINITY; @@ -327,7 +338,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let f = 7.0f16; /// let inf: f16 = f16::INFINITY; @@ -355,7 +366,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let min = f16::MIN_POSITIVE; // 6.1035e-5 /// let max = f16::MAX; @@ -384,7 +395,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let min = f16::MIN_POSITIVE; // 6.1035e-5 /// let max = f16::MAX; @@ -415,7 +426,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// use std::num::FpCategory; /// @@ -451,8 +462,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # // FIXME(f16_f128): LLVM crashes on s390x, llvm/llvm-project#50374 - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let f = 7.0_f16; /// let g = -7.0_f16; @@ -480,8 +490,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # // FIXME(f16_f128): LLVM crashes on s390x, llvm/llvm-project#50374 - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let f = 7.0_f16; /// let g = -7.0_f16; @@ -515,8 +524,7 @@ impl f16 { /// /// ```rust /// #![feature(f16)] - /// # // FIXME(f16_f128): ABI issues on MSVC - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// // f16::EPSILON is the difference between 1.0 and the next number up. /// assert_eq!(1.0f16.next_up(), 1.0 + f16::EPSILON); @@ -570,8 +578,7 @@ impl f16 { /// /// ```rust /// #![feature(f16)] - /// # // FIXME(f16_f128): ABI issues on MSVC - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let x = 1.0f16; /// // Clamp value into range [0, 1). @@ -614,8 +621,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # // FIXME(f16_f128): extendhfsf2, truncsfhf2, __gnu_h2f_ieee, __gnu_f2h_ieee missing for many platforms - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let x = 2.0_f16; /// let abs_difference = (x.recip() - (1.0 / x)).abs(); @@ -641,8 +647,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # // FIXME(f16_f128): extendhfsf2, truncsfhf2, __gnu_h2f_ieee, __gnu_f2h_ieee missing for many platforms - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let angle = std::f16::consts::PI; /// @@ -671,8 +676,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # // FIXME(f16_f128): extendhfsf2, truncsfhf2, __gnu_h2f_ieee, __gnu_f2h_ieee missing for many platforms - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let angle = 180.0f16; /// @@ -693,20 +697,25 @@ impl f16 { /// Returns the maximum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. + /// + /// The handling of NaNs follows the IEEE 754-2019 semantics for `maximumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `maxNum`. /// /// ``` /// #![feature(f16)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(target_has_reliable_f16)] { /// /// let x = 1.0f16; /// let y = 2.0f16; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f16::NAN), x); /// # } /// ``` #[inline] @@ -719,20 +728,25 @@ impl f16 { /// Returns the minimum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. + /// + /// The handling of NaNs follows the IEEE 754-2019 semantics for `minimumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `minNum`. /// /// ``` /// #![feature(f16)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(target_has_reliable_f16)] { /// /// let x = 1.0f16; /// let y = 2.0f16; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f16::NAN), x); /// # } /// ``` #[inline] @@ -745,13 +759,20 @@ impl f16 { /// Returns the maximum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f16::max`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f16::max`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `maximum`. /// /// ``` /// #![feature(f16)] /// #![feature(float_minimum_maximum)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(target_has_reliable_f16)] { /// /// let x = 1.0f16; /// let y = 2.0f16; @@ -760,13 +781,6 @@ impl f16 { /// assert!(x.maximum(f16::NAN).is_nan()); /// # } /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[inline] #[unstable(feature = "f16", issue = "116909")] // #[unstable(feature = "float_minimum_maximum", issue = "91079")] @@ -777,13 +791,20 @@ impl f16 { /// Returns the minimum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f16::min`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f16::min`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `minimum`. /// /// ``` /// #![feature(f16)] /// #![feature(float_minimum_maximum)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(target_has_reliable_f16)] { /// /// let x = 1.0f16; /// let y = 2.0f16; @@ -792,13 +813,6 @@ impl f16 { /// assert!(x.minimum(f16::NAN).is_nan()); /// # } /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[inline] #[unstable(feature = "f16", issue = "116909")] // #[unstable(feature = "float_minimum_maximum", issue = "91079")] @@ -816,7 +830,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885 + /// # #[cfg(target_has_reliable_f16)] { /// /// assert_eq!(1f16.midpoint(4.0), 2.5); /// assert_eq!((-5.5f16).midpoint(8.0), 1.25); @@ -846,7 +860,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let value = 4.6_f16; /// let rounded = unsafe { value.to_int_unchecked::() }; @@ -890,10 +904,9 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// - /// # // FIXME(f16_f128): enable this once const casting works - /// # // assert_ne!((1f16).to_bits(), 1f16 as u128); // to_bits() is not casting! + /// assert_ne!((1f16).to_bits(), 1f16 as u16); // to_bits() is not casting! /// assert_eq!((12.5f16).to_bits(), 0x4a40); /// # } /// ``` @@ -938,7 +951,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let v = f16::from_bits(0x4a40); /// assert_eq!(v, 12.5); @@ -964,8 +977,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # // FIXME(f16_f128): LLVM crashes on s390x, llvm/llvm-project#50374 - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let bytes = 12.5f16.to_be_bytes(); /// assert_eq!(bytes, [0x4a, 0x40]); @@ -988,8 +1000,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # // FIXME(f16_f128): LLVM crashes on s390x, llvm/llvm-project#50374 - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let bytes = 12.5f16.to_le_bytes(); /// assert_eq!(bytes, [0x40, 0x4a]); @@ -1018,8 +1029,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # // FIXME(f16_f128): LLVM crashes on s390x, llvm/llvm-project#50374 - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let bytes = 12.5f16.to_ne_bytes(); /// assert_eq!( @@ -1048,7 +1058,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let value = f16::from_be_bytes([0x4a, 0x40]); /// assert_eq!(value, 12.5); @@ -1070,7 +1080,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let value = f16::from_le_bytes([0x40, 0x4a]); /// assert_eq!(value, 12.5); @@ -1099,7 +1109,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let value = f16::from_ne_bytes(if cfg!(target_endian = "big") { /// [0x4a, 0x40] @@ -1149,8 +1159,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # // FIXME(f16_f128): extendhfsf2, truncsfhf2, __gnu_h2f_ieee, __gnu_f2h_ieee missing for many platforms - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// struct GoodBoy { /// name: &'static str, @@ -1222,7 +1231,8 @@ impl f16 { /// less than `min`. Otherwise this returns `self`. /// /// Note that this function returns NaN if the initial value was NaN as - /// well. + /// well. If the result is zero and among the three inputs `self`, `min`, and `max` there are + /// zeros with different sign, either `0.0` or `-0.0` is returned non-deterministically. /// /// # Panics /// @@ -1232,12 +1242,18 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// assert!((-3.0f16).clamp(-2.0, 1.0) == -2.0); /// assert!((0.0f16).clamp(-2.0, 1.0) == 0.0); /// assert!((2.0f16).clamp(-2.0, 1.0) == 1.0); /// assert!((f16::NAN).clamp(-2.0, 1.0).is_nan()); + /// + /// // These always returns zero, but the sign (which is ignored by `==`) is non-deterministic. + /// assert!((0.0f16).clamp(-0.0, -0.0) == 0.0); + /// assert!((1.0f16).clamp(-0.0, 0.0) == 0.0); + /// // This is definitely a negative zero. + /// assert!((-1.0f16).clamp(-0.0, 1.0).is_sign_negative()); /// # } /// ``` #[inline] @@ -1261,6 +1277,38 @@ impl f16 { self } + /// Clamps this number to a symmetric range centered around zero. + /// + /// The method clamps the number's magnitude (absolute value) to be at most `limit`. + /// + /// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more + /// explicit about the intent. + /// + /// # Panics + /// + /// Panics if `limit` is negative or NaN, as this indicates a logic error. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// #![feature(clamp_magnitude)] + /// # #[cfg(target_has_reliable_f16)] { + /// assert_eq!(5.0f16.clamp_magnitude(3.0), 3.0); + /// assert_eq!((-5.0f16).clamp_magnitude(3.0), -3.0); + /// assert_eq!(2.0f16.clamp_magnitude(3.0), 2.0); + /// assert_eq!((-2.0f16).clamp_magnitude(3.0), -2.0); + /// # } + /// ``` + #[inline] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[must_use = "this returns the clamped value and does not modify the original"] + pub fn clamp_magnitude(self, limit: f16) -> f16 { + assert!(limit >= 0.0, "limit must be non-negative"); + let limit = limit.abs(); // Canonicalises -0.0 to 0.0 + self.clamp(-limit, limit) + } + /// Computes the absolute value of `self`. /// /// This function always returns the precise result. @@ -1269,7 +1317,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let x = 3.5_f16; /// let y = -3.5_f16; @@ -1285,8 +1333,7 @@ impl f16 { #[rustc_const_unstable(feature = "f16", issue = "116909")] #[must_use = "method returns a new number and does not mutate the original value"] pub const fn abs(self) -> Self { - // FIXME(f16_f128): replace with `intrinsics::fabsf16` when available - Self::from_bits(self.to_bits() & !(1 << 15)) + intrinsics::fabsf16(self) } /// Returns a number that represents the sign of `self`. @@ -1299,7 +1346,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let f = 3.5_f16; /// @@ -1335,7 +1382,7 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { + /// # #[cfg(target_has_reliable_f16_math)] { /// /// let f = 3.5_f16; /// @@ -1425,7 +1472,7 @@ impl f16 { /// ``` /// #![feature(f16)] /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let f = 3.7_f16; /// let g = 3.0_f16; @@ -1454,7 +1501,7 @@ impl f16 { /// ``` /// #![feature(f16)] /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let f = 3.01_f16; /// let g = 4.0_f16; @@ -1483,7 +1530,7 @@ impl f16 { /// ``` /// #![feature(f16)] /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let f = 3.3_f16; /// let g = -3.3_f16; @@ -1517,7 +1564,7 @@ impl f16 { /// ``` /// #![feature(f16)] /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let f = 3.3_f16; /// let g = -3.3_f16; @@ -1549,7 +1596,7 @@ impl f16 { /// ``` /// #![feature(f16)] /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let f = 3.7_f16; /// let g = 3.0_f16; @@ -1579,7 +1626,7 @@ impl f16 { /// ``` /// #![feature(f16)] /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let x = 3.6_f16; /// let y = -3.6_f16; @@ -1618,7 +1665,7 @@ impl f16 { /// ``` /// #![feature(f16)] /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let m = 10.0_f16; /// let x = 4.0_f16; @@ -1642,7 +1689,6 @@ impl f16 { #[unstable(feature = "f16", issue = "116909")] #[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] - #[rustc_const_unstable(feature = "const_mul_add", issue = "146724")] pub const fn mul_add(self, a: f16, b: f16) -> f16 { intrinsics::fmaf16(self, a, b) } @@ -1664,7 +1710,7 @@ impl f16 { /// ``` /// #![feature(f16)] /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let a: f16 = 7.0; /// let b = 4.0; @@ -1686,7 +1732,8 @@ impl f16 { q } - /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// Calculates the least nonnegative remainder of `self` when + /// divided by `rhs`. /// /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in /// most cases. However, due to a floating point round-off error it can @@ -1707,7 +1754,7 @@ impl f16 { /// ``` /// #![feature(f16)] /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let a: f16 = 7.0; /// let b = 4.0; @@ -1735,6 +1782,11 @@ impl f16 { /// It might have a different sequence of rounding operations than `powf`, /// so the results are not guaranteed to agree. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f16::powi(f16::NAN, 0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, @@ -1745,7 +1797,7 @@ impl f16 { /// ``` /// #![feature(f16)] /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let x = 2.0_f16; /// let abs_difference = (x.powi(2) - (x * x)).abs(); @@ -1778,7 +1830,7 @@ impl f16 { /// ``` /// #![feature(f16)] /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let positive = 4.0_f16; /// let negative = -4.0_f16; @@ -1813,7 +1865,7 @@ impl f16 { /// ``` /// #![feature(f16)] /// # #[cfg(not(miri))] - /// # #[cfg(target_has_reliable_f16_math)] { + /// # #[cfg(target_has_reliable_f16)] { /// /// let x = 8.0f16; /// diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index a58652a668e8f..9fbe4a3795649 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -298,12 +298,12 @@ pub mod consts { pub const TAU: f32 = 6.28318530717958647692528676655900577_f32; /// The golden ratio (φ) - #[unstable(feature = "more_float_constants", issue = "146939")] - pub const PHI: f32 = 1.618033988749894848204586834365638118_f32; + #[stable(feature = "euler_gamma_golden_ratio", since = "1.94.0")] + pub const GOLDEN_RATIO: f32 = 1.618033988749894848204586834365638118_f32; /// The Euler-Mascheroni constant (γ) - #[unstable(feature = "more_float_constants", issue = "146939")] - pub const EGAMMA: f32 = 0.577215664901532860606512090082402431_f32; + #[stable(feature = "euler_gamma_golden_ratio", since = "1.94.0")] + pub const EULER_GAMMA: f32 = 0.577215664901532860606512090082402431_f32; /// π/2 #[stable(feature = "rust1", since = "1.0.0")] @@ -362,6 +362,14 @@ pub mod consts { #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f32 = 0.577350269189625764509148780501957456_f32; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const SQRT_5: f32 = 2.23606797749978969640917366873127623_f32; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const FRAC_1_SQRT_5: f32 = 0.44721359549995793928183473374625524_f32; + /// Euler's number (e) #[stable(feature = "rust1", since = "1.0.0")] pub const E: f32 = 2.71828182845904523536028747135266250_f32; @@ -396,6 +404,10 @@ impl f32 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const RADIX: u32 = 2; + /// The size of this float type in bits. + #[unstable(feature = "float_bits_const", issue = "151073")] + pub const BITS: u32 = 32; + /// Number of significant digits in base 2. /// /// Note that the size of the mantissa in the bitwise representation is one @@ -903,17 +915,22 @@ impl f32 { /// Returns the maximum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. + /// + /// The handling of NaNs follows the IEEE 754-2019 semantics for `maximumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `maxNum`. /// /// ``` /// let x = 1.0f32; /// let y = 2.0f32; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f32::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] @@ -925,17 +942,22 @@ impl f32 { /// Returns the minimum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. + /// + /// The handling of NaNs follows the IEEE 754-2019 semantics for `minimumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `minNum`. /// /// ``` /// let x = 1.0f32; /// let y = 2.0f32; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f32::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] @@ -947,8 +969,15 @@ impl f32 { /// Returns the maximum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f32::max`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f32::max`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `maximum`. /// /// ``` /// #![feature(float_minimum_maximum)] @@ -958,13 +987,6 @@ impl f32 { /// assert_eq!(x.maximum(y), y); /// assert!(x.maximum(f32::NAN).is_nan()); /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] @@ -974,8 +996,15 @@ impl f32 { /// Returns the minimum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f32::min`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f32::min`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `minimum`. /// /// ``` /// #![feature(float_minimum_maximum)] @@ -985,13 +1014,6 @@ impl f32 { /// assert_eq!(x.minimum(y), x); /// assert!(x.minimum(f32::NAN).is_nan()); /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] @@ -1403,7 +1425,8 @@ impl f32 { /// less than `min`. Otherwise this returns `self`. /// /// Note that this function returns NaN if the initial value was NaN as - /// well. + /// well. If the result is zero and among the three inputs `self`, `min`, and `max` there are + /// zeros with different sign, either `0.0` or `-0.0` is returned non-deterministically. /// /// # Panics /// @@ -1416,6 +1439,12 @@ impl f32 { /// assert!((0.0f32).clamp(-2.0, 1.0) == 0.0); /// assert!((2.0f32).clamp(-2.0, 1.0) == 1.0); /// assert!((f32::NAN).clamp(-2.0, 1.0).is_nan()); + /// + /// // These always returns zero, but the sign (which is ignored by `==`) is non-deterministic. + /// assert!((0.0f32).clamp(-0.0, -0.0) == 0.0); + /// assert!((1.0f32).clamp(-0.0, 0.0) == 0.0); + /// // This is definitely a negative zero. + /// assert!((-1.0f32).clamp(-0.0, 1.0).is_sign_negative()); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "clamp", since = "1.50.0")] @@ -1439,6 +1468,35 @@ impl f32 { self } + /// Clamps this number to a symmetric range centered around zero. + /// + /// The method clamps the number's magnitude (absolute value) to be at most `limit`. + /// + /// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more + /// explicit about the intent. + /// + /// # Panics + /// + /// Panics if `limit` is negative or NaN, as this indicates a logic error. + /// + /// # Examples + /// + /// ``` + /// #![feature(clamp_magnitude)] + /// assert_eq!(5.0f32.clamp_magnitude(3.0), 3.0); + /// assert_eq!((-5.0f32).clamp_magnitude(3.0), -3.0); + /// assert_eq!(2.0f32.clamp_magnitude(3.0), 2.0); + /// assert_eq!((-2.0f32).clamp_magnitude(3.0), -2.0); + /// ``` + #[must_use = "this returns the clamped value and does not modify the original"] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[inline] + pub fn clamp_magnitude(self, limit: f32) -> f32 { + assert!(limit >= 0.0, "limit must be non-negative"); + let limit = limit.abs(); // Canonicalises -0.0 to 0.0 + self.clamp(-limit, limit) + } + /// Computes the absolute value of `self`. /// /// This function always returns the precise result. @@ -1807,7 +1865,6 @@ pub mod math { #[doc(alias = "fmaf", alias = "fusedMultiplyAdd")] #[must_use = "method returns a new number and does not mutate the original value"] #[unstable(feature = "core_float_math", issue = "137578")] - #[rustc_const_unstable(feature = "const_mul_add", issue = "146724")] pub const fn mul_add(x: f32, y: f32, z: f32) -> f32 { intrinsics::fmaf32(x, y, z) } diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index ac056dd4c4988..fac845b8be317 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -298,12 +298,12 @@ pub mod consts { pub const TAU: f64 = 6.28318530717958647692528676655900577_f64; /// The golden ratio (φ) - #[unstable(feature = "more_float_constants", issue = "146939")] - pub const PHI: f64 = 1.618033988749894848204586834365638118_f64; + #[stable(feature = "euler_gamma_golden_ratio", since = "1.94.0")] + pub const GOLDEN_RATIO: f64 = 1.618033988749894848204586834365638118_f64; /// The Euler-Mascheroni constant (γ) - #[unstable(feature = "more_float_constants", issue = "146939")] - pub const EGAMMA: f64 = 0.577215664901532860606512090082402431_f64; + #[stable(feature = "euler_gamma_golden_ratio", since = "1.94.0")] + pub const EULER_GAMMA: f64 = 0.577215664901532860606512090082402431_f64; /// π/2 #[stable(feature = "rust1", since = "1.0.0")] @@ -362,6 +362,14 @@ pub mod consts { #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f64 = 0.577350269189625764509148780501957456_f64; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const SQRT_5: f64 = 2.23606797749978969640917366873127623_f64; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const FRAC_1_SQRT_5: f64 = 0.44721359549995793928183473374625524_f64; + /// Euler's number (e) #[stable(feature = "rust1", since = "1.0.0")] pub const E: f64 = 2.71828182845904523536028747135266250_f64; @@ -396,6 +404,10 @@ impl f64 { #[stable(feature = "assoc_int_consts", since = "1.43.0")] pub const RADIX: u32 = 2; + /// The size of this float type in bits. + #[unstable(feature = "float_bits_const", issue = "151073")] + pub const BITS: u32 = 64; + /// Number of significant digits in base 2. /// /// Note that the size of the mantissa in the bitwise representation is one @@ -921,17 +933,22 @@ impl f64 { /// Returns the maximum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for maxNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids maxNum's problems with associativity. - /// This also matches the behavior of libm’s fmax. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. + /// + /// The handling of NaNs follows the IEEE 754-2019 semantics for `maximumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `maxNum`. /// /// ``` /// let x = 1.0_f64; /// let y = 2.0_f64; /// /// assert_eq!(x.max(y), y); + /// assert_eq!(x.max(f64::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] @@ -943,17 +960,22 @@ impl f64 { /// Returns the minimum of the two numbers, ignoring NaN. /// - /// If one of the arguments is NaN, then the other argument is returned. - /// This follows the IEEE 754-2008 semantics for minNum, except for handling of signaling NaNs; - /// this function handles all NaNs the same way and avoids minNum's problems with associativity. - /// This also matches the behavior of libm’s fmin. In particular, if the inputs compare equal - /// (such as for the case of `+0.0` and `-0.0`), either input may be returned non-deterministically. + /// If exactly one of the arguments is NaN (quiet or signaling), then the other argument is + /// returned. If both arguments are NaN, the return value is NaN, with the bit pattern picked + /// using the usual [rules for arithmetic operations](f32#nan-bit-patterns). If the inputs + /// compare equal (such as for the case of `+0.0` and `-0.0`), either input may be returned + /// non-deterministically. + /// + /// The handling of NaNs follows the IEEE 754-2019 semantics for `minimumNumber`, treating all + /// NaNs the same way to ensure the operation is associative. The handling of signed zeros + /// follows the IEEE 754-2008 semantics for `minNum`. /// /// ``` /// let x = 1.0_f64; /// let y = 2.0_f64; /// /// assert_eq!(x.min(y), x); + /// assert_eq!(x.min(f64::NAN), x); /// ``` #[must_use = "this returns the result of the comparison, without modifying either input"] #[stable(feature = "rust1", since = "1.0.0")] @@ -965,8 +987,15 @@ impl f64 { /// Returns the maximum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f64::max`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f64::max`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `maximum`. /// /// ``` /// #![feature(float_minimum_maximum)] @@ -976,13 +1005,6 @@ impl f64 { /// assert_eq!(x.maximum(y), y); /// assert!(x.maximum(f64::NAN).is_nan()); /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the greater - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] @@ -992,8 +1014,15 @@ impl f64 { /// Returns the minimum of the two numbers, propagating NaN. /// - /// This returns NaN when *either* argument is NaN, as opposed to - /// [`f64::min`] which only returns NaN when *both* arguments are NaN. + /// If at least one of the arguments is NaN, the return value is NaN, with the bit pattern + /// picked using the usual [rules for arithmetic operations](f32#nan-bit-patterns). Furthermore, + /// `-0.0` is considered to be less than `+0.0`, making this function fully deterministic for + /// non-NaN inputs. + /// + /// This is in contrast to [`f64::min`] which only returns NaN when *both* arguments are NaN, + /// and which does not reliably order `-0.0` and `+0.0`. + /// + /// This follows the IEEE 754-2019 semantics for `minimum`. /// /// ``` /// #![feature(float_minimum_maximum)] @@ -1003,13 +1032,6 @@ impl f64 { /// assert_eq!(x.minimum(y), x); /// assert!(x.minimum(f64::NAN).is_nan()); /// ``` - /// - /// If one of the arguments is NaN, then NaN is returned. Otherwise this returns the lesser - /// of the two numbers. For this operation, -0.0 is considered to be less than +0.0. - /// Note that this follows the semantics specified in IEEE 754-2019. - /// - /// Also note that "propagation" of NaNs here doesn't necessarily mean that the bitpattern of a NaN - /// operand is conserved; see the [specification of NaN bit patterns](f32#nan-bit-patterns) for more info. #[must_use = "this returns the result of the comparison, without modifying either input"] #[unstable(feature = "float_minimum_maximum", issue = "91079")] #[inline] @@ -1401,7 +1423,8 @@ impl f64 { /// less than `min`. Otherwise this returns `self`. /// /// Note that this function returns NaN if the initial value was NaN as - /// well. + /// well. If the result is zero and among the three inputs `self`, `min`, and `max` there are + /// zeros with different sign, either `0.0` or `-0.0` is returned non-deterministically. /// /// # Panics /// @@ -1414,6 +1437,12 @@ impl f64 { /// assert!((0.0f64).clamp(-2.0, 1.0) == 0.0); /// assert!((2.0f64).clamp(-2.0, 1.0) == 1.0); /// assert!((f64::NAN).clamp(-2.0, 1.0).is_nan()); + /// + /// // These always returns zero, but the sign (which is ignored by `==`) is non-deterministic. + /// assert!((0.0f64).clamp(-0.0, -0.0) == 0.0); + /// assert!((1.0f64).clamp(-0.0, 0.0) == 0.0); + /// // This is definitely a negative zero. + /// assert!((-1.0f64).clamp(-0.0, 1.0).is_sign_negative()); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "clamp", since = "1.50.0")] @@ -1437,6 +1466,35 @@ impl f64 { self } + /// Clamps this number to a symmetric range centered around zero. + /// + /// The method clamps the number's magnitude (absolute value) to be at most `limit`. + /// + /// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more + /// explicit about the intent. + /// + /// # Panics + /// + /// Panics if `limit` is negative or NaN, as this indicates a logic error. + /// + /// # Examples + /// + /// ``` + /// #![feature(clamp_magnitude)] + /// assert_eq!(5.0f64.clamp_magnitude(3.0), 3.0); + /// assert_eq!((-5.0f64).clamp_magnitude(3.0), -3.0); + /// assert_eq!(2.0f64.clamp_magnitude(3.0), 2.0); + /// assert_eq!((-2.0f64).clamp_magnitude(3.0), -2.0); + /// ``` + #[must_use = "this returns the clamped value and does not modify the original"] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[inline] + pub fn clamp_magnitude(self, limit: f64) -> f64 { + assert!(limit >= 0.0, "limit must be non-negative"); + let limit = limit.abs(); // Canonicalises -0.0 to 0.0 + self.clamp(-limit, limit) + } + /// Computes the absolute value of `self`. /// /// This function always returns the precise result. @@ -1805,7 +1863,6 @@ pub mod math { #[doc(alias = "fma", alias = "fusedMultiplyAdd")] #[unstable(feature = "core_float_math", issue = "137578")] #[must_use = "method returns a new number and does not mutate the original value"] - #[rustc_const_unstable(feature = "const_mul_add", issue = "146724")] pub const fn mul_add(x: f64, a: f64, b: f64) -> f64 { intrinsics::fmaf64(x, a, b) } diff --git a/library/core/src/num/int_bits.rs b/library/core/src/num/int_bits.rs new file mode 100644 index 0000000000000..7e54591922358 --- /dev/null +++ b/library/core/src/num/int_bits.rs @@ -0,0 +1,171 @@ +//! Implementations for `uN::extract_bits` and `uN::deposit_bits` +//! +//! For the purposes of this implementation, the operations can be thought +//! of as operating on the input bits as a list, starting from the least +//! significant bit. Extraction is like `Vec::retain` that deletes bits +//! where the mask has a zero. Deposition is like doing the inverse by +//! inserting the zeros that extraction would delete. +//! +//! Key observation: Each extracted or deposited bit needs to be +//! shifted by the count of zeros up to the corresponding mask bit. +//! +//! With that in mind, the general idea is to decompose the operation into +//! a sequence of stages in `0..log2(BITS)`, where each stage shifts some +//! of the bits by `n = 1 << stage`. The masks for each stage are computed +//! via prefix counts of zeros in the mask. +//! +//! # Extraction +//! +//! Consider the input as a sequence of runs of data (bitstrings A,B,C,...), +//! split by fixed-width groups of zeros ('.'), initially at width `n = 1`. +//! Counting the groups of zeros, each stage shifts the odd-indexed runs of +//! data right by `n`, effectively swapping them with the preceding zeros. +//! For the next stage, `n` is doubled as all the zeros are now paired. +//! ```text +//! .A.B.C.D.E.F.G.H +//! ..AB..CD..EF..GH +//! ....ABCD....EFGH +//! ........ABCDEFGH +//! ``` +//! What makes this nontrivial is that the lengths of the bitstrings are not +//! the same. Using lowercase for individual bits, the above might look like +//! ```text +//! .a.bbb.ccccc.dd.e..g.hh +//! ..abbb..cccccdd..e..ghh +//! ....abbbcccccdd....eghh +//! ........abbbcccccddeghh +//! ``` +//! +//! # Deposition +//! +//! For `deposit_bits`, the stages are reversed. We start with a single run of +//! data in the low bits. Each stage then splits each run of data in two by +//! shifting part of it left by `n`, which is halved each stage. +//! ```text +//! ........ABCDEFGH +//! ....ABCD....EFGH +//! ..AB..CD..EF..GH +//! .A.B.C.D.E.F.G.H +//! ``` +//! +//! # Stage masks +//! +//! To facilitate the shifts at each stage, we compute a mask that covers both +//! the bitstrings to shift, and the zeros they shift into. +//! ```text +//! .A.B.C.D.E.F.G.H +//! ## ## ## ## +//! ..AB..CD..EF..GH +//! #### #### +//! ....ABCD....EFGH +//! ######## +//! ........ABCDEFGH +//! ``` + +macro_rules! uint_impl { + ($U:ident) => { + pub(super) mod $U { + const STAGES: usize = $U::BITS.ilog2() as usize; + #[inline] + const fn prepare(sparse: $U) -> [$U; STAGES] { + // We'll start with `zeros` as a mask of the bits to be removed, + // and compute into `masks` the parts that shift at each stage. + let mut zeros = !sparse; + let mut masks = [0; STAGES]; + let mut stage = 0; + while stage < STAGES { + let n = 1 << stage; + // Suppose `zeros` has bits set at ranges `{ a..a+n, b..b+n, ... }`. + // Then `parity` will be computed as `{ a.. } XOR { b.. } XOR ...`, + // which will be the ranges `{ a..b, c..d, e.. }`. + let mut parity = zeros; + let mut len = n; + while len < $U::BITS { + parity ^= parity << len; + len <<= 1; + } + masks[stage] = parity; + + // Toggle off the bits that are shifted into: + // { a..a+n, b..b+n, ... } & !{ a..b, c..d, e.. } + // == { b..b+n, d..d+n, ... } + zeros &= !parity; + // Expand the remaining ranges down to the bits that were + // shifted from: { b-n..b+n, d-n..d+n, ... } + zeros ^= zeros >> n; + + stage += 1; + } + masks + } + + #[inline(always)] + pub(in super::super) const fn extract_impl(mut x: $U, sparse: $U) -> $U { + let masks = prepare(sparse); + x &= sparse; + let mut stage = 0; + while stage < STAGES { + let n = 1 << stage; + // Consider each two runs of data with their leading + // groups of `n` 0-bits. Suppose that the run that is + // shifted right has length `a`, and the other one has + // length `b`. Assume that only zeros are shifted in. + // ```text + // [0; n], [X; a], [0; n], [Y; b] // x + // [0; n], [X; a], [0; n], [0; b] // q + // [0; n], [0; a + n], [Y; b] // x ^= q + // [0; n + n], [X; a], [0; b] // q >> n + // [0; n], [0; n], [X; a], [Y; b] // x ^= q << n + // ``` + // Only zeros are shifted out, satisfying the assumption + // for the next group. + + // In effect, the upper run of data is swapped with the + // group of `n` zeros below it. + let q = x & masks[stage]; + x ^= q; + x ^= q >> n; + + stage += 1; + } + x + } + #[inline(always)] + pub(in super::super) const fn deposit_impl(mut x: $U, sparse: $U) -> $U { + let masks = prepare(sparse); + let mut stage = STAGES; + while stage > 0 { + stage -= 1; + let n = 1 << stage; + // Consider each run of data with the `2 * n` arbitrary bits + // above it. Suppose that the run has length `a + b`, with + // `a` being the length of the part that needs to be + // shifted. Assume that only zeros are shifted in. + // ```text + // [_; n], [_; n], [X; a], [Y; b] // x + // [0; n], [_; n], [X; a], [0; b] // q + // [_; n], [0; n + a], [Y; b] // x ^= q + // [_; n], [X; a], [0; b + n] // q << n + // [_; n], [X; a], [0; n], [Y; b] // x ^= q << n + // ``` + // Only zeros are shifted out, satisfying the assumption + // for the next group. + + // In effect, `n` 0-bits are inserted somewhere in each run + // of data to spread it, and the two groups of `n` bits + // above are XOR'd together. + let q = x & masks[stage]; + x ^= q; + x ^= q << n; + } + x & sparse + } + } + }; +} + +uint_impl!(u8); +uint_impl!(u16); +uint_impl!(u32); +uint_impl!(u64); +uint_impl!(u128); diff --git a/library/core/src/num/int_log10.rs b/library/core/src/num/int_log10.rs index 649a736b6e7b5..af8e1f90968d6 100644 --- a/library/core/src/num/int_log10.rs +++ b/library/core/src/num/int_log10.rs @@ -1,9 +1,11 @@ //! These functions compute the integer logarithm of their type, assuming //! that someone has already checked that the value is strictly positive. +use crate::num::NonZero; + // 0 < val <= u8::MAX #[inline] -pub(super) const fn u8(val: u8) -> u32 { +const fn u8_impl(val: u8) -> u32 { let val = val as u32; // For better performance, avoid branches by assembling the solution @@ -45,13 +47,13 @@ const fn less_than_5(val: u32) -> u32 { // 0 < val <= u16::MAX #[inline] -pub(super) const fn u16(val: u16) -> u32 { +const fn u16_impl(val: u16) -> u32 { less_than_5(val as u32) } // 0 < val <= u32::MAX #[inline] -pub(super) const fn u32(mut val: u32) -> u32 { +const fn u32_impl(mut val: u32) -> u32 { let mut log = 0; if val >= 100_000 { val /= 100_000; @@ -62,7 +64,7 @@ pub(super) const fn u32(mut val: u32) -> u32 { // 0 < val <= u64::MAX #[inline] -pub(super) const fn u64(mut val: u64) -> u32 { +const fn u64_impl(mut val: u64) -> u32 { let mut log = 0; if val >= 10_000_000_000 { val /= 10_000_000_000; @@ -77,66 +79,87 @@ pub(super) const fn u64(mut val: u64) -> u32 { // 0 < val <= u128::MAX #[inline] -pub(super) const fn u128(mut val: u128) -> u32 { +const fn u128_impl(mut val: u128) -> u32 { let mut log = 0; if val >= 100_000_000_000_000_000_000_000_000_000_000 { val /= 100_000_000_000_000_000_000_000_000_000_000; log += 32; - return log + u32(val as u32); + return log + u32_impl(val as u32); } if val >= 10_000_000_000_000_000 { val /= 10_000_000_000_000_000; log += 16; } - log + u64(val as u64) + log + u64_impl(val as u64) } -#[cfg(target_pointer_width = "16")] -#[inline] -pub(super) const fn usize(val: usize) -> u32 { - u16(val as _) -} +macro_rules! define_unsigned_ilog10 { + ($($ty:ident => $impl_fn:ident,)*) => {$( + #[inline] + pub(super) const fn $ty(val: NonZero<$ty>) -> u32 { + let result = $impl_fn(val.get()); -#[cfg(target_pointer_width = "32")] -#[inline] -pub(super) const fn usize(val: usize) -> u32 { - u32(val as _) -} + // SAFETY: Integer logarithm is monotonic non-decreasing, so the computed `result` cannot + // exceed the value produced for the maximum input. + unsafe { crate::hint::assert_unchecked(result <= const { $impl_fn($ty::MAX) }) }; -#[cfg(target_pointer_width = "64")] -#[inline] -pub(super) const fn usize(val: usize) -> u32 { - u64(val as _) + result + } + )*}; } -// 0 < val <= i8::MAX -#[inline] -pub(super) const fn i8(val: i8) -> u32 { - u8(val as u8) +define_unsigned_ilog10! { + u8 => u8_impl, + u16 => u16_impl, + u32 => u32_impl, + u64 => u64_impl, + u128 => u128_impl, } -// 0 < val <= i16::MAX #[inline] -pub(super) const fn i16(val: i16) -> u32 { - u16(val as u16) -} +pub(super) const fn usize(val: NonZero) -> u32 { + #[cfg(target_pointer_width = "16")] + let impl_fn = u16; -// 0 < val <= i32::MAX -#[inline] -pub(super) const fn i32(val: i32) -> u32 { - u32(val as u32) + #[cfg(target_pointer_width = "32")] + let impl_fn = u32; + + #[cfg(target_pointer_width = "64")] + let impl_fn = u64; + + // SAFETY: We have selected the correct `impl_fn`, so the converting `val` to the argument is + // safe. + impl_fn(unsafe { NonZero::new_unchecked(val.get() as _) }) } -// 0 < val <= i64::MAX -#[inline] -pub(super) const fn i64(val: i64) -> u32 { - u64(val as u64) +macro_rules! define_signed_ilog10 { + ($($ty:ident => $impl_fn:ident,)*) => {$( + // 0 < val <= $ty::MAX + #[inline] + pub(super) const fn $ty(val: $ty) -> Option { + if val > 0 { + let result = $impl_fn(val.cast_unsigned()); + + // SAFETY: Integer logarithm is monotonic non-decreasing, so the computed `result` + // cannot exceed the value produced for the maximum input. + unsafe { + crate::hint::assert_unchecked(result <= const { $impl_fn($ty::MAX.cast_unsigned()) }); + } + + Some(result) + } else { + None + } + } + )*}; } -// 0 < val <= i128::MAX -#[inline] -pub(super) const fn i128(val: i128) -> u32 { - u128(val as u128) +define_signed_ilog10! { + i8 => u8_impl, + i16 => u16_impl, + i32 => u32_impl, + i64 => u64_impl, + i128 => u128_impl, } /// Instantiate this panic logic once, rather than for all the ilog methods diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index a533906b49df2..f6dfdeba33147 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -372,6 +372,8 @@ macro_rules! int_impl { /// /// On big endian this is a no-op. On little endian the bytes are swapped. /// + /// See also [from_be_bytes()](Self::from_be_bytes). + /// /// # Examples /// /// ``` @@ -402,6 +404,8 @@ macro_rules! int_impl { /// /// On little endian this is a no-op. On big endian the bytes are swapped. /// + /// See also [from_le_bytes()](Self::from_le_bytes). + /// /// # Examples /// /// ``` @@ -428,9 +432,15 @@ macro_rules! int_impl { } } - /// Converts `self` to big endian from the target's endianness. + /// Swaps bytes of `self` on little endian targets. /// - /// On big endian this is a no-op. On little endian the bytes are swapped. + /// On big endian this is a no-op. + /// + /// The returned value has the same type as `self`, and will be interpreted + /// as (a potentially different) value of a native-endian + #[doc = concat!("`", stringify!($SelfT), "`.")] + /// + /// See [`to_be_bytes()`](Self::to_be_bytes) for a type-safe alternative. /// /// # Examples /// @@ -459,9 +469,15 @@ macro_rules! int_impl { } } - /// Converts `self` to little endian from the target's endianness. + /// Swaps bytes of `self` on big endian targets. /// - /// On little endian this is a no-op. On big endian the bytes are swapped. + /// On little endian this is a no-op. + /// + /// The returned value has the same type as `self`, and will be interpreted + /// as (a potentially different) value of a native-endian + #[doc = concat!("`", stringify!($SelfT), "`.")] + /// + /// See [`to_le_bytes()`](Self::to_le_bytes) for a type-safe alternative. /// /// # Examples /// @@ -1262,11 +1278,8 @@ macro_rules! int_impl { /// i.e. when [`checked_neg`] would return `None`. /// #[doc = concat!("[`checked_neg`]: ", stringify!($SelfT), "::checked_neg")] - #[unstable( - feature = "unchecked_neg", - reason = "niche optimization path", - issue = "85122", - )] + #[stable(feature = "unchecked_neg", since = "1.93.0")] + #[rustc_const_stable(feature = "unchecked_neg", since = "1.93.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1384,11 +1397,8 @@ macro_rules! int_impl { /// i.e. when [`checked_shl`] would return `None`. /// #[doc = concat!("[`checked_shl`]: ", stringify!($SelfT), "::checked_shl")] - #[unstable( - feature = "unchecked_shifts", - reason = "niche optimization path", - issue = "85122", - )] + #[stable(feature = "unchecked_shifts", since = "1.93.0")] + #[rustc_const_stable(feature = "unchecked_shifts", since = "1.93.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1417,8 +1427,15 @@ macro_rules! int_impl { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(4), 0x10);")] - #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(129), 0);")] + #[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".unbounded_shl(4), 0x10);")] + #[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".unbounded_shl(129), 0);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".unbounded_shl(0), 0b101);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".unbounded_shl(1), 0b1010);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".unbounded_shl(2), 0b10100);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".unbounded_shl(", stringify!($BITS), "), 0);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".unbounded_shl(1).unbounded_shl(", stringify!($BITS_MINUS_ONE), "), 0);")] + #[doc = concat!("assert_eq!((-13_", stringify!($SelfT), ").unbounded_shl(", stringify!($BITS), "), 0);")] + #[doc = concat!("assert_eq!((-13_", stringify!($SelfT), ").unbounded_shl(1).unbounded_shl(", stringify!($BITS_MINUS_ONE), "), 0);")] /// ``` #[stable(feature = "unbounded_shifts", since = "1.87.0")] #[rustc_const_stable(feature = "unbounded_shifts", since = "1.87.0")] @@ -1561,11 +1578,8 @@ macro_rules! int_impl { /// i.e. when [`checked_shr`] would return `None`. /// #[doc = concat!("[`checked_shr`]: ", stringify!($SelfT), "::checked_shr")] - #[unstable( - feature = "unchecked_shifts", - reason = "niche optimization path", - issue = "85122", - )] + #[stable(feature = "unchecked_shifts", since = "1.93.0")] + #[rustc_const_stable(feature = "unchecked_shifts", since = "1.93.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1595,9 +1609,16 @@ macro_rules! int_impl { /// # Examples /// /// ``` - #[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!(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);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".unbounded_shr(0), 0b1010);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".unbounded_shr(1), 0b101);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".unbounded_shr(2), 0b10);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".unbounded_shr(", stringify!($BITS), "), 0);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".unbounded_shr(1).unbounded_shr(", stringify!($BITS_MINUS_ONE), "), 0);")] + #[doc = concat!("assert_eq!((-13_", stringify!($SelfT), ").unbounded_shr(", stringify!($BITS), "), -1);")] + #[doc = concat!("assert_eq!((-13_", stringify!($SelfT), ").unbounded_shr(1).unbounded_shr(", stringify!($BITS_MINUS_ONE), "), -1);")] /// ``` #[stable(feature = "unbounded_shifts", since = "1.87.0")] #[rustc_const_stable(feature = "unbounded_shifts", since = "1.87.0")] @@ -2291,6 +2312,13 @@ macro_rules! int_impl { /// Panic-free bitwise shift-left; yields `self << mask(rhs)`, where `mask` removes /// any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. /// + /// Beware that, unlike most other `wrapping_*` methods on integers, this + /// does *not* give the same result as doing the shift in infinite precision + /// then truncating as needed. The behaviour matches what shift instructions + /// do on many processors, and is what the `<<` operator does when overflow + /// checks are disabled, but numerically it's weird. Consider, instead, + /// using [`Self::unbounded_shl`] which has nicer behaviour. + /// /// Note that this is *not* the same as a rotate-left; the RHS of a wrapping shift-left is restricted to /// the range of the type, rather than the bits shifted out of the LHS being returned to the other end. /// The primitive integer types all implement a [`rotate_left`](Self::rotate_left) function, @@ -2299,8 +2327,11 @@ macro_rules! int_impl { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(7), -128);")] - #[doc = concat!("assert_eq!((-1", stringify!($SelfT), ").wrapping_shl(128), -1);")] + #[doc = concat!("assert_eq!((-1_", stringify!($SelfT), ").wrapping_shl(7), -128);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shl(", stringify!($BITS), "), 42);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shl(1).wrapping_shl(", stringify!($BITS_MINUS_ONE), "), 0);")] + #[doc = concat!("assert_eq!((-1_", stringify!($SelfT), ").wrapping_shl(128), -1);")] + #[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".wrapping_shl(1025), 10);")] /// ``` #[stable(feature = "num_wrapping", since = "1.2.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] @@ -2319,6 +2350,13 @@ macro_rules! int_impl { /// Panic-free bitwise shift-right; yields `self >> mask(rhs)`, where `mask` /// removes any high-order bits of `rhs` that would cause the shift to exceed the bitwidth of the type. /// + /// Beware that, unlike most other `wrapping_*` methods on integers, this + /// does *not* give the same result as doing the shift in infinite precision + /// then truncating as needed. The behaviour matches what shift instructions + /// do on many processors, and is what the `>>` operator does when overflow + /// checks are disabled, but numerically it's weird. Consider, instead, + /// using [`Self::unbounded_shr`] which has nicer behaviour. + /// /// Note that this is *not* the same as a rotate-right; the RHS of a wrapping shift-right is restricted /// to the range of the type, rather than the bits shifted out of the LHS being returned to the other /// end. The primitive integer types all implement a [`rotate_right`](Self::rotate_right) function, @@ -2327,8 +2365,11 @@ macro_rules! int_impl { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!((-128", stringify!($SelfT), ").wrapping_shr(7), -1);")] - /// assert_eq!((-128i16).wrapping_shr(64), -128); + #[doc = concat!("assert_eq!((-128_", stringify!($SelfT), ").wrapping_shr(7), -1);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shr(", stringify!($BITS), "), 42);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shr(1).wrapping_shr(", stringify!($BITS_MINUS_ONE), "), 0);")] + /// assert_eq!((-128_i16).wrapping_shr(64), -128); + #[doc = concat!("assert_eq!(10_", stringify!($SelfT), ".wrapping_shr(1025), 5);")] /// ``` #[stable(feature = "num_wrapping", since = "1.2.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] @@ -2495,7 +2536,7 @@ macro_rules! int_impl { /// # Examples /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// // Only the most significant word is signed. /// // #[doc = concat!("// 10 MAX (a = 10 × 2^", stringify!($BITS), " + 2^", stringify!($BITS), " - 1)")] @@ -2517,7 +2558,7 @@ macro_rules! int_impl { /// /// assert_eq!((sum1, sum0), (6, 8)); /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2598,7 +2639,7 @@ macro_rules! int_impl { /// # Examples /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// // Only the most significant word is signed. /// // #[doc = concat!("// 6 8 (a = 6 × 2^", stringify!($BITS), " + 8)")] @@ -2620,7 +2661,7 @@ macro_rules! int_impl { /// #[doc = concat!("assert_eq!((diff1, diff0), (10, ", stringify!($UnsignedT), "::MAX));")] /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2690,12 +2731,12 @@ macro_rules! int_impl { /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] /// assert_eq!(5i32.widening_mul(-2), (4294967286, -1)); /// assert_eq!(1_000_000_000i32.widening_mul(-10), (2884901888, -3)); /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "widening_mul", issue = "152016")] + #[rustc_const_unstable(feature = "widening_mul", issue = "152016")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2720,7 +2761,7 @@ macro_rules! int_impl { /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// assert_eq!(5i32.carrying_mul(-2, 0), (4294967286, -1)); /// assert_eq!(5i32.carrying_mul(-2, 10), (0, 0)); /// assert_eq!(1_000_000_000i32.carrying_mul(-10, 0), (2884901888, -3)); @@ -2730,8 +2771,8 @@ macro_rules! int_impl { "(", stringify!($SelfT), "::MAX.unsigned_abs() + 1, ", stringify!($SelfT), "::MAX / 2));" )] /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] + #[rustc_const_unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2757,7 +2798,7 @@ macro_rules! int_impl { /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// assert_eq!(5i32.carrying_mul_add(-2, 0, 0), (4294967286, -1)); /// assert_eq!(5i32.carrying_mul_add(-2, 10, 10), (10, 0)); /// assert_eq!(1_000_000_000i32.carrying_mul_add(-10, 0, 0), (2884901888, -3)); @@ -2767,8 +2808,8 @@ macro_rules! int_impl { "(", stringify!($UnsignedT), "::MAX, ", stringify!($SelfT), "::MAX / 2));" )] /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] + #[rustc_const_unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3154,7 +3195,8 @@ macro_rules! int_impl { } - /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// Calculates the least nonnegative remainder of `self` when + /// divided by `rhs`. /// /// This is done as if by the Euclidean division algorithm -- given /// `r = self.rem_euclid(rhs)`, the result satisfies @@ -3534,11 +3576,7 @@ macro_rules! int_impl { without modifying the original"] #[inline] pub const fn checked_ilog10(self) -> Option { - if self > 0 { - Some(int_log10::$ActualT(self as $ActualT)) - } else { - None - } + int_log10::$ActualT(self as $ActualT) } /// Computes the absolute value of `self`. @@ -3885,5 +3923,32 @@ macro_rules! int_impl { pub const fn max_value() -> Self { Self::MAX } + + /// Clamps this number to a symmetric range centred around zero. + /// + /// The method clamps the number's magnitude (absolute value) to be at most `limit`. + /// + /// This is functionally equivalent to `self.clamp(-limit, limit)`, but is more + /// explicit about the intent. + /// + /// # Examples + /// + /// ``` + /// #![feature(clamp_magnitude)] + #[doc = concat!("assert_eq!(120", stringify!($SelfT), ".clamp_magnitude(100), 100);")] + #[doc = concat!("assert_eq!(-120", stringify!($SelfT), ".clamp_magnitude(100), -100);")] + #[doc = concat!("assert_eq!(80", stringify!($SelfT), ".clamp_magnitude(100), 80);")] + #[doc = concat!("assert_eq!(-80", stringify!($SelfT), ".clamp_magnitude(100), -80);")] + /// ``` + #[must_use = "this returns the clamped value and does not modify the original"] + #[unstable(feature = "clamp_magnitude", issue = "148519")] + #[inline] + pub fn clamp_magnitude(self, limit: $UnsignedT) -> Self { + if let Ok(limit) = core::convert::TryInto::<$SelfT>::try_into(limit) { + self.clamp(-limit, limit) + } else { + self + } + } } } diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 3e47debf38f8f..a44f8138c8d42 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -48,6 +48,7 @@ mod int_macros; // import int_impl! mod uint_macros; // import uint_impl! mod error; +mod int_bits; mod int_log10; mod int_sqrt; pub(crate) mod libm; @@ -2174,8 +2175,6 @@ mod verify { checked_f128_to_int_unchecked_i32, i64, checked_f128_to_int_unchecked_i64, - i128, - checked_f128_to_int_unchecked_i128, isize, checked_f128_to_int_unchecked_isize, u8, @@ -2191,4 +2190,25 @@ mod verify { usize, checked_f128_to_int_unchecked_usize ); + + // FIXME(kani): this harness is hand-written (instead of being generated via + // generate_to_int_unchecked_harness! above) because Kani's f128 -> i128 lower bound in + // float_to_int_in_range is unsound (-2^128 instead of -(2^127 + 2^15)), so the contract + // instrumentation admits out-of-range values; see + // https://github.com/model-checking/kani/issues/4662. Until the fix + // (https://github.com/model-checking/kani/pull/4663) is part of the Kani version pinned + // here, constrain the input to the sound range explicitly. + #[kani::proof_for_contract(f128::to_int_unchecked)] + pub fn checked_f128_to_int_unchecked_i128() { + let num1: f128 = kani::any_where(|f: &f128| { + // -(2^127 + 2^15) is the largest f128 whose truncation is below i128::MIN; + // require the value to be strictly above it (and below i128::MAX + 1 = 2^127). + f.is_finite() + && *f > -170141183460469231731687303715884138496.0 + && *f < 170141183460469231731687303715884105728.0 + }); + let result = unsafe { num1.to_int_unchecked::() }; + + assert_eq!(result, num1 as i128); + } } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 63494a5b29d54..de9ac9663c3b2 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1700,7 +1700,7 @@ macro_rules! nonzero_integer_signedness_dependent_methods { without modifying the original"] #[inline] pub const fn ilog10(self) -> u32 { - super::int_log10::$Int(self.get()) + super::int_log10::$Int(self) } /// Calculates the midpoint (average) between `self` and `rhs`. diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 58189da925395..bb199f2ce01f7 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -93,6 +93,28 @@ macro_rules! uint_impl { #[doc = concat!("let max = ", stringify!($SelfT),"::MAX;")] /// assert_eq!(max.count_zeros(), 0); /// ``` + /// + /// This is heavily dependent on the width of the type, and thus + /// might give surprising results depending on type inference: + /// ``` + /// # fn foo(_: u8) {} + /// # fn bar(_: u16) {} + /// let lucky = 7; + /// foo(lucky); + /// assert_eq!(lucky.count_zeros(), 5); + /// assert_eq!(lucky.count_ones(), 3); + /// + /// let lucky = 7; + /// bar(lucky); + /// assert_eq!(lucky.count_zeros(), 13); + /// assert_eq!(lucky.count_ones(), 3); + /// ``` + /// You might want to use [`Self::count_ones`] instead, or emphasize + /// the type you're using in the call rather than method syntax: + /// ``` + /// let small = 1; + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::count_zeros(small), ", stringify!($BITS_MINUS_ONE) ,");")] + /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] #[must_use = "this returns the result of the operation, \ @@ -479,6 +501,40 @@ macro_rules! uint_impl { intrinsics::bswap(self as $ActualT) as Self } + /// Returns an integer with the bit locations specified by `mask` packed + /// contiguously into the least significant bits of the result. + /// ``` + /// #![feature(uint_gather_scatter_bits)] + #[doc = concat!("let n: ", stringify!($SelfT), " = 0b1011_1100;")] + /// + /// assert_eq!(n.extract_bits(0b0010_0100), 0b0000_0011); + /// assert_eq!(n.extract_bits(0xF0), 0b0000_1011); + /// ``` + #[unstable(feature = "uint_gather_scatter_bits", issue = "149069")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn extract_bits(self, mask: Self) -> Self { + crate::num::int_bits::$ActualT::extract_impl(self as $ActualT, mask as $ActualT) as $SelfT + } + + /// Returns an integer with the least significant bits of `self` + /// distributed to the bit locations specified by `mask`. + /// ``` + /// #![feature(uint_gather_scatter_bits)] + #[doc = concat!("let n: ", stringify!($SelfT), " = 0b1010_1101;")] + /// + /// assert_eq!(n.deposit_bits(0b0101_0101), 0b0101_0001); + /// assert_eq!(n.deposit_bits(0xF0), 0b1101_0000); + /// ``` + #[unstable(feature = "uint_gather_scatter_bits", issue = "149069")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn deposit_bits(self, mask: Self) -> Self { + crate::num::int_bits::$ActualT::deposit_impl(self as $ActualT, mask as $ActualT) as $SelfT + } + /// Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, /// second least-significant bit becomes second most-significant bit, etc. /// @@ -1786,11 +1842,8 @@ macro_rules! uint_impl { /// i.e. when [`checked_shl`] would return `None`. /// #[doc = concat!("[`checked_shl`]: ", stringify!($SelfT), "::checked_shl")] - #[unstable( - feature = "unchecked_shifts", - reason = "niche optimization path", - issue = "85122", - )] + #[stable(feature = "unchecked_shifts", since = "1.93.0")] + #[rustc_const_stable(feature = "unchecked_shifts", since = "1.93.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1819,8 +1872,24 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(4), 0x10);")] - #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".unbounded_shl(129), 0);")] + #[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".unbounded_shl(4), 0x10);")] + #[doc = concat!("assert_eq!(0x1_", stringify!($SelfT), ".unbounded_shl(129), 0);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".unbounded_shl(0), 0b101);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".unbounded_shl(1), 0b1010);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".unbounded_shl(2), 0b10100);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".unbounded_shl(", stringify!($BITS), "), 0);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".unbounded_shl(1).unbounded_shl(", stringify!($BITS_MINUS_ONE), "), 0);")] + /// + #[doc = concat!("let start : ", stringify!($SelfT), " = 13;")] + /// let mut running = start; + /// for i in 0..160 { + /// // The unbounded shift left by i is the same as `<< 1` i times + /// assert_eq!(running, start.unbounded_shl(i)); + /// // Which is not always the case for a wrapping shift + #[doc = concat!(" assert_eq!(running == start.wrapping_shl(i), i < ", stringify!($BITS), ");")] + /// + /// running <<= 1; + /// } /// ``` #[stable(feature = "unbounded_shifts", since = "1.87.0")] #[rustc_const_stable(feature = "unbounded_shifts", since = "1.87.0")] @@ -1960,11 +2029,8 @@ macro_rules! uint_impl { /// i.e. when [`checked_shr`] would return `None`. /// #[doc = concat!("[`checked_shr`]: ", stringify!($SelfT), "::checked_shr")] - #[unstable( - feature = "unchecked_shifts", - reason = "niche optimization path", - issue = "85122", - )] + #[stable(feature = "unchecked_shifts", since = "1.93.0")] + #[rustc_const_stable(feature = "unchecked_shifts", since = "1.93.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline(always)] @@ -1993,8 +2059,24 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - #[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!(0x10_", stringify!($SelfT), ".unbounded_shr(4), 0x1);")] + #[doc = concat!("assert_eq!(0x10_", stringify!($SelfT), ".unbounded_shr(129), 0);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".unbounded_shr(0), 0b1010);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".unbounded_shr(1), 0b101);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".unbounded_shr(2), 0b10);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".unbounded_shr(", stringify!($BITS), "), 0);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".unbounded_shr(1).unbounded_shr(", stringify!($BITS_MINUS_ONE), "), 0);")] + /// + #[doc = concat!("let start = ", stringify!($SelfT), "::rotate_right(13, 4);")] + /// let mut running = start; + /// for i in 0..160 { + /// // The unbounded shift right by i is the same as `>> 1` i times + /// assert_eq!(running, start.unbounded_shr(i)); + /// // Which is not always the case for a wrapping shift + #[doc = concat!(" assert_eq!(running == start.wrapping_shr(i), i < ", stringify!($BITS), ");")] + /// + /// running >>= 1; + /// } /// ``` #[stable(feature = "unbounded_shifts", since = "1.87.0")] #[rustc_const_stable(feature = "unbounded_shifts", since = "1.87.0")] @@ -2539,6 +2621,13 @@ macro_rules! uint_impl { /// where `mask` removes any high-order bits of `rhs` that /// would cause the shift to exceed the bitwidth of the type. /// + /// Beware that, unlike most other `wrapping_*` methods on integers, this + /// does *not* give the same result as doing the shift in infinite precision + /// then truncating as needed. The behaviour matches what shift instructions + /// do on many processors, and is what the `<<` operator does when overflow + /// checks are disabled, but numerically it's weird. Consider, instead, + /// using [`Self::unbounded_shl`] which has nicer behaviour. + /// /// Note that this is *not* the same as a rotate-left; the /// RHS of a wrapping shift-left is restricted to the range /// of the type, rather than the bits shifted out of the LHS @@ -2549,8 +2638,15 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_shl(7), 128);")] - #[doc = concat!("assert_eq!(1", stringify!($SelfT), ".wrapping_shl(128), 1);")] + #[doc = concat!("assert_eq!(1_", stringify!($SelfT), ".wrapping_shl(7), 128);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".wrapping_shl(0), 0b101);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".wrapping_shl(1), 0b1010);")] + #[doc = concat!("assert_eq!(0b101_", stringify!($SelfT), ".wrapping_shl(2), 0b10100);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_shl(2), ", stringify!($SelfT), "::MAX - 3);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shl(", stringify!($BITS), "), 42);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shl(1).wrapping_shl(", stringify!($BITS_MINUS_ONE), "), 0);")] + #[doc = concat!("assert_eq!(1_", stringify!($SelfT), ".wrapping_shl(128), 1);")] + #[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".wrapping_shl(1025), 10);")] /// ``` #[stable(feature = "num_wrapping", since = "1.2.0")] #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] @@ -2570,6 +2666,13 @@ macro_rules! uint_impl { /// where `mask` removes any high-order bits of `rhs` that /// would cause the shift to exceed the bitwidth of the type. /// + /// Beware that, unlike most other `wrapping_*` methods on integers, this + /// does *not* give the same result as doing the shift in infinite precision + /// then truncating as needed. The behaviour matches what shift instructions + /// do on many processors, and is what the `>>` operator does when overflow + /// checks are disabled, but numerically it's weird. Consider, instead, + /// using [`Self::unbounded_shr`] which has nicer behaviour. + /// /// Note that this is *not* the same as a rotate-right; the /// RHS of a wrapping shift-right is restricted to the range /// of the type, rather than the bits shifted out of the LHS @@ -2580,8 +2683,15 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".wrapping_shr(7), 1);")] - #[doc = concat!("assert_eq!(128", stringify!($SelfT), ".wrapping_shr(128), 128);")] + #[doc = concat!("assert_eq!(128_", stringify!($SelfT), ".wrapping_shr(7), 1);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".wrapping_shr(0), 0b1010);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".wrapping_shr(1), 0b101);")] + #[doc = concat!("assert_eq!(0b1010_", stringify!($SelfT), ".wrapping_shr(2), 0b10);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.wrapping_shr(1), ", stringify!($SignedT), "::MAX.cast_unsigned());")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shr(", stringify!($BITS), "), 42);")] + #[doc = concat!("assert_eq!(42_", stringify!($SelfT), ".wrapping_shr(1).wrapping_shr(", stringify!($BITS_MINUS_ONE), "), 0);")] + #[doc = concat!("assert_eq!(128_", stringify!($SelfT), ".wrapping_shr(128), 128);")] + #[doc = concat!("assert_eq!(10_", stringify!($SelfT), ".wrapping_shr(1025), 5);")] /// ``` #[stable(feature = "num_wrapping", since = "1.2.0")] #[rustc_const_stable(feature = "const_wrapping_math", since = "1.32.0")] @@ -2711,7 +2821,7 @@ macro_rules! uint_impl { /// assert_eq!((sum1, sum0), (9, 6)); /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2803,7 +2913,7 @@ macro_rules! uint_impl { #[doc = concat!("assert_eq!((diff1, diff0), (3, ", stringify!($SelfT), "::MAX));")] /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2915,14 +3025,14 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] #[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".widening_mul(7), (35, 0));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.widening_mul(", stringify!($SelfT), "::MAX), (1, ", stringify!($SelfT), "::MAX - 1));")] /// ``` /// /// Compared to other `*_mul` methods: /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::widening_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), (0, 3));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::overflowing_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), (0, true));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::wrapping_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), 0);")] @@ -2932,12 +3042,12 @@ macro_rules! uint_impl { /// Please note that this example is shared among integer types, which is why `u32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] /// assert_eq!(5u32.widening_mul(2), (10, 0)); /// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2)); /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "widening_mul", issue = "152016")] + #[rustc_const_unstable(feature = "widening_mul", issue = "152016")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2976,7 +3086,7 @@ macro_rules! uint_impl { /// implementing it for wider-than-native types. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(const_unsigned_bigint_helpers)] /// fn scalar_mul_eq(little_endian_digits: &mut Vec, multiplicand: u16) { /// let mut carry = 0; /// for d in little_endian_digits.iter_mut() { @@ -3001,7 +3111,7 @@ macro_rules! uint_impl { /// except that it gives the value of the overflow instead of just whether one happened: /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(const_unsigned_bigint_helpers)] /// let r = u8::carrying_mul(7, 13, 0); /// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13)); /// let r = u8::carrying_mul(13, 42, 0); @@ -3013,14 +3123,14 @@ macro_rules! uint_impl { /// [`wrapping_add`](Self::wrapping_add) methods: /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(const_unsigned_bigint_helpers)] /// assert_eq!( /// 789_u16.carrying_mul(456, 123).0, /// 789_u16.wrapping_mul(456).wrapping_add(123), /// ); /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3086,7 +3196,7 @@ macro_rules! uint_impl { /// ); /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3432,7 +3542,8 @@ macro_rules! uint_impl { } - /// Calculates the least remainder of `self (mod rhs)`. + /// Calculates the least remainder of `self` when divided by + /// `rhs`. /// /// Since, for the positive integers, all common /// definitions of division are equal, this diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs index 479368ba8f801..efe5c0871fb8b 100644 --- a/library/core/src/ops/function.rs +++ b/library/core/src/ops/function.rs @@ -73,7 +73,7 @@ use crate::marker::Tuple; #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] #[rustc_const_unstable(feature = "const_trait_impl", issue = "143874")] -pub const trait Fn: FnMut { +pub const trait Fn: [const] FnMut { /// Performs the call operation. #[unstable(feature = "fn_traits", issue = "29625")] extern "rust-call" fn call(&self, args: Args) -> Self::Output; diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index a0b74ff383ea4..c15c8f20c16be 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -773,7 +773,7 @@ impl Bound<&T> { /// ``` #[unstable(feature = "bound_copied", issue = "145966")] #[must_use] - pub fn copied(self) -> Bound { + pub const fn copied(self) -> Bound { match self { Bound::Unbounded => Bound::Unbounded, Bound::Included(x) => Bound::Included(*x), diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index 204291886589e..34000f6d6b218 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -1,3 +1,4 @@ +use crate::marker::{Destruct, PhantomData}; use crate::ops::ControlFlow; /// The `?` operator and `try {}` blocks. @@ -363,6 +364,7 @@ where pub const trait Residual: Sized { /// The "return" type of this meta-function. #[unstable(feature = "try_trait_v2_residual", issue = "91285")] + // FIXME: ought to be implied type TryType: [const] Try; } @@ -396,6 +398,25 @@ pub(crate) type ChangeOutputType>, V> = /// Not currently planned to be exposed publicly, so just `pub(crate)`. #[repr(transparent)] pub(crate) struct NeverShortCircuit(pub T); +// FIXME(const-hack): replace with `|a| NeverShortCircuit(f(a))` when const closures added. +pub(crate) struct Wrapped T> { + f: F, + p: PhantomData<(T, A)>, +} +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl T + [const] Destruct> const FnOnce<(A,)> for Wrapped { + type Output = NeverShortCircuit; + + extern "rust-call" fn call_once(mut self, args: (A,)) -> Self::Output { + self.call_mut(args) + } +} +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl T> const FnMut<(A,)> for Wrapped { + extern "rust-call" fn call_mut(&mut self, (args,): (A,)) -> Self::Output { + NeverShortCircuit((self.f)(args)) + } +} impl NeverShortCircuit { /// Wraps a unary function to produce one that wraps the output into a `NeverShortCircuit`. @@ -403,10 +424,11 @@ impl NeverShortCircuit { /// This is useful for implementing infallible functions in terms of the `try_` ones, /// without accidentally capturing extra generic parameters in a closure. #[inline] - pub(crate) fn wrap_mut_1( - mut f: impl FnMut(A) -> T, - ) -> impl FnMut(A) -> NeverShortCircuit { - move |a| NeverShortCircuit(f(a)) + pub(crate) const fn wrap_mut_1(f: F) -> Wrapped + where + F: [const] FnMut(A) -> T, + { + Wrapped { f, p: PhantomData } } #[inline] @@ -417,7 +439,8 @@ impl NeverShortCircuit { pub(crate) enum NeverShortCircuitResidual {} -impl Try for NeverShortCircuit { +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl const Try for NeverShortCircuit { type Output = T; type Residual = NeverShortCircuitResidual; @@ -431,15 +454,15 @@ impl Try for NeverShortCircuit { NeverShortCircuit(x) } } - -impl FromResidual for NeverShortCircuit { +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl const FromResidual for NeverShortCircuit { #[inline] fn from_residual(never: NeverShortCircuitResidual) -> Self { match never {} } } - -impl Residual for NeverShortCircuitResidual { +#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] +impl const Residual for NeverShortCircuitResidual { type TryType = NeverShortCircuit; } diff --git a/library/core/src/option.rs b/library/core/src/option.rs index aad6d244ef769..6c898c38b99e2 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -584,7 +584,7 @@ use crate::clone::TrivialClone; use crate::iter::{self, FusedIterator, TrustedLen}; use crate::marker::Destruct; -use crate::ops::{self, ControlFlow, Deref, DerefMut}; +use crate::ops::{self, ControlFlow, Deref, DerefMut, Residual, Try}; use crate::panicking::{panic, panic_display}; use crate::pin::Pin; use crate::{cmp, convert, hint, mem, slice}; @@ -1816,6 +1816,49 @@ impl Option { unsafe { self.as_mut().unwrap_unchecked() } } + /// If the option is `None`, calls the closure and inserts its output if successful. + /// + /// If the closure returns a residual value such as `Err` or `None`, + /// that residual value is returned and nothing is inserted. + /// + /// If the option is `Some`, nothing is inserted. + /// + /// Unless a residual is returned, a mutable reference to the value + /// of the option will be output. + /// + /// # Examples + /// + /// ``` + /// #![feature(option_get_or_try_insert_with)] + /// let mut o1: Option = None; + /// let mut o2: Option = None; + /// + /// let number = "12345"; + /// + /// assert_eq!(o1.get_or_try_insert_with(|| number.parse()).copied(), Ok(12345)); + /// assert!(o2.get_or_try_insert_with(|| number.parse()).is_err()); + /// assert_eq!(o1, Some(12345)); + /// assert_eq!(o2, None); + /// ``` + #[inline] + #[unstable(feature = "option_get_or_try_insert_with", issue = "143648")] + pub fn get_or_try_insert_with<'a, R, F>( + &'a mut self, + f: F, + ) -> >::TryType + where + F: FnOnce() -> R, + R: Try>, + { + if let None = self { + *self = Some(f()?); + } + // SAFETY: a `None` variant for `self` would have been replaced by a `Some` + // variant in the code above. + + Try::from_output(unsafe { self.as_mut().unwrap_unchecked() }) + } + ///////////////////////////////////////////////////////////////////////// // Misc ///////////////////////////////////////////////////////////////////////// @@ -2008,6 +2051,30 @@ impl Option { } } +impl Option { + /// Transforms an optional iterator into an iterator. + /// + /// If `self` is `None`, the resulting iterator is empty. + /// Otherwise, an iterator is made from the `Some` value and returned. + /// # Examples + /// ``` + /// #![feature(option_into_flat_iter)] + /// + /// let o1 = Some([1, 2]); + /// let o2 = None::<&[usize]>; + /// + /// assert_eq!(o1.into_flat_iter().collect::>(), [1, 2]); + /// assert_eq!(o2.into_flat_iter().collect::>(), Vec::<&usize>::new()); + /// ``` + #[unstable(feature = "option_into_flat_iter", issue = "148441")] + pub fn into_flat_iter(self) -> OptionFlatten + where + T: IntoIterator, + { + OptionFlatten { iter: self.map(IntoIterator::into_iter) } + } +} + impl Option<(T, U)> { /// Unzips an option containing a tuple of two options. /// @@ -2079,10 +2146,7 @@ impl Option<&T> { where T: Clone, { - match self { - Some(t) => Some(t.clone()), - None => None, - } + self.map(T::clone) } } @@ -2130,10 +2194,7 @@ impl Option<&mut T> { where T: Clone, { - match self { - Some(t) => Some(t.clone()), - None => None, - } + self.as_deref().map(T::clone) } } @@ -2239,7 +2300,8 @@ impl const Default for Option { } #[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for Option { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +impl const IntoIterator for Option { type Item = T; type IntoIter = IntoIter; @@ -2411,7 +2473,8 @@ struct Item { opt: Option, } -impl Iterator for Item { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +impl const Iterator for Item { type Item = A; #[inline] @@ -2421,7 +2484,7 @@ impl Iterator for Item { #[inline] fn size_hint(&self) -> (usize, Option) { - let len = self.len(); + let len = self.opt.len(); (len, Some(len)) } } @@ -2545,7 +2608,8 @@ pub struct IntoIter { } #[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { +#[rustc_const_unstable(feature = "const_iter", issue = "92476")] +impl const Iterator for IntoIter { type Item = A; #[inline] @@ -2575,6 +2639,42 @@ impl FusedIterator for IntoIter {} #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for IntoIter {} +/// The iterator produced by [`Option::into_flat_iter`]. See its documentation for more. +#[derive(Clone, Debug)] +#[unstable(feature = "option_into_flat_iter", issue = "148441")] +pub struct OptionFlatten { + iter: Option, +} + +#[unstable(feature = "option_into_flat_iter", issue = "148441")] +impl Iterator for OptionFlatten { + type Item = A::Item; + + fn next(&mut self) -> Option { + self.iter.as_mut()?.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.as_ref().map(|i| i.size_hint()).unwrap_or((0, Some(0))) + } +} + +#[unstable(feature = "option_into_flat_iter", issue = "148441")] +impl DoubleEndedIterator for OptionFlatten { + fn next_back(&mut self) -> Option { + self.iter.as_mut()?.next_back() + } +} + +#[unstable(feature = "option_into_flat_iter", issue = "148441")] +impl ExactSizeIterator for OptionFlatten {} + +#[unstable(feature = "option_into_flat_iter", issue = "148441")] +impl FusedIterator for OptionFlatten {} + +#[unstable(feature = "option_into_flat_iter", issue = "148441")] +unsafe impl TrustedLen for OptionFlatten {} + ///////////////////////////////////////////////////////////////////////////// // FromIterator ///////////////////////////////////////////////////////////////////////////// @@ -2737,6 +2837,95 @@ impl Option> { } } +impl<'a, T> Option<&'a Option> { + /// Converts from `Option<&Option>` to `Option<&T>`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(option_reference_flattening)] + /// + /// let x: Option<&Option> = Some(&Some(6)); + /// assert_eq!(Some(&6), x.flatten_ref()); + /// + /// let x: Option<&Option> = Some(&None); + /// assert_eq!(None, x.flatten_ref()); + /// + /// let x: Option<&Option> = None; + /// assert_eq!(None, x.flatten_ref()); + /// ``` + #[inline] + #[unstable(feature = "option_reference_flattening", issue = "149221")] + pub const fn flatten_ref(self) -> Option<&'a T> { + match self { + Some(inner) => inner.as_ref(), + None => None, + } + } +} + +impl<'a, T> Option<&'a mut Option> { + /// Converts from `Option<&mut Option>` to `&Option`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(option_reference_flattening)] + /// + /// let y = &mut Some(6); + /// let x: Option<&mut Option> = Some(y); + /// assert_eq!(Some(&6), x.flatten_ref()); + /// + /// let y: &mut Option = &mut None; + /// let x: Option<&mut Option> = Some(y); + /// assert_eq!(None, x.flatten_ref()); + /// + /// let x: Option<&mut Option> = None; + /// assert_eq!(None, x.flatten_ref()); + /// ``` + #[inline] + #[unstable(feature = "option_reference_flattening", issue = "149221")] + pub const fn flatten_ref(self) -> Option<&'a T> { + match self { + Some(inner) => inner.as_ref(), + None => None, + } + } + + /// Converts from `Option<&mut Option>` to `Option<&mut T>`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(option_reference_flattening)] + /// + /// let y: &mut Option = &mut Some(6); + /// let x: Option<&mut Option> = Some(y); + /// assert_eq!(Some(&mut 6), x.flatten_mut()); + /// + /// let y: &mut Option = &mut None; + /// let x: Option<&mut Option> = Some(y); + /// assert_eq!(None, x.flatten_mut()); + /// + /// let x: Option<&mut Option> = None; + /// assert_eq!(None, x.flatten_mut()); + /// ``` + #[inline] + #[unstable(feature = "option_reference_flattening", issue = "149221")] + pub const fn flatten_mut(self) -> Option<&'a mut T> { + match self { + Some(inner) => inner.as_mut(), + None => None, + } + } +} + impl [Option; N] { /// Transposes a `[Option; N]` into a `Option<[T; N]>`. /// diff --git a/library/core/src/os/mod.rs b/library/core/src/os/mod.rs index 897f59f530ed0..5ac2b637941ae 100644 --- a/library/core/src/os/mod.rs +++ b/library/core/src/os/mod.rs @@ -1,6 +1,7 @@ //! OS-specific functionality. #![unstable(feature = "darwin_objc", issue = "145496")] +#![allow(missing_docs)] #[cfg(all( doc, diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 7a42b3d0fc780..cf07504466808 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -20,7 +20,7 @@ use crate::any::Any; #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] #[allow_internal_unstable(panic_internals, const_format_args)] #[rustc_diagnostic_item = "core_panic_2015_macro"] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro panic_2015 { () => ( $crate::panicking::panic("explicit panic") @@ -47,7 +47,7 @@ pub macro panic_2015 { #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] #[allow_internal_unstable(panic_internals, const_format_args)] #[rustc_diagnostic_item = "core_panic_2021_macro"] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro panic_2021 { () => ( $crate::panicking::panic("explicit panic") @@ -67,7 +67,7 @@ pub macro panic_2021 { #[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")] #[allow_internal_unstable(panic_internals)] #[rustc_diagnostic_item = "unreachable_2015_macro"] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro unreachable_2015 { () => ( $crate::panicking::panic("internal error: entered unreachable code") @@ -85,7 +85,7 @@ pub macro unreachable_2015 { #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")] #[allow_internal_unstable(panic_internals)] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro unreachable_2021 { () => ( $crate::panicking::panic("internal error: entered unreachable code") @@ -166,10 +166,9 @@ pub macro const_panic { const fn do_panic($($arg: $ty),*) -> ! { $crate::intrinsics::const_eval_select!( @capture { $($arg: $ty = $arg),* } -> !: - #[noinline] - if const #[track_caller] #[inline] { // Inline this, to prevent codegen + if const #[track_caller] { $crate::panic!($const_msg) - } else #[track_caller] { // Do not inline this, it makes perf worse + } else #[track_caller] { $crate::panic!($runtime_msg) } ) @@ -195,7 +194,7 @@ pub macro const_panic { #[doc(hidden)] pub macro const_assert { ($condition: expr, $const_msg:literal, $runtime_msg:literal, $($arg:tt)*) => {{ - if !$crate::intrinsics::likely($condition) { + if !($condition) { $crate::panic::const_panic!($const_msg, $runtime_msg, $($arg)*) } }} diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 8176af03d13a5..f37f5370997e1 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -105,38 +105,72 @@ impl<'a> Location<'a> { /// ```standalone_crate /// use std::panic::Location; /// - /// /// Returns the [`Location`] at which it is called. + /// /// ``` + /// /// |1 |11 |21 |31 |41 + /// /// +-|---------|---------|---------|---------|-------- + /// /// 15 | #[track_caller] + /// /// 16 | fn new_location() -> &'static Location<'static> { + /// /// 17 | Location::caller() + /// /// | ------------------| the value of this expression depends on the caller, + /// /// | | since the function is marked #[track_caller] + /// /// 18 | } + /// /// ``` /// #[track_caller] - /// fn get_caller_location() -> &'static Location<'static> { + /// fn new_location() -> &'static Location<'static> { /// Location::caller() /// } /// - /// /// Returns a [`Location`] from within this function's definition. - /// fn get_just_one_location() -> &'static Location<'static> { - /// get_caller_location() + /// /// ``` + /// /// |1 |5 |11 |21 |31 |41 |51 + /// /// +-|---|-----|---------|---------|---------|---------|--- + /// /// 29 | fn constant_location() -> &'static Location<'static> { + /// /// 30 | new_location() + /// /// | ^ any invocation of constant_location() points here, + /// /// | no matter the location it is called from + /// /// 31 | } + /// /// ``` + /// fn constant_location() -> &'static Location<'static> { + /// new_location() /// } /// - /// let fixed_location = get_just_one_location(); - /// assert_eq!(fixed_location.file(), file!()); - /// assert_eq!(fixed_location.line(), 14); - /// assert_eq!(fixed_location.column(), 5); + /// fn main() { + /// // |1 |5 |11 |21 |31 |41 |51 + /// // +-|---|-----|---------|---------|---------|---------|--- + /// // 29 | fn constant_location() -> &'static Location<'static> { + /// // 30 | new_location() + /// // | ^ `let constant` points here + /// // 31 | } + /// let constant = constant_location(); + /// assert_eq!(constant.file(), file!()); + /// assert_eq!((constant.line(), constant.column()), (30, 5)); /// - /// // running the same untracked function in a different location gives us the same result - /// let second_fixed_location = get_just_one_location(); - /// assert_eq!(fixed_location.file(), second_fixed_location.file()); - /// assert_eq!(fixed_location.line(), second_fixed_location.line()); - /// assert_eq!(fixed_location.column(), second_fixed_location.column()); + /// let constant_2 = constant_location(); + /// assert_eq!( + /// (constant.file(), constant.line(), constant.column()), + /// (constant_2.file(), constant_2.line(), constant_2.column()) + /// ); /// - /// let this_location = get_caller_location(); - /// assert_eq!(this_location.file(), file!()); - /// assert_eq!(this_location.line(), 28); - /// assert_eq!(this_location.column(), 21); + /// // |1 |11 |16 |21 |31 + /// // +-|---------|----|----|---------|------ + /// // 55 | let here = new_location(); + /// // | ^ `let here` points here, as `new_location()` is the callsite + /// // 56 | assert_eq!(here.file(), file!()); + /// let here = new_location(); + /// assert_eq!(here.file(), file!()); + /// assert_eq!((here.line(), here.column()), (55, 16)); /// - /// // running the tracked function in a different location produces a different value - /// let another_location = get_caller_location(); - /// assert_eq!(this_location.file(), another_location.file()); - /// assert_ne!(this_location.line(), another_location.line()); - /// assert_ne!(this_location.column(), another_location.column()); + /// // |1 |11 |21 ||32 |41 |51 + /// // +-|---------|---------|---------||--------|---------|------ + /// // 64 | let yet_another_location = new_location(); + /// // | ^ `let yet_another_location` points here + /// // 65 | assert_eq!(here.file(), yet_another_location.file()); + /// let yet_another_location = new_location(); + /// assert_eq!(here.file(), yet_another_location.file()); + /// assert_ne!( + /// (here.line(), here.column()), + /// (yet_another_location.line(), yet_another_location.column()) + /// ); + /// } /// ``` #[must_use] #[stable(feature = "track_caller", since = "1.46.0")] diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 81c2dabf0d1d8..c4981facc04c3 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -831,15 +831,13 @@ //! fn get_pin_mut(self: [Pin]<[`&mut Self`]>) -> [Pin]<[`&mut T`]>. //! Then we could do the following: //! ```compile_fail -//! # use std::cell::RefCell; -//! # use std::pin::Pin; -//! fn exploit_ref_cell(rc: Pin<&mut RefCell>) { +//! fn exploit_ref_cell(mut rc: Pin<&mut RefCell>) { //! // Here we get pinned access to the `T`. //! let _: Pin<&mut T> = rc.as_mut().get_pin_mut(); //! //! // And here we have `&mut T` to the same data. //! let shared: &RefCell = rc.into_ref().get_ref(); -//! let borrow = shared.borrow_mut(); +//! let mut borrow = shared.borrow_mut(); //! let content = &mut *borrow; //! } //! ``` @@ -1825,7 +1823,7 @@ where { } -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] /// Trait that indicates that this is a pointer or a wrapper for one, where /// unsizing can be performed on the pointee when it is pinned. /// @@ -2027,7 +2025,7 @@ unsafe impl PinCoerceUnsized for *mut T {} /// /// [`Box::pin`]: ../../std/boxed/struct.Box.html#method.pin #[stable(feature = "pin_macro", since = "1.68.0")] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] #[allow_internal_unstable(super_let)] #[rustc_diagnostic_item = "pin_macro"] // `super` gets removed by rustfmt diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index a4be66b90cab3..354be271ff131 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -59,12 +59,31 @@ pub use crate::hash::macros::Hash; #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[doc(no_inline)] +#[expect(deprecated)] pub use crate::{ - assert, cfg, column, compile_error, concat, env, file, format_args, - format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, - stringify, trace_macros, + assert, assert_eq, assert_ne, cfg, column, compile_error, concat, debug_assert, debug_assert_eq, + debug_assert_ne, file, format_args, include, include_bytes, include_str, line, matches, + module_path, option_env, stringify, todo, r#try, unimplemented, unreachable, write, writeln, }; +// These macros need special handling, so that we don't export them *and* the modules of the same +// name. We only want the macros in the prelude so we shadow the original modules with private +// modules with the same names. +mod ambiguous_macros_only { + mod env {} + #[expect(hidden_glob_reexports)] + mod panic {} + #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] + pub use crate::*; +} +#[stable(feature = "builtin_macro_prelude", since = "1.38.0")] +#[doc(no_inline)] +pub use self::ambiguous_macros_only::{env, panic}; + +#[unstable(feature = "cfg_select", issue = "115585")] +#[doc(no_inline)] +pub use crate::cfg_select; + #[unstable( feature = "concat_bytes", issue = "87555", @@ -73,6 +92,30 @@ pub use crate::{ #[doc(no_inline)] pub use crate::concat_bytes; +#[unstable(feature = "const_format_args", issue = "none")] +#[doc(no_inline)] +pub use crate::const_format_args; + +#[unstable( + feature = "log_syntax", + issue = "29598", + reason = "`log_syntax!` is not stable enough for use and is subject to change" +)] +#[doc(no_inline)] +pub use crate::log_syntax; + +#[unstable(feature = "pattern_type_macro", issue = "123646")] +#[doc(no_inline)] +pub use crate::pattern_type; + +#[unstable( + feature = "trace_macros", + issue = "29598", + reason = "`trace_macros` is not stable enough for use and is subject to change" +)] +#[doc(no_inline)] +pub use crate::trace_macros; + // Do not `doc(no_inline)` so that they become doc items on their own // (no public module for them to be re-exported from). #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] @@ -117,3 +160,9 @@ pub use crate::macros::builtin::deref; reason = "`type_alias_impl_trait` has open design concerns" )] pub use crate::macros::builtin::define_opaque; + +#[unstable(feature = "extern_item_impls", issue = "125418")] +pub use crate::macros::builtin::{eii, unsafe_eii}; + +#[unstable(feature = "eii_internals", issue = "none")] +pub use crate::macros::builtin::eii_declaration; diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 15ba72bccaa9b..3a4e1e657a3da 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -591,7 +591,7 @@ impl () {} /// # pub unsafe fn malloc(_size: usize) -> *mut core::ffi::c_void { core::ptr::NonNull::dangling().as_ptr() } /// # pub unsafe fn free(_ptr: *mut core::ffi::c_void) {} /// # } -/// # #[cfg(any())] +/// # #[cfg(false)] /// #[allow(unused_extern_crates)] /// extern crate libc; /// diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 21e77700744ef..b14277428b557 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -4,6 +4,7 @@ use safety::{ensures, invariant, requires}; #[cfg(kani)] use crate::kani; +use crate::marker::MetaSized; use crate::num::NonZero; #[cfg(kani)] use crate::ub_checks::Invariant; @@ -18,10 +19,15 @@ use crate::{cmp, fmt, hash, mem, num}; #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[derive(Copy, Clone, PartialEq, Eq)] #[repr(transparent)] -// uses .0 instead of .as_usize() to permit proving as_usize so that its proof does not itself use -// as_usize -#[invariant((self.0 as usize).is_power_of_two())] -pub struct Alignment(AlignmentEnum); +// uses the field directly instead of .as_usize() to permit proving as_usize so that its proof +// does not itself use as_usize +#[invariant((self._inner_repr_trick as usize).is_power_of_two())] +pub struct Alignment { + // This field is never used directly (nor is the enum), + // as it's just there to convey the validity invariant. + // (Hopefully it'll eventually be a pattern type instead.) + _inner_repr_trick: AlignmentEnum, +} // Alignment is `repr(usize)`, but via extra steps. const _: () = assert!(size_of::() == size_of::()); @@ -45,7 +51,7 @@ impl Alignment { /// assert_eq!(Alignment::MIN.as_usize(), 1); /// ``` #[unstable(feature = "ptr_alignment_type", issue = "102070")] - pub const MIN: Self = Self(AlignmentEnum::_Align1Shl0); + pub const MIN: Self = Self::new(1).unwrap(); /// Returns the alignment for a type. /// @@ -61,6 +67,79 @@ impl Alignment { const { Alignment::new(align_of::()).unwrap() } } + /// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. + /// + /// Every reference to a value of the type `T` must be a multiple of this number. + /// + /// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface + /// + /// # Examples + /// + /// ``` + /// #![feature(ptr_alignment_type)] + /// use std::ptr::Alignment; + /// + /// assert_eq!(Alignment::of_val(&5i32).as_usize(), 4); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ptr_alignment_type", issue = "102070")] + pub const fn of_val(val: &T) -> Self { + let align = mem::align_of_val(val); + // SAFETY: `align_of_val` returns valid alignment + unsafe { Alignment::new_unchecked(align) } + } + + /// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. + /// + /// Every reference to a value of the type `T` must be a multiple of this number. + /// + /// [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface + /// + /// # Safety + /// + /// This function is only safe to call if the following conditions hold: + /// + /// - If `T` is `Sized`, this function is always safe to call. + /// - If the unsized tail of `T` is: + /// - a [slice], then the length of the slice tail must be an initialized + /// integer, and the size of the *entire value* + /// (dynamic tail length + statically sized prefix) must fit in `isize`. + /// For the special case where the dynamic tail length is 0, this function + /// is safe to call. + /// - a [trait object], then the vtable part of the pointer must point + /// to a valid vtable acquired by an unsizing coercion, and the size + /// of the *entire value* (dynamic tail length + statically sized prefix) + /// must fit in `isize`. + /// - an (unstable) [extern type], then this function is always safe to + /// call, but may panic or otherwise return the wrong value, as the + /// extern type's layout is not known. This is the same behavior as + /// [`Alignment::of_val`] on a reference to a type with an extern type tail. + /// - otherwise, it is conservatively not allowed to call this function. + /// + /// [trait object]: ../../book/ch17-02-trait-objects.html + /// [extern type]: ../../unstable-book/language-features/extern-types.html + /// + /// # Examples + /// + /// ``` + /// #![feature(ptr_alignment_type)] + /// #![feature(layout_for_ptr)] + /// use std::ptr::Alignment; + /// + /// assert_eq!(unsafe { Alignment::of_val_raw(&5i32) }.as_usize(), 4); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "ptr_alignment_type", issue = "102070")] + // #[unstable(feature = "layout_for_ptr", issue = "69835")] + pub const unsafe fn of_val_raw(val: *const T) -> Self { + // SAFETY: precondition propagated to the caller + let align = unsafe { mem::align_of_val_raw(val) }; + // SAFETY: `align_of_val_raw` returns valid alignment + unsafe { Alignment::new_unchecked(align) } + } + /// Creates an `Alignment` from a `usize`, or returns `None` if it's /// not a power of two. /// @@ -109,14 +188,19 @@ impl Alignment { #[inline] #[ensures(|result| result.is_power_of_two())] pub const fn as_usize(self) -> usize { - self.0 as usize + // Going through `as_nonzero` helps this be more clearly the inverse of + // `new_unchecked`, letting MIR optimizations fold it away. + + self.as_nonzero().get() } /// Returns the alignment as a [NonZero]<[usize]>. #[unstable(feature = "ptr_alignment_type", issue = "102070")] #[inline] #[ensures(|result| result.get().is_power_of_two())] - #[ensures(|result| result.get() == self.as_usize())] + // uses the field directly instead of self.as_usize(): as_usize's body calls as_nonzero, so + // referencing it here would make the contract instrumentation infinitely recursive + #[ensures(|result| result.get() == self._inner_repr_trick as usize)] pub const fn as_nonzero(self) -> NonZero { // This transmutes directly to avoid the UbCheck in `NonZero::new_unchecked` // since there's no way for the user to trip that check anyway -- the @@ -408,7 +492,7 @@ mod verify { impl kani::Arbitrary for Alignment { fn any() -> Self { - let obj = Self { 0: kani::any() }; + let obj = Self { _inner_repr_trick: kani::any() }; kani::assume(obj.is_safe()); obj } diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 084b0c21c2f77..f241d1f835602 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -243,7 +243,7 @@ impl *const T { /// let ptr: *const u8 = &10u8 as *const u8; /// /// unsafe { - /// let val_back = &*ptr; + /// let val_back = ptr.as_ref_unchecked(); /// assert_eq!(val_back, &10); /// } /// ``` @@ -263,6 +263,7 @@ impl *const T { /// /// [`is_null`]: #method.is_null /// [`as_uninit_ref`]: #method.as_uninit_ref + /// [`as_ref_unchecked`]: #method.as_ref_unchecked #[stable(feature = "ptr_as_ref", since = "1.9.0")] #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] #[inline] @@ -287,15 +288,14 @@ impl *const T { /// # Examples /// /// ``` - /// #![feature(ptr_as_ref_unchecked)] /// let ptr: *const u8 = &10u8 as *const u8; /// /// unsafe { /// assert_eq!(ptr.as_ref_unchecked(), &10); /// } /// ``` - // FIXME: mention it in the docs for `as_ref` and `as_uninit_ref` once stabilized. - #[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")] + #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const unsafe fn as_ref_unchecked<'a>(self) -> &'a T { @@ -1497,6 +1497,43 @@ impl *const T { pub const fn cast_uninit(self) -> *const MaybeUninit { self as _ } + + /// Forms a raw slice from a pointer and a length. + /// + /// The `len` argument is the number of **elements**, not the number of bytes. + /// + /// This function is safe, but actually using the return value is unsafe. + /// See the documentation of [`slice::from_raw_parts`] for slice safety requirements. + /// + /// [`slice::from_raw_parts`]: crate::slice::from_raw_parts + /// + /// # Examples + /// + /// ```rust + /// #![feature(ptr_cast_slice)] + /// // create a slice pointer when starting out with a pointer to the first element + /// let x = [5, 6, 7]; + /// let raw_pointer = x.as_ptr(); + /// let slice = raw_pointer.cast_slice(3); + /// assert_eq!(unsafe { &*slice }[2], 7); + /// ``` + /// + /// You must ensure that the pointer is valid and not null before dereferencing + /// the raw slice. A slice reference must never have a null pointer, even if it's empty. + /// + /// ```rust,should_panic + /// #![feature(ptr_cast_slice)] + /// use std::ptr; + /// let danger: *const [u8] = ptr::null::().cast_slice(0); + /// unsafe { + /// danger.as_ref().expect("references must not be null"); + /// } + /// ``` + #[inline] + #[unstable(feature = "ptr_cast_slice", issue = "149103")] + pub const fn cast_slice(self, len: usize) -> *const [T] { + slice_from_raw_parts(self, len) + } } impl *const MaybeUninit { /// Casts from a maybe-uninitialized type to its initialized version. @@ -1573,8 +1610,8 @@ impl *const [T] { /// Gets a raw pointer to the underlying array. /// /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "core_slice_as_array", since = "1.93.0")] + #[rustc_const_stable(feature = "core_slice_as_array", since = "1.93.0")] #[inline] #[must_use] pub const fn as_array(self) -> Option<*const [T; N]> { @@ -1678,6 +1715,10 @@ impl *const [T; N] { /// Pointer equality is by address, as produced by the [`<*const T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialEq for *const T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] @@ -1688,10 +1729,18 @@ impl PartialEq for *const T { /// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Eq for *const T {} /// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Ord for *const T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] @@ -1708,6 +1757,10 @@ impl Ord for *const T { /// Pointer comparison is by address, as produced by the `[`<*const T>::addr`](pointer::addr)` method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialOrd for *const T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] diff --git a/library/core/src/ptr/docs/as_ref.md b/library/core/src/ptr/docs/as_ref.md index 0c0d2768c7482..2c7d6e149b76a 100644 --- a/library/core/src/ptr/docs/as_ref.md +++ b/library/core/src/ptr/docs/as_ref.md @@ -1,6 +1,7 @@ Returns `None` if the pointer is null, or else returns a shared reference to the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`] -must be used instead. +must be used instead. If the value is known to be non-null, [`as_ref_unchecked`] +can be used instead. # Safety @@ -14,6 +15,5 @@ determined to be null or not. See [`is_null`] for more information. # Null-unchecked version -If you are sure the pointer can never be null and are looking for some kind of -`as_ref_unchecked` that returns the `&T` instead of `Option<&T>`, know that you can -dereference the pointer directly. +If you are sure the pointer can never be null, you can use `as_ref_unchecked` which returns +`&mut T` instead of `Option<&mut T>`. diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index 998a5b031c280..1eeadf1217b5f 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -55,7 +55,7 @@ use crate::ptr::NonNull; /// [`to_raw_parts`]: *const::to_raw_parts #[lang = "pointee_trait"] #[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] +#[rustc_dyn_incompatible_trait] pub trait Pointee: PointeeSized { /// The type for metadata in pointers and references to `Self`. #[lang = "metadata_type"] @@ -233,7 +233,7 @@ impl Clone for DynMetadata { } #[doc(hidden)] -unsafe impl TrivialClone for DynMetadata {} +unsafe impl TrivialClone for DynMetadata {} impl Eq for DynMetadata {} diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 52556a7019014..2dbab7819a65b 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -882,6 +882,7 @@ pub const fn null_mut() -> *mut T { #[must_use] #[stable(feature = "strict_provenance", since = "1.84.0")] #[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")] +#[rustc_diagnostic_item = "ptr_without_provenance"] pub const fn without_provenance(addr: usize) -> *const T { without_provenance_mut(addr) } @@ -920,6 +921,7 @@ pub const fn dangling() -> *const T { #[must_use] #[stable(feature = "strict_provenance", since = "1.84.0")] #[rustc_const_stable(feature = "strict_provenance", since = "1.84.0")] +#[rustc_diagnostic_item = "ptr_without_provenance_mut"] #[allow(integer_to_ptr_transmutes)] // Expected semantics here. pub const fn without_provenance_mut(addr: usize) -> *mut T { // An int-to-pointer transmute currently has exactly the intended semantics: it creates a @@ -2562,6 +2564,10 @@ pub fn hash(hashee: *const T, into: &mut S) { } #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialEq for F { #[inline] fn eq(&self, other: &Self) -> bool { @@ -2569,9 +2575,17 @@ impl PartialEq for F { } } #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Eq for F {} #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialOrd for F { #[inline] fn partial_cmp(&self, other: &Self) -> Option { @@ -2579,6 +2593,10 @@ impl PartialOrd for F { } } #[stable(feature = "fnptr_impls", since = "1.4.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Ord for F { #[inline] fn cmp(&self, other: &Self) -> Ordering { @@ -2686,7 +2704,7 @@ impl fmt::Debug for F { /// same requirements apply to field projections, even inside `addr_of!`. (In particular, it makes /// no difference whether the pointer is null or dangling.) #[stable(feature = "raw_ref_macros", since = "1.51.0")] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro addr_of($place:expr) { &raw const $place } @@ -2776,7 +2794,7 @@ pub macro addr_of($place:expr) { /// same requirements apply to field projections, even inside `addr_of_mut!`. (In particular, it /// makes no difference whether the pointer is null or dangling.) #[stable(feature = "raw_ref_macros", since = "1.51.0")] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro addr_of_mut($place:expr) { &raw mut $place } diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 5422a4ade0088..b32a51b26930e 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -234,7 +234,7 @@ impl *mut T { /// let ptr: *mut u8 = &mut 10u8 as *mut u8; /// /// unsafe { - /// let val_back = &*ptr; + /// let val_back = ptr.as_ref_unchecked(); /// println!("We got back the value: {val_back}!"); /// } /// ``` @@ -256,7 +256,8 @@ impl *mut T { /// For the mutable counterpart see [`as_mut`]. /// /// [`is_null`]: #method.is_null-1 - /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1 + /// [`as_uninit_ref`]: #method.as_uninit_ref-1 + /// [`as_ref_unchecked`]: #method.as_ref_unchecked-1 /// [`as_mut`]: #method.as_mut #[stable(feature = "ptr_as_ref", since = "1.9.0")] @@ -285,15 +286,14 @@ impl *mut T { /// # Examples /// /// ``` - /// #![feature(ptr_as_ref_unchecked)] /// let ptr: *mut u8 = &mut 10u8 as *mut u8; /// /// unsafe { /// println!("We got back the value: {}!", ptr.as_ref_unchecked()); /// } /// ``` - // FIXME: mention it in the docs for `as_ref` and `as_uninit_ref` once stabilized. - #[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")] + #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const unsafe fn as_ref_unchecked<'a>(self) -> &'a T { @@ -562,11 +562,13 @@ impl *mut T { /// Returns `None` if the pointer is null, or else returns a unique reference to /// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_mut`] - /// must be used instead. + /// must be used instead. If the value is known to be non-null, [`as_mut_unchecked`] + /// can be used instead. /// /// For the shared counterpart see [`as_ref`]. /// /// [`as_uninit_mut`]: #method.as_uninit_mut + /// [`as_mut_unchecked`]: #method.as_mut_unchecked /// [`as_ref`]: pointer#method.as_ref-1 /// /// # Safety @@ -595,14 +597,13 @@ impl *mut T { /// /// # Null-unchecked version /// - /// If you are sure the pointer can never be null and are looking for some kind of - /// `as_mut_unchecked` that returns the `&mut T` instead of `Option<&mut T>`, know that - /// you can dereference the pointer directly. + /// If you are sure the pointer can never be null, you can use `as_mut_unchecked` which returns + /// `&mut T` instead of `Option<&mut T>`. /// /// ``` /// let mut s = [1, 2, 3]; /// let ptr: *mut u32 = s.as_mut_ptr(); - /// let first_value = unsafe { &mut *ptr }; + /// let first_value = unsafe { ptr.as_mut_unchecked() }; /// *first_value = 4; /// # assert_eq!(s, [4, 2, 3]); /// println!("{s:?}"); // It'll print: "[4, 2, 3]". @@ -634,7 +635,6 @@ impl *mut T { /// # Examples /// /// ``` - /// #![feature(ptr_as_ref_unchecked)] /// let mut s = [1, 2, 3]; /// let ptr: *mut u32 = s.as_mut_ptr(); /// let first_value = unsafe { ptr.as_mut_unchecked() }; @@ -642,8 +642,8 @@ impl *mut T { /// # assert_eq!(s, [4, 2, 3]); /// println!("{s:?}"); // It'll print: "[4, 2, 3]". /// ``` - // FIXME: mention it in the docs for `as_mut` and `as_uninit_mut` once stabilized. - #[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")] + #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const unsafe fn as_mut_unchecked<'a>(self) -> &'a mut T { @@ -1774,6 +1774,51 @@ impl *mut T { pub const fn cast_uninit(self) -> *mut MaybeUninit { self as _ } + + /// Forms a raw mutable slice from a pointer and a length. + /// + /// The `len` argument is the number of **elements**, not the number of bytes. + /// + /// Performs the same functionality as [`cast_slice`] on a `*const T`, except that a + /// raw mutable slice is returned, as opposed to a raw immutable slice. + /// + /// This function is safe, but actually using the return value is unsafe. + /// See the documentation of [`slice::from_raw_parts_mut`] for slice safety requirements. + /// + /// [`slice::from_raw_parts_mut`]: crate::slice::from_raw_parts_mut + /// [`cast_slice`]: pointer::cast_slice + /// + /// # Examples + /// + /// ```rust + /// #![feature(ptr_cast_slice)] + /// + /// let x = &mut [5, 6, 7]; + /// let slice = x.as_mut_ptr().cast_slice(3); + /// + /// unsafe { + /// (*slice)[2] = 99; // assign a value at an index in the slice + /// }; + /// + /// assert_eq!(unsafe { &*slice }[2], 99); + /// ``` + /// + /// You must ensure that the pointer is valid and not null before dereferencing + /// the raw slice. A slice reference must never have a null pointer, even if it's empty. + /// + /// ```rust,should_panic + /// #![feature(ptr_cast_slice)] + /// use std::ptr; + /// let danger: *mut [u8] = ptr::null_mut::().cast_slice(0); + /// unsafe { + /// danger.as_mut().expect("references must not be null"); + /// } + /// ``` + #[inline] + #[unstable(feature = "ptr_cast_slice", issue = "149103")] + pub const fn cast_slice(self, len: usize) -> *mut [T] { + slice_from_raw_parts_mut(self, len) + } } impl *mut MaybeUninit { /// Casts from a maybe-uninitialized type to its initialized version. @@ -1831,8 +1876,8 @@ impl *mut [T] { /// Gets a raw, mutable pointer to the underlying array. /// /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "core_slice_as_array", since = "1.93.0")] + #[rustc_const_stable(feature = "core_slice_as_array", since = "1.93.0")] #[inline] #[must_use] pub const fn as_mut_array(self) -> Option<*mut [T; N]> { @@ -1861,7 +1906,7 @@ impl *mut [T] { /// that is at least `mid * size_of::()` bytes long. Not upholding these /// requirements is *[undefined behavior]* even if the resulting pointers are not used. /// - /// Since `len` being in-bounds it is not a safety invariant of `*mut [T]` the + /// Since `len` being in-bounds is not a safety invariant of `*mut [T]` the /// safety requirements of this method are the same as for [`split_at_mut_unchecked`]. /// The explicit bounds check is only as useful as `len` is correct. /// @@ -2119,6 +2164,10 @@ impl *mut [T; N] { /// Pointer equality is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialEq for *mut T { #[inline(always)] #[allow(ambiguous_wide_pointer_comparisons)] @@ -2129,10 +2178,18 @@ impl PartialEq for *mut T { /// Pointer equality is an equivalence relation. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Eq for *mut T {} /// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl Ord for *mut T { #[inline] #[allow(ambiguous_wide_pointer_comparisons)] @@ -2149,6 +2206,10 @@ impl Ord for *mut T { /// Pointer comparison is by address, as produced by the [`<*mut T>::addr`](pointer::addr) method. #[stable(feature = "rust1", since = "1.0.0")] +#[diagnostic::on_const( + message = "pointers cannot be reliably compared during const eval", + note = "see issue #53020 for more information" +)] impl PartialOrd for *mut T { #[inline(always)] #[allow(ambiguous_wide_pointer_comparisons)] diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 775034399717d..272a2cf03461e 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1550,6 +1550,35 @@ impl NonNull { pub const fn cast_uninit(self) -> NonNull> { self.cast() } + + /// Creates a non-null raw slice from a thin pointer and a length. + /// + /// The `len` argument is the number of **elements**, not the number of bytes. + /// + /// This function is safe, but dereferencing the return value is unsafe. + /// See the documentation of [`slice::from_raw_parts`] for slice safety requirements. + /// + /// # Examples + /// + /// ```rust + /// #![feature(ptr_cast_slice)] + /// use std::ptr::NonNull; + /// + /// // create a slice pointer when starting out with a pointer to the first element + /// let mut x = [5, 6, 7]; + /// let nonnull_pointer = NonNull::new(x.as_mut_ptr()).unwrap(); + /// let slice = nonnull_pointer.cast_slice(3); + /// assert_eq!(unsafe { slice.as_ref()[2] }, 7); + /// ``` + /// + /// (Note that this example artificially demonstrates a use of this method, + /// but `let slice = NonNull::from(&x[..]);` would be a better way to write code like this.) + #[inline] + #[must_use] + #[unstable(feature = "ptr_cast_slice", issue = "149103")] + pub const fn cast_slice(self, len: usize) -> NonNull<[T]> { + NonNull::slice_from_raw_parts(self, len) + } } impl NonNull> { /// Casts from a maybe-uninitialized type to its initialized version. @@ -1855,7 +1884,7 @@ impl Copy for NonNull {} #[doc(hidden)] #[unstable(feature = "trivial_clone", issue = "none")] -unsafe impl TrivialClone for NonNull {} +unsafe impl TrivialClone for NonNull {} #[unstable(feature = "coerce_unsized", issue = "18598")] impl CoerceUnsized> for NonNull where T: Unsize {} diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index b45176d331800..0422d3d595067 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -178,7 +178,7 @@ impl Copy for Unique {} #[doc(hidden)] #[unstable(feature = "trivial_clone", issue = "none")] -unsafe impl TrivialClone for Unique {} +unsafe impl TrivialClone for Unique {} #[unstable(feature = "ptr_internals", issue = "none")] impl CoerceUnsized> for Unique where T: Unsize {} @@ -186,7 +186,7 @@ impl CoerceUnsized> for Unique wh #[unstable(feature = "ptr_internals", issue = "none")] impl DispatchFromDyn> for Unique where T: Unsize {} -#[unstable(feature = "pin_coerce_unsized_trait", issue = "123430")] +#[unstable(feature = "pin_coerce_unsized_trait", issue = "150112")] unsafe impl PinCoerceUnsized for Unique {} #[unstable(feature = "ptr_internals", issue = "none")] diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 2df520d35b398..4b87d426bda76 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -26,7 +26,7 @@ pub mod legacy; use Bound::{Excluded, Included, Unbounded}; #[doc(inline)] -pub use iter::{IterRange, IterRangeFrom, IterRangeInclusive}; +pub use iter::{RangeFromIter, RangeInclusiveIter, RangeIter}; #[doc(inline)] pub use crate::iter::Step; @@ -89,7 +89,7 @@ impl Range { /// ``` #[unstable(feature = "new_range_api", issue = "125687")] #[inline] - pub fn iter(&self) -> IterRange { + pub fn iter(&self) -> RangeIter { self.clone().into_iter() } } @@ -340,7 +340,7 @@ impl RangeInclusive { /// ``` #[unstable(feature = "new_range_api", issue = "125687")] #[inline] - pub fn iter(&self) -> IterRangeInclusive { + pub fn iter(&self) -> RangeInclusiveIter { self.clone().into_iter() } } @@ -477,7 +477,7 @@ impl RangeFrom { /// ``` #[unstable(feature = "new_range_api", issue = "125687")] #[inline] - pub fn iter(&self) -> IterRangeFrom { + pub fn iter(&self) -> RangeFromIter { self.clone().into_iter() } } diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs index 4c43960551be5..6631fb2ce583c 100644 --- a/library/core/src/range/iter.rs +++ b/library/core/src/range/iter.rs @@ -12,9 +12,9 @@ use crate::{intrinsics, mem}; /// By-value [`Range`] iterator. #[unstable(feature = "new_range_api", issue = "125687")] #[derive(Debug, Clone)] -pub struct IterRange(legacy::Range); +pub struct RangeIter(legacy::Range); -impl IterRange { +impl RangeIter { /// Returns the remainder of the range being iterated over. pub fn remainder(self) -> Range { Range { start: self.0.start, end: self.0.end } @@ -27,11 +27,11 @@ macro_rules! unsafe_range_trusted_random_access_impl { ($($t:ty)*) => ($( #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] - unsafe impl TrustedRandomAccess for IterRange<$t> {} + unsafe impl TrustedRandomAccess for RangeIter<$t> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] - unsafe impl TrustedRandomAccessNoCoerce for IterRange<$t> { + unsafe impl TrustedRandomAccessNoCoerce for RangeIter<$t> { const MAY_HAVE_SIDE_EFFECT: bool = false; } )*) @@ -54,7 +54,7 @@ unsafe_range_trusted_random_access_impl! { } #[unstable(feature = "new_range_api", issue = "125687")] -impl Iterator for IterRange { +impl Iterator for RangeIter { type Item = A; #[inline] @@ -124,7 +124,7 @@ impl Iterator for IterRange { } #[unstable(feature = "new_range_api", issue = "125687")] -impl DoubleEndedIterator for IterRange { +impl DoubleEndedIterator for RangeIter { #[inline] fn next_back(&mut self) -> Option { self.0.next_back() @@ -142,27 +142,27 @@ impl DoubleEndedIterator for IterRange { } #[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IterRange {} +unsafe impl TrustedLen for RangeIter {} #[unstable(feature = "new_range_api", issue = "125687")] -impl FusedIterator for IterRange {} +impl FusedIterator for RangeIter {} #[unstable(feature = "new_range_api", issue = "125687")] impl IntoIterator for Range { type Item = A; - type IntoIter = IterRange; + type IntoIter = RangeIter; fn into_iter(self) -> Self::IntoIter { - IterRange(self.into()) + RangeIter(self.into()) } } /// By-value [`RangeInclusive`] iterator. #[unstable(feature = "new_range_api", issue = "125687")] #[derive(Debug, Clone)] -pub struct IterRangeInclusive(legacy::RangeInclusive); +pub struct RangeInclusiveIter(legacy::RangeInclusive); -impl IterRangeInclusive { +impl RangeInclusiveIter { /// Returns the remainder of the range being iterated over. /// /// If the iterator is exhausted or empty, returns `None`. @@ -176,7 +176,7 @@ impl IterRangeInclusive { } #[unstable(feature = "new_range_api", issue = "125687")] -impl Iterator for IterRangeInclusive { +impl Iterator for RangeInclusiveIter { type Item = A; #[inline] @@ -232,7 +232,7 @@ impl Iterator for IterRangeInclusive { } #[unstable(feature = "new_range_api", issue = "125687")] -impl DoubleEndedIterator for IterRangeInclusive { +impl DoubleEndedIterator for RangeInclusiveIter { #[inline] fn next_back(&mut self) -> Option { self.0.next_back() @@ -250,18 +250,18 @@ impl DoubleEndedIterator for IterRangeInclusive { } #[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IterRangeInclusive {} +unsafe impl TrustedLen for RangeInclusiveIter {} #[unstable(feature = "new_range_api", issue = "125687")] -impl FusedIterator for IterRangeInclusive {} +impl FusedIterator for RangeInclusiveIter {} #[unstable(feature = "new_range_api", issue = "125687")] impl IntoIterator for RangeInclusive { type Item = A; - type IntoIter = IterRangeInclusive; + type IntoIter = RangeInclusiveIter; fn into_iter(self) -> Self::IntoIter { - IterRangeInclusive(self.into()) + RangeInclusiveIter(self.into()) } } @@ -276,14 +276,14 @@ impl IntoIterator for RangeInclusive { macro_rules! range_exact_iter_impl { ($($t:ty)*) => ($( #[unstable(feature = "new_range_api", issue = "125687")] - impl ExactSizeIterator for IterRange<$t> { } + impl ExactSizeIterator for RangeIter<$t> { } )*) } macro_rules! range_incl_exact_iter_impl { ($($t:ty)*) => ($( #[unstable(feature = "new_range_api", issue = "125687")] - impl ExactSizeIterator for IterRangeInclusive<$t> { } + impl ExactSizeIterator for RangeInclusiveIter<$t> { } )*) } @@ -300,14 +300,14 @@ range_incl_exact_iter_impl! { /// By-value [`RangeFrom`] iterator. #[unstable(feature = "new_range_api", issue = "125687")] #[derive(Debug, Clone)] -pub struct IterRangeFrom { +pub struct RangeFromIter { start: A, /// Whether the first element of the iterator has yielded. /// Only used when overflow checks are enabled. first: bool, } -impl IterRangeFrom { +impl RangeFromIter { /// Returns the remainder of the range being iterated over. #[inline] #[rustc_inherit_overflow_checks] @@ -323,7 +323,7 @@ impl IterRangeFrom { } #[unstable(feature = "new_range_api", issue = "125687")] -impl Iterator for IterRangeFrom { +impl Iterator for RangeFromIter { type Item = A; #[inline] @@ -372,17 +372,17 @@ impl Iterator for IterRangeFrom { } #[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for IterRangeFrom {} +unsafe impl TrustedLen for RangeFromIter {} #[unstable(feature = "new_range_api", issue = "125687")] -impl FusedIterator for IterRangeFrom {} +impl FusedIterator for RangeFromIter {} #[unstable(feature = "new_range_api", issue = "125687")] impl IntoIterator for RangeFrom { type Item = A; - type IntoIter = IterRangeFrom; + type IntoIter = RangeFromIter; fn into_iter(self) -> Self::IntoIter { - IterRangeFrom { start: self.start, first: true } + RangeFromIter { start: self.start, first: true } } } diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 9afa71ec0f117..5f438d72ac13c 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -690,7 +690,7 @@ impl Result { /// Converts from `Result` to [`Option`]. /// /// Converts `self` into an [`Option`], consuming `self`, - /// and discarding the error, if any. + /// and converting the error to `None`, if any. /// /// # Examples /// @@ -1354,7 +1354,7 @@ impl Result { /// let s: String = only_good_news().into_ok(); /// println!("{s}"); /// ``` - #[unstable(feature = "unwrap_infallible", reason = "newly added", issue = "61695")] + #[unstable(feature = "unwrap_infallible", issue = "61695")] #[inline] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] @@ -1391,7 +1391,7 @@ impl Result { /// let error: String = only_bad_news().into_err(); /// println!("{error}"); /// ``` - #[unstable(feature = "unwrap_infallible", reason = "newly added", issue = "61695")] + #[unstable(feature = "unwrap_infallible", issue = "61695")] #[inline] #[rustc_allow_const_fn_unstable(const_precise_live_drops)] #[rustc_const_unstable(feature = "const_convert", issue = "143773")] diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs index ae438f2fa5ca6..82c45a04e3dd7 100644 --- a/library/core/src/slice/ascii.rs +++ b/library/core/src/slice/ascii.rs @@ -3,10 +3,7 @@ use core::ascii::EscapeDefault; use crate::fmt::{self, Write}; -#[cfg(not(any( - all(target_arch = "x86_64", target_feature = "sse2"), - all(target_arch = "loongarch64", target_feature = "lsx") -)))] +#[cfg(not(all(target_arch = "loongarch64", target_feature = "lsx")))] use crate::intrinsics::const_eval_select; #[cfg(kani)] use crate::kani; @@ -67,6 +64,25 @@ impl [u8] { return false; } + #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] + { + const CHUNK_SIZE: usize = 16; + // The following function has two invariants: + // 1. The slice lengths must be equal, which we checked above. + // 2. The slice lengths must greater than or equal to N, which this + // if-statement is checking. + if self.len() >= CHUNK_SIZE { + return self.eq_ignore_ascii_case_chunks::(other); + } + } + + self.eq_ignore_ascii_case_simple(other) + } + + /// ASCII case-insensitive equality check without chunk-at-a-time + /// optimization. + #[inline] + const fn eq_ignore_ascii_case_simple(&self, other: &[u8]) -> bool { // FIXME(const-hack): This implementation can be reverted when // `core::iter::zip` is allowed in const. The original implementation: // self.len() == other.len() && iter::zip(self, other).all(|(a, b)| a.eq_ignore_ascii_case(b)) @@ -85,6 +101,65 @@ impl [u8] { true } + /// Optimized version of `eq_ignore_ascii_case` to process chunks at a time. + /// + /// Platforms that have SIMD instructions may benefit from this + /// implementation over `eq_ignore_ascii_case_simple`. + /// + /// # Invariants + /// + /// The caller must guarantee that the slices are equal in length, and the + /// slice lengths are greater than or equal to `N` bytes. + #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] + #[inline] + const fn eq_ignore_ascii_case_chunks(&self, other: &[u8]) -> bool { + // FIXME(const-hack): The while-loops that follow should be replaced by + // for-loops when available in const. + + let (self_chunks, self_rem) = self.as_chunks::(); + let (other_chunks, _) = other.as_chunks::(); + + // Branchless check to encourage auto-vectorization + #[inline(always)] + const fn eq_ignore_ascii_inner(lhs: &[u8; L], rhs: &[u8; L]) -> bool { + let mut equal_ascii = true; + let mut j = 0; + while j < L { + equal_ascii &= lhs[j].eq_ignore_ascii_case(&rhs[j]); + j += 1; + } + + equal_ascii + } + + // Process the chunks, returning early if an inequality is found + let mut i = 0; + while i < self_chunks.len() && i < other_chunks.len() { + if !eq_ignore_ascii_inner(&self_chunks[i], &other_chunks[i]) { + return false; + } + i += 1; + } + + // Check the length invariant which is necessary for the tail-handling + // logic to be correct. This should have been upheld by the caller, + // otherwise lengths less than N will compare as true without any + // checking. + debug_assert!(self.len() >= N); + + // If there are remaining tails, load the last N bytes in the slices to + // avoid falling back to per-byte checking. + if !self_rem.is_empty() { + if let (Some(a_rem), Some(b_rem)) = (self.last_chunk::(), other.last_chunk::()) { + if !eq_ignore_ascii_inner(a_rem, b_rem) { + return false; + } + } + } + + true + } + /// Converts this slice to its ASCII upper case equivalent in-place. /// /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', @@ -469,19 +544,83 @@ const fn is_ascii(s: &[u8]) -> bool { ) } -/// ASCII test optimized to use the `pmovmskb` instruction on `x86-64` and the -/// `vmskltz.b` instruction on `loongarch64`. +/// Chunk size for SSE2 vectorized ASCII checking (4x 16-byte loads). +#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] +const SSE2_CHUNK_SIZE: usize = 64; + +#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] +#[inline] +fn is_ascii_sse2(bytes: &[u8]) -> bool { + use crate::arch::x86_64::{__m128i, _mm_loadu_si128, _mm_movemask_epi8, _mm_or_si128}; + + let (chunks, rest) = bytes.as_chunks::(); + + for chunk in chunks { + let ptr = chunk.as_ptr(); + // SAFETY: chunk is 64 bytes. SSE2 is baseline on x86_64. + let mask = unsafe { + let a1 = _mm_loadu_si128(ptr as *const __m128i); + let a2 = _mm_loadu_si128(ptr.add(16) as *const __m128i); + let b1 = _mm_loadu_si128(ptr.add(32) as *const __m128i); + let b2 = _mm_loadu_si128(ptr.add(48) as *const __m128i); + // OR all chunks - if any byte has high bit set, combined will too. + let combined = _mm_or_si128(_mm_or_si128(a1, a2), _mm_or_si128(b1, b2)); + // Create a mask from the MSBs of each byte. + // If any byte is >= 128, its MSB is 1, so the mask will be non-zero. + _mm_movemask_epi8(combined) + }; + if mask != 0 { + return false; + } + } + + // Handle remaining bytes + rest.iter().all(|b| b.is_ascii()) +} + +/// ASCII test optimized to use the `pmovmskb` instruction on `x86-64`. +/// +/// Uses explicit SSE2 intrinsics to prevent LLVM from auto-vectorizing with +/// broken AVX-512 code that extracts mask bits one-by-one. +#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] +#[inline] +#[rustc_allow_const_fn_unstable(const_eval_select)] +const fn is_ascii(bytes: &[u8]) -> bool { + const USIZE_SIZE: usize = size_of::(); + const NONASCII_MASK: usize = usize::MAX / 255 * 0x80; + + const_eval_select!( + @capture { bytes: &[u8] } -> bool: + if const { + is_ascii_simple(bytes) + } else { + // For small inputs, use usize-at-a-time processing to avoid SSE2 call overhead. + if bytes.len() < SSE2_CHUNK_SIZE { + let chunks = bytes.chunks_exact(USIZE_SIZE); + let remainder = chunks.remainder(); + for chunk in chunks { + let word = usize::from_ne_bytes(chunk.try_into().unwrap()); + if (word & NONASCII_MASK) != 0 { + return false; + } + } + return remainder.iter().all(|b| b.is_ascii()); + } + + is_ascii_sse2(bytes) + } + ) +} + +/// ASCII test optimized to use the `vmskltz.b` instruction on `loongarch64`. /// /// Other platforms are not likely to benefit from this code structure, so they /// use SWAR techniques to test for ASCII in `usize`-sized chunks. -#[cfg(any( - all(target_arch = "x86_64", target_feature = "sse2"), - all(target_arch = "loongarch64", target_feature = "lsx") -))] +#[cfg(all(target_arch = "loongarch64", target_feature = "lsx"))] #[inline] const fn is_ascii(bytes: &[u8]) -> bool { // Process chunks of 32 bytes at a time in the fast path to enable - // auto-vectorization and use of `pmovmskb`. Two 128-bit vector registers + // auto-vectorization and use of `vmskltz.b`. Two 128-bit vector registers // can be OR'd together and then the resulting vector can be tested for // non-ASCII bytes. const CHUNK_SIZE: usize = 32; @@ -492,7 +631,7 @@ const fn is_ascii(bytes: &[u8]) -> bool { while i + CHUNK_SIZE <= bytes.len() { let chunk_end = i + CHUNK_SIZE; - // Get LLVM to produce a `pmovmskb` instruction on x86-64 which + // Get LLVM to produce a `vmskltz.b` instruction on loongarch64 which // creates a mask from the most significant bit of each byte. // ASCII bytes are less than 128 (0x80), so their most significant // bit is unset. diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index fd1ca23fb79c5..2390ca74a8e09 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -4,6 +4,7 @@ use super::{from_raw_parts, memchr}; use crate::ascii; use crate::cmp::{self, BytewiseEq, Ordering}; use crate::intrinsics::compare_bytes; +use crate::mem::SizedTypeProperties; use crate::num::NonZero; use crate::ops::ControlFlow; @@ -13,12 +14,16 @@ impl const PartialEq<[U]> for [T] where T: [const] PartialEq, { + #[inline] fn eq(&self, other: &[U]) -> bool { - SlicePartialEq::equal(self, other) - } - - fn ne(&self, other: &[U]) -> bool { - SlicePartialEq::not_equal(self, other) + let len = self.len(); + if len == other.len() { + // SAFETY: Just checked that they're the same length, and the pointers + // come from references-to-slices so they're guaranteed readable. + unsafe { SlicePartialEq::equal_same_length(self.as_ptr(), other.as_ptr(), len) } + } else { + false + } } } @@ -98,32 +103,31 @@ impl PartialOrd for [T] { // intermediate trait for specialization of slice's PartialEq #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const trait SlicePartialEq { - fn equal(&self, other: &[B]) -> bool; - - fn not_equal(&self, other: &[B]) -> bool { - !self.equal(other) - } + /// # Safety + /// `lhs` and `rhs` are both readable for `len` elements + unsafe fn equal_same_length(lhs: *const Self, rhs: *const B, len: usize) -> bool; } // Generic slice equality #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -impl const SlicePartialEq for [A] +impl const SlicePartialEq for A where A: [const] PartialEq, { - default fn equal(&self, other: &[B]) -> bool { - if self.len() != other.len() { - return false; - } - + // It's not worth trying to inline the loops underneath here *in MIR*, + // and preventing it encourages more useful inlining upstream, + // such as in `::eq`. + // The codegen backend can still inline it later if needed. + #[rustc_no_mir_inline] + default unsafe fn equal_same_length(lhs: *const Self, rhs: *const B, len: usize) -> bool { // Implemented as explicit indexing rather // than zipped iterators for performance reasons. // See PR https://github.com/rust-lang/rust/pull/116846 - // FIXME(const_hack): make this a `for idx in 0..self.len()` loop. + // FIXME(const_hack): make this a `for idx in 0..len` loop. let mut idx = 0; - while idx < self.len() { - // bound checks are optimized away - if self[idx] != other[idx] { + while idx < len { + // SAFETY: idx < len, so both are in-bounds and readable + if unsafe { *lhs.add(idx) != *rhs.add(idx) } { return false; } idx += 1; @@ -136,20 +140,18 @@ where // When each element can be compared byte-wise, we can compare all the bytes // from the whole size in one call to the intrinsics. #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -impl const SlicePartialEq for [A] +impl const SlicePartialEq for A where A: [const] BytewiseEq, { - fn equal(&self, other: &[B]) -> bool { - if self.len() != other.len() { - return false; - } - - // SAFETY: `self` and `other` are references and are thus guaranteed to be valid. - // The two slices have been checked to have the same size above. + #[inline] + unsafe fn equal_same_length(lhs: *const Self, rhs: *const B, len: usize) -> bool { + // SAFETY: by our precondition, `lhs` and `rhs` are guaranteed to be valid + // for reading `len` values, which also means the size is guaranteed + // not to overflow because it exists in memory; unsafe { - let size = size_of_val(self); - compare_bytes(self.as_ptr() as *const u8, other.as_ptr() as *const u8, size) == 0 + let size = crate::intrinsics::unchecked_mul(len, Self::SIZE); + compare_bytes(lhs as _, rhs as _, size) == 0 } } } diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index d8ed521f44353..59802989c18fb 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -910,29 +910,7 @@ where R: [const] ops::RangeBounds + [const] Destruct, { let len = bounds.end; - - let end = match range.end_bound() { - ops::Bound::Included(&end) if end >= len => slice_index_fail(0, end, len), - // Cannot overflow because `end < len` implies `end < usize::MAX`. - ops::Bound::Included(&end) => end + 1, - - ops::Bound::Excluded(&end) if end > len => slice_index_fail(0, end, len), - ops::Bound::Excluded(&end) => end, - ops::Bound::Unbounded => len, - }; - - let start = match range.start_bound() { - ops::Bound::Excluded(&start) if start >= end => slice_index_fail(start, end, len), - // Cannot overflow because `start < end` implies `start < usize::MAX`. - ops::Bound::Excluded(&start) => start + 1, - - ops::Bound::Included(&start) if start > end => slice_index_fail(start, end, len), - ops::Bound::Included(&start) => start, - - ops::Bound::Unbounded => 0, - }; - - ops::Range { start, end } + into_slice_range(len, (range.start_bound().copied(), range.end_bound().copied())) } /// Performs bounds checking of a range without panicking. @@ -972,20 +950,8 @@ where R: ops::RangeBounds, { let len = bounds.end; - - let start = match range.start_bound() { - ops::Bound::Included(&start) => start, - ops::Bound::Excluded(start) => start.checked_add(1)?, - ops::Bound::Unbounded => 0, - }; - - let end = match range.end_bound() { - ops::Bound::Included(end) => end.checked_add(1)?, - ops::Bound::Excluded(&end) => end, - ops::Bound::Unbounded => len, - }; - - if start > end || end > len { None } else { Some(ops::Range { start, end }) } + let r = into_range(len, (range.start_bound().copied(), range.end_bound().copied()))?; + if r.start > r.end || r.end > len { None } else { Some(r) } } /// Converts a pair of `ops::Bound`s into `ops::Range` without performing any @@ -1011,6 +977,7 @@ pub(crate) const fn into_range_unchecked( /// Converts pair of `ops::Bound`s into `ops::Range`. /// Returns `None` on overflowing indices. #[rustc_const_unstable(feature = "const_range", issue = "none")] +#[inline] pub(crate) const fn into_range( len: usize, (start, end): (ops::Bound, ops::Bound), @@ -1036,7 +1003,8 @@ pub(crate) const fn into_range( /// Converts pair of `ops::Bound`s into `ops::Range`. /// Panics on overflowing indices. -pub(crate) fn into_slice_range( +#[inline] +pub(crate) const fn into_slice_range( len: usize, (start, end): (ops::Bound, ops::Bound), ) -> ops::Range { diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index b6de37033cc47..44c85a50c8b00 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -144,7 +144,7 @@ impl<'a, T> Iter<'a, T> { } } -iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, as_ref, { +iterator! {struct Iter -> *const T, &'a T, const, {/* no mut */}, as_ref, each_ref, { fn is_sorted_by(self, mut compare: F) -> bool where Self: Sized, @@ -422,7 +422,7 @@ impl AsRef<[T]> for IterMut<'_, T> { // } // } -iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, as_mut, {}} +iterator! {struct IterMut -> *mut T, &'a mut T, mut, {mut}, as_mut, each_mut, {}} /// An internal abstraction over the splitting iterators, so that /// splitn, splitn_mut etc can be implemented once. @@ -1470,26 +1470,21 @@ impl<'a, T> Iterator for Windows<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> DoubleEndedIterator for Windows<'a, T> { #[inline] - fn next_back(&mut self) -> Option<&'a [T]> { - if self.size.get() > self.v.len() { - None - } else { - let ret = Some(&self.v[self.v.len() - self.size.get()..]); - self.v = &self.v[..self.v.len() - 1]; - ret - } + fn next_back(&mut self) -> Option { + self.nth_back(0) } #[inline] fn nth_back(&mut self, n: usize) -> Option { - let (end, overflow) = self.v.len().overflowing_sub(n); - if end < self.size.get() || overflow { + if let Some(end) = self.v.len().checked_sub(n) + && let Some(start) = end.checked_sub(self.size.get()) + { + let res = &self.v[start..end]; + self.v = &self.v[..end - 1]; + Some(res) + } else { self.v = &self.v[..0]; // cheaper than &[] None - } else { - let ret = &self.v[end - self.size.get()..end]; - self.v = &self.v[..end - 1]; - Some(ret) } } } @@ -1578,9 +1573,7 @@ impl<'a, T> Iterator for Chunks<'a, T> { if self.v.is_empty() { (0, Some(0)) } else { - let n = self.v.len() / self.chunk_size; - let rem = self.v.len() % self.chunk_size; - let n = if rem > 0 { n + 1 } else { n }; + let n = self.v.len().div_ceil(self.chunk_size); (n, Some(n)) } } @@ -1592,13 +1585,13 @@ impl<'a, T> Iterator for Chunks<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option { - let (start, overflow) = n.overflowing_mul(self.chunk_size); - // min(len) makes a wrong start harmless, but enables optimizing this to brachless code - let chunk_start = &self.v[start.min(self.v.len())..]; - let (nth, remainder) = chunk_start.split_at(self.chunk_size.min(chunk_start.len())); - if !overflow && start < self.v.len() { - self.v = remainder; - Some(nth) + if let Some(start) = n.checked_mul(self.chunk_size) + && start < self.v.len() + { + let rest = &self.v[start..]; + let (chunk, rest) = rest.split_at(self.chunk_size.min(rest.len())); + self.v = rest; + Some(chunk) } else { self.v = &self.v[..0]; // cheaper than &[] None @@ -1664,18 +1657,15 @@ impl<'a, T> DoubleEndedIterator for Chunks<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &self.v[..0]; // cheaper than &[] - None - } else { + if n < len { let start = (len - 1 - n) * self.chunk_size; - let end = match start.checked_add(self.chunk_size) { - Some(res) => cmp::min(self.v.len(), res), - None => self.v.len(), - }; + let end = start + (self.v.len() - start).min(self.chunk_size); let nth_back = &self.v[start..end]; self.v = &self.v[..start]; Some(nth_back) + } else { + self.v = &self.v[..0]; // cheaper than &[] + None } } } @@ -1761,9 +1751,7 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { if self.v.is_empty() { (0, Some(0)) } else { - let n = self.v.len() / self.chunk_size; - let rem = self.v.len() % self.chunk_size; - let n = if rem > 0 { n + 1 } else { n }; + let n = self.v.len().div_ceil(self.chunk_size); (n, Some(n)) } } @@ -1775,22 +1763,19 @@ impl<'a, T> Iterator for ChunksMut<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { - let (start, overflow) = n.overflowing_mul(self.chunk_size); - if start >= self.v.len() || overflow { + if let Some(start) = n.checked_mul(self.chunk_size) + && start < self.v.len() + { + // SAFETY: `start < self.v.len()` ensures this is in bounds + let (_, rest) = unsafe { self.v.split_at_mut(start) }; + // SAFETY: `.min(rest.len()` ensures this is in bounds + let (chunk, rest) = unsafe { rest.split_at_mut(self.chunk_size.min(rest.len())) }; + self.v = rest; + // SAFETY: Nothing else points to or will point to the contents of this slice. + Some(unsafe { &mut *chunk }) + } else { self.v = &mut []; None - } else { - let end = match start.checked_add(self.chunk_size) { - Some(sum) => cmp::min(self.v.len(), sum), - None => self.v.len(), - }; - // SAFETY: The self.v contract ensures that any split_at_mut is valid. - let (head, tail) = unsafe { self.v.split_at_mut(end) }; - // SAFETY: The self.v contract ensures that any split_at_mut is valid. - let (_, nth) = unsafe { head.split_at_mut(start) }; - self.v = tail; - // SAFETY: Nothing else points to or will point to the contents of this slice. - Some(unsafe { &mut *nth }) } } @@ -1842,10 +1827,7 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &mut []; - None - } else { + if n < len { let start = (len - 1 - n) * self.chunk_size; let end = match start.checked_add(self.chunk_size) { Some(res) => cmp::min(self.v.len(), res), @@ -1858,6 +1840,9 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { self.v = head; // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth_back }) + } else { + self.v = &mut []; + None } } } @@ -1966,13 +1951,10 @@ impl<'a, T> Iterator for ChunksExact<'a, T> { #[inline] fn next(&mut self) -> Option<&'a [T]> { - if self.v.len() < self.chunk_size { - None - } else { - let (fst, snd) = self.v.split_at(self.chunk_size); - self.v = snd; - Some(fst) - } + self.v.split_at_checked(self.chunk_size).and_then(|(chunk, rest)| { + self.v = rest; + Some(chunk) + }) } #[inline] @@ -1988,14 +1970,14 @@ impl<'a, T> Iterator for ChunksExact<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option { - let (start, overflow) = n.overflowing_mul(self.chunk_size); - if start >= self.v.len() || overflow { + if let Some(start) = n.checked_mul(self.chunk_size) + && start < self.v.len() + { + self.v = &self.v[start..]; + self.next() + } else { self.v = &self.v[..0]; // cheaper than &[] None - } else { - let (_, snd) = self.v.split_at(start); - self.v = snd; - self.next() } } @@ -2028,15 +2010,15 @@ impl<'a, T> DoubleEndedIterator for ChunksExact<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &self.v[..0]; // cheaper than &[] - None - } else { + if n < len { let start = (len - 1 - n) * self.chunk_size; let end = start + self.chunk_size; let nth_back = &self.v[start..end]; self.v = &self.v[..start]; Some(nth_back) + } else { + self.v = &self.v[..0]; // cheaper than &[] + None } } } @@ -2125,15 +2107,11 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> { #[inline] fn next(&mut self) -> Option<&'a mut [T]> { - if self.v.len() < self.chunk_size { - None - } else { - // SAFETY: self.chunk_size is inbounds because we compared above against self.v.len() - let (head, tail) = unsafe { self.v.split_at_mut(self.chunk_size) }; - self.v = tail; - // SAFETY: Nothing else points to or will point to the contents of this slice. - Some(unsafe { &mut *head }) - } + // SAFETY: we have `&mut self`, so are allowed to temporarily materialize a mut slice + unsafe { &mut *self.v }.split_at_mut_checked(self.chunk_size).and_then(|(chunk, rest)| { + self.v = rest; + Some(chunk) + }) } #[inline] @@ -2149,15 +2127,15 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { - let (start, overflow) = n.overflowing_mul(self.chunk_size); - if start >= self.v.len() || overflow { + if let Some(start) = n.checked_mul(self.chunk_size) + && start < self.v.len() + { + // SAFETY: `start < self.v.len()` + self.v = unsafe { self.v.split_at_mut(start).1 }; + self.next() + } else { self.v = &mut []; None - } else { - // SAFETY: The self.v contract ensures that any split_at_mut is valid. - let (_, snd) = unsafe { self.v.split_at_mut(start) }; - self.v = snd; - self.next() } } @@ -2192,10 +2170,7 @@ impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &mut []; - None - } else { + if n < len { let start = (len - 1 - n) * self.chunk_size; let end = start + self.chunk_size; // SAFETY: The self.v contract ensures that any split_at_mut is valid. @@ -2205,6 +2180,9 @@ impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> { self.v = head; // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth_back }) + } else { + self.v = &mut []; + None } } } @@ -2246,8 +2224,6 @@ unsafe impl Sync for ChunksExactMut<'_, T> where T: Sync {} /// # Example /// /// ``` -/// #![feature(array_windows)] -/// /// let slice = [0, 1, 2, 3]; /// let mut iter = slice.array_windows::<2>(); /// assert_eq!(iter.next(), Some(&[0, 1])); @@ -2259,7 +2235,7 @@ unsafe impl Sync for ChunksExactMut<'_, T> where T: Sync {} /// [`array_windows`]: slice::array_windows /// [slices]: slice #[derive(Debug, Clone, Copy)] -#[unstable(feature = "array_windows", issue = "75027")] +#[stable(feature = "array_windows", since = "1.94.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ArrayWindows<'a, T: 'a, const N: usize> { v: &'a [T], @@ -2272,7 +2248,7 @@ impl<'a, T: 'a, const N: usize> ArrayWindows<'a, T, N> { } } -#[unstable(feature = "array_windows", issue = "75027")] +#[stable(feature = "array_windows", since = "1.94.0")] impl<'a, T, const N: usize> Iterator for ArrayWindows<'a, T, N> { type Item = &'a [T; N]; @@ -2309,7 +2285,7 @@ impl<'a, T, const N: usize> Iterator for ArrayWindows<'a, T, N> { } } -#[unstable(feature = "array_windows", issue = "75027")] +#[stable(feature = "array_windows", since = "1.94.0")] impl<'a, T, const N: usize> DoubleEndedIterator for ArrayWindows<'a, T, N> { #[inline] fn next_back(&mut self) -> Option<&'a [T; N]> { @@ -2328,7 +2304,7 @@ impl<'a, T, const N: usize> DoubleEndedIterator for ArrayWindows<'a, T, N> { } } -#[unstable(feature = "array_windows", issue = "75027")] +#[stable(feature = "array_windows", since = "1.94.0")] impl ExactSizeIterator for ArrayWindows<'_, T, N> { fn is_empty(&self) -> bool { self.v.len() < N @@ -2388,16 +2364,12 @@ impl<'a, T> Iterator for RChunks<'a, T> { if self.v.is_empty() { None } else { - let len = self.v.len(); - let chunksz = cmp::min(len, self.chunk_size); - // SAFETY: split_at_unchecked just requires the argument be less - // than the length. This could only happen if the expression `len - - // chunksz` overflows. This could only happen if `chunksz > len`, - // which is impossible as we initialize it as the `min` of `len` and - // `self.chunk_size`. - let (fst, snd) = unsafe { self.v.split_at_unchecked(len - chunksz) }; - self.v = fst; - Some(snd) + let idx = self.v.len().saturating_sub(self.chunk_size); + // SAFETY: self.chunk_size() > 0, so 0 <= idx < self.v.len(). + // Thus `idx` is in-bounds for `self.v` and can be used as a valid argument for `split_at_mut_unchecked`. + let (rest, chunk) = unsafe { self.v.split_at_unchecked(idx) }; + self.v = rest; + Some(chunk) } } @@ -2406,9 +2378,7 @@ impl<'a, T> Iterator for RChunks<'a, T> { if self.v.is_empty() { (0, Some(0)) } else { - let n = self.v.len() / self.chunk_size; - let rem = self.v.len() % self.chunk_size; - let n = if rem > 0 { n + 1 } else { n }; + let n = self.v.len().div_ceil(self.chunk_size); (n, Some(n)) } } @@ -2420,20 +2390,17 @@ impl<'a, T> Iterator for RChunks<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option { - let (end, overflow) = n.overflowing_mul(self.chunk_size); - if end >= self.v.len() || overflow { + if let Some(end) = n.checked_mul(self.chunk_size) + && end < self.v.len() + { + let end = self.v.len() - end; + let rest = &self.v[..end]; + let (rest, chunk) = rest.split_at(end.saturating_sub(self.chunk_size)); + self.v = rest; + Some(chunk) + } else { self.v = &self.v[..0]; // cheaper than &[] None - } else { - // Can't underflow because of the check above - let end = self.v.len() - end; - let start = match end.checked_sub(self.chunk_size) { - Some(sum) => sum, - None => 0, - }; - let nth = &self.v[start..end]; - self.v = &self.v[0..start]; - Some(nth) } } @@ -2451,10 +2418,7 @@ impl<'a, T> Iterator for RChunks<'a, T> { #[requires(idx < self.len())] unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { let end = self.v.len() - idx * self.chunk_size; - let start = match end.checked_sub(self.chunk_size) { - None => 0, - Some(start) => start, - }; + let start = end.saturating_sub(self.chunk_size); // SAFETY: mostly identical to `Chunks::__iterator_get_unchecked`. unsafe { from_raw_parts(self.v.as_ptr().add(start), end - start) } } @@ -2479,17 +2443,16 @@ impl<'a, T> DoubleEndedIterator for RChunks<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &self.v[..0]; // cheaper than &[] - None - } else { - // can't underflow because `n < len` + if n < len { let offset_from_end = (len - 1 - n) * self.chunk_size; let end = self.v.len() - offset_from_end; let start = end.saturating_sub(self.chunk_size); let nth_back = &self.v[start..end]; self.v = &self.v[end..]; Some(nth_back) + } else { + self.v = &self.v[..0]; // cheaper than &[] + None } } } @@ -2561,17 +2524,13 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { if self.v.is_empty() { None } else { - let sz = cmp::min(self.v.len(), self.chunk_size); - let len = self.v.len(); - // SAFETY: split_at_mut_unchecked just requires the argument be less - // than the length. This could only happen if the expression - // `len - sz` overflows. This could only happen if `sz > - // len`, which is impossible as we initialize it as the `min` of - // `self.v.len()` (e.g. `len`) and `self.chunk_size`. - let (head, tail) = unsafe { self.v.split_at_mut_unchecked(len - sz) }; - self.v = head; + let idx = self.v.len().saturating_sub(self.chunk_size); + // SAFETY: self.chunk_size() > 0, so 0 <= idx < self.v.len(). + // Thus `idx` is in-bounds for `self.v` and can be used as a valid argument for `split_at_mut_unchecked`. + let (rest, chunk) = unsafe { self.v.split_at_mut_unchecked(idx) }; + self.v = rest; // SAFETY: Nothing else points to or will point to the contents of this slice. - Some(unsafe { &mut *tail }) + Some(unsafe { &mut *chunk }) } } @@ -2580,9 +2539,7 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { if self.v.is_empty() { (0, Some(0)) } else { - let n = self.v.len() / self.chunk_size; - let rem = self.v.len() % self.chunk_size; - let n = if rem > 0 { n + 1 } else { n }; + let n = self.v.len().div_ceil(self.chunk_size); (n, Some(n)) } } @@ -2594,26 +2551,20 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { - let (end, overflow) = n.overflowing_mul(self.chunk_size); - if end >= self.v.len() || overflow { - self.v = &mut []; - None - } else { - // Can't underflow because of the check above + if let Some(end) = n.checked_mul(self.chunk_size) + && end < self.v.len() + { let end = self.v.len() - end; - let start = match end.checked_sub(self.chunk_size) { - Some(sum) => sum, - None => 0, - }; - // SAFETY: This type ensures that self.v is a valid pointer with a correct len. - // Therefore the bounds check in split_at_mut guarantees the split point is inbounds. - let (head, tail) = unsafe { self.v.split_at_mut(start) }; - // SAFETY: This type ensures that self.v is a valid pointer with a correct len. - // Therefore the bounds check in split_at_mut guarantees the split point is inbounds. - let (nth, _) = unsafe { tail.split_at_mut(end - start) }; - self.v = head; + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (rest, _) = unsafe { self.v.split_at_mut(end) }; + // SAFETY: The self.v contract ensures that any split_at_mut is valid. + let (rest, chunk) = unsafe { rest.split_at_mut(end.saturating_sub(self.chunk_size)) }; + self.v = rest; // SAFETY: Nothing else points to or will point to the contents of this slice. - Some(unsafe { &mut *nth }) + Some(unsafe { &mut *chunk }) + } else { + self.v = &mut []; + None } } @@ -2632,10 +2583,7 @@ impl<'a, T> Iterator for RChunksMut<'a, T> { #[requires(idx < self.len())] unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { let end = self.v.len() - idx * self.chunk_size; - let start = match end.checked_sub(self.chunk_size) { - None => 0, - Some(start) => start, - }; + let start = end.saturating_sub(self.chunk_size); // SAFETY: see comments for `RChunks::__iterator_get_unchecked` and // `ChunksMut::__iterator_get_unchecked`, `self.v`. unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), end - start) } @@ -2662,10 +2610,7 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &mut []; - None - } else { + if n < len { // can't underflow because `n < len` let offset_from_end = (len - 1 - n) * self.chunk_size; let end = self.v.len() - offset_from_end; @@ -2677,6 +2622,9 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> { self.v = tail; // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth_back }) + } else { + self.v = &mut []; + None } } } @@ -2807,14 +2755,14 @@ impl<'a, T> Iterator for RChunksExact<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option { - let (end, overflow) = n.overflowing_mul(self.chunk_size); - if end >= self.v.len() || overflow { + if let Some(end) = n.checked_mul(self.chunk_size) + && end < self.v.len() + { + self.v = &self.v[..self.v.len() - end]; + self.next() + } else { self.v = &self.v[..0]; // cheaper than &[] None - } else { - let (fst, _) = self.v.split_at(self.v.len() - end); - self.v = fst; - self.next() } } @@ -2848,10 +2796,7 @@ impl<'a, T> DoubleEndedIterator for RChunksExact<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &self.v[..0]; // cheaper than &[] - None - } else { + if n < len { // now that we know that `n` corresponds to a chunk, // none of these operations can underflow/overflow let offset = (len - n) * self.chunk_size; @@ -2860,6 +2805,9 @@ impl<'a, T> DoubleEndedIterator for RChunksExact<'a, T> { let nth_back = &self.v[start..end]; self.v = &self.v[end..]; Some(nth_back) + } else { + self.v = &self.v[..0]; // cheaper than &[] + None } } } @@ -2972,16 +2920,17 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option<&'a mut [T]> { - let (end, overflow) = n.overflowing_mul(self.chunk_size); - if end >= self.v.len() || overflow { - self.v = &mut []; - None - } else { - let len = self.v.len(); + if let Some(end) = n.checked_mul(self.chunk_size) + && end < self.v.len() + { + let idx = self.v.len() - end; // SAFETY: The self.v contract ensures that any split_at_mut is valid. - let (fst, _) = unsafe { self.v.split_at_mut(len - end) }; + let (fst, _) = unsafe { self.v.split_at_mut(idx) }; self.v = fst; self.next() + } else { + self.v = &mut []; + None } } @@ -3017,10 +2966,7 @@ impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let len = self.len(); - if n >= len { - self.v = &mut []; - None - } else { + if n < len { // now that we know that `n` corresponds to a chunk, // none of these operations can underflow/overflow let offset = (len - n) * self.chunk_size; @@ -3033,6 +2979,9 @@ impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> { self.v = tail; // SAFETY: Nothing else points to or will point to the contents of this slice. Some(unsafe { &mut *nth_back }) + } else { + self.v = &mut []; + None } } } diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index d5ab717d461f8..967b2ae960e0e 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -68,6 +68,7 @@ macro_rules! iterator { $raw_mut:tt, {$( $mut_:tt )?}, $into_ref:ident, + $array_ref:ident, {$($extra:tt)*} ) => { impl<'a, T> $name<'a, T> { @@ -199,6 +200,29 @@ macro_rules! iterator { } } + fn next_chunk(&mut self) -> Result<[$elem; N], crate::array::IntoIter<$elem, N>> { + if T::IS_ZST { + return crate::array::iter_next_chunk(self); + } + let len = len!(self); + if len >= N { + // SAFETY: we are just getting an array of [T; N] and moving the pointer over a little + let r = unsafe { self.post_inc_start(N).cast_array().$into_ref() } + .$array_ref(); // must convert &[T; N] to [&T; N] + Ok(r) + } else { + // cant use $array_ref because theres no builtin for &mut [MU; N] -> [&mut MU; N] + // cant use copy_nonoverlapping as the $elem is of type &{mut} T instead of T + let mut a = [const { crate::mem::MaybeUninit::<$elem>::uninit() }; N]; + for into in (&mut a).into_iter().take(len) { + // SAFETY: take(n) limits to remainder (slice produces worse codegen) + into.write(unsafe { self.post_inc_start(1).$into_ref() }); + } + // SAFETY: we just initialized elements 0..len + unsafe { Err(crate::array::IntoIter::new_unchecked(a, 0..len)) } + } + } + #[inline] fn size_hint(&self) -> (usize, Option) { let exact = len!(self); diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 8e19bbdca0cd4..0b3cb7f8832b2 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -13,6 +13,7 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::intrinsics::{exact_div, unchecked_sub}; #[cfg(kani)] use crate::kani; +use crate::marker::Destruct; use crate::mem::{self, MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; use crate::ops::{OneSidedRange, OneSidedRangeBound, Range, RangeBounds, RangeInclusive}; @@ -55,7 +56,7 @@ pub use ascii::is_ascii_simple; pub use index::SliceIndex; #[unstable(feature = "slice_range", issue = "76393")] pub use index::{range, try_range}; -#[unstable(feature = "array_windows", issue = "75027")] +#[stable(feature = "array_windows", since = "1.94.0")] pub use iter::ArrayWindows; #[stable(feature = "slice_group_by", since = "1.77.0")] pub use iter::{ChunkBy, ChunkByMut}; @@ -846,8 +847,8 @@ impl [T] { /// Gets a reference to the underlying array. /// /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "core_slice_as_array", since = "1.93.0")] + #[rustc_const_stable(feature = "core_slice_as_array", since = "1.93.0")] #[inline] #[must_use] pub const fn as_array(&self) -> Option<&[T; N]> { @@ -865,8 +866,8 @@ impl [T] { /// Gets a mutable reference to the slice's underlying array. /// /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. - #[stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "core_slice_as_array", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "core_slice_as_array", since = "1.93.0")] + #[rustc_const_stable(feature = "core_slice_as_array", since = "1.93.0")] #[inline] #[must_use] pub const fn as_mut_array(&mut self) -> Option<&mut [T; N]> { @@ -1631,13 +1632,15 @@ impl [T] { /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time - /// error before this method gets stabilized. + /// Panics if `N` is zero. + /// + /// Note that this check is against a const generic parameter, not a runtime + /// value, and thus a particular monomorphization will either always panic + /// or it will never panic. /// /// # Examples /// /// ``` - /// #![feature(array_windows)] /// let slice = [0, 1, 2, 3]; /// let mut iter = slice.array_windows(); /// assert_eq!(iter.next().unwrap(), &[0, 1]); @@ -1647,7 +1650,7 @@ impl [T] { /// ``` /// /// [`windows`]: slice::windows - #[unstable(feature = "array_windows", issue = "75027")] + #[stable(feature = "array_windows", since = "1.94.0")] #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] #[track_caller] @@ -2528,7 +2531,7 @@ impl [T] { /// ))); /// assert_eq!(s.split_once(|&x| x == 0), None); /// ``` - #[unstable(feature = "slice_split_once", reason = "newly added", issue = "112811")] + #[unstable(feature = "slice_split_once", issue = "112811")] #[inline] pub fn split_once(&self, pred: F) -> Option<(&[T], &[T])> where @@ -2556,7 +2559,7 @@ impl [T] { /// ))); /// assert_eq!(s.rsplit_once(|&x| x == 0), None); /// ``` - #[unstable(feature = "slice_split_once", reason = "newly added", issue = "112811")] + #[unstable(feature = "slice_split_once", issue = "112811")] #[inline] pub fn rsplit_once(&self, pred: F) -> Option<(&[T], &[T])> where @@ -3253,6 +3256,219 @@ impl [T] { sort::unstable::sort(self, &mut |a, b| f(a).lt(&f(b))); } + /// Partially sorts the slice in ascending order **without** preserving the initial order of equal elements. + /// + /// Upon completion, for the specified range `start..end`, it's guaranteed that: + /// + /// 1. Every element in `self[..start]` is smaller than or equal to + /// 2. Every element in `self[start..end]`, which is sorted, and smaller than or equal to + /// 3. Every element in `self[end..]`. + /// + /// This partial sort is unstable, meaning it may reorder equal elements in the specified range. + /// It may reorder elements outside the specified range as well, but the guarantees above still hold. + /// + /// This partial sort is in-place (i.e., does not allocate), and *O*(*n* + *k* \* log(*k*)) worst-case, + /// where *n* is the length of the slice and *k* is the length of the specified range. + /// + /// See the documentation of [`sort_unstable`] for implementation notes. + /// + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `T` does not implement a total order, or if + /// the [`Ord`] implementation panics, or if the specified range is out of bounds. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partial_sort_unstable)] + /// + /// let mut v = [4, -5, 1, -3, 2]; + /// + /// // empty range at the beginning, nothing changed + /// v.partial_sort_unstable(0..0); + /// assert_eq!(v, [4, -5, 1, -3, 2]); + /// + /// // empty range in the middle, partitioning the slice + /// v.partial_sort_unstable(2..2); + /// for i in 0..2 { + /// assert!(v[i] <= v[2]); + /// } + /// for i in 3..v.len() { + /// assert!(v[2] <= v[i]); + /// } + /// + /// // single element range, same as select_nth_unstable + /// v.partial_sort_unstable(2..3); + /// for i in 0..2 { + /// assert!(v[i] <= v[2]); + /// } + /// for i in 3..v.len() { + /// assert!(v[2] <= v[i]); + /// } + /// + /// // partial sort a subrange + /// v.partial_sort_unstable(1..4); + /// assert_eq!(&v[1..4], [-3, 1, 2]); + /// + /// // partial sort the whole range, same as sort_unstable + /// v.partial_sort_unstable(..); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); + /// ``` + /// + /// [`sort_unstable`]: slice::sort_unstable + #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] + #[inline] + pub fn partial_sort_unstable(&mut self, range: R) + where + T: Ord, + R: RangeBounds, + { + sort::unstable::partial_sort(self, range, T::lt); + } + + /// Partially sorts the slice in ascending order with a comparison function, **without** + /// preserving the initial order of equal elements. + /// + /// Upon completion, for the specified range `start..end`, it's guaranteed that: + /// + /// 1. Every element in `self[..start]` is smaller than or equal to + /// 2. Every element in `self[start..end]`, which is sorted, and smaller than or equal to + /// 3. Every element in `self[end..]`. + /// + /// This partial sort is unstable, meaning it may reorder equal elements in the specified range. + /// It may reorder elements outside the specified range as well, but the guarantees above still hold. + /// + /// This partial sort is in-place (i.e., does not allocate), and *O*(*n* + *k* \* log(*k*)) worst-case, + /// where *n* is the length of the slice and *k* is the length of the specified range. + /// + /// See the documentation of [`sort_unstable_by`] for implementation notes. + /// + /// # Panics + /// + /// May panic if the `compare` does not implement a total order, or if + /// the `compare` itself panics, or if the specified range is out of bounds. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partial_sort_unstable)] + /// + /// let mut v = [4, -5, 1, -3, 2]; + /// + /// // empty range at the beginning, nothing changed + /// v.partial_sort_unstable_by(0..0, |a, b| b.cmp(a)); + /// assert_eq!(v, [4, -5, 1, -3, 2]); + /// + /// // empty range in the middle, partitioning the slice + /// v.partial_sort_unstable_by(2..2, |a, b| b.cmp(a)); + /// for i in 0..2 { + /// assert!(v[i] >= v[2]); + /// } + /// for i in 3..v.len() { + /// assert!(v[2] >= v[i]); + /// } + /// + /// // single element range, same as select_nth_unstable + /// v.partial_sort_unstable_by(2..3, |a, b| b.cmp(a)); + /// for i in 0..2 { + /// assert!(v[i] >= v[2]); + /// } + /// for i in 3..v.len() { + /// assert!(v[2] >= v[i]); + /// } + /// + /// // partial sort a subrange + /// v.partial_sort_unstable_by(1..4, |a, b| b.cmp(a)); + /// assert_eq!(&v[1..4], [2, 1, -3]); + /// + /// // partial sort the whole range, same as sort_unstable + /// v.partial_sort_unstable_by(.., |a, b| b.cmp(a)); + /// assert_eq!(v, [4, 2, 1, -3, -5]); + /// ``` + /// + /// [`sort_unstable_by`]: slice::sort_unstable_by + #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] + #[inline] + pub fn partial_sort_unstable_by(&mut self, range: R, mut compare: F) + where + F: FnMut(&T, &T) -> Ordering, + R: RangeBounds, + { + sort::unstable::partial_sort(self, range, |a, b| compare(a, b) == Less); + } + + /// Partially sorts the slice in ascending order with a key extraction function, **without** + /// preserving the initial order of equal elements. + /// + /// Upon completion, for the specified range `start..end`, it's guaranteed that: + /// + /// 1. Every element in `self[..start]` is smaller than or equal to + /// 2. Every element in `self[start..end]`, which is sorted, and smaller than or equal to + /// 3. Every element in `self[end..]`. + /// + /// This partial sort is unstable, meaning it may reorder equal elements in the specified range. + /// It may reorder elements outside the specified range as well, but the guarantees above still hold. + /// + /// This partial sort is in-place (i.e., does not allocate), and *O*(*n* + *k* \* log(*k*)) worst-case, + /// where *n* is the length of the slice and *k* is the length of the specified range. + /// + /// See the documentation of [`sort_unstable_by_key`] for implementation notes. + /// + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `K` does not implement a total order, or if + /// the [`Ord`] implementation panics, or if the specified range is out of bounds. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partial_sort_unstable)] + /// + /// let mut v = [4i32, -5, 1, -3, 2]; + /// + /// // empty range at the beginning, nothing changed + /// v.partial_sort_unstable_by_key(0..0, |k| k.abs()); + /// assert_eq!(v, [4, -5, 1, -3, 2]); + /// + /// // empty range in the middle, partitioning the slice + /// v.partial_sort_unstable_by_key(2..2, |k| k.abs()); + /// for i in 0..2 { + /// assert!(v[i].abs() <= v[2].abs()); + /// } + /// for i in 3..v.len() { + /// assert!(v[2].abs() <= v[i].abs()); + /// } + /// + /// // single element range, same as select_nth_unstable + /// v.partial_sort_unstable_by_key(2..3, |k| k.abs()); + /// for i in 0..2 { + /// assert!(v[i].abs() <= v[2].abs()); + /// } + /// for i in 3..v.len() { + /// assert!(v[2].abs() <= v[i].abs()); + /// } + /// + /// // partial sort a subrange + /// v.partial_sort_unstable_by_key(1..4, |k| k.abs()); + /// assert_eq!(&v[1..4], [2, -3, 4]); + /// + /// // partial sort the whole range, same as sort_unstable + /// v.partial_sort_unstable_by_key(.., |k| k.abs()); + /// assert_eq!(v, [1, 2, -3, 4, -5]); + /// ``` + /// + /// [`sort_unstable_by_key`]: slice::sort_unstable_by_key + #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] + #[inline] + pub fn partial_sort_unstable_by_key(&mut self, range: R, mut f: F) + where + F: FnMut(&T) -> K, + K: Ord, + R: RangeBounds, + { + sort::unstable::partial_sort(self, range, |a, b| f(a).lt(&f(b))); + } + /// Reorders the slice such that the element at `index` is at a sort-order position. All /// elements before `index` will be `<=` to this value, and all elements after will be `>=` to /// it. @@ -3734,6 +3950,219 @@ impl [T] { } } + /// Moves the elements of this slice `N` places to the left, returning the ones + /// that "fall off" the front, and putting `inserted` at the end. + /// + /// Equivalently, you can think of concatenating `self` and `inserted` into one + /// long sequence, then returning the left-most `N` items and the rest into `self`: + /// + /// ```text + /// self (before) inserted + /// vvvvvvvvvvvvvvv vvv + /// [1, 2, 3, 4, 5] [9] + /// ↙ ↙ ↙ ↙ ↙ ↙ + /// [1] [2, 3, 4, 5, 9] + /// ^^^ ^^^^^^^^^^^^^^^ + /// returned self (after) + /// ``` + /// + /// See also [`Self::shift_right`] and compare [`Self::rotate_left`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_shift)] + /// + /// // Same as the diagram above + /// let mut a = [1, 2, 3, 4, 5]; + /// let inserted = [9]; + /// let returned = a.shift_left(inserted); + /// assert_eq!(returned, [1]); + /// assert_eq!(a, [2, 3, 4, 5, 9]); + /// + /// // You can shift multiple items at a time + /// let mut a = *b"Hello world"; + /// assert_eq!(a.shift_left(*b" peace"), *b"Hello "); + /// assert_eq!(a, *b"world peace"); + /// + /// // The name comes from this operation's similarity to bitshifts + /// let mut a: u8 = 0b10010110; + /// a <<= 3; + /// assert_eq!(a, 0b10110000_u8); + /// let mut a: [_; 8] = [1, 0, 0, 1, 0, 1, 1, 0]; + /// a.shift_left([0; 3]); + /// assert_eq!(a, [1, 0, 1, 1, 0, 0, 0, 0]); + /// + /// // Remember you can sub-slice to affect less that the whole slice. + /// // For example, this is similar to `.remove(1)` + `.insert(4, 'Z')` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// assert_eq!(a[1..=4].shift_left(['Z']), ['b']); + /// assert_eq!(a, ['a', 'c', 'd', 'e', 'Z', 'f']); + /// + /// // If the size matches it's equivalent to `mem::replace` + /// let mut a = [1, 2, 3]; + /// assert_eq!(a.shift_left([7, 8, 9]), [1, 2, 3]); + /// assert_eq!(a, [7, 8, 9]); + /// + /// // Some of the "inserted" elements end up returned if the slice is too short + /// let mut a = []; + /// assert_eq!(a.shift_left([1, 2, 3]), [1, 2, 3]); + /// let mut a = [9]; + /// assert_eq!(a.shift_left([1, 2, 3]), [9, 1, 2]); + /// assert_eq!(a, [3]); + /// ``` + #[unstable(feature = "slice_shift", issue = "151772")] + pub const fn shift_left(&mut self, inserted: [T; N]) -> [T; N] { + if let Some(shift) = self.len().checked_sub(N) { + // SAFETY: Having just checked that the inserted/returned arrays are + // shorter than (or the same length as) the slice: + // 1. The read for the items to return is in-bounds + // 2. We can `memmove` the slice over to cover the items we're returning + // to ensure those aren't double-dropped + // 3. Then we write (in-bounds for the same reason as the read) the + // inserted items atop the items of the slice that we just duplicated + // + // And none of this can panic, so there's no risk of intermediate unwinds. + unsafe { + let ptr = self.as_mut_ptr(); + let returned = ptr.cast_array::().read(); + ptr.copy_from(ptr.add(N), shift); + ptr.add(shift).cast_array::().write(inserted); + returned + } + } else { + // SAFETY: Having checked that the slice is strictly shorter than the + // inserted/returned arrays, it means we'll be copying the whole slice + // into the returned array, but that's not enough on its own. We also + // need to copy some of the inserted array into the returned array, + // with the rest going into the slice. Because `&mut` is exclusive + // and we own both `inserted` and `returned`, they're all disjoint + // allocations from each other as we can use `nonoverlapping` copies. + // + // We avoid double-frees by `ManuallyDrop`ing the inserted items, + // since we always copy them to other locations that will drop them + // instead. Plus nothing in here can panic -- it's just memcpy three + // times -- so there's no intermediate unwinds to worry about. + unsafe { + let len = self.len(); + let slice = self.as_mut_ptr(); + let inserted = mem::ManuallyDrop::new(inserted); + let inserted = (&raw const inserted).cast::(); + + let mut returned = MaybeUninit::<[T; N]>::uninit(); + let ptr = returned.as_mut_ptr().cast::(); + ptr.copy_from_nonoverlapping(slice, len); + ptr.add(len).copy_from_nonoverlapping(inserted, N - len); + slice.copy_from_nonoverlapping(inserted.add(N - len), len); + returned.assume_init() + } + } + } + + /// Moves the elements of this slice `N` places to the right, returning the ones + /// that "fall off" the back, and putting `inserted` at the beginning. + /// + /// Equivalently, you can think of concatenating `inserted` and `self` into one + /// long sequence, then returning the right-most `N` items and the rest into `self`: + /// + /// ```text + /// inserted self (before) + /// vvv vvvvvvvvvvvvvvv + /// [0] [5, 6, 7, 8, 9] + /// ↘ ↘ ↘ ↘ ↘ ↘ + /// [0, 5, 6, 7, 8] [9] + /// ^^^^^^^^^^^^^^^ ^^^ + /// self (after) returned + /// ``` + /// + /// See also [`Self::shift_left`] and compare [`Self::rotate_right`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_shift)] + /// + /// // Same as the diagram above + /// let mut a = [5, 6, 7, 8, 9]; + /// let inserted = [0]; + /// let returned = a.shift_right(inserted); + /// assert_eq!(returned, [9]); + /// assert_eq!(a, [0, 5, 6, 7, 8]); + /// + /// // The name comes from this operation's similarity to bitshifts + /// let mut a: u8 = 0b10010110; + /// a >>= 3; + /// assert_eq!(a, 0b00010010_u8); + /// let mut a: [_; 8] = [1, 0, 0, 1, 0, 1, 1, 0]; + /// a.shift_right([0; 3]); + /// assert_eq!(a, [0, 0, 0, 1, 0, 0, 1, 0]); + /// + /// // Remember you can sub-slice to affect less that the whole slice. + /// // For example, this is similar to `.remove(4)` + `.insert(1, 'Z')` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// assert_eq!(a[1..=4].shift_right(['Z']), ['e']); + /// assert_eq!(a, ['a', 'Z', 'b', 'c', 'd', 'f']); + /// + /// // If the size matches it's equivalent to `mem::replace` + /// let mut a = [1, 2, 3]; + /// assert_eq!(a.shift_right([7, 8, 9]), [1, 2, 3]); + /// assert_eq!(a, [7, 8, 9]); + /// + /// // Some of the "inserted" elements end up returned if the slice is too short + /// let mut a = []; + /// assert_eq!(a.shift_right([1, 2, 3]), [1, 2, 3]); + /// let mut a = [9]; + /// assert_eq!(a.shift_right([1, 2, 3]), [2, 3, 9]); + /// assert_eq!(a, [1]); + /// ``` + #[unstable(feature = "slice_shift", issue = "151772")] + pub const fn shift_right(&mut self, inserted: [T; N]) -> [T; N] { + if let Some(shift) = self.len().checked_sub(N) { + // SAFETY: Having just checked that the inserted/returned arrays are + // shorter than (or the same length as) the slice: + // 1. The read for the items to return is in-bounds + // 2. We can `memmove` the slice over to cover the items we're returning + // to ensure those aren't double-dropped + // 3. Then we write (in-bounds for the same reason as the read) the + // inserted items atop the items of the slice that we just duplicated + // + // And none of this can panic, so there's no risk of intermediate unwinds. + unsafe { + let ptr = self.as_mut_ptr(); + let returned = ptr.add(shift).cast_array::().read(); + ptr.add(N).copy_from(ptr, shift); + ptr.cast_array::().write(inserted); + returned + } + } else { + // SAFETY: Having checked that the slice is strictly shorter than the + // inserted/returned arrays, it means we'll be copying the whole slice + // into the returned array, but that's not enough on its own. We also + // need to copy some of the inserted array into the returned array, + // with the rest going into the slice. Because `&mut` is exclusive + // and we own both `inserted` and `returned`, they're all disjoint + // allocations from each other as we can use `nonoverlapping` copies. + // + // We avoid double-frees by `ManuallyDrop`ing the inserted items, + // since we always copy them to other locations that will drop them + // instead. Plus nothing in here can panic -- it's just memcpy three + // times -- so there's no intermediate unwinds to worry about. + unsafe { + let len = self.len(); + let slice = self.as_mut_ptr(); + let inserted = mem::ManuallyDrop::new(inserted); + let inserted = (&raw const inserted).cast::(); + + let mut returned = MaybeUninit::<[T; N]>::uninit(); + let ptr = returned.as_mut_ptr().cast::(); + ptr.add(N - len).copy_from_nonoverlapping(slice, len); + ptr.copy_from_nonoverlapping(inserted.add(len), N - len); + slice.copy_from_nonoverlapping(inserted, len); + returned.assume_init() + } + } + } + /// Fills `self` with elements by cloning `value`. /// /// # Examples @@ -3832,9 +4261,10 @@ impl [T] { /// [`split_at_mut`]: slice::split_at_mut #[stable(feature = "clone_from_slice", since = "1.7.0")] #[track_caller] - pub fn clone_from_slice(&mut self, src: &[T]) + #[rustc_const_unstable(feature = "const_clone", issue = "142757")] + pub const fn clone_from_slice(&mut self, src: &[T]) where - T: Clone, + T: [const] Clone + [const] Destruct, { self.spec_clone_from(src); } @@ -4303,7 +4733,6 @@ impl [T] { where Simd: AsRef<[T; LANES]>, T: simd::SimdElement, - simd::LaneCount: simd::SupportedLaneCount, { // These are expected to always match, as vector types are laid out like // arrays per , but we @@ -4339,7 +4768,6 @@ impl [T] { where Simd: AsMut<[T; LANES]>, T: simd::SimdElement, - simd::LaneCount: simd::SupportedLaneCount, { // These are expected to always match, as vector types are laid out like // arrays per , but we @@ -4884,8 +5312,6 @@ impl [T] { /// # Examples /// Basic usage: /// ``` - /// #![feature(substr_range)] - /// /// let nums: &[u32] = &[1, 7, 1, 1]; /// let num = &nums[2]; /// @@ -4894,8 +5320,6 @@ impl [T] { /// ``` /// Returning `None` with an unaligned element: /// ``` - /// #![feature(substr_range)] - /// /// let arr: &[[u32; 2]] = &[[0, 1], [2, 3]]; /// let flat_arr: &[u32] = arr.as_flattened(); /// @@ -4909,7 +5333,7 @@ impl [T] { /// assert_eq!(arr.element_offset(weird_elm), None); // Points between element 0 and 1 /// ``` #[must_use] - #[unstable(feature = "substr_range", issue = "126769")] + #[stable(feature = "element_offset", since = "1.94.0")] pub fn element_offset(&self, element: &T) -> Option { if T::IS_ZST { panic!("elements are zero-sized"); @@ -4983,6 +5407,28 @@ impl [T] { if start <= self.len() && end <= self.len() { Some(start..end) } else { None } } + + /// Returns the same slice `&[T]`. + /// + /// This method is redundant when used directly on `&[T]`, but + /// it helps dereferencing other "container" types to slices, + /// for example `Box<[T]>` or `Arc<[T]>`. + #[inline] + #[unstable(feature = "str_as_str", issue = "130366")] + pub const fn as_slice(&self) -> &[T] { + self + } + + /// Returns the same slice `&mut [T]`. + /// + /// This method is redundant when used directly on `&mut [T]`, but + /// it helps dereferencing other "container" types to slices, + /// for example `Box<[T]>` or `MutexGuard<[T]>`. + #[inline] + #[unstable(feature = "str_as_str", issue = "130366")] + pub const fn as_mut_slice(&mut self) -> &mut [T] { + self + } } impl [MaybeUninit] { @@ -5211,13 +5657,17 @@ const unsafe fn copy_from_slice_impl(dest: &mut [T], src: &[T]) { } } -trait CloneFromSpec { - fn spec_clone_from(&mut self, src: &[T]); +#[rustc_const_unstable(feature = "const_clone", issue = "142757")] +const trait CloneFromSpec { + fn spec_clone_from(&mut self, src: &[T]) + where + T: [const] Destruct; } -impl CloneFromSpec for [T] +#[rustc_const_unstable(feature = "const_clone", issue = "142757")] +impl const CloneFromSpec for [T] where - T: Clone, + T: [const] Clone + [const] Destruct, { #[track_caller] default fn spec_clone_from(&mut self, src: &[T]) { @@ -5227,15 +5677,19 @@ where // But since it can't be relied on we also have an explicit specialization for T: Copy. let len = self.len(); let src = &src[..len]; - for i in 0..len { - self[i].clone_from(&src[i]); + // FIXME(const_hack): make this a `for idx in 0..self.len()` loop. + let mut idx = 0; + while idx < self.len() { + self[idx].clone_from(&src[idx]); + idx += 1; } } } -impl CloneFromSpec for [T] +#[rustc_const_unstable(feature = "const_clone", issue = "142757")] +impl const CloneFromSpec for [T] where - T: TrivialClone, + T: [const] TrivialClone + [const] Destruct, { #[track_caller] fn spec_clone_from(&mut self, src: &[T]) { diff --git a/library/core/src/slice/sort/stable/merge.rs b/library/core/src/slice/sort/stable/merge.rs index bb2747bfc78ac..26d8480b7f71f 100644 --- a/library/core/src/slice/sort/stable/merge.rs +++ b/library/core/src/slice/sort/stable/merge.rs @@ -35,7 +35,7 @@ pub fn merge bool>( // 1. Protects integrity of `v` from panics in `is_less`. // 2. Fills the remaining gap in `v` if the longer run gets consumed first. - let buf = MaybeUninit::slice_as_mut_ptr(scratch); + let buf = scratch.as_mut_ptr().cast_init(); let v_base = v.as_mut_ptr(); let v_mid = v_base.add(mid); diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs index 0439ba870bd2b..acc8a5e838e12 100644 --- a/library/core/src/slice/sort/stable/quicksort.rs +++ b/library/core/src/slice/sort/stable/quicksort.rs @@ -1,6 +1,6 @@ //! This module contains a stable quicksort and partition implementation. -use crate::mem::{ManuallyDrop, MaybeUninit}; +use crate::mem::MaybeUninit; use crate::slice::sort::shared::FreezeMarker; use crate::slice::sort::shared::pivot::choose_pivot; use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl; @@ -41,8 +41,11 @@ pub fn quicksort bool>( // SAFETY: We only access the temporary copy for Freeze types, otherwise // self-modifications via `is_less` would not be observed and this would // be unsound. Our temporary copy does not escape this scope. - let pivot_copy = unsafe { ManuallyDrop::new(ptr::read(&v[pivot_pos])) }; - let pivot_ref = (!has_direct_interior_mutability::()).then_some(&*pivot_copy); + // We use `MaybeUninit` to avoid re-tag issues. FIXME: use `MaybeDangling`. + let pivot_copy = unsafe { ptr::read((&raw const v[pivot_pos]).cast::>()) }; + let pivot_ref = + // SAFETY: We created the value in an init state. + (!has_direct_interior_mutability::()).then_some(unsafe { &*pivot_copy.as_ptr() }); // We choose a pivot, and check if this pivot is equal to our left // ancestor. If true, we do a partition putting equal elements on the @@ -97,7 +100,7 @@ fn stable_partition bool>( } let v_base = v.as_ptr(); - let scratch_base = MaybeUninit::slice_as_mut_ptr(scratch); + let scratch_base = scratch.as_mut_ptr().cast_init(); // The core idea is to write the values that compare as less-than to the left // side of `scratch`, while the values that compared as greater or equal than diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index d4df8d3a264db..7ca95a3b1b198 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -1,11 +1,13 @@ //! This module contains the entry points for `slice::sort_unstable`. use crate::mem::SizedTypeProperties; +use crate::ops::{Range, RangeBounds}; +use crate::slice::sort::select::partition_at_index; #[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::find_existing_run; #[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; -use crate::{cfg_select, intrinsics}; +use crate::{cfg_select, intrinsics, slice}; pub(crate) mod heapsort; pub(crate) mod quicksort; @@ -17,7 +19,10 @@ pub(crate) mod quicksort; /// Upholds all safety properties outlined here: /// #[inline(always)] -pub fn sort bool>(v: &mut [T], is_less: &mut F) { +pub fn sort(v: &mut [T], is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ // Arrays of zero-sized types are always all-equal, and thus sorted. if T::IS_ZST { return; @@ -52,6 +57,54 @@ pub fn sort bool>(v: &mut [T], is_less: &mut F) { } } +/// Unstable partial sort the range `start..end`, after which it's guaranteed that: +/// +/// 1. Every element in `v[..start]` is smaller than or equal to +/// 2. Every element in `v[start..end]`, which is sorted, and smaller than or equal to +/// 3. Every element in `v[end..]`. +#[inline] +pub fn partial_sort(v: &mut [T], range: R, mut is_less: F) +where + F: FnMut(&T, &T) -> bool, + R: RangeBounds, +{ + // Arrays of zero-sized types are always all-equal, and thus sorted. + if T::IS_ZST { + return; + } + + let len = v.len(); + let Range { start, end } = slice::range(range, ..len); + + if end - start <= 1 { + // Empty range or single element. This case can be resolved in at most + // single partition_at_index call, without further sorting. + + if end == 0 || start == len { + // Do nothing if it is an empty range at start or end: all guarantees + // are already upheld. + return; + } + + partition_at_index(v, start, &mut is_less); + return; + } + + // A heuristic factor to decide whether to partition the slice or not. + // If the range bound is close to the edges of the slice, it's not worth + // partitioning first. + const PARTITION_THRESHOLD: usize = 8; + let mut v = v; + if end + PARTITION_THRESHOLD <= len { + v = partition_at_index(v, end - 1, &mut is_less).0; + } + if start >= PARTITION_THRESHOLD { + v = partition_at_index(v, start, &mut is_less).2; + } + + sort(v, &mut is_less); +} + /// See [`sort`] /// /// Deliberately don't inline the main sorting routine entrypoint to ensure the diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 0c5552a0b81cc..22f46ec385ced 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -130,16 +130,18 @@ //! //! * PowerPC and MIPS platforms with 32-bit pointers do not have `AtomicU64` or //! `AtomicI64` types. -//! * ARM platforms like `armv5te` that aren't for Linux only provide `load` -//! and `store` operations, and do not support Compare and Swap (CAS) -//! operations, such as `swap`, `fetch_add`, etc. Additionally on Linux, -//! these CAS operations are implemented via [operating system support], which -//! may come with a performance penalty. -//! * ARM targets with `thumbv6m` only provide `load` and `store` operations, -//! and do not support Compare and Swap (CAS) operations, such as `swap`, -//! `fetch_add`, etc. +//! * Legacy ARM platforms like ARMv4T and ARMv5TE have very limited hardware +//! support for atomics. The bare-metal targets disable this module +//! entirely, but the Linux targets [use the kernel] to assist (which comes +//! with a performance penalty). It's not until ARMv6K onwards that ARM CPUs +//! have support for load/store and Compare and Swap (CAS) atomics in hardware. +//! * ARMv6-M and ARMv8-M baseline targets (`thumbv6m-*` and +//! `thumbv8m.base-*`) only provide `load` and `store` operations, and do +//! not support Compare and Swap (CAS) operations, such as `swap`, +//! `fetch_add`, etc. Full CAS support is available on ARMv7-M and ARMv8-M +//! Mainline (`thumbv7m-*`, `thumbv7em*` and `thumbv8m.main-*`). //! -//! [operating system support]: https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt +//! [use the kernel]: https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt //! //! Note that future platforms may be added that also do not have support for //! some atomic operations. Maximally portable code will want to be careful @@ -1149,8 +1151,8 @@ impl AtomicBool { /// assert_eq!(foo.fetch_or(false, Ordering::SeqCst), true); /// assert_eq!(foo.load(Ordering::SeqCst), true); /// - /// let foo = AtomicBool::new(true); - /// assert_eq!(foo.fetch_or(true, Ordering::SeqCst), true); + /// let foo = AtomicBool::new(false); + /// assert_eq!(foo.fetch_or(true, Ordering::SeqCst), false); /// assert_eq!(foo.load(Ordering::SeqCst), true); /// /// let foo = AtomicBool::new(false); @@ -1548,6 +1550,24 @@ impl AtomicPtr { unsafe { &*ptr.cast() } } + /// Creates a new `AtomicPtr` initialized with a null pointer. + /// + /// # Examples + /// + /// ``` + /// #![feature(atomic_ptr_null)] + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let atomic_ptr = AtomicPtr::<()>::null(); + /// assert!(atomic_ptr.load(Ordering::Relaxed).is_null()); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "atomic_ptr_null", issue = "150733")] + pub const fn null() -> AtomicPtr { + AtomicPtr::new(crate::ptr::null_mut()) + } + /// Returns a mutable reference to the underlying pointer. /// /// This is safe because the mutable reference guarantees that no other threads are diff --git a/library/core/src/task/ready.rs b/library/core/src/task/ready.rs index 495d72fd14be2..468b3b4e528ed 100644 --- a/library/core/src/task/ready.rs +++ b/library/core/src/task/ready.rs @@ -46,7 +46,7 @@ /// # } /// ``` #[stable(feature = "ready_macro", since = "1.64.0")] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro ready($e:expr) { match $e { $crate::task::Poll::Ready(t) => t, diff --git a/library/core/src/time.rs b/library/core/src/time.rs index cc7663bee0ef7..2277794723dfc 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -344,8 +344,8 @@ impl Duration { /// assert_eq!(10_u64.pow(15), duration.as_secs()); /// assert_eq!(321, duration.subsec_nanos()); /// ``` - #[stable(feature = "duration_from_nanos_u128", since = "CURRENT_RUSTC_VERSION")] - #[rustc_const_stable(feature = "duration_from_nanos_u128", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "duration_from_nanos_u128", since = "1.93.0")] + #[rustc_const_stable(feature = "duration_from_nanos_u128", since = "1.93.0")] #[must_use] #[inline] #[track_caller] @@ -698,11 +698,10 @@ impl Duration { let mut nanos = self.nanos.as_inner() + rhs.nanos.as_inner(); if nanos >= NANOS_PER_SEC { nanos -= NANOS_PER_SEC; - if let Some(new_secs) = secs.checked_add(1) { - secs = new_secs; - } else { + let Some(new_secs) = secs.checked_add(1) else { return None; - } + }; + secs = new_secs; } debug_assert!(nanos < NANOS_PER_SEC); Some(Duration::new(secs, nanos)) @@ -1162,6 +1161,46 @@ impl Duration { let rhs_nanos = (rhs.secs as f32) * (NANOS_PER_SEC as f32) + (rhs.nanos.as_inner() as f32); self_nanos / rhs_nanos } + + /// Divides `Duration` by `Duration` and returns `u128`, rounding the result towards zero. + /// + /// # Examples + /// ``` + /// #![feature(duration_integer_division)] + /// use std::time::Duration; + /// + /// let dur = Duration::new(2, 0); + /// assert_eq!(dur.div_duration_floor(Duration::new(1, 000_000_001)), 1); + /// assert_eq!(dur.div_duration_floor(Duration::new(1, 000_000_000)), 2); + /// assert_eq!(dur.div_duration_floor(Duration::new(0, 999_999_999)), 2); + /// ``` + #[unstable(feature = "duration_integer_division", issue = "149573")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn div_duration_floor(self, rhs: Duration) -> u128 { + self.as_nanos().div_floor(rhs.as_nanos()) + } + + /// Divides `Duration` by `Duration` and returns `u128`, rounding the result towards positive infinity. + /// + /// # Examples + /// ``` + /// #![feature(duration_integer_division)] + /// use std::time::Duration; + /// + /// let dur = Duration::new(2, 0); + /// assert_eq!(dur.div_duration_ceil(Duration::new(1, 000_000_001)), 2); + /// assert_eq!(dur.div_duration_ceil(Duration::new(1, 000_000_000)), 2); + /// assert_eq!(dur.div_duration_ceil(Duration::new(0, 999_999_999)), 3); + /// ``` + #[unstable(feature = "duration_integer_division", issue = "149573")] + #[must_use = "this returns the result of the operation, \ + without modifying the original"] + #[inline] + pub const fn div_duration_ceil(self, rhs: Duration) -> u128 { + self.as_nanos().div_ceil(rhs.as_nanos()) + } } #[stable(feature = "duration", since = "1.3.0")] @@ -1346,39 +1385,54 @@ impl fmt::Debug for Duration { // need to perform rounding to match the semantics of printing // normal floating point numbers. However, we only need to do work // when rounding up. This happens if the first digit of the - // remaining ones is >= 5. + // remaining ones is >= 5. When the first digit is exactly 5, rounding + // follows IEEE-754 round-ties-to-even semantics: we only round up + // if the last written digit is odd. let integer_part = if fractional_part > 0 && fractional_part >= divisor * 5 { - // Round up the number contained in the buffer. We go through - // the buffer backwards and keep track of the carry. - let mut rev_pos = pos; - let mut carry = true; - while carry && rev_pos > 0 { - rev_pos -= 1; - - // If the digit in the buffer is not '9', we just need to - // increment it and can stop then (since we don't have a - // carry anymore). Otherwise, we set it to '0' (overflow) - // and continue. - if buf[rev_pos] < b'9' { - buf[rev_pos] += 1; - carry = false; - } else { - buf[rev_pos] = b'0'; - } - } - - // If we still have the carry bit set, that means that we set - // the whole buffer to '0's and need to increment the integer - // part. - if carry { - // If `integer_part == u64::MAX` and precision < 9, any - // carry of the overflow during rounding of the - // `fractional_part` into the `integer_part` will cause the - // `integer_part` itself to overflow. Avoid this by using an - // `Option`, with `None` representing `u64::MAX + 1`. - integer_part.checked_add(1) + // For ties (fractional_part == divisor * 5), only round up if last digit is odd + let is_tie = fractional_part == divisor * 5; + let last_digit_is_odd = if pos > 0 { + (buf[pos - 1] - b'0') % 2 == 1 } else { + // No fractional digits - check the integer part + (integer_part % 2) == 1 + }; + + if is_tie && !last_digit_is_odd { Some(integer_part) + } else { + // Round up the number contained in the buffer. We go through + // the buffer backwards and keep track of the carry. + let mut rev_pos = pos; + let mut carry = true; + while carry && rev_pos > 0 { + rev_pos -= 1; + + // If the digit in the buffer is not '9', we just need to + // increment it and can stop then (since we don't have a + // carry anymore). Otherwise, we set it to '0' (overflow) + // and continue. + if buf[rev_pos] < b'9' { + buf[rev_pos] += 1; + carry = false; + } else { + buf[rev_pos] = b'0'; + } + } + + // If we still have the carry bit set, that means that we set + // the whole buffer to '0's and need to increment the integer + // part. + if carry { + // If `integer_part == u64::MAX` and precision < 9, any + // carry of the overflow during rounding of the + // `fractional_part` into the `integer_part` will cause the + // `integer_part` itself to overflow. Avoid this by using an + // `Option`, with `None` representing `u64::MAX + 1`. + integer_part.checked_add(1) + } else { + Some(integer_part) + } } } else { Some(integer_part) diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs index e1d1b9fd3b9d6..2e05943aafc82 100644 --- a/library/core/src/ub_checks.rs +++ b/library/core/src/ub_checks.rs @@ -95,17 +95,16 @@ pub use intrinsics::ub_checks as check_library_ub; #[rustc_allow_const_fn_unstable(const_eval_select)] pub(crate) const fn check_language_ub() -> bool { // Only used for UB checks so we may const_eval_select. - intrinsics::ub_checks() - && const_eval_select!( - @capture { } -> bool: - if const { - // Always disable UB checks. - false - } else { - // Disable UB checks in Miri. - !cfg!(miri) - } - ) + const_eval_select!( + @capture { } -> bool: + if const { + // Always disable UB checks. + false + } else { + // Disable UB checks in Miri. + !cfg!(miri) + } + ) && intrinsics::ub_checks() } /// Checks whether `ptr` is properly aligned with respect to the given alignment, and diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index 3c38b44224f87..429b60a68f439 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -767,7 +767,8 @@ pub mod conversions { LOWERCASE_TABLE .binary_search_by(|&(key, _)| key.cmp(&c)) .map(|i| { - let u = LOWERCASE_TABLE[i].1; + // SAFETY: i is the result of the binary search + let u = unsafe { LOWERCASE_TABLE.get_unchecked(i) }.1; char::from_u32(u).map(|c| [c, '\0', '\0']).unwrap_or_else(|| { // SAFETY: Index comes from statically generated table unsafe { *LOWERCASE_TABLE_MULTI.get_unchecked((u & (INDEX_MASK - 1)) as usize) } @@ -784,7 +785,8 @@ pub mod conversions { UPPERCASE_TABLE .binary_search_by(|&(key, _)| key.cmp(&c)) .map(|i| { - let u = UPPERCASE_TABLE[i].1; + // SAFETY: i is the result of the binary search + let u = unsafe { UPPERCASE_TABLE.get_unchecked(i) }.1; char::from_u32(u).map(|c| [c, '\0', '\0']).unwrap_or_else(|| { // SAFETY: Index comes from statically generated table unsafe { *UPPERCASE_TABLE_MULTI.get_unchecked((u & (INDEX_MASK - 1)) as usize) } diff --git a/library/coretests/benches/lib.rs b/library/coretests/benches/lib.rs index 32d15c386cb1b..150b9b33f4578 100644 --- a/library/coretests/benches/lib.rs +++ b/library/coretests/benches/lib.rs @@ -8,6 +8,7 @@ #![feature(iter_array_chunks)] #![feature(iter_next_chunk)] #![feature(iter_advance_by)] +#![feature(uint_gather_scatter_bits)] extern crate test; diff --git a/library/coretests/benches/num/int_bits/mod.rs b/library/coretests/benches/num/int_bits/mod.rs new file mode 100644 index 0000000000000..65ba48609e80c --- /dev/null +++ b/library/coretests/benches/num/int_bits/mod.rs @@ -0,0 +1,63 @@ +const BYTES: usize = 1 << 10; + +macro_rules! bench_template { + ($op:path, $name:ident, $mask:expr) => { + #[bench] + fn $name(bench: &mut ::test::Bencher) { + use ::rand::distr::{Distribution, Uniform}; + let mut rng = crate::bench_rng(); + let mut dst = vec![0; ITERATIONS]; + let distr = &Uniform::try_from(0..=U::MAX).unwrap(); + let src1: Vec = distr.sample_iter(&mut rng).take(ITERATIONS).collect(); + let mut src2: Vec = distr.sample_iter(&mut rng).take(ITERATIONS).collect(); + // Fix the loop invariant mask + src2[0] = U::MAX / 3; + let dst = dst.first_chunk_mut().unwrap(); + let src1 = src1.first_chunk().unwrap(); + let src2 = src2.first_chunk().unwrap(); + + #[allow(unused)] + fn vectored(dst: &mut Data, src1: &Data, src2: &Data) { + let mask = $mask; + for k in 0..ITERATIONS { + dst[k] = $op(src1[k], mask(src2, k)); + } + } + let f: fn(&mut Data, &Data, &Data) = vectored; + let f = ::test::black_box(f); + + bench.iter(|| { + f(dst, src1, src2); + }); + } + }; +} + +macro_rules! bench_type { + ($U:ident) => { + mod $U { + type U = $U; + const ITERATIONS: usize = super::BYTES / size_of::(); + type Data = [U; ITERATIONS]; + bench_mask_kind!(constant, |_, _| const { U::MAX / 3 }); + bench_mask_kind!(invariant, |src: &Data, _| src[0]); + bench_mask_kind!(variable, |src: &Data, k| src[k]); + } + }; +} + +macro_rules! bench_mask_kind { + ($mask_kind:ident, $mask:expr) => { + mod $mask_kind { + use super::{Data, ITERATIONS, U}; + bench_template!(U::extract_bits, extract_bits, $mask); + bench_template!(U::deposit_bits, deposit_bits, $mask); + } + }; +} + +bench_type!(u8); +bench_type!(u16); +bench_type!(u32); +bench_type!(u64); +bench_type!(u128); diff --git a/library/coretests/benches/num/int_sqrt/mod.rs b/library/coretests/benches/num/int_sqrt/mod.rs index 05cb3c5383b27..56172f71dd88c 100644 --- a/library/coretests/benches/num/int_sqrt/mod.rs +++ b/library/coretests/benches/num/int_sqrt/mod.rs @@ -1,3 +1,5 @@ +use std::iter; + use rand::Rng; use test::{Bencher, black_box}; @@ -20,7 +22,9 @@ macro_rules! int_sqrt_bench { let mut rng = crate::bench_rng(); /* Exponentially distributed random numbers from the whole range of the type. */ let numbers: Vec<$t> = - (0..256).map(|_| rng.random::<$t>() >> rng.random_range(0..<$t>::BITS)).collect(); + iter::repeat_with(|| rng.random::<$t>() >> rng.random_range(0..<$t>::BITS)) + .take(256) + .collect(); bench.iter(|| { for x in &numbers { black_box(black_box(x).isqrt()); @@ -32,9 +36,10 @@ macro_rules! int_sqrt_bench { fn $random_small(bench: &mut Bencher) { let mut rng = crate::bench_rng(); /* Exponentially distributed random numbers from the range 0..256. */ - let numbers: Vec<$t> = (0..256) - .map(|_| (rng.random::() >> rng.random_range(0..u8::BITS)) as $t) - .collect(); + let numbers: Vec<$t> = + iter::repeat_with(|| (rng.random::() >> rng.random_range(0..u8::BITS)) as $t) + .take(256) + .collect(); bench.iter(|| { for x in &numbers { black_box(black_box(x).isqrt()); @@ -44,9 +49,9 @@ macro_rules! int_sqrt_bench { #[bench] fn $random_uniform(bench: &mut Bencher) { - let mut rng = crate::bench_rng(); + let rng = crate::bench_rng(); /* Exponentially distributed random numbers from the whole range of the type. */ - let numbers: Vec<$t> = (0..256).map(|_| rng.random::<$t>()).collect(); + let numbers: Vec<$t> = rng.random_iter().take(256).collect(); bench.iter(|| { for x in &numbers { black_box(black_box(x).isqrt()); diff --git a/library/coretests/benches/num/mod.rs b/library/coretests/benches/num/mod.rs index b36100e59a97a..a131b3454f0cc 100644 --- a/library/coretests/benches/num/mod.rs +++ b/library/coretests/benches/num/mod.rs @@ -1,5 +1,6 @@ mod dec2flt; mod flt2dec; +mod int_bits; mod int_log; mod int_pow; mod int_sqrt; diff --git a/library/coretests/benches/str.rs b/library/coretests/benches/str.rs index 2f7d9d56a70b7..bf45a8f0a79bc 100644 --- a/library/coretests/benches/str.rs +++ b/library/coretests/benches/str.rs @@ -5,6 +5,7 @@ use test::{Bencher, black_box}; mod char_count; mod corpora; mod debug; +mod eq_ignore_ascii_case; mod iter; #[bench] diff --git a/library/coretests/benches/str/eq_ignore_ascii_case.rs b/library/coretests/benches/str/eq_ignore_ascii_case.rs new file mode 100644 index 0000000000000..29129b933bc46 --- /dev/null +++ b/library/coretests/benches/str/eq_ignore_ascii_case.rs @@ -0,0 +1,45 @@ +use test::{Bencher, black_box}; + +use super::corpora::*; + +#[bench] +fn bench_str_under_8_bytes_eq(b: &mut Bencher) { + let s = black_box("foo"); + let other = black_box("foo"); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_str_of_8_bytes_eq(b: &mut Bencher) { + let s = black_box(en::TINY); + let other = black_box(en::TINY); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_str_17_bytes_eq(b: &mut Bencher) { + let s = black_box(&en::SMALL[..17]); + let other = black_box(&en::SMALL[..17]); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_str_31_bytes_eq(b: &mut Bencher) { + let s = black_box(&en::SMALL[..31]); + let other = black_box(&en::SMALL[..31]); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_medium_str_eq(b: &mut Bencher) { + let s = black_box(en::MEDIUM); + let other = black_box(en::MEDIUM); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} + +#[bench] +fn bench_large_str_eq(b: &mut Bencher) { + let s = black_box(en::LARGE); + let other = black_box(en::LARGE); + b.iter(|| assert!(s.eq_ignore_ascii_case(other))) +} diff --git a/library/coretests/tests/alloc.rs b/library/coretests/tests/alloc.rs index a4af6fd32a11d..aac1d60ce32c4 100644 --- a/library/coretests/tests/alloc.rs +++ b/library/coretests/tests/alloc.rs @@ -6,7 +6,7 @@ fn const_unchecked_layout() { const SIZE: usize = 0x2000; const ALIGN: usize = 0x1000; const LAYOUT: Layout = unsafe { Layout::from_size_align_unchecked(SIZE, ALIGN) }; - const DANGLING: NonNull = LAYOUT.dangling(); + const DANGLING: NonNull = LAYOUT.dangling_ptr(); assert_eq!(LAYOUT.size(), SIZE); assert_eq!(LAYOUT.align(), ALIGN); assert_eq!(Some(DANGLING), NonNull::new(ptr::without_provenance_mut(ALIGN))); diff --git a/library/coretests/tests/array.rs b/library/coretests/tests/array.rs index c4a8fc74feca3..2b4429092e98b 100644 --- a/library/coretests/tests/array.rs +++ b/library/coretests/tests/array.rs @@ -724,3 +724,20 @@ fn array_eq() { let not_true = [0u8] == [].as_slice(); assert!(!not_true); } + +#[test] +fn const_array_ops() { + const fn doubler(x: usize) -> usize { + x * 2 + } + const fn maybe_doubler(x: usize) -> Option { + x.checked_mul(2) + } + assert_eq!(const { std::array::from_fn::<_, 5, _>(doubler) }, [0, 2, 4, 6, 8]); + assert_eq!(const { [5, 6, 1, 2].map(doubler) }, [10, 12, 2, 4]); + assert_eq!(const { [1, usize::MAX, 2, 8].try_map(maybe_doubler) }, None); + assert_eq!(const { std::array::try_from_fn::<_, 5, _>(maybe_doubler) }, Some([0, 2, 4, 6, 8])); + #[derive(Debug, PartialEq)] + struct Zst; + assert_eq!([(); 10].try_map(|()| Some(Zst)), Some([const { Zst }; 10])); +} diff --git a/library/coretests/tests/bool.rs b/library/coretests/tests/bool.rs index eb5f0f50663e1..802e43045b6e6 100644 --- a/library/coretests/tests/bool.rs +++ b/library/coretests/tests/bool.rs @@ -82,6 +82,10 @@ pub fn test_bool_not() { } } +const fn zero() -> i32 { + 0 +} + #[test] fn test_bool_to_option() { assert_eq!(false.then_some(0), None); @@ -89,11 +93,6 @@ fn test_bool_to_option() { assert_eq!(false.then(|| 0), None); assert_eq!(true.then(|| 0), Some(0)); - /* FIXME(#110395) - const fn zero() -> i32 { - 0 - } - const A: Option = false.then_some(0); const B: Option = true.then_some(0); const C: Option = false.then(zero); @@ -103,7 +102,6 @@ fn test_bool_to_option() { assert_eq!(B, Some(0)); assert_eq!(C, None); assert_eq!(D, Some(0)); - */ } #[test] @@ -112,4 +110,14 @@ fn test_bool_to_result() { assert_eq!(true.ok_or(0), Ok(())); assert_eq!(false.ok_or_else(|| 0), Err(0)); assert_eq!(true.ok_or_else(|| 0), Ok(())); + + const A: Result<(), i32> = false.ok_or(0); + const B: Result<(), i32> = true.ok_or(0); + const C: Result<(), i32> = false.ok_or_else(zero); + const D: Result<(), i32> = true.ok_or_else(zero); + + assert_eq!(A, Err(0)); + assert_eq!(B, Ok(())); + assert_eq!(C, Err(0)); + assert_eq!(D, Ok(())); } diff --git a/library/coretests/tests/clone.rs b/library/coretests/tests/clone.rs index 054b1d3d4986c..68871dbccf07b 100644 --- a/library/coretests/tests/clone.rs +++ b/library/coretests/tests/clone.rs @@ -121,3 +121,74 @@ fn cstr_metadata_is_length_with_nul() { let bytes: *const [u8] = p as *const [u8]; assert_eq!(s.to_bytes_with_nul().len(), bytes.len()); } + +#[test] +fn test_const_clone() { + const { + let bool: bool = Default::default(); + let char: char = Default::default(); + let ascii_char: std::ascii::Char = Default::default(); + let usize: usize = Default::default(); + let u8: u8 = Default::default(); + let u16: u16 = Default::default(); + let u32: u32 = Default::default(); + let u64: u64 = Default::default(); + let u128: u128 = Default::default(); + let i8: i8 = Default::default(); + let i16: i16 = Default::default(); + let i32: i32 = Default::default(); + let i64: i64 = Default::default(); + let i128: i128 = Default::default(); + let f16: f16 = Default::default(); + let f32: f32 = Default::default(); + let f64: f64 = Default::default(); + let f128: f128 = Default::default(); + + let bool_clone: bool = bool.clone(); + let char_clone: char = char.clone(); + let ascii_char_clone: std::ascii::Char = ascii_char.clone(); + + let usize_clone: usize = usize.clone(); + let u8_clone: u8 = u8.clone(); + let u16_clone: u16 = u16.clone(); + let u32_clone: u32 = u32.clone(); + let u64_clone: u64 = u64.clone(); + let u128_clone: u128 = u128.clone(); + let i8_clone: i8 = i8.clone(); + let i16_clone: i16 = i16.clone(); + let i32_clone: i32 = i32.clone(); + let i64_clone: i64 = i64.clone(); + let i128_clone: i128 = i128.clone(); + let f16_clone: f16 = f16.clone(); + let f32_clone: f32 = f32.clone(); + let f64_clone: f64 = f64.clone(); + let f128_clone: f128 = f128.clone(); + + assert!(bool == bool_clone); + assert!(char == char_clone); + assert!(ascii_char == ascii_char_clone); + assert!(usize == usize_clone); + assert!(u8 == u8_clone); + assert!(u16 == u16_clone); + assert!(u32 == u32_clone); + assert!(u64 == u64_clone); + assert!(u128 == u128_clone); + assert!(i8 == i8_clone); + assert!(i16 == i16_clone); + assert!(i32 == i32_clone); + assert!(i64 == i64_clone); + assert!(i128 == i128_clone); + assert!(f16 == f16_clone); + assert!(f32 == f32_clone); + assert!(f64 == f64_clone); + assert!(f128 == f128_clone); + + let src: [i32; 4] = [1, 2, 3, 4]; + let mut dst: [i32; 2] = [0, 0]; + + dst.clone_from_slice(&src[2..]); + + assert!(src == [1, 2, 3, 4]); + assert!(dst == [3, 4]); + } +} diff --git a/library/coretests/tests/convert.rs b/library/coretests/tests/convert.rs index f1048f4cf09cb..1eb7468e56ea7 100644 --- a/library/coretests/tests/convert.rs +++ b/library/coretests/tests/convert.rs @@ -14,3 +14,20 @@ fn convert() { const BAR: Vec = into(Vec::new()); assert_eq!(BAR, Vec::::new()); } + +#[test] +fn into_as_try_into() { + struct A; + struct B; + + impl Into for A { + fn into(self) -> B { + B + } + } + + // This wouldn't compile if the `TryInto`/`TryFrom` blanket impls used + // `U: From` instead of `T: Into` + let Ok(B) = A.try_into(); + let Ok(B) = B::try_from(A); +} diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs deleted file mode 100644 index 8e4f0c9899e1c..0000000000000 --- a/library/coretests/tests/floats/f128.rs +++ /dev/null @@ -1,50 +0,0 @@ -// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy -#![cfg(target_has_reliable_f128)] - -use super::assert_biteq; - -// Note these tolerances make sense around zero, but not for more extreme exponents. - -/// Default tolerances. Works for values that should be near precise but not exact. Roughly -/// the precision carried by `100 * 100`. -#[allow(unused)] -const TOL: f128 = 1e-12; - -/// For operations that are near exact, usually not involving math of different -/// signs. -#[allow(unused)] -const TOL_PRECISE: f128 = 1e-28; - -// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support -// the intrinsics. - -#[test] -fn test_from() { - assert_biteq!(f128::from(false), 0.0); - assert_biteq!(f128::from(true), 1.0); - assert_biteq!(f128::from(u8::MIN), 0.0); - assert_biteq!(f128::from(42_u8), 42.0); - assert_biteq!(f128::from(u8::MAX), 255.0); - assert_biteq!(f128::from(i8::MIN), -128.0); - assert_biteq!(f128::from(42_i8), 42.0); - assert_biteq!(f128::from(i8::MAX), 127.0); - assert_biteq!(f128::from(u16::MIN), 0.0); - assert_biteq!(f128::from(42_u16), 42.0); - assert_biteq!(f128::from(u16::MAX), 65535.0); - assert_biteq!(f128::from(i16::MIN), -32768.0); - assert_biteq!(f128::from(42_i16), 42.0); - assert_biteq!(f128::from(i16::MAX), 32767.0); - assert_biteq!(f128::from(u32::MIN), 0.0); - assert_biteq!(f128::from(42_u32), 42.0); - assert_biteq!(f128::from(u32::MAX), 4294967295.0); - assert_biteq!(f128::from(i32::MIN), -2147483648.0); - assert_biteq!(f128::from(42_i32), 42.0); - assert_biteq!(f128::from(i32::MAX), 2147483647.0); - // FIXME(f16_f128): Uncomment these tests once the From<{u64,i64}> impls are added. - // assert_eq!(f128::from(u64::MIN), 0.0); - // assert_eq!(f128::from(42_u64), 42.0); - // assert_eq!(f128::from(u64::MAX), 18446744073709551615.0); - // assert_eq!(f128::from(i64::MIN), -9223372036854775808.0); - // assert_eq!(f128::from(42_i64), 42.0); - // assert_eq!(f128::from(i64::MAX), 9223372036854775807.0); -} diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs deleted file mode 100644 index 3cff4259de54f..0000000000000 --- a/library/coretests/tests/floats/f16.rs +++ /dev/null @@ -1,35 +0,0 @@ -// FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy -#![cfg(target_has_reliable_f16)] - -use super::assert_biteq; - -/// Tolerance for results on the order of 10.0e-2 -#[allow(unused)] -const TOL_N2: f16 = 0.0001; - -/// Tolerance for results on the order of 10.0e+0 -#[allow(unused)] -const TOL_0: f16 = 0.01; - -/// Tolerance for results on the order of 10.0e+2 -#[allow(unused)] -const TOL_P2: f16 = 0.5; - -/// Tolerance for results on the order of 10.0e+4 -#[allow(unused)] -const TOL_P4: f16 = 10.0; - -// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support -// the intrinsics. - -#[test] -fn test_from() { - assert_biteq!(f16::from(false), 0.0); - assert_biteq!(f16::from(true), 1.0); - assert_biteq!(f16::from(u8::MIN), 0.0); - assert_biteq!(f16::from(42_u8), 42.0); - assert_biteq!(f16::from(u8::MAX), 255.0); - assert_biteq!(f16::from(i8::MIN), -128.0); - assert_biteq!(f16::from(42_i8), 42.0); - assert_biteq!(f16::from(i8::MAX), 127.0); -} diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index 63d5b8fb2c6e9..06fc3c96eafc8 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -375,9 +375,6 @@ macro_rules! float_test { }; } -mod f128; -mod f16; - float_test! { name: num, attrs: { @@ -394,7 +391,7 @@ float_test! { } } -// FIXME(f16_f128): merge into `num` once the required `fmodl`/`fmodf128` function is available on +// FIXME(f128): merge into `num` once the required `fmodl`/`fmodf128` function is available on // all platforms. float_test! { name: num_rem, @@ -1360,15 +1357,11 @@ float_test! { } } -// FIXME(f16): Tests involving sNaN are disabled because without optimizations, `total_cmp` is -// getting incorrectly lowered to code that includes a `extend`/`trunc` round trip, which quiets -// sNaNs. See: https://github.com/llvm/llvm-project/issues/104915 - float_test! { name: total_cmp_s_nan, attrs: { const: #[cfg(false)], - f16: #[cfg(miri)], + f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, test { @@ -1582,3 +1575,78 @@ float_test! { assert_biteq!((flt(-3.2)).mul_add(2.4, neg_inf), neg_inf); } } + +float_test! { + name: from, + attrs: { + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(any(miri, target_has_reliable_f128))], + }, + test { + assert_biteq!(Float::from(false), Float::ZERO); + assert_biteq!(Float::from(true), Float::ONE); + + assert_biteq!(Float::from(u8::MIN), Float::ZERO); + assert_biteq!(Float::from(42_u8), 42.0); + assert_biteq!(Float::from(u8::MAX), 255.0); + + assert_biteq!(Float::from(i8::MIN), -128.0); + assert_biteq!(Float::from(42_i8), 42.0); + assert_biteq!(Float::from(i8::MAX), 127.0); + } +} + +float_test! { + name: from_u16_i16, + attrs: { + f16: #[cfg(false)], + const f16: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128))], + }, + test { + assert_biteq!(Float::from(u16::MIN), Float::ZERO); + assert_biteq!(Float::from(42_u16), 42.0); + assert_biteq!(Float::from(u16::MAX), 65535.0); + assert_biteq!(Float::from(i16::MIN), -32768.0); + assert_biteq!(Float::from(42_i16), 42.0); + assert_biteq!(Float::from(i16::MAX), 32767.0); + } +} + +float_test! { + name: from_u32_i32, + attrs: { + f16: #[cfg(false)], + const f16: #[cfg(false)], + f32: #[cfg(false)], + const f32: #[cfg(false)], + f128: #[cfg(any(miri, target_has_reliable_f128))], + }, + test { + assert_biteq!(Float::from(u32::MIN), Float::ZERO); + assert_biteq!(Float::from(42_u32), 42.0); + assert_biteq!(Float::from(u32::MAX), 4294967295.0); + assert_biteq!(Float::from(i32::MIN), -2147483648.0); + assert_biteq!(Float::from(42_i32), 42.0); + assert_biteq!(Float::from(i32::MAX), 2147483647.0); + } +} + +// FIXME(f128): Uncomment and adapt these tests once the From<{u64,i64}> impls are added. +// float_test! { +// name: from_u64_i64, +// attrs: { +// f16: #[cfg(false)], +// f32: #[cfg(false)], +// f64: #[cfg(false)], +// f128: #[cfg(any(miri, target_has_reliable_f128))], +// }, +// test { +// assert_biteq!(Float::from(u64::MIN), Float::ZERO); +// assert_biteq!(Float::from(42_u64), 42.0); +// assert_biteq!(Float::from(u64::MAX), 18446744073709551615.0); +// assert_biteq!(Float::from(i64::MIN), -9223372036854775808.0); +// assert_biteq!(Float::from(42_i64), 42.0); +// assert_biteq!(Float::from(i64::MAX), 9223372036854775807.0); +// } +// } diff --git a/library/coretests/tests/fmt/mod.rs b/library/coretests/tests/fmt/mod.rs index d0ae7124f4742..67ba49db4d919 100644 --- a/library/coretests/tests/fmt/mod.rs +++ b/library/coretests/tests/fmt/mod.rs @@ -30,6 +30,12 @@ fn test_format_flags() { assert_eq!(format!("{:p} {:x}", p, 16), format!("{p:p} 10")); assert_eq!(format!("{: >3}", 'a'), " a"); + + /// Regression test for . + fn show(a: fn() -> f32, b: fn(&Vec) -> f32) { + println!("the two pointers: {:p} {:p}", a, b); + } + show(|| 1.0, |_| 2.0); } #[test] @@ -70,6 +76,25 @@ fn test_fmt_debug_of_mut_reference() { assert_eq!(format!("{:?}", &mut x), "0"); } +#[test] +fn test_fmt_pointer() { + use std::rc::Rc; + use std::sync::Arc; + let p: *const u8 = std::ptr::null(); + let rc = Rc::new(1usize); + let arc = Arc::new(1usize); + let b = Box::new("hi"); + + let _ = format!("{rc:p}{arc:p}{b:p}"); + + if cfg!(target_pointer_width = "32") { + assert_eq!(format!("{:#p}", p), "0x00000000"); + } else { + assert_eq!(format!("{:#p}", p), "0x0000000000000000"); + } + assert_eq!(format!("{:p}", p), "0x0"); +} + #[test] fn test_default_write_impls() { use core::fmt::Write; diff --git a/library/coretests/tests/intrinsics.rs b/library/coretests/tests/intrinsics.rs index 744a6a0d2dd8f..c6d841b8383a8 100644 --- a/library/coretests/tests/intrinsics.rs +++ b/library/coretests/tests/intrinsics.rs @@ -1,5 +1,8 @@ use core::any::TypeId; -use core::intrinsics::assume; +use core::intrinsics::{assume, vtable_for}; +use std::fmt::Debug; +use std::option::Option; +use std::ptr::DynMetadata; #[test] fn test_typeid_sized_types() { @@ -193,3 +196,17 @@ fn carrying_mul_add_fallback_i128() { (u128::MAX - 1, -(i128::MIN / 2)), ); } + +#[test] +fn test_vtable_for() { + #[derive(Debug)] + struct A {} + + struct B {} + + const A_VTABLE: Option> = vtable_for::(); + assert!(A_VTABLE.is_some()); + + const B_VTABLE: Option> = vtable_for::(); + assert!(B_VTABLE.is_none()); +} diff --git a/library/coretests/tests/iter/mod.rs b/library/coretests/tests/iter/mod.rs index 5b2769d04698d..f300f421f7c13 100644 --- a/library/coretests/tests/iter/mod.rs +++ b/library/coretests/tests/iter/mod.rs @@ -99,3 +99,18 @@ pub fn extend_for_unit() { } assert_eq!(x, 5); } + +#[test] +pub fn test_const_iter() { + const X: bool = { + let it = Some(42); + let mut run = false; + #[expect(for_loops_over_fallibles)] + for x in it { + assert!(x == 42); + run = true; + } + run + }; + assert_eq!(true, X); +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index e190536abcf9f..d085e4ad1a8fe 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -1,34 +1,39 @@ // tidy-alphabetical-start #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_select))] -#![feature(alloc_layout_extra)] #![feature(array_ptr_get)] #![feature(array_try_from_fn)] -#![feature(array_windows)] +#![feature(array_try_map)] #![feature(ascii_char)] #![feature(ascii_char_variants)] #![feature(async_iter_from_iter)] #![feature(async_iterator)] -#![feature(bigint_helper_methods)] #![feature(bool_to_result)] #![feature(bstr)] #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_internals)] #![feature(char_max_len)] +#![feature(clamp_magnitude)] #![feature(clone_to_uninit)] +#![feature(const_array)] +#![feature(const_bool)] #![feature(const_cell_traits)] +#![feature(const_clone)] #![feature(const_cmp)] #![feature(const_convert)] +#![feature(const_default)] #![feature(const_destruct)] #![feature(const_drop_in_place)] #![feature(const_eval_select)] -#![feature(const_mul_add)] +#![feature(const_index)] +#![feature(const_iter)] #![feature(const_ops)] #![feature(const_option_ops)] #![feature(const_ref_cell)] #![feature(const_result_trait_fn)] #![feature(const_select_unpredictable)] #![feature(const_trait_impl)] +#![feature(const_unsigned_bigint_helpers)] #![feature(control_flow_ok)] #![feature(core_float_math)] #![feature(core_intrinsics)] @@ -78,10 +83,8 @@ #![feature(iterator_try_collect)] #![feature(iterator_try_reduce)] #![feature(layout_for_ptr)] -#![feature(lazy_get)] #![feature(maybe_uninit_fill)] #![feature(maybe_uninit_uninit_array_transpose)] -#![feature(maybe_uninit_write_slice)] #![feature(min_specialization)] #![feature(never_type)] #![feature(new_range_api)] @@ -91,15 +94,16 @@ #![feature(one_sided_range)] #![feature(option_reduce)] #![feature(pattern)] -#![feature(peekable_next_if_map)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(ptr_metadata)] #![feature(result_option_map_or_default)] +#![feature(signed_bigint_helpers)] #![feature(slice_from_ptr_range)] #![feature(slice_index_methods)] #![feature(slice_internals)] #![feature(slice_partition_dedup)] +#![feature(slice_shift)] #![feature(slice_split_once)] #![feature(sliceindex_wrappers)] #![feature(split_array)] @@ -114,9 +118,12 @@ #![feature(try_blocks)] #![feature(try_find)] #![feature(try_trait_v2)] +#![feature(type_info)] #![feature(uint_bit_width)] +#![feature(uint_gather_scatter_bits)] #![feature(unsize)] #![feature(unwrap_infallible)] +#![feature(widening_mul)] // tidy-alphabetical-end #![allow(internal_features)] #![deny(fuzzy_provenance_casts)] diff --git a/library/coretests/tests/mem.rs b/library/coretests/tests/mem.rs index e896c61ef4881..193d5416b06a7 100644 --- a/library/coretests/tests/mem.rs +++ b/library/coretests/tests/mem.rs @@ -1,3 +1,5 @@ +mod type_info; + use core::mem::*; use core::{array, ptr}; use std::cell::Cell; @@ -815,7 +817,7 @@ fn drop_guard_into_inner() { let dropped = Cell::new(false); let value = DropGuard::new(42, |_| dropped.set(true)); let guard = DropGuard::new(value, |_| dropped.set(true)); - let inner = DropGuard::into_inner(guard); + let inner = DropGuard::dismiss(guard); assert_eq!(dropped.get(), false); assert_eq!(*inner, 42); } @@ -837,7 +839,7 @@ fn drop_guard_always_drops_value_if_closure_drop_unwinds() { // run the destructor of the value we passed, which we validate. let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { let guard = DropGuard::new(value_with_tracked_destruction, closure_that_panics_on_drop); - DropGuard::into_inner(guard); + DropGuard::dismiss(guard); })); assert!(value_was_dropped); } diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs new file mode 100644 index 0000000000000..87f2d5dd8289c --- /dev/null +++ b/library/coretests/tests/mem/type_info.rs @@ -0,0 +1,294 @@ +use std::any::{Any, TypeId}; +use std::mem::type_info::{Type, TypeKind}; + +#[test] +fn test_arrays() { + // Normal array. + match const { Type::of::<[u16; 4]>() }.kind { + TypeKind::Array(array) => { + assert_eq!(array.element_ty, TypeId::of::()); + assert_eq!(array.len, 4); + } + _ => unreachable!(), + } + + // Zero-length array. + match const { Type::of::<[bool; 0]>() }.kind { + TypeKind::Array(array) => { + assert_eq!(array.element_ty, TypeId::of::()); + assert_eq!(array.len, 0); + } + _ => unreachable!(), + } +} + +#[test] +fn test_slices() { + match const { Type::of::<[usize]>() }.kind { + TypeKind::Slice(slice) => assert_eq!(slice.element_ty, TypeId::of::()), + _ => unreachable!(), + } +} + +#[test] +fn test_tuples() { + fn assert_tuple_arity() { + match const { Type::of::() }.kind { + TypeKind::Tuple(tup) => { + assert_eq!(tup.fields.len(), N); + } + _ => unreachable!(), + } + } + + assert_tuple_arity::<(), 0>(); + assert_tuple_arity::<(u8,), 1>(); + assert_tuple_arity::<(u8, u8), 2>(); + + const { + match Type::of::<(i8, u8)>().kind { + TypeKind::Tuple(tup) => { + let [a, b] = tup.fields else { unreachable!() }; + + assert!(a.offset == 0); + assert!(b.offset == 1); + + match (a.ty.info().kind, b.ty.info().kind) { + (TypeKind::Int(a), TypeKind::Int(b)) => { + assert!(a.bits == 8 && a.signed); + assert!(b.bits == 8 && !b.signed); + } + _ => unreachable!(), + } + } + _ => unreachable!(), + } + } +} + +#[test] +fn test_primitives() { + use TypeKind::*; + + let Type { kind: Bool(_ty), size, .. } = (const { Type::of::() }) else { panic!() }; + assert_eq!(size, Some(1)); + + let Type { kind: Char(_ty), size, .. } = (const { Type::of::() }) else { panic!() }; + assert_eq!(size, Some(4)); + + let Type { kind: Int(ty), size, .. } = (const { Type::of::() }) else { panic!() }; + assert_eq!(size, Some(4)); + assert_eq!(ty.bits, 32); + assert!(ty.signed); + + let Type { kind: Int(ty), size, .. } = (const { Type::of::() }) else { panic!() }; + assert_eq!(size, Some(size_of::())); + assert_eq!(ty.bits as usize, size_of::() * 8); + assert!(ty.signed); + + let Type { kind: Int(ty), size, .. } = (const { Type::of::() }) else { panic!() }; + assert_eq!(size, Some(4)); + assert_eq!(ty.bits, 32); + assert!(!ty.signed); + + let Type { kind: Int(ty), size, .. } = (const { Type::of::() }) else { panic!() }; + assert_eq!(size, Some(size_of::())); + assert_eq!(ty.bits as usize, size_of::() * 8); + assert!(!ty.signed); + + let Type { kind: Float(ty), size, .. } = (const { Type::of::() }) else { panic!() }; + assert_eq!(size, Some(4)); + assert_eq!(ty.bits, 32); + + let Type { kind: Str(_ty), size, .. } = (const { Type::of::() }) else { panic!() }; + assert_eq!(size, None); +} + +#[test] +fn test_references() { + // Immutable reference. + match const { Type::of::<&u8>() }.kind { + TypeKind::Reference(reference) => { + assert_eq!(reference.pointee, TypeId::of::()); + assert!(!reference.mutable); + } + _ => unreachable!(), + } + + // Mutable references. + match const { Type::of::<&mut u64>() }.kind { + TypeKind::Reference(reference) => { + assert_eq!(reference.pointee, TypeId::of::()); + assert!(reference.mutable); + } + _ => unreachable!(), + } + + // Wide references. + match const { Type::of::<&dyn Any>() }.kind { + TypeKind::Reference(reference) => { + assert_eq!(reference.pointee, TypeId::of::()); + assert!(!reference.mutable); + } + _ => unreachable!(), + } +} + +#[test] +fn test_pointers() { + // Immutable pointer. + match const { Type::of::<*const u8>() }.kind { + TypeKind::Pointer(pointer) => { + assert_eq!(pointer.pointee, TypeId::of::()); + assert!(!pointer.mutable); + } + _ => unreachable!(), + } + + // Mutable pointer. + match const { Type::of::<*mut u64>() }.kind { + TypeKind::Pointer(pointer) => { + assert_eq!(pointer.pointee, TypeId::of::()); + assert!(pointer.mutable); + } + _ => unreachable!(), + } + + // Wide pointer. + match const { Type::of::<*const dyn Any>() }.kind { + TypeKind::Pointer(pointer) => { + assert_eq!(pointer.pointee, TypeId::of::()); + assert!(!pointer.mutable); + } + _ => unreachable!(), + } +} + +#[test] +fn test_dynamic_traits() { + use std::collections::HashSet; + use std::mem::type_info::DynTraitPredicate; + trait A {} + + trait B { + type Foo; + } + + trait FooTrait<'a, 'b, const CONST_NUM: i32> {} + + trait ProjectorTrait<'a, 'b> {} + + fn preds_of() -> &'static [DynTraitPredicate] { + match const { Type::of::() }.kind { + TypeKind::DynTrait(d) => d.predicates, + _ => unreachable!(), + } + } + + fn pred<'a>(preds: &'a [DynTraitPredicate], want: TypeId) -> &'a DynTraitPredicate { + preds + .iter() + .find(|p| p.trait_ty.ty == want) + .unwrap_or_else(|| panic!("missing predicate for {want:?}")) + } + + fn assert_typeid_set_eq(actual: &[TypeId], expected: &[TypeId]) { + let actual_set: HashSet = actual.iter().copied().collect(); + let expected_set: HashSet = expected.iter().copied().collect(); + assert_eq!(actual.len(), actual_set.len(), "duplicates present: {actual:?}"); + assert_eq!( + actual_set, expected_set, + "unexpected ids.\nactual: {actual:?}\nexpected: {expected:?}" + ); + } + + fn assert_predicates_exact(preds: &[DynTraitPredicate], expected_pred_ids: &[TypeId]) { + let actual_pred_ids: Vec = preds.iter().map(|p| p.trait_ty.ty).collect(); + assert_typeid_set_eq(&actual_pred_ids, expected_pred_ids); + } + + // dyn Send + { + let preds = preds_of::(); + assert_predicates_exact(preds, &[TypeId::of::()]); + + let p = pred(preds, TypeId::of::()); + assert!(p.trait_ty.is_auto); + } + + // dyn A + { + let preds = preds_of::>(); + assert_predicates_exact(preds, &[TypeId::of::>()]); + + let p = pred(preds, TypeId::of::>()); + assert!(!p.trait_ty.is_auto); + } + + // dyn B<5, Foo = i32> + { + let preds = preds_of::>(); + assert_predicates_exact(preds, &[TypeId::of::>()]); + + let e = pred(preds, TypeId::of::>()); + assert!(!e.trait_ty.is_auto); + } + + // dyn for<'a> FooTrait<'a, 'a, 7> + { + let preds = preds_of:: FooTrait<'a, 'a, 7>>(); + assert_predicates_exact(preds, &[TypeId::of:: FooTrait<'a, 'a, 7>>()]); + + let foo = pred(preds, TypeId::of:: FooTrait<'a, 'a, 7>>()); + assert!(!foo.trait_ty.is_auto); + } + + // dyn FooTrait<'static, 'static, 7> + { + let preds = preds_of::>(); + assert_predicates_exact(preds, &[TypeId::of::>()]); + + let foo = pred(preds, TypeId::of::>()); + assert!(!foo.trait_ty.is_auto); + } + + // dyn for<'a, 'b> FooTrait<'a, 'b, 7> + { + let preds = preds_of:: FooTrait<'a, 'b, 7>>(); + assert_predicates_exact(preds, &[TypeId::of:: FooTrait<'a, 'b, 7>>()]); + + let foo = pred(preds, TypeId::of:: FooTrait<'a, 'b, 7>>()); + assert!(!foo.trait_ty.is_auto); + } + + // dyn for<'a, 'b> ProjectorTrait<'a, 'b> + { + let preds = preds_of:: ProjectorTrait<'a, 'b>>(); + assert_predicates_exact(preds, &[TypeId::of:: ProjectorTrait<'a, 'b>>()]); + + let proj = pred(preds, TypeId::of:: ProjectorTrait<'a, 'b>>()); + assert!(!proj.trait_ty.is_auto); + } + + // dyn for<'a> FooTrait<'a, 'a, 7> + Send + Sync + { + let preds = preds_of:: FooTrait<'a, 'a, 7> + Send + Sync>(); + assert_predicates_exact( + preds, + &[ + TypeId::of:: FooTrait<'a, 'a, 7>>(), + TypeId::of::(), + TypeId::of::(), + ], + ); + + let foo = pred(preds, TypeId::of:: FooTrait<'a, 'a, 7>>()); + assert!(!foo.trait_ty.is_auto); + + let send = pred(preds, TypeId::of::()); + assert!(send.trait_ty.is_auto); + + let sync = pred(preds, TypeId::of::()); + assert!(sync.trait_ty.is_auto); + } +} diff --git a/library/coretests/tests/num/clamp_magnitude.rs b/library/coretests/tests/num/clamp_magnitude.rs new file mode 100644 index 0000000000000..0f96e55f6914e --- /dev/null +++ b/library/coretests/tests/num/clamp_magnitude.rs @@ -0,0 +1,139 @@ +macro_rules! check_int_clamp { + ($t:ty, $ut:ty) => { + let min = <$t>::MIN; + let max = <$t>::MAX; + let max_u = <$ut>::MAX; + + // Basic clamping + assert_eq!((100 as $t).clamp_magnitude(50), 50); + assert_eq!((-100 as $t).clamp_magnitude(50), -50); + assert_eq!((30 as $t).clamp_magnitude(50), 30); + assert_eq!((-30 as $t).clamp_magnitude(50), -30); + + // Exact boundary + assert_eq!((50 as $t).clamp_magnitude(50), 50); + assert_eq!((-50 as $t).clamp_magnitude(50), -50); + + // Zero cases + assert_eq!((0 as $t).clamp_magnitude(100), 0); + assert_eq!((0 as $t).clamp_magnitude(0), 0); + assert_eq!((100 as $t).clamp_magnitude(0), 0); + assert_eq!((-100 as $t).clamp_magnitude(0), 0); + + // MIN/MAX values + // Symmetric range [-MAX, MAX] + assert_eq!(max.clamp_magnitude(max as $ut), max); + assert_eq!(min.clamp_magnitude(max as $ut), -max); + + // Full range (limit covers MIN) + let min_abs = min.unsigned_abs(); + assert_eq!(min.clamp_magnitude(min_abs), min); + + // Limit larger than type max (uN > iN::MAX) + assert_eq!(max.clamp_magnitude(max_u), max); + assert_eq!(min.clamp_magnitude(max_u), min); + }; +} + +#[test] +fn test_clamp_magnitude_i8() { + check_int_clamp!(i8, u8); +} + +#[test] +fn test_clamp_magnitude_i16() { + check_int_clamp!(i16, u16); +} + +#[test] +fn test_clamp_magnitude_i32() { + check_int_clamp!(i32, u32); +} + +#[test] +fn test_clamp_magnitude_i64() { + check_int_clamp!(i64, u64); +} + +#[test] +fn test_clamp_magnitude_i128() { + check_int_clamp!(i128, u128); +} + +#[test] +fn test_clamp_magnitude_isize() { + check_int_clamp!(isize, usize); +} + +macro_rules! check_float_clamp { + ($t:ty) => { + // Basic clamping + assert_eq!((5.0 as $t).clamp_magnitude(3.0), 3.0); + assert_eq!((-5.0 as $t).clamp_magnitude(3.0), -3.0); + assert_eq!((2.0 as $t).clamp_magnitude(3.0), 2.0); + assert_eq!((-2.0 as $t).clamp_magnitude(3.0), -2.0); + + // Exact boundary + assert_eq!((3.0 as $t).clamp_magnitude(3.0), 3.0); + assert_eq!((-3.0 as $t).clamp_magnitude(3.0), -3.0); + + // Zero cases + assert_eq!((0.0 as $t).clamp_magnitude(1.0), 0.0); + assert_eq!((-0.0 as $t).clamp_magnitude(1.0), 0.0); + assert_eq!((5.0 as $t).clamp_magnitude(0.0), 0.0); + assert_eq!((-5.0 as $t).clamp_magnitude(0.0), 0.0); + + // Special values - Infinity + let inf = <$t>::INFINITY; + let neg_inf = <$t>::NEG_INFINITY; + assert_eq!(inf.clamp_magnitude(100.0), 100.0); + assert_eq!(neg_inf.clamp_magnitude(100.0), -100.0); + assert_eq!(inf.clamp_magnitude(inf), inf); + + // Value with infinite limit + assert_eq!((1.0 as $t).clamp_magnitude(inf), 1.0); + assert_eq!((-1.0 as $t).clamp_magnitude(inf), -1.0); + + // MIN and MAX + let max = <$t>::MAX; + let min = <$t>::MIN; + // Large limit + let huge = 1e30; + assert_eq!(max.clamp_magnitude(huge), huge); + assert_eq!(min.clamp_magnitude(huge), -huge); + }; +} + +#[test] +fn test_clamp_magnitude_f32() { + check_float_clamp!(f32); +} + +#[test] +fn test_clamp_magnitude_f64() { + check_float_clamp!(f64); +} + +#[test] +#[should_panic(expected = "limit must be non-negative")] +fn test_clamp_magnitude_f32_panic_negative_limit() { + let _ = 1.0f32.clamp_magnitude(-1.0); +} + +#[test] +#[should_panic(expected = "limit must be non-negative")] +fn test_clamp_magnitude_f64_panic_negative_limit() { + let _ = 1.0f64.clamp_magnitude(-1.0); +} + +#[test] +#[should_panic] +fn test_clamp_magnitude_f32_panic_nan_limit() { + let _ = 1.0f32.clamp_magnitude(f32::NAN); +} + +#[test] +#[should_panic] +fn test_clamp_magnitude_f64_panic_nan_limit() { + let _ = 1.0f64.clamp_magnitude(f64::NAN); +} diff --git a/library/coretests/tests/num/dec2flt/decimal.rs b/library/coretests/tests/num/dec2flt/decimal.rs index f759e1dbde6cb..f5ecc604a99a1 100644 --- a/library/coretests/tests/num/dec2flt/decimal.rs +++ b/library/coretests/tests/num/dec2flt/decimal.rs @@ -7,7 +7,6 @@ const FPATHS_F32: &[FPath] = const FPATHS_F64: &[FPath] = &[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))]; -// FIXME(f16_f128): enable on all targets once possible. #[test] #[cfg(target_has_reliable_f16)] fn check_fast_path_f16() { diff --git a/library/coretests/tests/num/dec2flt/float.rs b/library/coretests/tests/num/dec2flt/float.rs index 8bf4094ced72f..734cb7e4f7dbd 100644 --- a/library/coretests/tests/num/dec2flt/float.rs +++ b/library/coretests/tests/num/dec2flt/float.rs @@ -2,7 +2,6 @@ use core::num::dec2flt::float::RawFloat; use crate::num::{ldexp_f32, ldexp_f64}; -// FIXME(f16_f128): enable on all targets once possible. #[test] #[cfg(target_has_reliable_f16)] fn test_f16_integer_decode() { @@ -54,7 +53,6 @@ fn test_f64_integer_decode() { /* Sanity checks of computed magic numbers */ -// FIXME(f16_f128): enable on all targets once possible. #[test] #[cfg(target_has_reliable_f16)] fn test_f16_consts() { diff --git a/library/coretests/tests/num/dec2flt/lemire.rs b/library/coretests/tests/num/dec2flt/lemire.rs index 6d49d85170e2d..ba359a0495fef 100644 --- a/library/coretests/tests/num/dec2flt/lemire.rs +++ b/library/coretests/tests/num/dec2flt/lemire.rs @@ -17,7 +17,6 @@ fn compute_float64(q: i64, w: u64) -> (i32, u64) { (fp.p_biased, fp.m) } -// FIXME(f16_f128): enable on all targets once possible. #[test] #[cfg(target_has_reliable_f16)] fn compute_float_f16_rounding() { diff --git a/library/coretests/tests/num/dec2flt/mod.rs b/library/coretests/tests/num/dec2flt/mod.rs index b8ca220847cfa..37136e62c6f43 100644 --- a/library/coretests/tests/num/dec2flt/mod.rs +++ b/library/coretests/tests/num/dec2flt/mod.rs @@ -92,8 +92,6 @@ fn fast_path_correct() { test_literal!(1.448997445238699); } -// FIXME(f16_f128): remove gates once tests work on all targets - #[test] fn lonely_dot() { #[cfg(target_has_reliable_f16)] diff --git a/library/coretests/tests/num/dec2flt/parse.rs b/library/coretests/tests/num/dec2flt/parse.rs index dccb6b5528d4c..65f1289d531b3 100644 --- a/library/coretests/tests/num/dec2flt/parse.rs +++ b/library/coretests/tests/num/dec2flt/parse.rs @@ -204,8 +204,8 @@ fn many_digits() { "1.175494140627517859246175898662808184331245864732796240031385942718174675986064769972472277004271745681762695312500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-38" ); assert_float_result_bits_eq!( - 0x7ffffe, - f32, - "1.175494140627517859246175898662808184331245864732796240031385942718174675986064769972472277004271745681762695312500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-38" + 0x7ffffffffffffe, + f64, + "2.8480945388892171379514712013899006561925415836684281158317117472799232118121416898288331376688117187382156519616555919469923055562697333532151065976805407006156569379201589813489881051279092585294136284133893120582268661680741374638184352784785121471797815387841323043061183721896440504100014432145713413240639315126126485370149254502658324386101932795656342787961697168715161403422599767855490566539165801964388919848888980308652852766053138934233069066651644954964960514065582826296567812754916294792554028205611526494813491373571799099361175786448799007387647056059512705071170383000860694587462575533337769237800154235626508997957207085308694919708914563870082480025309479610576861709891209343110514894825624524442569257542956506767950486391782760620117187500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e-306" ); } diff --git a/library/coretests/tests/num/flt2dec/mod.rs b/library/coretests/tests/num/flt2dec/mod.rs index 4e73bd1f12ee9..be1bc6ac460ba 100644 --- a/library/coretests/tests/num/flt2dec/mod.rs +++ b/library/coretests/tests/num/flt2dec/mod.rs @@ -287,7 +287,7 @@ where check_exact!(f(f16::MIN_POSITIVE) => b"6103515625 ", -4); check_exact!(f(minf16) => b"59604644775390625", -7); - // FIXME(f16_f128): these should gain the check_exact_one tests like `f32` and `f64` have, + // FIXME(f16): these should gain the check_exact_one tests like `f32` and `f64` have, // but these values are not easy to generate. The algorithm from the Paxon paper [1] needs // to be adapted to binary16. } diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs index b89a371efcc25..7c4fb22599c03 100644 --- a/library/coretests/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs @@ -127,6 +127,52 @@ macro_rules! uint_module { assert_eq_const_safe!($T: _1.swap_bytes(), _1); } + fn test_extract_bits() { + assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0000_0011), 0b_0001); + assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0000_0110), 0b_0010); + assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0000_1100), 0b_0001); + assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0001_1000), 0b_0000); + assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0011_0000), 0b_0010); + assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b0110_0000), 0b_0001); + assert_eq_const_safe!($T: $T::extract_bits(0b1010_0101, 0b1100_0000), 0b_0010); + + assert_eq_const_safe!($T: A.extract_bits(_0), 0); + assert_eq_const_safe!($T: B.extract_bits(_0), 0); + assert_eq_const_safe!($T: C.extract_bits(_0), 0); + assert_eq_const_safe!($T: _0.extract_bits(A), 0); + assert_eq_const_safe!($T: _0.extract_bits(B), 0); + assert_eq_const_safe!($T: _0.extract_bits(C), 0); + + assert_eq_const_safe!($T: A.extract_bits(_1), A); + assert_eq_const_safe!($T: B.extract_bits(_1), B); + assert_eq_const_safe!($T: C.extract_bits(_1), C); + assert_eq_const_safe!($T: _1.extract_bits(0b0010_0001), 0b0000_0011); + assert_eq_const_safe!($T: _1.extract_bits(0b0010_1100), 0b0000_0111); + assert_eq_const_safe!($T: _1.extract_bits(0b0111_1001), 0b0001_1111); + } + + fn test_deposit_bits() { + assert_eq_const_safe!($T: $T::deposit_bits(0b1111, 0b1001_0110), 0b1001_0110); + assert_eq_const_safe!($T: $T::deposit_bits(0b0001, 0b1001_0110), 0b0000_0010); + assert_eq_const_safe!($T: $T::deposit_bits(0b0010, 0b1001_0110), 0b0000_0100); + assert_eq_const_safe!($T: $T::deposit_bits(0b0100, 0b1001_0110), 0b0001_0000); + assert_eq_const_safe!($T: $T::deposit_bits(0b1000, 0b1001_0110), 0b1000_0000); + + assert_eq_const_safe!($T: A.deposit_bits(_0), 0); + assert_eq_const_safe!($T: B.deposit_bits(_0), 0); + assert_eq_const_safe!($T: C.deposit_bits(_0), 0); + assert_eq_const_safe!($T: _0.deposit_bits(A), 0); + assert_eq_const_safe!($T: _0.deposit_bits(B), 0); + assert_eq_const_safe!($T: _0.deposit_bits(C), 0); + + assert_eq_const_safe!($T: A.deposit_bits(_1), A); + assert_eq_const_safe!($T: B.deposit_bits(_1), B); + assert_eq_const_safe!($T: C.deposit_bits(_1), C); + assert_eq_const_safe!($T: _1.deposit_bits(A), A); + assert_eq_const_safe!($T: _1.deposit_bits(B), B); + assert_eq_const_safe!($T: _1.deposit_bits(C), C); + } + fn test_reverse_bits() { assert_eq_const_safe!($T: A.reverse_bits().reverse_bits(), A); assert_eq_const_safe!($T: B.reverse_bits().reverse_bits(), B); @@ -341,6 +387,68 @@ macro_rules! uint_module { } } + #[cfg(not(miri))] // Miri is too slow + #[test] + fn test_lots_of_extract_deposit() { + // Generate a handful of bit patterns to use as inputs + let xs = { + let mut xs = vec![]; + let mut x: $T = !0; + let mut w = $T::BITS; + while w > 0 { + w >>= 1; + xs.push(x); + xs.push(!x); + x ^= x << w; + } + xs + }; + if $T::BITS == 8 { + assert_eq!(&xs, &[0xff, 0x00, 0x0f, 0xf0, 0x33, 0xcc, 0x55, 0xaa]); + } + + // `256 * (BITS / 5)` masks + let sparse_masks = (i8::MIN..=i8::MAX) + .map(|i| (i as i128 as $T).rotate_right(4)) + .flat_map(|x| (0..$T::BITS).step_by(5).map(move |r| x.rotate_right(r))); + + for sparse in sparse_masks { + // Collect the set bits to sequential low bits + let dense = sparse.extract_bits(sparse); + let count = sparse.count_ones(); + assert_eq!(count, dense.count_ones()); + assert_eq!(count, dense.trailing_ones()); + + // Check that each bit is individually mapped correctly + let mut t = sparse; + let mut bit = 1 as $T; + for _ in 0..count { + let lowest_one = t.isolate_lowest_one(); + assert_eq!(lowest_one, bit.deposit_bits(sparse)); + assert_eq!(bit, lowest_one.extract_bits(sparse)); + t ^= lowest_one; + bit <<= 1; + } + // Other bits are ignored + assert_eq!(0, bit.wrapping_neg().deposit_bits(sparse)); + assert_eq!(0, (!sparse).extract_bits(sparse)); + + for &x in &xs { + // Gather bits from `x & sparse` to `dense` + let dx = x.extract_bits(sparse); + assert_eq!(dx & !dense, 0); + + // Scatter bits from `x & dense` to `sparse` + let sx = x.deposit_bits(sparse); + assert_eq!(sx & !sparse, 0); + + // The other recovers the input (within the mask) + assert_eq!(dx.deposit_bits(sparse), x & sparse); + assert_eq!(sx.extract_bits(sparse), x & dense); + } + } + } + test_runtime_and_compiletime! { fn test_div_floor() { assert_eq_const_safe!($T: (8 as $T).div_floor(3), 2); diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index 110c4e5f3b406..2bb62f36bb0e6 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -432,6 +432,13 @@ fn test_chunks_exact_mut_zip_aliasing() { assert_eq!(first, (&mut [0, 1][..], &[6, 7][..])); } +#[test] +fn test_chunks_zst() { + const SIZE: usize = 16; + let mut it = [(); usize::MAX].chunks(SIZE); + assert_eq!(it.nth_back(0), Some(&[(); SIZE - 1][..])); +} + #[test] fn test_rchunks_mut_zip_aliasing() { let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; @@ -2500,3 +2507,41 @@ fn test_slice_from_raw_parts_in_const() { assert_eq!(EMPTY_SLICE.as_ptr().addr(), 123456); assert_eq!(EMPTY_SLICE.len(), 0); } + +#[test] +fn test_shift_left() { + #[track_caller] + fn case( + mut a: [i32; M], + i: [i32; N], + j: [i32; N], + b: [i32; M], + ) { + assert_eq!((a.shift_left(i), a), (j, b)); + } + case([], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], []); + case([1], [2, 3, 4, 5], [1, 2, 3, 4], [5]); + case([1, 2], [3, 4, 5], [1, 2, 3], [4, 5]); + case([1, 2, 3], [4, 5], [1, 2], [3, 4, 5]); + case([1, 2, 3, 4], [5], [1], [2, 3, 4, 5]); + case([1, 2, 3, 4, 5], [], [], [1, 2, 3, 4, 5]); +} + +#[test] +fn test_shift_right() { + #[track_caller] + fn case( + i: [i32; N], + mut a: [i32; M], + b: [i32; M], + j: [i32; N], + ) { + assert_eq!((a.shift_right(i), a), (j, b)); + } + case([], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], []); + case([1], [2, 3, 4, 5], [1, 2, 3, 4], [5]); + case([1, 2], [3, 4, 5], [1, 2, 3], [4, 5]); + case([1, 2, 3], [4, 5], [1, 2], [3, 4, 5]); + case([1, 2, 3, 4], [5], [1], [2, 3, 4, 5]); + case([1, 2, 3, 4, 5], [], [], [1, 2, 3, 4, 5]); +} diff --git a/library/coretests/tests/time.rs b/library/coretests/tests/time.rs index fb3c50f9bde98..ff80ff680943f 100644 --- a/library/coretests/tests/time.rs +++ b/library/coretests/tests/time.rs @@ -439,7 +439,6 @@ fn debug_formatting_precision_two() { assert_eq!(format!("{:.2?}", Duration::new(4, 001_000_000)), "4.00s"); assert_eq!(format!("{:.2?}", Duration::new(2, 100_000_000)), "2.10s"); assert_eq!(format!("{:.2?}", Duration::new(2, 104_990_000)), "2.10s"); - assert_eq!(format!("{:.2?}", Duration::new(2, 105_000_000)), "2.11s"); assert_eq!(format!("{:.2?}", Duration::new(8, 999_999_999)), "9.00s"); } @@ -480,6 +479,15 @@ fn debug_formatting_precision_high() { assert_eq!(format!("{:.20?}", Duration::new(4, 001_000_000)), "4.00100000000000000000s"); } +#[test] +fn debug_formatting_round_to_even() { + assert_eq!(format!("{:.0?}", Duration::new(1, 500_000_000)), "2s"); + assert_eq!(format!("{:.0?}", Duration::new(2, 500_000_000)), "2s"); + assert_eq!(format!("{:.0?}", Duration::new(0, 1_500_000)), "2ms"); + assert_eq!(format!("{:.0?}", Duration::new(0, 2_500_000)), "2ms"); + assert_eq!(format!("{:.2?}", Duration::new(2, 105_000_000)), "2.10s"); +} + #[test] fn duration_const() { // test that the methods of `Duration` are usable in a const context diff --git a/library/portable-simd/.github/workflows/ci.yml b/library/portable-simd/.github/workflows/ci.yml index 3984d8f0d8d99..de7efa3552836 100644 --- a/library/portable-simd/.github/workflows/ci.yml +++ b/library/portable-simd/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: strategy: fail-fast: false matrix: - target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, i586-pc-windows-msvc, x86_64-unknown-linux-gnu] + target: [x86_64-pc-windows-msvc, i686-pc-windows-msvc, x86_64-unknown-linux-gnu] # `default` means we use the default target config for the target, # `native` means we run with `-Ctarget-cpu=native`, and anything else is # an arg to `-Ctarget-feature` @@ -68,18 +68,12 @@ jobs: exclude: # -Ctarget-cpu=native sounds like bad-news if target != host - { target: i686-pc-windows-msvc, target_feature: native } - - { target: i586-pc-windows-msvc, target_feature: native } include: # Populate the `matrix.os` field - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest } - { target: x86_64-pc-windows-msvc, os: windows-latest } - { target: i686-pc-windows-msvc, os: windows-latest } - - { target: i586-pc-windows-msvc, os: windows-latest } - - # These are globally available on all the other targets. - - { target: i586-pc-windows-msvc, target_feature: +sse, os: windows-latest } - - { target: i586-pc-windows-msvc, target_feature: +sse2, os: windows-latest } # Annoyingly, the x86_64-unknown-linux-gnu runner *almost* always has # avx512vl, but occasionally doesn't. Maybe one day we can enable it. @@ -129,7 +123,7 @@ jobs: run: cargo doc --verbose --target=${{ matrix.target }} env: RUSTDOCFLAGS: -Dwarnings - + macos-tests: name: ${{ matrix.target }} runs-on: macos-latest @@ -246,9 +240,18 @@ jobs: miri: runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3, 4] env: PROPTEST_CASES: 16 steps: - uses: actions/checkout@v4 - - name: Test (Miri) - run: cargo miri test + + - name: Install cargo-nextest + uses: taiki-e/install-action@nextest + + - name: Test (Miri) (partition ${{ matrix.shard }}/4) + run: | + cargo miri nextest run --partition count:${{ matrix.shard }}/4 diff --git a/library/portable-simd/Cargo.lock b/library/portable-simd/Cargo.lock index 1584c704fb221..5a5f0d8907ae3 100644 --- a/library/portable-simd/Cargo.lock +++ b/library/portable-simd/Cargo.lock @@ -1,12 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "autocfg" -version = "1.1.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" @@ -16,31 +16,30 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "cfg-if" -version = "1.0.0" +name = "cc" +version = "1.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" +dependencies = [ + "shlex", +] [[package]] -name = "console_error_panic_hook" -version = "0.1.7" +name = "cfg-if" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "core_simd" @@ -53,47 +52,70 @@ dependencies = [ "wasm-bindgen-test", ] +[[package]] +name = "float-cmp" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" +dependencies = [ + "num-traits", +] + [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "log" -version = "0.4.20" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "minicov" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" +dependencies = [ + "cc", + "walkdir", +] [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -114,9 +136,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -167,10 +189,25 @@ dependencies = [ ] [[package]] -name = "scoped-tls" -version = "1.0.1" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "std_float" @@ -184,9 +221,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.29" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -197,34 +234,46 @@ dependencies = [ name = "test_helpers" version = "0.1.0" dependencies = [ + "float-cmp", "proptest", ] [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "walkdir" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -233,21 +282,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -255,9 +305,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -268,19 +318,21 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-bindgen-test" -version = "0.3.37" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671" +checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" dependencies = [ - "console_error_panic_hook", "js-sys", - "scoped-tls", + "minicov", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -288,20 +340,123 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.37" +version = "0.3.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" +checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" dependencies = [ "proc-macro2", "quote", + "syn", ] [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", ] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/library/portable-simd/beginners-guide.md b/library/portable-simd/beginners-guide.md index dc08d847ced50..4250a18315a6e 100644 --- a/library/portable-simd/beginners-guide.md +++ b/library/portable-simd/beginners-guide.md @@ -25,7 +25,7 @@ SIMD has a few special vocabulary terms you should know: * **Scalar:** "Scalar" in mathematical contexts refers to values that can be represented as a single element, mostly numbers like 6, 3.14, or -2. It can also be used to describe "scalar operations" that use strictly scalar values, like addition. This term is mostly used to differentiate between vectorized operations that use SIMD instructions and scalar operations that don't. -* **Lane:** A single element position within a vector is called a lane. If you have `N` lanes available then they're numbered from `0` to `N-1` when referring to them, again like an array. The biggest difference between an array element and a vector lane is that in general is *relatively costly* to access an individual lane value. On most architectures, the vector has to be pushed out of the SIMD register onto the stack, then an individual lane is accessed while it's on the stack (and possibly the stack value is read back into a register). For this reason, when working with SIMD you should avoid reading or writing the value of an individual lane during hot loops. +* **Lane:** A single element position within a vector is called a lane. If you have `N` lanes available then they're numbered from `0` to `N-1` when referring to them, again like an array. The biggest difference between an array element and a vector lane is that in general it is *relatively costly* to access an individual lane value. On most architectures, the vector has to be pushed out of the SIMD register onto the stack, then an individual lane is accessed while it's on the stack (and possibly the stack value is read back into a register). For this reason, when working with SIMD you should avoid reading or writing the value of an individual lane during hot loops. * **Bit Widths:** When talking about SIMD, the bit widths used are the bit size of the vectors involved, *not* the individual elements. So "128-bit SIMD" has 128-bit vectors, and that might be `f32x4`, `i32x4`, `i16x8`, or other variations. While 128-bit SIMD is the most common, there's also 64-bit, 256-bit, and even 512-bit on the newest CPUs. diff --git a/library/portable-simd/crates/core_simd/examples/dot_product.rs b/library/portable-simd/crates/core_simd/examples/dot_product.rs index 75d152ae7f0e3..4ef32bfa60b5e 100644 --- a/library/portable-simd/crates/core_simd/examples/dot_product.rs +++ b/library/portable-simd/crates/core_simd/examples/dot_product.rs @@ -1,8 +1,6 @@ //! Code taken from the `packed_simd` crate. //! Run this code with `cargo test --example dot_product`. -#![feature(array_chunks)] -#![feature(slice_as_chunks)] // Add these imports to use the stdsimd library #![feature(portable_simd)] use core_simd::simd::prelude::*; @@ -33,7 +31,7 @@ pub fn dot_prod_scalar_1(a: &[f32], b: &[f32]) -> f32 { } // We now move on to the SIMD implementations: notice the following constructs: -// `array_chunks::<4>`: mapping this over the vector will let use construct SIMD vectors +// `as_chunks::<4>`: mapping this over the vector will let us construct SIMD vectors // `f32x4::from_array`: construct the SIMD vector from a slice // `(a * b).reduce_sum()`: Multiply both f32x4 vectors together, and then reduce them. // This approach essentially uses SIMD to produce a vector of length N/4 of all the products, @@ -42,9 +40,11 @@ pub fn dot_prod_scalar_1(a: &[f32], b: &[f32]) -> f32 { pub fn dot_prod_simd_0(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len()); // TODO handle remainder when a.len() % 4 != 0 - a.array_chunks::<4>() + a.as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .map(|(a, b)| (a * b).reduce_sum()) .sum() } @@ -60,9 +60,11 @@ pub fn dot_prod_simd_0(a: &[f32], b: &[f32]) -> f32 { pub fn dot_prod_simd_1(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len()); // TODO handle remainder when a.len() % 4 != 0 - a.array_chunks::<4>() + a.as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .fold(f32x4::splat(0.0), |acc, zipped| acc + zipped.0 * zipped.1) .reduce_sum() } @@ -74,9 +76,11 @@ pub fn dot_prod_simd_2(a: &[f32], b: &[f32]) -> f32 { assert_eq!(a.len(), b.len()); // TODO handle remainder when a.len() % 4 != 0 let mut res = f32x4::splat(0.0); - a.array_chunks::<4>() + a.as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .for_each(|(a, b)| { res = a.mul_add(b, res); }); @@ -113,9 +117,11 @@ pub fn dot_prod_simd_3(a: &[f32], b: &[f32]) -> f32 { // next example. pub fn dot_prod_simd_4(a: &[f32], b: &[f32]) -> f32 { let mut sum = a - .array_chunks::<4>() + .as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .map(|(a, b)| a * b) .fold(f32x4::splat(0.0), std::ops::Add::add) .reduce_sum(); @@ -131,9 +137,11 @@ pub fn dot_prod_simd_4(a: &[f32], b: &[f32]) -> f32 { // This version allocates a single `XMM` register for accumulation, and the folds don't allocate on top of that. // Notice the use of `mul_add`, which can do a multiply and an add operation ber iteration. pub fn dot_prod_simd_5(a: &[f32], b: &[f32]) -> f32 { - a.array_chunks::<4>() + a.as_chunks::<4>() + .0 + .iter() .map(|&a| f32x4::from_array(a)) - .zip(b.array_chunks::<4>().map(|&b| f32x4::from_array(b))) + .zip(b.as_chunks::<4>().0.iter().map(|&b| f32x4::from_array(b))) .fold(f32x4::splat(0.), |acc, (a, b)| a.mul_add(b, acc)) .reduce_sum() } diff --git a/library/portable-simd/crates/core_simd/examples/matrix_inversion.rs b/library/portable-simd/crates/core_simd/examples/matrix_inversion.rs index bad86414401d7..ad2eea9153e08 100644 --- a/library/portable-simd/crates/core_simd/examples/matrix_inversion.rs +++ b/library/portable-simd/crates/core_simd/examples/matrix_inversion.rs @@ -1,7 +1,7 @@ //! 4x4 matrix inverse // Code ported from the `packed_simd` crate // Run this code with `cargo test --example matrix_inversion` -#![feature(array_chunks, portable_simd)] +#![feature(portable_simd)] use core_simd::simd::prelude::*; // Gotta define our own 4x4 matrix since Rust doesn't ship multidim arrays yet :^) diff --git a/library/portable-simd/crates/core_simd/src/fmt.rs b/library/portable-simd/crates/core_simd/src/fmt.rs index 3a540f5a04908..90c520e75bb39 100644 --- a/library/portable-simd/crates/core_simd/src/fmt.rs +++ b/library/portable-simd/crates/core_simd/src/fmt.rs @@ -1,9 +1,8 @@ -use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{Simd, SimdElement}; use core::fmt; impl fmt::Debug for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + fmt::Debug, { /// A `Simd` has a debug format like the one for `[T]`: diff --git a/library/portable-simd/crates/core_simd/src/iter.rs b/library/portable-simd/crates/core_simd/src/iter.rs index b3732fd74d5f6..fdc458efeda48 100644 --- a/library/portable-simd/crates/core_simd/src/iter.rs +++ b/library/portable-simd/crates/core_simd/src/iter.rs @@ -1,4 +1,4 @@ -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; +use crate::simd::Simd; use core::{ iter::{Product, Sum}, ops::{Add, Mul}, @@ -7,8 +7,6 @@ use core::{ macro_rules! impl_traits { { $type:ty } => { impl Sum for Simd<$type, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn sum>(iter: I) -> Self { @@ -17,8 +15,6 @@ macro_rules! impl_traits { } impl Product for Simd<$type, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn product>(iter: I) -> Self { @@ -27,8 +23,6 @@ macro_rules! impl_traits { } impl<'a, const N: usize> Sum<&'a Self> for Simd<$type, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn sum>(iter: I) -> Self { @@ -37,8 +31,6 @@ macro_rules! impl_traits { } impl<'a, const N: usize> Product<&'a Self> for Simd<$type, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn product>(iter: I) -> Self { diff --git a/library/portable-simd/crates/core_simd/src/lane_count.rs b/library/portable-simd/crates/core_simd/src/lane_count.rs deleted file mode 100644 index bbdfd5f5f3ed3..0000000000000 --- a/library/portable-simd/crates/core_simd/src/lane_count.rs +++ /dev/null @@ -1,40 +0,0 @@ -mod sealed { - pub trait Sealed {} -} -use sealed::Sealed; - -/// Specifies the number of lanes in a SIMD vector as a type. -pub struct LaneCount; - -impl LaneCount { - /// The number of bytes in a bitmask with this many lanes. - pub const BITMASK_LEN: usize = N.div_ceil(8); -} - -/// Statically guarantees that a lane count is marked as supported. -/// -/// This trait is *sealed*: the list of implementors below is total. -/// Users do not have the ability to mark additional `LaneCount` values as supported. -/// Only SIMD vectors with supported lane counts are constructable. -pub trait SupportedLaneCount: Sealed { - #[doc(hidden)] - type BitMask: Copy + Default + AsRef<[u8]> + AsMut<[u8]>; -} - -impl Sealed for LaneCount {} - -macro_rules! supported_lane_count { - ($($lanes:literal),+) => { - $( - impl SupportedLaneCount for LaneCount<$lanes> { - type BitMask = [u8; ($lanes + 7) / 8]; - } - )+ - }; -} - -supported_lane_count!( - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 -); diff --git a/library/portable-simd/crates/core_simd/src/lib.rs b/library/portable-simd/crates/core_simd/src/lib.rs index 717b882b64ba1..fe26d99b9194c 100644 --- a/library/portable-simd/crates/core_simd/src/lib.rs +++ b/library/portable-simd/crates/core_simd/src/lib.rs @@ -9,7 +9,8 @@ simd_ffi, staged_api, prelude_import, - ptr_metadata + ptr_metadata, + rustc_attrs )] #![cfg_attr( all( @@ -30,10 +31,6 @@ any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc) )] -#![cfg_attr( - all(target_arch = "x86_64", target_feature = "avx512f"), - feature(stdarch_x86_avx512) -)] #![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really #![deny( unsafe_op_in_unsafe_fn, @@ -41,7 +38,7 @@ clippy::undocumented_unsafe_blocks )] #![doc(test(attr(deny(warnings))))] -#![allow(internal_features)] +#![allow(internal_features, clippy::repr_packed_without_abi)] #![unstable(feature = "portable_simd", issue = "86656")] //! Portable SIMD module. diff --git a/library/portable-simd/crates/core_simd/src/masks.rs b/library/portable-simd/crates/core_simd/src/masks.rs index 19d45f4d3b31a..3e2209556b66b 100644 --- a/library/portable-simd/crates/core_simd/src/masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks.rs @@ -2,20 +2,33 @@ //! Types representing #![allow(non_camel_case_types)] -#[cfg_attr( - not(all(target_arch = "x86_64", target_feature = "avx512f")), - path = "masks/full_masks.rs" -)] -#[cfg_attr( - all(target_arch = "x86_64", target_feature = "avx512f"), - path = "masks/bitmask.rs" -)] -mod mask_impl; - -use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount}; +use crate::simd::{Select, Simd, SimdCast, SimdElement}; use core::cmp::Ordering; use core::{fmt, mem}; +pub(crate) trait FixEndianness { + fn fix_endianness(self) -> Self; +} + +macro_rules! impl_fix_endianness { + { $($int:ty),* } => { + $( + impl FixEndianness for $int { + #[inline(always)] + fn fix_endianness(self) -> Self { + if cfg!(target_endian = "big") { + <$int>::reverse_bits(self) + } else { + self + } + } + } + )* + } +} + +impl_fix_endianness! { u8, u16, u32, u64 } + mod sealed { use super::*; @@ -28,7 +41,6 @@ mod sealed { pub trait Sealed { fn valid(values: Simd) -> bool where - LaneCount: SupportedLaneCount, Self: SimdElement; fn eq(self, other: Self) -> bool; @@ -56,8 +68,6 @@ macro_rules! impl_element { impl Sealed for $ty { #[inline] fn valid(value: Simd) -> bool - where - LaneCount: SupportedLaneCount, { // We can't use `Simd` directly, because `Simd`'s functions call this function and // we will end up with an infinite loop. @@ -108,23 +118,19 @@ impl_element! { isize, usize } /// The layout of this type is unspecified, and may change between platforms /// and/or Rust versions, and code should not assume that it is equivalent to /// `[T; N]`. +/// +/// `N` cannot be 0 and may be at most 64. This limit may be increased in +/// the future. #[repr(transparent)] -pub struct Mask(mask_impl::Mask) +pub struct Mask(Simd) where - T: MaskElement, - LaneCount: SupportedLaneCount; + T: MaskElement; -impl Copy for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ -} +impl Copy for Mask where T: MaskElement {} impl Clone for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn clone(&self) -> Self { @@ -135,12 +141,12 @@ where impl Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { /// Constructs a mask by setting all elements to the given value. #[inline] - pub fn splat(value: bool) -> Self { - Self(mask_impl::Mask::splat(value)) + #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] + pub const fn splat(value: bool) -> Self { + Self(Simd::splat(if value { T::TRUE } else { T::FALSE })) } /// Converts an array of bools to a SIMD mask. @@ -156,7 +162,7 @@ where let bytes: [u8; N] = mem::transmute_copy(&array); let bools: Simd = core::intrinsics::simd::simd_ne(Simd::from_array(bytes), Simd::splat(0u8)); - Mask::from_int_unchecked(core::intrinsics::simd::simd_cast(bools)) + Mask::from_simd_unchecked(core::intrinsics::simd::simd_cast(bools)) } } @@ -174,7 +180,7 @@ where // This would be hypothetically valid as an "in-place" transmute, // but these are "dependently-sized" types, so copy elision it is! unsafe { - let mut bytes: Simd = core::intrinsics::simd::simd_cast(self.to_int()); + let mut bytes: Simd = core::intrinsics::simd::simd_cast(self.to_simd()); bytes &= Simd::splat(1i8); mem::transmute_copy(&bytes) } @@ -187,12 +193,12 @@ where /// All elements must be either 0 or -1. #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] - pub unsafe fn from_int_unchecked(value: Simd) -> Self { + pub unsafe fn from_simd_unchecked(value: Simd) -> Self { // Safety: the caller must confirm this invariant unsafe { core::intrinsics::assume(::valid(value)); - Self(mask_impl::Mask::from_int_unchecked(value)) } + Self(value) } /// Converts a vector of integers to a mask, where 0 represents `false` and -1 @@ -203,25 +209,26 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] #[track_caller] - pub fn from_int(value: Simd) -> Self { + pub fn from_simd(value: Simd) -> Self { assert!(T::valid(value), "all values must be either 0 or -1",); // Safety: the validity has been checked - unsafe { Self::from_int_unchecked(value) } + unsafe { Self::from_simd_unchecked(value) } } /// Converts the mask to a vector of integers, where 0 represents `false` and -1 /// represents `true`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - pub fn to_int(self) -> Simd { - self.0.to_int() + pub fn to_simd(self) -> Simd { + self.0 } /// Converts the mask to a mask of any other element size. #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub fn cast(self) -> Mask { - Mask(self.0.convert()) + // Safety: mask elements are integers + unsafe { Mask(core::intrinsics::simd::simd_as(self.0)) } } /// Tests the value of the specified element. @@ -232,7 +239,7 @@ where #[must_use = "method returns a new bool and does not mutate the original value"] pub unsafe fn test_unchecked(&self, index: usize) -> bool { // Safety: the caller must confirm this invariant - unsafe { self.0.test_unchecked(index) } + unsafe { T::eq(*self.0.as_array().get_unchecked(index), T::TRUE) } } /// Tests the value of the specified element. @@ -243,9 +250,7 @@ where #[must_use = "method returns a new bool and does not mutate the original value"] #[track_caller] pub fn test(&self, index: usize) -> bool { - assert!(index < N, "element index out of range"); - // Safety: the element index has been checked - unsafe { self.test_unchecked(index) } + T::eq(self.0[index], T::TRUE) } /// Sets the value of the specified element. @@ -256,7 +261,7 @@ where pub unsafe fn set_unchecked(&mut self, index: usize, value: bool) { // Safety: the caller must confirm this invariant unsafe { - self.0.set_unchecked(index, value); + *self.0.as_mut_array().get_unchecked_mut(index) = if value { T::TRUE } else { T::FALSE } } } @@ -267,35 +272,65 @@ where #[inline] #[track_caller] pub fn set(&mut self, index: usize, value: bool) { - assert!(index < N, "element index out of range"); - // Safety: the element index has been checked - unsafe { - self.set_unchecked(index, value); - } + self.0[index] = if value { T::TRUE } else { T::FALSE } } /// Returns true if any element is set, or false otherwise. #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] pub fn any(self) -> bool { - self.0.any() + // Safety: `self` is a mask vector + unsafe { core::intrinsics::simd::simd_reduce_any(self.0) } } /// Returns true if all elements are set, or false otherwise. #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] pub fn all(self) -> bool { - self.0.all() + // Safety: `self` is a mask vector + unsafe { core::intrinsics::simd::simd_reduce_all(self.0) } } /// Creates a bitmask from a mask. /// /// Each bit is set if the corresponding element in the mask is `true`. - /// If the mask contains more than 64 elements, the bitmask is truncated to the first 64. #[inline] #[must_use = "method returns a new integer and does not mutate the original value"] pub fn to_bitmask(self) -> u64 { - self.0.to_bitmask_integer() + const { + assert!(N <= 64, "number of elements can't be greater than 64"); + } + + #[inline] + unsafe fn to_bitmask_impl( + mask: Mask, + ) -> U + where + T: MaskElement, + { + let resized = mask.resize::(false); + + // Safety: `resized` is an integer vector with length M, which must match T + let bitmask: U = unsafe { core::intrinsics::simd::simd_bitmask(resized.0) }; + + // LLVM assumes bit order should match endianness + bitmask.fix_endianness() + } + + // TODO modify simd_bitmask to zero-extend output, making this unnecessary + if N <= 8 { + // Safety: bitmask matches length + unsafe { to_bitmask_impl::(self) as u64 } + } else if N <= 16 { + // Safety: bitmask matches length + unsafe { to_bitmask_impl::(self) as u64 } + } else if N <= 32 { + // Safety: bitmask matches length + unsafe { to_bitmask_impl::(self) as u64 } + } else { + // Safety: bitmask matches length + unsafe { to_bitmask_impl::(self) } + } } /// Creates a mask from a bitmask. @@ -305,7 +340,7 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub fn from_bitmask(bitmask: u64) -> Self { - Self(mask_impl::Mask::from_bitmask_integer(bitmask)) + Self(bitmask.select(Simd::splat(T::TRUE), Simd::splat(T::FALSE))) } /// Finds the index of the first set element. @@ -351,7 +386,7 @@ where // Safety: the input and output are integer vectors let index: Simd = unsafe { core::intrinsics::simd::simd_cast(index) }; - let masked_index = self.select(index, Self::splat(true).to_int()); + let masked_index = self.select(index, Self::splat(true).to_simd()); // Safety: the input and output are integer vectors let masked_index: Simd = @@ -376,7 +411,6 @@ where impl From<[bool; N]> for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn from(array: [bool; N]) -> Self { @@ -387,7 +421,6 @@ where impl From> for [bool; N] where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn from(vector: Mask) -> Self { @@ -398,7 +431,6 @@ where impl Default for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn default() -> Self { @@ -409,7 +441,6 @@ where impl PartialEq for Mask where T: MaskElement + PartialEq, - LaneCount: SupportedLaneCount, { #[inline] fn eq(&self, other: &Self) -> bool { @@ -420,7 +451,6 @@ where impl PartialOrd for Mask where T: MaskElement + PartialOrd, - LaneCount: SupportedLaneCount, { #[inline] fn partial_cmp(&self, other: &Self) -> Option { @@ -431,7 +461,6 @@ where impl fmt::Debug for Mask where T: MaskElement + fmt::Debug, - LaneCount: SupportedLaneCount, { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -444,19 +473,18 @@ where impl core::ops::BitAnd for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] fn bitand(self, rhs: Self) -> Self { - Self(self.0 & rhs.0) + // Safety: `self` is an integer vector + unsafe { Self(core::intrinsics::simd::simd_and(self.0, rhs.0)) } } } impl core::ops::BitAnd for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -468,7 +496,6 @@ where impl core::ops::BitAnd> for bool where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Mask; #[inline] @@ -480,19 +507,18 @@ where impl core::ops::BitOr for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] fn bitor(self, rhs: Self) -> Self { - Self(self.0 | rhs.0) + // Safety: `self` is an integer vector + unsafe { Self(core::intrinsics::simd::simd_or(self.0, rhs.0)) } } } impl core::ops::BitOr for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -504,7 +530,6 @@ where impl core::ops::BitOr> for bool where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Mask; #[inline] @@ -516,19 +541,18 @@ where impl core::ops::BitXor for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] fn bitxor(self, rhs: Self) -> Self::Output { - Self(self.0 ^ rhs.0) + // Safety: `self` is an integer vector + unsafe { Self(core::intrinsics::simd::simd_xor(self.0, rhs.0)) } } } impl core::ops::BitXor for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -540,7 +564,6 @@ where impl core::ops::BitXor> for bool where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Mask; #[inline] @@ -552,30 +575,27 @@ where impl core::ops::Not for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { type Output = Mask; #[inline] fn not(self) -> Self::Output { - Self(!self.0) + Self::splat(true) ^ self } } impl core::ops::BitAndAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitand_assign(&mut self, rhs: Self) { - self.0 = self.0 & rhs.0; + *self = *self & rhs; } } impl core::ops::BitAndAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitand_assign(&mut self, rhs: bool) { @@ -586,18 +606,16 @@ where impl core::ops::BitOrAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitor_assign(&mut self, rhs: Self) { - self.0 = self.0 | rhs.0; + *self = *self | rhs; } } impl core::ops::BitOrAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitor_assign(&mut self, rhs: bool) { @@ -608,18 +626,16 @@ where impl core::ops::BitXorAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitxor_assign(&mut self, rhs: Self) { - self.0 = self.0 ^ rhs.0; + *self = *self ^ rhs; } } impl core::ops::BitXorAssign for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { #[inline] fn bitxor_assign(&mut self, rhs: bool) { @@ -631,8 +647,6 @@ macro_rules! impl_from { { $from:ty => $($to:ty),* } => { $( impl From> for Mask<$to, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn from(value: Mask<$from, N>) -> Self { diff --git a/library/portable-simd/crates/core_simd/src/masks/bitmask.rs b/library/portable-simd/crates/core_simd/src/masks/bitmask.rs deleted file mode 100644 index 32d37b5533926..0000000000000 --- a/library/portable-simd/crates/core_simd/src/masks/bitmask.rs +++ /dev/null @@ -1,228 +0,0 @@ -#![allow(unused_imports)] -use super::MaskElement; -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; -use core::marker::PhantomData; - -/// A mask where each lane is represented by a single bit. -#[repr(transparent)] -pub(crate) struct Mask( - as SupportedLaneCount>::BitMask, - PhantomData, -) -where - T: MaskElement, - LaneCount: SupportedLaneCount; - -impl Copy for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ -} - -impl Clone for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn clone(&self) -> Self { - *self - } -} - -impl PartialEq for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn eq(&self, other: &Self) -> bool { - self.0.as_ref() == other.0.as_ref() - } -} - -impl PartialOrd for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - self.0.as_ref().partial_cmp(other.0.as_ref()) - } -} - -impl Eq for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ -} - -impl Ord for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.0.as_ref().cmp(other.0.as_ref()) - } -} - -impl Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) fn splat(value: bool) -> Self { - let mut mask = as SupportedLaneCount>::BitMask::default(); - if value { - mask.as_mut().fill(u8::MAX) - } else { - mask.as_mut().fill(u8::MIN) - } - if N % 8 > 0 { - *mask.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - N % 8); - } - Self(mask, PhantomData) - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) unsafe fn test_unchecked(&self, lane: usize) -> bool { - (self.0.as_ref()[lane / 8] >> (lane % 8)) & 0x1 > 0 - } - - #[inline] - pub(crate) unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { - unsafe { - self.0.as_mut()[lane / 8] ^= ((value ^ self.test_unchecked(lane)) as u8) << (lane % 8) - } - } - - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub(crate) fn to_int(self) -> Simd { - unsafe { - core::intrinsics::simd::simd_select_bitmask( - self.0, - Simd::splat(T::TRUE), - Simd::splat(T::FALSE), - ) - } - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) unsafe fn from_int_unchecked(value: Simd) -> Self { - unsafe { Self(core::intrinsics::simd::simd_bitmask(value), PhantomData) } - } - - #[inline] - pub(crate) fn to_bitmask_integer(self) -> u64 { - let mut bitmask = [0u8; 8]; - bitmask[..self.0.as_ref().len()].copy_from_slice(self.0.as_ref()); - u64::from_ne_bytes(bitmask) - } - - #[inline] - pub(crate) fn from_bitmask_integer(bitmask: u64) -> Self { - let mut bytes = as SupportedLaneCount>::BitMask::default(); - let len = bytes.as_mut().len(); - bytes - .as_mut() - .copy_from_slice(&bitmask.to_ne_bytes()[..len]); - Self(bytes, PhantomData) - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) fn convert(self) -> Mask - where - U: MaskElement, - { - // Safety: bitmask layout does not depend on the element width - unsafe { core::mem::transmute_copy(&self) } - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) fn any(self) -> bool { - self != Self::splat(false) - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) fn all(self) -> bool { - self == Self::splat(true) - } -} - -impl core::ops::BitAnd for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, - as SupportedLaneCount>::BitMask: AsRef<[u8]> + AsMut<[u8]>, -{ - type Output = Self; - #[inline] - fn bitand(mut self, rhs: Self) -> Self { - for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { - *l &= r; - } - self - } -} - -impl core::ops::BitOr for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, - as SupportedLaneCount>::BitMask: AsRef<[u8]> + AsMut<[u8]>, -{ - type Output = Self; - #[inline] - fn bitor(mut self, rhs: Self) -> Self { - for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { - *l |= r; - } - self - } -} - -impl core::ops::BitXor for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn bitxor(mut self, rhs: Self) -> Self::Output { - for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { - *l ^= r; - } - self - } -} - -impl core::ops::Not for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn not(mut self) -> Self::Output { - for x in self.0.as_mut() { - *x = !*x; - } - if N % 8 > 0 { - *self.0.as_mut().last_mut().unwrap() &= u8::MAX >> (8 - N % 8); - } - self - } -} diff --git a/library/portable-simd/crates/core_simd/src/masks/full_masks.rs b/library/portable-simd/crates/core_simd/src/masks/full_masks.rs deleted file mode 100644 index 4e98db4070a9d..0000000000000 --- a/library/portable-simd/crates/core_simd/src/masks/full_masks.rs +++ /dev/null @@ -1,296 +0,0 @@ -//! Masks that take up full SIMD vector registers. - -use crate::simd::{LaneCount, MaskElement, Simd, SupportedLaneCount}; - -#[repr(transparent)] -pub(crate) struct Mask(Simd) -where - T: MaskElement, - LaneCount: SupportedLaneCount; - -impl Copy for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ -} - -impl Clone for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn clone(&self) -> Self { - *self - } -} - -impl PartialEq for Mask -where - T: MaskElement + PartialEq, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn eq(&self, other: &Self) -> bool { - self.0.eq(&other.0) - } -} - -impl PartialOrd for Mask -where - T: MaskElement + PartialOrd, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - -impl Eq for Mask -where - T: MaskElement + Eq, - LaneCount: SupportedLaneCount, -{ -} - -impl Ord for Mask -where - T: MaskElement + Ord, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - self.0.cmp(&other.0) - } -} - -// Used for bitmask bit order workaround -pub(crate) trait ReverseBits { - // Reverse the least significant `n` bits of `self`. - // (Remaining bits must be 0.) - fn reverse_bits(self, n: usize) -> Self; -} - -macro_rules! impl_reverse_bits { - { $($int:ty),* } => { - $( - impl ReverseBits for $int { - #[inline(always)] - fn reverse_bits(self, n: usize) -> Self { - let rev = <$int>::reverse_bits(self); - let bitsize = size_of::<$int>() * 8; - if n < bitsize { - // Shift things back to the right - rev >> (bitsize - n) - } else { - rev - } - } - } - )* - } -} - -impl_reverse_bits! { u8, u16, u32, u64 } - -impl Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) fn splat(value: bool) -> Self { - Self(Simd::splat(if value { T::TRUE } else { T::FALSE })) - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) unsafe fn test_unchecked(&self, lane: usize) -> bool { - T::eq(self.0[lane], T::TRUE) - } - - #[inline] - pub(crate) unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { - self.0[lane] = if value { T::TRUE } else { T::FALSE } - } - - #[inline] - #[must_use = "method returns a new vector and does not mutate the original value"] - pub(crate) fn to_int(self) -> Simd { - self.0 - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) unsafe fn from_int_unchecked(value: Simd) -> Self { - Self(value) - } - - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub(crate) fn convert(self) -> Mask - where - U: MaskElement, - { - // Safety: masks are simply integer vectors of 0 and -1, and we can cast the element type. - unsafe { Mask(core::intrinsics::simd::simd_cast(self.0)) } - } - - #[inline] - unsafe fn to_bitmask_impl(self) -> U - where - LaneCount: SupportedLaneCount, - { - let resized = self.to_int().resize::(T::FALSE); - - // Safety: `resized` is an integer vector with length M, which must match T - let bitmask: U = unsafe { core::intrinsics::simd::simd_bitmask(resized) }; - - // LLVM assumes bit order should match endianness - if cfg!(target_endian = "big") { - bitmask.reverse_bits(M) - } else { - bitmask - } - } - - #[inline] - unsafe fn from_bitmask_impl(bitmask: U) -> Self - where - LaneCount: SupportedLaneCount, - { - // LLVM assumes bit order should match endianness - let bitmask = if cfg!(target_endian = "big") { - bitmask.reverse_bits(M) - } else { - bitmask - }; - - // SAFETY: `mask` is the correct bitmask type for a u64 bitmask - let mask: Simd = unsafe { - core::intrinsics::simd::simd_select_bitmask( - bitmask, - Simd::::splat(T::TRUE), - Simd::::splat(T::FALSE), - ) - }; - - // SAFETY: `mask` only contains `T::TRUE` or `T::FALSE` - unsafe { Self::from_int_unchecked(mask.resize::(T::FALSE)) } - } - - #[inline] - pub(crate) fn to_bitmask_integer(self) -> u64 { - // TODO modify simd_bitmask to zero-extend output, making this unnecessary - if N <= 8 { - // Safety: bitmask matches length - unsafe { self.to_bitmask_impl::() as u64 } - } else if N <= 16 { - // Safety: bitmask matches length - unsafe { self.to_bitmask_impl::() as u64 } - } else if N <= 32 { - // Safety: bitmask matches length - unsafe { self.to_bitmask_impl::() as u64 } - } else { - // Safety: bitmask matches length - unsafe { self.to_bitmask_impl::() } - } - } - - #[inline] - pub(crate) fn from_bitmask_integer(bitmask: u64) -> Self { - // TODO modify simd_bitmask_select to truncate input, making this unnecessary - if N <= 8 { - // Safety: bitmask matches length - unsafe { Self::from_bitmask_impl::(bitmask as u8) } - } else if N <= 16 { - // Safety: bitmask matches length - unsafe { Self::from_bitmask_impl::(bitmask as u16) } - } else if N <= 32 { - // Safety: bitmask matches length - unsafe { Self::from_bitmask_impl::(bitmask as u32) } - } else { - // Safety: bitmask matches length - unsafe { Self::from_bitmask_impl::(bitmask) } - } - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) fn any(self) -> bool { - // Safety: use `self` as an integer vector - unsafe { core::intrinsics::simd::simd_reduce_any(self.to_int()) } - } - - #[inline] - #[must_use = "method returns a new bool and does not mutate the original value"] - pub(crate) fn all(self) -> bool { - // Safety: use `self` as an integer vector - unsafe { core::intrinsics::simd::simd_reduce_all(self.to_int()) } - } -} - -impl From> for Simd -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - #[inline] - fn from(value: Mask) -> Self { - value.0 - } -} - -impl core::ops::BitAnd for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn bitand(self, rhs: Self) -> Self { - // Safety: `self` is an integer vector - unsafe { Self(core::intrinsics::simd::simd_and(self.0, rhs.0)) } - } -} - -impl core::ops::BitOr for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn bitor(self, rhs: Self) -> Self { - // Safety: `self` is an integer vector - unsafe { Self(core::intrinsics::simd::simd_or(self.0, rhs.0)) } - } -} - -impl core::ops::BitXor for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn bitxor(self, rhs: Self) -> Self { - // Safety: `self` is an integer vector - unsafe { Self(core::intrinsics::simd::simd_xor(self.0, rhs.0)) } - } -} - -impl core::ops::Not for Mask -where - T: MaskElement, - LaneCount: SupportedLaneCount, -{ - type Output = Self; - #[inline] - fn not(self) -> Self::Output { - Self::splat(true) ^ self - } -} diff --git a/library/portable-simd/crates/core_simd/src/mod.rs b/library/portable-simd/crates/core_simd/src/mod.rs index 45b1a0f975141..5f635d80a178f 100644 --- a/library/portable-simd/crates/core_simd/src/mod.rs +++ b/library/portable-simd/crates/core_simd/src/mod.rs @@ -5,7 +5,6 @@ mod alias; mod cast; mod fmt; mod iter; -mod lane_count; mod masks; mod ops; mod select; @@ -27,8 +26,8 @@ pub mod simd { pub use crate::core_simd::alias::*; pub use crate::core_simd::cast::*; - pub use crate::core_simd::lane_count::{LaneCount, SupportedLaneCount}; pub use crate::core_simd::masks::*; + pub use crate::core_simd::select::*; pub use crate::core_simd::swizzle::*; pub use crate::core_simd::to_bytes::ToBytes; pub use crate::core_simd::vector::*; diff --git a/library/portable-simd/crates/core_simd/src/ops.rs b/library/portable-simd/crates/core_simd/src/ops.rs index f36e8d01a73bb..eb6601f734831 100644 --- a/library/portable-simd/crates/core_simd/src/ops.rs +++ b/library/portable-simd/crates/core_simd/src/ops.rs @@ -1,4 +1,4 @@ -use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount, cmp::SimdPartialEq}; +use crate::simd::{Select, Simd, SimdElement, cmp::SimdPartialEq}; use core::ops::{Add, Mul}; use core::ops::{BitAnd, BitOr, BitXor}; use core::ops::{Div, Rem, Sub}; @@ -12,7 +12,6 @@ mod unary; impl core::ops::Index for Simd where T: SimdElement, - LaneCount: SupportedLaneCount, I: core::slice::SliceIndex<[T]>, { type Output = I::Output; @@ -25,7 +24,6 @@ where impl core::ops::IndexMut for Simd where T: SimdElement, - LaneCount: SupportedLaneCount, I: core::slice::SliceIndex<[T]>, { #[inline] @@ -130,7 +128,6 @@ macro_rules! for_base_types { impl $op for Simd<$scalar, N> where $scalar: SimdElement, - LaneCount: SupportedLaneCount, { type Output = $out; diff --git a/library/portable-simd/crates/core_simd/src/ops/assign.rs b/library/portable-simd/crates/core_simd/src/ops/assign.rs index d21d867de26d6..c1830c35df778 100644 --- a/library/portable-simd/crates/core_simd/src/ops/assign.rs +++ b/library/portable-simd/crates/core_simd/src/ops/assign.rs @@ -21,7 +21,6 @@ macro_rules! assign_ops { where Self: $trait, T: SimdElement, - LaneCount: SupportedLaneCount, { #[inline] fn $assign_call(&mut self, rhs: U) { diff --git a/library/portable-simd/crates/core_simd/src/ops/deref.rs b/library/portable-simd/crates/core_simd/src/ops/deref.rs index 913cbbe977c46..360b83c403468 100644 --- a/library/portable-simd/crates/core_simd/src/ops/deref.rs +++ b/library/portable-simd/crates/core_simd/src/ops/deref.rs @@ -13,7 +13,6 @@ macro_rules! deref_lhs { where T: SimdElement, $simd: $trait<$simd, Output = $simd>, - LaneCount: SupportedLaneCount, { type Output = Simd; @@ -33,7 +32,6 @@ macro_rules! deref_rhs { where T: SimdElement, $simd: $trait<$simd, Output = $simd>, - LaneCount: SupportedLaneCount, { type Output = Simd; @@ -64,7 +62,6 @@ macro_rules! deref_ops { where T: SimdElement, $simd: $trait<$simd, Output = $simd>, - LaneCount: SupportedLaneCount, { type Output = $simd; diff --git a/library/portable-simd/crates/core_simd/src/ops/shift_scalar.rs b/library/portable-simd/crates/core_simd/src/ops/shift_scalar.rs index f5115a5a5e935..7ca83dc40f617 100644 --- a/library/portable-simd/crates/core_simd/src/ops/shift_scalar.rs +++ b/library/portable-simd/crates/core_simd/src/ops/shift_scalar.rs @@ -1,13 +1,11 @@ // Shift operations uniquely typically only have a scalar on the right-hand side. // Here, we implement shifts for scalar RHS arguments. -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; +use crate::simd::Simd; macro_rules! impl_splatted_shifts { { impl $trait:ident :: $trait_fn:ident for $ty:ty } => { impl core::ops::$trait<$ty> for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -17,8 +15,6 @@ macro_rules! impl_splatted_shifts { } impl core::ops::$trait<&$ty> for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Output = Self; #[inline] @@ -28,8 +24,6 @@ macro_rules! impl_splatted_shifts { } impl<'lhs, const N: usize> core::ops::$trait<$ty> for &'lhs Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Output = Simd<$ty, N>; #[inline] @@ -39,8 +33,6 @@ macro_rules! impl_splatted_shifts { } impl<'lhs, const N: usize> core::ops::$trait<&$ty> for &'lhs Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Output = Simd<$ty, N>; #[inline] diff --git a/library/portable-simd/crates/core_simd/src/ops/unary.rs b/library/portable-simd/crates/core_simd/src/ops/unary.rs index 412a5b801171b..e1c06167f9790 100644 --- a/library/portable-simd/crates/core_simd/src/ops/unary.rs +++ b/library/portable-simd/crates/core_simd/src/ops/unary.rs @@ -1,4 +1,4 @@ -use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{Simd, SimdElement}; use core::ops::{Neg, Not}; // unary ops macro_rules! neg { @@ -6,7 +6,6 @@ macro_rules! neg { $(impl Neg for Simd<$scalar, N> where $scalar: SimdElement, - LaneCount: SupportedLaneCount, { type Output = Self; @@ -40,7 +39,6 @@ macro_rules! not { $(impl Not for Simd<$scalar, N> where $scalar: SimdElement, - LaneCount: SupportedLaneCount, { type Output = Self; diff --git a/library/portable-simd/crates/core_simd/src/select.rs b/library/portable-simd/crates/core_simd/src/select.rs index f33aa261a928f..404f54d8f3827 100644 --- a/library/portable-simd/crates/core_simd/src/select.rs +++ b/library/portable-simd/crates/core_simd/src/select.rs @@ -1,54 +1,155 @@ -use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{FixEndianness, Mask, MaskElement, Simd, SimdElement}; -impl Mask +/// Choose elements from two vectors using a mask. +/// +/// For each element in the mask, choose the corresponding element from `true_values` if +/// that element mask is true, and `false_values` if that element mask is false. +/// +/// If the mask is `u64`, it's treated as a bitmask with the least significant bit +/// corresponding to the first element. +/// +/// # Examples +/// +/// ## Selecting values from `Simd` +/// ``` +/// # #![feature(portable_simd)] +/// # #[cfg(feature = "as_crate")] use core_simd::simd; +/// # #[cfg(not(feature = "as_crate"))] use core::simd; +/// # use simd::{Simd, Mask, Select}; +/// let a = Simd::from_array([0, 1, 2, 3]); +/// let b = Simd::from_array([4, 5, 6, 7]); +/// let mask = Mask::::from_array([true, false, false, true]); +/// let c = mask.select(a, b); +/// assert_eq!(c.to_array(), [0, 5, 6, 3]); +/// ``` +/// +/// ## Selecting values from `Mask` +/// ``` +/// # #![feature(portable_simd)] +/// # #[cfg(feature = "as_crate")] use core_simd::simd; +/// # #[cfg(not(feature = "as_crate"))] use core::simd; +/// # use simd::{Mask, Select}; +/// let a = Mask::::from_array([true, true, false, false]); +/// let b = Mask::::from_array([false, false, true, true]); +/// let mask = Mask::::from_array([true, false, false, true]); +/// let c = mask.select(a, b); +/// assert_eq!(c.to_array(), [true, false, true, false]); +/// ``` +/// +/// ## Selecting with a bitmask +/// ``` +/// # #![feature(portable_simd)] +/// # #[cfg(feature = "as_crate")] use core_simd::simd; +/// # #[cfg(not(feature = "as_crate"))] use core::simd; +/// # use simd::{Mask, Select}; +/// let a = Mask::::from_array([true, true, false, false]); +/// let b = Mask::::from_array([false, false, true, true]); +/// let mask = 0b1001; +/// let c = mask.select(a, b); +/// assert_eq!(c.to_array(), [true, false, true, false]); +/// ``` +pub trait Select { + /// Choose elements + fn select(self, true_values: T, false_values: T) -> T; +} + +impl Select> for Mask +where + T: SimdElement, + U: MaskElement, +{ + #[inline] + fn select(self, true_values: Simd, false_values: Simd) -> Simd { + // Safety: + // simd_as between masks is always safe (they're vectors of ints). + // simd_select uses a mask that matches the width and number of elements + unsafe { + let mask: Simd = core::intrinsics::simd::simd_as(self.to_simd()); + core::intrinsics::simd::simd_select(mask, true_values, false_values) + } + } +} + +impl Select> for u64 +where + T: SimdElement, +{ + #[inline] + fn select(self, true_values: Simd, false_values: Simd) -> Simd { + const { + assert!(N <= 64, "number of elements can't be greater than 64"); + } + + #[inline] + unsafe fn select_impl( + bitmask: U, + true_values: Simd, + false_values: Simd, + ) -> Simd + where + T: SimdElement, + { + let default = true_values[0]; + let true_values = true_values.resize::(default); + let false_values = false_values.resize::(default); + + // LLVM assumes bit order should match endianness + let bitmask = bitmask.fix_endianness(); + + // Safety: the caller guarantees that the size of U matches M + let selected = unsafe { + core::intrinsics::simd::simd_select_bitmask(bitmask, true_values, false_values) + }; + + selected.resize::(default) + } + + // TODO modify simd_bitmask_select to truncate input, making this unnecessary + if N <= 8 { + let bitmask = self as u8; + // Safety: bitmask matches length + unsafe { select_impl::(bitmask, true_values, false_values) } + } else if N <= 16 { + let bitmask = self as u16; + // Safety: bitmask matches length + unsafe { select_impl::(bitmask, true_values, false_values) } + } else if N <= 32 { + let bitmask = self as u32; + // Safety: bitmask matches length + unsafe { select_impl::(bitmask, true_values, false_values) } + } else { + let bitmask = self; + // Safety: bitmask matches length + unsafe { select_impl::(bitmask, true_values, false_values) } + } + } +} + +impl Select> for Mask where T: MaskElement, - LaneCount: SupportedLaneCount, + U: MaskElement, { - /// Choose elements from two vectors. - /// - /// For each element in the mask, choose the corresponding element from `true_values` if - /// that element mask is true, and `false_values` if that element mask is false. - /// - /// # Examples - /// ``` - /// # #![feature(portable_simd)] - /// # use core::simd::{Simd, Mask}; - /// let a = Simd::from_array([0, 1, 2, 3]); - /// let b = Simd::from_array([4, 5, 6, 7]); - /// let mask = Mask::from_array([true, false, false, true]); - /// let c = mask.select(a, b); - /// assert_eq!(c.to_array(), [0, 5, 6, 3]); - /// ``` #[inline] - #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn select(self, true_values: Simd, false_values: Simd) -> Simd - where - U: SimdElement, - { - // Safety: The mask has been cast to a vector of integers, - // and the operands to select between are vectors of the same type and length. - unsafe { core::intrinsics::simd::simd_select(self.to_int(), true_values, false_values) } + fn select(self, true_values: Mask, false_values: Mask) -> Mask { + let selected: Simd = + Select::select(self, true_values.to_simd(), false_values.to_simd()); + + // Safety: all values come from masks + unsafe { Mask::from_simd_unchecked(selected) } } +} - /// Choose elements from two masks. - /// - /// For each element in the mask, choose the corresponding element from `true_values` if - /// that element mask is true, and `false_values` if that element mask is false. - /// - /// # Examples - /// ``` - /// # #![feature(portable_simd)] - /// # use core::simd::Mask; - /// let a = Mask::::from_array([true, true, false, false]); - /// let b = Mask::::from_array([false, false, true, true]); - /// let mask = Mask::::from_array([true, false, false, true]); - /// let c = mask.select_mask(a, b); - /// assert_eq!(c.to_array(), [true, false, true, false]); - /// ``` +impl Select> for u64 +where + T: MaskElement, +{ #[inline] - #[must_use = "method returns a new mask and does not mutate the original inputs"] - pub fn select_mask(self, true_values: Self, false_values: Self) -> Self { - self & true_values | !self & false_values + fn select(self, true_values: Mask, false_values: Mask) -> Mask { + let selected: Simd = + Select::select(self, true_values.to_simd(), false_values.to_simd()); + + // Safety: all values come from masks + unsafe { Mask::from_simd_unchecked(selected) } } } diff --git a/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs b/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs index 2312ba401fa78..d553d6c040c91 100644 --- a/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs +++ b/library/portable-simd/crates/core_simd/src/simd/cmp/eq.rs @@ -1,5 +1,5 @@ use crate::simd::{ - LaneCount, Mask, Simd, SimdElement, SupportedLaneCount, + Mask, Simd, SimdElement, ptr::{SimdConstPtr, SimdMutPtr}, }; @@ -21,8 +21,6 @@ macro_rules! impl_number { { $($number:ty),* } => { $( impl SimdPartialEq for Simd<$number, N> - where - LaneCount: SupportedLaneCount, { type Mask = Mask<<$number as SimdElement>::Mask, N>; @@ -30,14 +28,14 @@ macro_rules! impl_number { fn simd_eq(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_eq(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_eq(self, other)) } } #[inline] fn simd_ne(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ne(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_ne(self, other)) } } } )* @@ -50,8 +48,6 @@ macro_rules! impl_mask { { $($integer:ty),* } => { $( impl SimdPartialEq for Mask<$integer, N> - where - LaneCount: SupportedLaneCount, { type Mask = Self; @@ -59,14 +55,14 @@ macro_rules! impl_mask { fn simd_eq(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_eq(self.to_int(), other.to_int())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_eq(self.to_simd(), other.to_simd())) } } #[inline] fn simd_ne(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ne(self.to_int(), other.to_int())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_ne(self.to_simd(), other.to_simd())) } } } )* @@ -75,10 +71,7 @@ macro_rules! impl_mask { impl_mask! { i8, i16, i32, i64, isize } -impl SimdPartialEq for Simd<*const T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdPartialEq for Simd<*const T, N> { type Mask = Mask; #[inline] @@ -92,10 +85,7 @@ where } } -impl SimdPartialEq for Simd<*mut T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdPartialEq for Simd<*mut T, N> { type Mask = Mask; #[inline] diff --git a/library/portable-simd/crates/core_simd/src/simd/cmp/ord.rs b/library/portable-simd/crates/core_simd/src/simd/cmp/ord.rs index e813e7613032c..5672fbbf54caa 100644 --- a/library/portable-simd/crates/core_simd/src/simd/cmp/ord.rs +++ b/library/portable-simd/crates/core_simd/src/simd/cmp/ord.rs @@ -1,5 +1,5 @@ use crate::simd::{ - LaneCount, Mask, Simd, SupportedLaneCount, + Mask, Select, Simd, cmp::SimdPartialEq, ptr::{SimdConstPtr, SimdMutPtr}, }; @@ -49,41 +49,37 @@ macro_rules! impl_integer { { $($integer:ty),* } => { $( impl SimdPartialOrd for Simd<$integer, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_lt(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_lt(self, other)) } } #[inline] fn simd_le(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_le(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_le(self, other)) } } #[inline] fn simd_gt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_gt(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_gt(self, other)) } } #[inline] fn simd_ge(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ge(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_ge(self, other)) } } } impl SimdOrd for Simd<$integer, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_max(self, other: Self) -> Self { @@ -115,35 +111,33 @@ macro_rules! impl_float { { $($float:ty),* } => { $( impl SimdPartialOrd for Simd<$float, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_lt(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_lt(self, other)) } } #[inline] fn simd_le(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_le(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_le(self, other)) } } #[inline] fn simd_gt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_gt(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_gt(self, other)) } } #[inline] fn simd_ge(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Mask::from_int_unchecked(core::intrinsics::simd::simd_ge(self, other)) } + unsafe { Mask::from_simd_unchecked(core::intrinsics::simd::simd_ge(self, other)) } } } )* @@ -156,50 +150,46 @@ macro_rules! impl_mask { { $($integer:ty),* } => { $( impl SimdPartialOrd for Mask<$integer, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_lt(self.to_int(), other.to_int())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_lt(self.to_simd(), other.to_simd())) } } #[inline] fn simd_le(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_le(self.to_int(), other.to_int())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_le(self.to_simd(), other.to_simd())) } } #[inline] fn simd_gt(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_gt(self.to_int(), other.to_int())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_gt(self.to_simd(), other.to_simd())) } } #[inline] fn simd_ge(self, other: Self) -> Self::Mask { // Safety: `self` is a vector, and the result of the comparison // is always a valid mask. - unsafe { Self::from_int_unchecked(core::intrinsics::simd::simd_ge(self.to_int(), other.to_int())) } + unsafe { Self::from_simd_unchecked(core::intrinsics::simd::simd_ge(self.to_simd(), other.to_simd())) } } } impl SimdOrd for Mask<$integer, N> - where - LaneCount: SupportedLaneCount, { #[inline] fn simd_max(self, other: Self) -> Self { - self.simd_gt(other).select_mask(other, self) + self.simd_gt(other).select(other, self) } #[inline] fn simd_min(self, other: Self) -> Self { - self.simd_lt(other).select_mask(other, self) + self.simd_lt(other).select(other, self) } #[inline] @@ -218,10 +208,7 @@ macro_rules! impl_mask { impl_mask! { i8, i16, i32, i64, isize } -impl SimdPartialOrd for Simd<*const T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdPartialOrd for Simd<*const T, N> { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { self.addr().simd_lt(other.addr()) @@ -243,10 +230,7 @@ where } } -impl SimdOrd for Simd<*const T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdOrd for Simd<*const T, N> { #[inline] fn simd_max(self, other: Self) -> Self { self.simd_lt(other).select(other, self) @@ -268,10 +252,7 @@ where } } -impl SimdPartialOrd for Simd<*mut T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdPartialOrd for Simd<*mut T, N> { #[inline] fn simd_lt(self, other: Self) -> Self::Mask { self.addr().simd_lt(other.addr()) @@ -293,10 +274,7 @@ where } } -impl SimdOrd for Simd<*mut T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdOrd for Simd<*mut T, N> { #[inline] fn simd_max(self, other: Self) -> Self { self.simd_lt(other).select(other, self) diff --git a/library/portable-simd/crates/core_simd/src/simd/num/float.rs b/library/portable-simd/crates/core_simd/src/simd/num/float.rs index b5972c47373bb..efd7c2469512b 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/float.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/float.rs @@ -1,6 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, + Mask, Select, Simd, SimdCast, SimdElement, cmp::{SimdPartialEq, SimdPartialOrd}, }; @@ -240,15 +240,9 @@ pub trait SimdFloat: Copy + Sealed { macro_rules! impl_trait { { $($ty:ty { bits: $bits_ty:ty, mask: $mask_ty:ty }),* } => { $( - impl Sealed for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, - { - } + impl Sealed for Simd<$ty, N> {} impl SimdFloat for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Mask = Mask<<$mask_ty as SimdElement>::Mask, N>; type Scalar = $ty; diff --git a/library/portable-simd/crates/core_simd/src/simd/num/int.rs b/library/portable-simd/crates/core_simd/src/simd/num/int.rs index e7253313f036c..eee54d3968808 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/int.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/int.rs @@ -1,7 +1,6 @@ use super::sealed::Sealed; use crate::simd::{ - LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::SimdOrd, - cmp::SimdPartialOrd, num::SimdUint, + Mask, Select, Simd, SimdCast, SimdElement, cmp::SimdOrd, cmp::SimdPartialOrd, num::SimdUint, }; /// Operations on SIMD vectors of signed integers. @@ -242,16 +241,9 @@ pub trait SimdInt: Copy + Sealed { macro_rules! impl_trait { { $($ty:ident ($unsigned:ident)),* } => { $( - impl Sealed for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, - { - } + impl Sealed for Simd<$ty, N> {} - impl SimdInt for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, - { + impl SimdInt for Simd<$ty, N> { type Mask = Mask<<$ty as SimdElement>::Mask, N>; type Scalar = $ty; type Unsigned = Simd<$unsigned, N>; diff --git a/library/portable-simd/crates/core_simd/src/simd/num/uint.rs b/library/portable-simd/crates/core_simd/src/simd/num/uint.rs index e3ba8658bd803..606107a1f06f8 100644 --- a/library/portable-simd/crates/core_simd/src/simd/num/uint.rs +++ b/library/portable-simd/crates/core_simd/src/simd/num/uint.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{LaneCount, Simd, SimdCast, SimdElement, SupportedLaneCount, cmp::SimdOrd}; +use crate::simd::{Simd, SimdCast, SimdElement, cmp::SimdOrd}; /// Operations on SIMD vectors of unsigned integers. pub trait SimdUint: Copy + Sealed { @@ -124,15 +124,9 @@ pub trait SimdUint: Copy + Sealed { macro_rules! impl_trait { { $($ty:ident ($signed:ident)),* } => { $( - impl Sealed for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, - { - } + impl Sealed for Simd<$ty, N> {} impl SimdUint for Simd<$ty, N> - where - LaneCount: SupportedLaneCount, { type Scalar = $ty; type Cast = Simd; diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs index 36452e7ae920d..7ef9dc21373ec 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/const_ptr.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{LaneCount, Mask, Simd, SupportedLaneCount, cmp::SimdPartialEq, num::SimdUint}; +use crate::simd::{Mask, Simd, cmp::SimdPartialEq, num::SimdUint}; /// Operations on SIMD vectors of constant pointers. pub trait SimdConstPtr: Copy + Sealed { @@ -88,12 +88,9 @@ pub trait SimdConstPtr: Copy + Sealed { fn wrapping_sub(self, count: Self::Usize) -> Self; } -impl Sealed for Simd<*const T, N> where LaneCount: SupportedLaneCount {} +impl Sealed for Simd<*const T, N> {} -impl SimdConstPtr for Simd<*const T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdConstPtr for Simd<*const T, N> { type Usize = Simd; type Isize = Simd; type CastPtr = Simd<*const U, N>; diff --git a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs index c644f390c20a5..3b9b75ddf5660 100644 --- a/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs +++ b/library/portable-simd/crates/core_simd/src/simd/ptr/mut_ptr.rs @@ -1,5 +1,5 @@ use super::sealed::Sealed; -use crate::simd::{LaneCount, Mask, Simd, SupportedLaneCount, cmp::SimdPartialEq, num::SimdUint}; +use crate::simd::{Mask, Simd, cmp::SimdPartialEq, num::SimdUint}; /// Operations on SIMD vectors of mutable pointers. pub trait SimdMutPtr: Copy + Sealed { @@ -85,12 +85,9 @@ pub trait SimdMutPtr: Copy + Sealed { fn wrapping_sub(self, count: Self::Usize) -> Self; } -impl Sealed for Simd<*mut T, N> where LaneCount: SupportedLaneCount {} +impl Sealed for Simd<*mut T, N> {} -impl SimdMutPtr for Simd<*mut T, N> -where - LaneCount: SupportedLaneCount, -{ +impl SimdMutPtr for Simd<*mut T, N> { type Usize = Simd; type Isize = Simd; type CastPtr = Simd<*mut U, N>; diff --git a/library/portable-simd/crates/core_simd/src/swizzle.rs b/library/portable-simd/crates/core_simd/src/swizzle.rs index dbdd6ef40eba7..02dcd71356dd6 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle.rs @@ -1,4 +1,4 @@ -use crate::simd::{LaneCount, Mask, MaskElement, Simd, SimdElement, SupportedLaneCount}; +use crate::simd::{Mask, MaskElement, Simd, SimdElement}; /// Constructs a new SIMD vector by copying elements from selected elements in other vectors. /// @@ -82,8 +82,6 @@ pub trait Swizzle { fn swizzle(vector: Simd) -> Simd where T: SimdElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, { // Safety: `vector` is a vector, and the index is a const vector of u32. unsafe { @@ -122,8 +120,6 @@ pub trait Swizzle { fn concat_swizzle(first: Simd, second: Simd) -> Simd where T: SimdElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, { // Safety: `first` and `second` are vectors, and the index is a const vector of u32. unsafe { @@ -161,11 +157,9 @@ pub trait Swizzle { fn swizzle_mask(mask: Mask) -> Mask where T: MaskElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, { // SAFETY: all elements of this mask come from another mask - unsafe { Mask::from_int_unchecked(Self::swizzle(mask.to_int())) } + unsafe { Mask::from_simd_unchecked(Self::swizzle(mask.to_simd())) } } /// Creates a new mask from the elements of `first` and `second`. @@ -177,18 +171,17 @@ pub trait Swizzle { fn concat_swizzle_mask(first: Mask, second: Mask) -> Mask where T: MaskElement, - LaneCount: SupportedLaneCount, - LaneCount: SupportedLaneCount, { // SAFETY: all elements of this mask come from another mask - unsafe { Mask::from_int_unchecked(Self::concat_swizzle(first.to_int(), second.to_int())) } + unsafe { + Mask::from_simd_unchecked(Self::concat_swizzle(first.to_simd(), second.to_simd())) + } } } impl Simd where T: SimdElement, - LaneCount: SupportedLaneCount, { /// Reverse the order of the elements in the vector. #[inline] @@ -462,10 +455,7 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn resize(self, value: T) -> Simd - where - LaneCount: SupportedLaneCount, - { + pub fn resize(self, value: T) -> Simd { struct Resize; impl Swizzle for Resize { const INDEX: [usize; M] = const { @@ -493,10 +483,7 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn extract(self) -> Simd - where - LaneCount: SupportedLaneCount, - { + pub fn extract(self) -> Simd { struct Extract; impl Swizzle for Extract { const INDEX: [usize; LEN] = const { @@ -517,14 +504,13 @@ where impl Mask where T: MaskElement, - LaneCount: SupportedLaneCount, { /// Reverse the order of the elements in the mask. #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn reverse(self) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_int().reverse()) } + unsafe { Self::from_simd_unchecked(self.to_simd().reverse()) } } /// Rotates the mask such that the first `OFFSET` elements of the slice move to the end @@ -534,7 +520,7 @@ where #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn rotate_elements_left(self) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_left::()) } + unsafe { Self::from_simd_unchecked(self.to_simd().rotate_elements_left::()) } } /// Rotates the mask such that the first `self.len() - OFFSET` elements of the mask move to @@ -544,7 +530,7 @@ where #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn rotate_elements_right(self) -> Self { // Safety: swizzles are safe for masks - unsafe { Self::from_int_unchecked(self.to_int().rotate_elements_right::()) } + unsafe { Self::from_simd_unchecked(self.to_simd().rotate_elements_right::()) } } /// Shifts the mask elements to the left by `OFFSET`, filling in with @@ -554,7 +540,7 @@ where pub fn shift_elements_left(self, padding: bool) -> Self { // Safety: swizzles are safe for masks unsafe { - Self::from_int_unchecked(self.to_int().shift_elements_left::(if padding { + Self::from_simd_unchecked(self.to_simd().shift_elements_left::(if padding { T::TRUE } else { T::FALSE @@ -569,7 +555,7 @@ where pub fn shift_elements_right(self, padding: bool) -> Self { // Safety: swizzles are safe for masks unsafe { - Self::from_int_unchecked(self.to_int().shift_elements_right::(if padding { + Self::from_simd_unchecked(self.to_simd().shift_elements_right::(if padding { T::TRUE } else { T::FALSE @@ -598,9 +584,9 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn interleave(self, other: Self) -> (Self, Self) { - let (lo, hi) = self.to_int().interleave(other.to_int()); + let (lo, hi) = self.to_simd().interleave(other.to_simd()); // Safety: swizzles are safe for masks - unsafe { (Self::from_int_unchecked(lo), Self::from_int_unchecked(hi)) } + unsafe { (Self::from_simd_unchecked(lo), Self::from_simd_unchecked(hi)) } } /// Deinterleave two masks. @@ -627,12 +613,12 @@ where #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] pub fn deinterleave(self, other: Self) -> (Self, Self) { - let (even, odd) = self.to_int().deinterleave(other.to_int()); + let (even, odd) = self.to_simd().deinterleave(other.to_simd()); // Safety: swizzles are safe for masks unsafe { ( - Self::from_int_unchecked(even), - Self::from_int_unchecked(odd), + Self::from_simd_unchecked(even), + Self::from_simd_unchecked(odd), ) } } @@ -653,13 +639,10 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn resize(self, value: bool) -> Mask - where - LaneCount: SupportedLaneCount, - { + pub fn resize(self, value: bool) -> Mask { // Safety: swizzles are safe for masks unsafe { - Mask::::from_int_unchecked(self.to_int().resize::(if value { + Mask::::from_simd_unchecked(self.to_simd().resize::(if value { T::TRUE } else { T::FALSE @@ -679,11 +662,8 @@ where /// ``` #[inline] #[must_use = "method returns a new vector and does not mutate the original inputs"] - pub fn extract(self) -> Mask - where - LaneCount: SupportedLaneCount, - { + pub fn extract(self) -> Mask { // Safety: swizzles are safe for masks - unsafe { Mask::::from_int_unchecked(self.to_int().extract::()) } + unsafe { Mask::::from_simd_unchecked(self.to_simd().extract::()) } } } diff --git a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs index 773bd028bae09..ae0b174973da7 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs @@ -1,10 +1,7 @@ -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; +use crate::simd::Simd; use core::mem; -impl Simd -where - LaneCount: SupportedLaneCount, -{ +impl Simd { /// Swizzle a vector of bytes according to the index vector. /// Indices within range select the appropriate byte. /// Indices "out of bounds" instead select 0. @@ -139,7 +136,7 @@ unsafe fn armv7_neon_swizzle_u8x16(bytes: Simd, idxs: Simd) -> S #[inline] #[allow(clippy::let_and_return)] unsafe fn avx2_pshufb(bytes: Simd, idxs: Simd) -> Simd { - use crate::simd::cmp::SimdPartialOrd; + use crate::simd::{Select, cmp::SimdPartialOrd}; #[cfg(target_arch = "x86")] use core::arch::x86; #[cfg(target_arch = "x86_64")] @@ -184,10 +181,7 @@ unsafe fn transize( f: unsafe fn(T, T) -> T, a: Simd, b: Simd, -) -> Simd -where - LaneCount: SupportedLaneCount, -{ +) -> Simd { // SAFETY: Same obligation to use this function as to use mem::transmute_copy. unsafe { mem::transmute_copy(&f(mem::transmute_copy(&a), mem::transmute_copy(&b))) } } @@ -196,11 +190,8 @@ where #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[allow(unused)] #[inline(always)] -fn zeroing_idxs(idxs: Simd) -> Simd -where - LaneCount: SupportedLaneCount, -{ - use crate::simd::cmp::SimdPartialOrd; +fn zeroing_idxs(idxs: Simd) -> Simd { + use crate::simd::{Select, cmp::SimdPartialOrd}; idxs.simd_lt(Simd::splat(N as u8)) .select(idxs, Simd::splat(u8::MAX)) } diff --git a/library/portable-simd/crates/core_simd/src/to_bytes.rs b/library/portable-simd/crates/core_simd/src/to_bytes.rs index fee2cc06c5b09..1fd285e457db8 100644 --- a/library/portable-simd/crates/core_simd/src/to_bytes.rs +++ b/library/portable-simd/crates/core_simd/src/to_bytes.rs @@ -1,12 +1,12 @@ use crate::simd::{ - LaneCount, Simd, SimdElement, SupportedLaneCount, + Simd, SimdElement, num::{SimdFloat, SimdInt, SimdUint}, }; mod sealed { use super::*; pub trait Sealed {} - impl Sealed for Simd where LaneCount: SupportedLaneCount {} + impl Sealed for Simd {} } use sealed::Sealed; diff --git a/library/portable-simd/crates/core_simd/src/vector.rs b/library/portable-simd/crates/core_simd/src/vector.rs index 3503f4f145161..e75327f175453 100644 --- a/library/portable-simd/crates/core_simd/src/vector.rs +++ b/library/portable-simd/crates/core_simd/src/vector.rs @@ -1,5 +1,7 @@ +use core::intrinsics::simd::SimdAlign; + use crate::simd::{ - LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, + Mask, MaskElement, cmp::SimdPartialOrd, num::SimdUint, ptr::{SimdConstPtr, SimdMutPtr}, @@ -54,6 +56,8 @@ use crate::kani; /// Thus it is sound to [`transmute`] `Simd` to `[T; N]` and should optimize to "zero cost", /// but the reverse transmutation may require a copy the compiler cannot simply elide. /// +/// `N` cannot be 0 and may be at most 64. This limit may be increased in the future. +/// /// # ABI "Features" /// Due to Rust's safety guarantees, `Simd` is currently passed and returned via memory, /// not SIMD registers, except as an optimization. Using `#[inline]` on functions that accept @@ -103,15 +107,14 @@ use crate::kani; // avoided, as it will likely become illegal on `#[repr(simd)]` structs in the future. It also // causes rustc to emit illegal LLVM IR in some cases. #[repr(simd, packed)] +#[rustc_simd_monomorphize_lane_limit = "64"] #[cfg_attr(kani, derive(kani::Arbitrary))] pub struct Simd([T; N]) where - LaneCount: SupportedLaneCount, T: SimdElement; impl Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { /// Number of elements in this vector. @@ -150,30 +153,8 @@ where #[inline] #[rustc_const_unstable(feature = "portable_simd", issue = "86656")] pub const fn splat(value: T) -> Self { - const fn splat_const(value: T) -> Simd - where - T: SimdElement, - LaneCount: SupportedLaneCount, - { - Simd::from_array([value; N]) - } - - fn splat_rt(value: T) -> Simd - where - T: SimdElement, - LaneCount: SupportedLaneCount, - { - // This is preferred over `[value; N]`, since it's explicitly a splat: - // https://github.com/rust-lang/rust/issues/97804 - struct Splat; - impl Swizzle for Splat { - const INDEX: [usize; N] = [0; N]; - } - - Splat::swizzle::(Simd::::from([value])) - } - - core::intrinsics::const_eval_select((value,), splat_const, splat_rt) + // SAFETY: T is a SimdElement, and the item type of Self. + unsafe { core::intrinsics::simd::simd_splat(value) } } /// Returns an array reference containing the entire SIMD vector. @@ -199,7 +180,7 @@ where /// Returns a mutable array reference containing the entire SIMD vector. #[inline] - pub fn as_mut_array(&mut self) -> &mut [T; N] { + pub const fn as_mut_array(&mut self) -> &mut [T; N] { // SAFETY: `Simd` is just an overaligned `[T; N]` with // potential padding at the end, so pointer casting to a // `&mut [T; N]` is safe. @@ -328,7 +309,7 @@ where /// ``` #[inline] #[track_caller] - pub fn copy_to_slice(self, slice: &mut [T]) { + pub const fn copy_to_slice(self, slice: &mut [T]) { assert!( slice.len() >= Self::LEN, "slice length must be at least the number of elements" @@ -469,7 +450,7 @@ where /// value from `or` is passed through. /// /// # Safety - /// Enabled `ptr` elements must be safe to read as if by `std::ptr::read`. + /// Enabled `ptr` elements must be safe to read as if by `core::ptr::read`. #[must_use] #[inline] pub unsafe fn load_select_ptr( @@ -479,12 +460,11 @@ where ) -> Self { // SAFETY: The safety of reading elements through `ptr` is ensured by the caller. unsafe { - core::intrinsics::simd::simd_masked_load::< - _, - _, - _, - { core::intrinsics::simd::SimdAlign::Element }, - >(enable.to_int(), ptr, or) + core::intrinsics::simd::simd_masked_load::<_, _, _, { SimdAlign::Element }>( + enable.to_simd(), + ptr, + or, + ) } } @@ -663,7 +643,7 @@ where or: Self, ) -> Self { // Safety: The caller is responsible for upholding all invariants - unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_int()) } + unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_simd()) } } /// Conditionally write contiguous elements to `slice`. The `enable` mask controls @@ -735,12 +715,11 @@ where pub unsafe fn store_select_ptr(self, ptr: *mut T, enable: Mask<::Mask, N>) { // SAFETY: The safety of writing elements through `ptr` is ensured by the caller. unsafe { - core::intrinsics::simd::simd_masked_store::< - _, - _, - _, - { core::intrinsics::simd::SimdAlign::Element }, - >(enable.to_int(), ptr, self) + core::intrinsics::simd::simd_masked_store::<_, _, _, { SimdAlign::Element }>( + enable.to_simd(), + ptr, + self, + ) } } @@ -900,20 +879,14 @@ where #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn scatter_select_ptr(self, dest: Simd<*mut T, N>, enable: Mask) { // Safety: The caller is responsible for upholding all invariants - unsafe { core::intrinsics::simd::simd_scatter(self, dest, enable.to_int()) } + unsafe { core::intrinsics::simd::simd_scatter(self, dest, enable.to_simd()) } } } -impl Copy for Simd -where - LaneCount: SupportedLaneCount, - T: SimdElement, -{ -} +impl Copy for Simd where T: SimdElement {} impl Clone for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -924,7 +897,6 @@ where impl Default for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + Default, { #[inline] @@ -935,7 +907,6 @@ where impl PartialEq for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + PartialEq, { #[inline] @@ -944,7 +915,7 @@ where let mask = unsafe { let tfvec: Simd<::Mask, N> = core::intrinsics::simd::simd_eq(*self, *other); - Mask::from_int_unchecked(tfvec) + Mask::from_simd_unchecked(tfvec) }; // Two vectors are equal if all elements are equal when compared elementwise @@ -958,7 +929,7 @@ where let mask = unsafe { let tfvec: Simd<::Mask, N> = core::intrinsics::simd::simd_ne(*self, *other); - Mask::from_int_unchecked(tfvec) + Mask::from_simd_unchecked(tfvec) }; // Two vectors are non-equal if any elements are non-equal when compared elementwise @@ -969,7 +940,6 @@ where /// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead. impl PartialOrd for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + PartialOrd, { #[inline] @@ -979,17 +949,11 @@ where } } -impl Eq for Simd -where - LaneCount: SupportedLaneCount, - T: SimdElement + Eq, -{ -} +impl Eq for Simd where T: SimdElement + Eq {} /// Lexicographic order. For the SIMD elementwise minimum and maximum, use simd_min and simd_max instead. impl Ord for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + Ord, { #[inline] @@ -1001,7 +965,6 @@ where impl core::hash::Hash for Simd where - LaneCount: SupportedLaneCount, T: SimdElement + core::hash::Hash, { #[inline] @@ -1016,7 +979,6 @@ where // array references impl AsRef<[T; N]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1027,7 +989,6 @@ where impl AsMut<[T; N]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1039,7 +1000,6 @@ where // slice references impl AsRef<[T]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1050,7 +1010,6 @@ where impl AsMut<[T]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1062,7 +1021,6 @@ where // vector/array conversion impl From<[T; N]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1073,7 +1031,6 @@ where impl From> for [T; N] where - LaneCount: SupportedLaneCount, T: SimdElement, { #[inline] @@ -1084,7 +1041,6 @@ where impl TryFrom<&[T]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { type Error = core::array::TryFromSliceError; @@ -1097,7 +1053,6 @@ where impl TryFrom<&mut [T]> for Simd where - LaneCount: SupportedLaneCount, T: SimdElement, { type Error = core::array::TryFromSliceError; @@ -1235,10 +1190,7 @@ where } #[inline] -fn lane_indices() -> Simd -where - LaneCount: SupportedLaneCount, -{ +fn lane_indices() -> Simd { #![allow(clippy::needless_range_loop)] let mut index = [0; N]; for i in 0..N { @@ -1250,7 +1202,6 @@ where #[inline] fn mask_up_to(len: usize) -> Mask where - LaneCount: SupportedLaneCount, M: MaskElement, { let index = lane_indices::(); diff --git a/library/portable-simd/crates/core_simd/src/vendor/loongarch64.rs b/library/portable-simd/crates/core_simd/src/vendor/loongarch64.rs index 1290bc166b2b8..1f84cdb971ecb 100644 --- a/library/portable-simd/crates/core_simd/src/vendor/loongarch64.rs +++ b/library/portable-simd/crates/core_simd/src/vendor/loongarch64.rs @@ -1,31 +1,26 @@ use crate::simd::*; use core::arch::loongarch64::*; -from_transmute! { unsafe u8x16 => v16u8 } -from_transmute! { unsafe u8x32 => v32u8 } -from_transmute! { unsafe i8x16 => v16i8 } -from_transmute! { unsafe i8x32 => v32i8 } +from_transmute! { unsafe u8x16 => m128i } +from_transmute! { unsafe u8x32 => m256i } +from_transmute! { unsafe i8x16 => m128i } +from_transmute! { unsafe i8x32 => m256i } -from_transmute! { unsafe u16x8 => v8u16 } -from_transmute! { unsafe u16x16 => v16u16 } -from_transmute! { unsafe i16x8 => v8i16 } -from_transmute! { unsafe i16x16 => v16i16 } +from_transmute! { unsafe u16x8 => m128i } +from_transmute! { unsafe u16x16 => m256i } +from_transmute! { unsafe i16x8 => m128i } +from_transmute! { unsafe i16x16 => m256i } -from_transmute! { unsafe u32x4 => v4u32 } -from_transmute! { unsafe u32x8 => v8u32 } -from_transmute! { unsafe i32x4 => v4i32 } -from_transmute! { unsafe i32x8 => v8i32 } -from_transmute! { unsafe f32x4 => v4f32 } -from_transmute! { unsafe f32x8 => v8f32 } +from_transmute! { unsafe u32x4 => m128i } +from_transmute! { unsafe u32x8 => m256i } +from_transmute! { unsafe i32x4 => m128i } +from_transmute! { unsafe i32x8 => m256i } +from_transmute! { unsafe f32x4 => m128 } +from_transmute! { unsafe f32x8 => m256 } -from_transmute! { unsafe u64x2 => v2u64 } -from_transmute! { unsafe u64x4 => v4u64 } -from_transmute! { unsafe i64x2 => v2i64 } -from_transmute! { unsafe i64x4 => v4i64 } -from_transmute! { unsafe f64x2 => v2f64 } -from_transmute! { unsafe f64x4 => v4f64 } - -from_transmute! { unsafe usizex2 => v2u64 } -from_transmute! { unsafe usizex4 => v4u64 } -from_transmute! { unsafe isizex2 => v2i64 } -from_transmute! { unsafe isizex4 => v4i64 } +from_transmute! { unsafe u64x2 => m128i } +from_transmute! { unsafe u64x4 => m256i } +from_transmute! { unsafe i64x2 => m128i } +from_transmute! { unsafe i64x4 => m256i } +from_transmute! { unsafe f64x2 => m128d } +from_transmute! { unsafe f64x4 => m256d } diff --git a/library/portable-simd/crates/core_simd/src/vendor/wasm32.rs b/library/portable-simd/crates/core_simd/src/vendor/wasm32.rs index ef3baf885b0fb..1fdb2bc86d346 100644 --- a/library/portable-simd/crates/core_simd/src/vendor/wasm32.rs +++ b/library/portable-simd/crates/core_simd/src/vendor/wasm32.rs @@ -14,17 +14,3 @@ from_transmute! { unsafe f32x4 => v128 } from_transmute! { unsafe u64x2 => v128 } from_transmute! { unsafe i64x2 => v128 } from_transmute! { unsafe f64x2 => v128 } - -#[cfg(target_pointer_width = "32")] -mod p32 { - use super::*; - from_transmute! { unsafe usizex4 => v128 } - from_transmute! { unsafe isizex4 => v128 } -} - -#[cfg(target_pointer_width = "64")] -mod p64 { - use super::*; - from_transmute! { unsafe usizex2 => v128 } - from_transmute! { unsafe isizex2 => v128 } -} diff --git a/library/portable-simd/crates/core_simd/src/vendor/x86.rs b/library/portable-simd/crates/core_simd/src/vendor/x86.rs index 66aaf90eef597..eae42e6fd0d0a 100644 --- a/library/portable-simd/crates/core_simd/src/vendor/x86.rs +++ b/library/portable-simd/crates/core_simd/src/vendor/x86.rs @@ -39,25 +39,3 @@ from_transmute! { unsafe i64x8 => __m512i } from_transmute! { unsafe f64x2 => __m128d } from_transmute! { unsafe f64x4 => __m256d } from_transmute! { unsafe f64x8 => __m512d } - -#[cfg(target_pointer_width = "32")] -mod p32 { - use super::*; - from_transmute! { unsafe usizex4 => __m128i } - from_transmute! { unsafe usizex8 => __m256i } - from_transmute! { unsafe Simd => __m512i } - from_transmute! { unsafe isizex4 => __m128i } - from_transmute! { unsafe isizex8 => __m256i } - from_transmute! { unsafe Simd => __m512i } -} - -#[cfg(target_pointer_width = "64")] -mod p64 { - use super::*; - from_transmute! { unsafe usizex2 => __m128i } - from_transmute! { unsafe usizex4 => __m256i } - from_transmute! { unsafe usizex8 => __m512i } - from_transmute! { unsafe isizex2 => __m128i } - from_transmute! { unsafe isizex4 => __m256i } - from_transmute! { unsafe isizex8 => __m512i } -} diff --git a/library/portable-simd/crates/core_simd/tests/masks.rs b/library/portable-simd/crates/core_simd/tests/masks.rs index 48786d02440b3..53fb2367b6055 100644 --- a/library/portable-simd/crates/core_simd/tests/masks.rs +++ b/library/portable-simd/crates/core_simd/tests/masks.rs @@ -65,9 +65,9 @@ macro_rules! test_mask_api { fn roundtrip_int_conversion() { let values = [true, false, false, true, false, false, true, false]; let mask = Mask::<$type, 8>::from_array(values); - let int = mask.to_int(); + let int = mask.to_simd(); assert_eq!(int.to_array(), [-1, 0, 0, -1, 0, 0, -1, 0]); - assert_eq!(Mask::<$type, 8>::from_int(int), mask); + assert_eq!(Mask::<$type, 8>::from_simd(int), mask); } #[test] diff --git a/library/portable-simd/crates/std_float/src/lib.rs b/library/portable-simd/crates/std_float/src/lib.rs index 148aa5f9f1771..b269efc9b1d76 100644 --- a/library/portable-simd/crates/std_float/src/lib.rs +++ b/library/portable-simd/crates/std_float/src/lib.rs @@ -11,7 +11,7 @@ use core_simd::simd; use core::intrinsics::simd as intrinsics; -use simd::{LaneCount, Simd, SupportedLaneCount}; +use simd::Simd; #[cfg(feature = "as_crate")] mod experimental { @@ -66,28 +66,43 @@ pub trait StdFloat: Sealed + Sized { /// Produces a vector where every element has the sine of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn sin(self) -> Self; + fn sin(self) -> Self { + unsafe { intrinsics::simd_fsin(self) } + } /// Produces a vector where every element has the cosine of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn cos(self) -> Self; + fn cos(self) -> Self { + unsafe { intrinsics::simd_fcos(self) } + } /// Produces a vector where every element has the exponential (base e) of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn exp(self) -> Self; + fn exp(self) -> Self { + unsafe { intrinsics::simd_fexp(self) } + } /// Produces a vector where every element has the exponential (base 2) of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn exp2(self) -> Self; + fn exp2(self) -> Self { + unsafe { intrinsics::simd_fexp2(self) } + } /// Produces a vector where every element has the natural logarithm of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn ln(self) -> Self; + fn ln(self) -> Self { + unsafe { intrinsics::simd_flog(self) } + } /// Produces a vector where every element has the logarithm with respect to an arbitrary /// in the equivalently-indexed elements in `self` and `base`. @@ -99,13 +114,19 @@ pub trait StdFloat: Sealed + Sized { /// Produces a vector where every element has the base-2 logarithm of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn log2(self) -> Self; + fn log2(self) -> Self { + unsafe { intrinsics::simd_flog2(self) } + } /// Produces a vector where every element has the base-10 logarithm of the value /// in the equivalently-indexed element in `self`. + #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn log10(self) -> Self; + fn log10(self) -> Self { + unsafe { intrinsics::simd_flog10(self) } + } /// Returns the smallest integer greater than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] @@ -140,68 +161,19 @@ pub trait StdFloat: Sealed + Sized { fn fract(self) -> Self; } -impl Sealed for Simd where LaneCount: SupportedLaneCount {} -impl Sealed for Simd where LaneCount: SupportedLaneCount {} - -macro_rules! impl_float { - { - $($fn:ident: $intrinsic:ident,)* - } => { - impl StdFloat for Simd - where - LaneCount: SupportedLaneCount, - { - #[inline] - fn fract(self) -> Self { - self - self.trunc() - } - - $( - #[inline] - fn $fn(self) -> Self { - unsafe { intrinsics::$intrinsic(self) } - } - )* - } - - impl StdFloat for Simd - where - LaneCount: SupportedLaneCount, - { - #[inline] - fn fract(self) -> Self { - self - self.trunc() - } - - $( - #[inline] - fn $fn(self) -> Self { - // https://github.com/llvm/llvm-project/issues/83729 - #[cfg(target_arch = "aarch64")] - { - let mut ln = Self::splat(0f64); - for i in 0..N { - ln[i] = self[i].$fn() - } - ln - } - - #[cfg(not(target_arch = "aarch64"))] - { - unsafe { intrinsics::$intrinsic(self) } - } - } - )* - } +impl Sealed for Simd {} +impl Sealed for Simd {} + +impl StdFloat for Simd { + #[inline] + fn fract(self) -> Self { + self - self.trunc() } } -impl_float! { - sin: simd_fsin, - cos: simd_fcos, - exp: simd_fexp, - exp2: simd_fexp2, - ln: simd_flog, - log2: simd_flog2, - log10: simd_flog10, +impl StdFloat for Simd { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } } diff --git a/library/portable-simd/crates/std_float/tests/float.rs b/library/portable-simd/crates/std_float/tests/float.rs index c66c968f8c667..c608ba49564e0 100644 --- a/library/portable-simd/crates/std_float/tests/float.rs +++ b/library/portable-simd/crates/std_float/tests/float.rs @@ -16,15 +16,33 @@ macro_rules! unary_test { } } -macro_rules! binary_test { +macro_rules! unary_approx_test { { $scalar:tt, $($func:tt),+ } => { test_helpers::test_lanes! { $( fn $func() { - test_helpers::test_binary_elementwise( + test_helpers::test_unary_elementwise_approx( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_| true, + 8, + ) + } + )* + } + } +} + +macro_rules! binary_approx_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_binary_elementwise_approx( &core_simd::simd::Simd::<$scalar, LANES>::$func, &$scalar::$func, &|_, _| true, + 16, ) } )* @@ -53,10 +71,13 @@ macro_rules! impl_tests { mod $scalar { use std_float::StdFloat; - unary_test! { $scalar, sqrt, sin, cos, exp, exp2, ln, log2, log10, ceil, floor, round, trunc } - binary_test! { $scalar, log } + unary_test! { $scalar, sqrt, ceil, floor, round, trunc } ternary_test! { $scalar, mul_add } + // https://github.com/rust-lang/miri/issues/3555 + unary_approx_test! { $scalar, sin, cos, exp, exp2, ln, log2, log10 } + binary_approx_test! { $scalar, log } + test_helpers::test_lanes! { fn fract() { test_helpers::test_unary_elementwise_flush_subnormals( diff --git a/library/portable-simd/crates/test_helpers/Cargo.toml b/library/portable-simd/crates/test_helpers/Cargo.toml index a5359b9abc84d..408bb04c7aa40 100644 --- a/library/portable-simd/crates/test_helpers/Cargo.toml +++ b/library/portable-simd/crates/test_helpers/Cargo.toml @@ -6,3 +6,4 @@ publish = false [dependencies] proptest = { version = "0.10", default-features = false, features = ["alloc"] } +float-cmp = "0.10" diff --git a/library/portable-simd/crates/test_helpers/src/approxeq.rs b/library/portable-simd/crates/test_helpers/src/approxeq.rs new file mode 100644 index 0000000000000..57b43a16bc6fe --- /dev/null +++ b/library/portable-simd/crates/test_helpers/src/approxeq.rs @@ -0,0 +1,110 @@ +//! Compare numeric types approximately. + +use float_cmp::Ulps; + +pub trait ApproxEq { + fn approxeq(&self, other: &Self, _ulps: i64) -> bool; + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result; +} + +impl ApproxEq for bool { + fn approxeq(&self, other: &Self, _ulps: i64) -> bool { + self == other + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?}", self) + } +} + +macro_rules! impl_integer_approxeq { + { $($type:ty),* } => { + $( + impl ApproxEq for $type { + fn approxeq(&self, other: &Self, _ulps: i64) -> bool { + self == other + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?} ({:x})", self, self) + } + } + )* + }; +} + +impl_integer_approxeq! { u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize } + +macro_rules! impl_float_approxeq { + { $($type:ty),* } => { + $( + impl ApproxEq for $type { + fn approxeq(&self, other: &Self, ulps: i64) -> bool { + if self.is_nan() && other.is_nan() { + true + } else { + (self.ulps(other) as i64).abs() <= ulps + } + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?} ({:x})", self, self.to_bits()) + } + } + )* + }; +} + +impl_float_approxeq! { f32, f64 } + +impl ApproxEq for [T; N] { + fn approxeq(&self, other: &Self, ulps: i64) -> bool { + self.iter() + .zip(other.iter()) + .fold(true, |value, (left, right)| { + value && left.approxeq(right, ulps) + }) + } + + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + #[repr(transparent)] + struct Wrapper<'a, T: ApproxEq>(&'a T); + + impl core::fmt::Debug for Wrapper<'_, T> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(f) + } + } + + f.debug_list() + .entries(self.iter().map(|x| Wrapper(x))) + .finish() + } +} + +#[doc(hidden)] +pub struct ApproxEqWrapper<'a, T>(pub &'a T, pub i64); + +impl PartialEq for ApproxEqWrapper<'_, T> { + fn eq(&self, other: &T) -> bool { + self.0.approxeq(other, self.1) + } +} + +impl core::fmt::Debug for ApproxEqWrapper<'_, T> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(f) + } +} + +#[macro_export] +macro_rules! prop_assert_approxeq { + { $a:expr, $b:expr, $ulps:expr $(,)? } => { + { + use $crate::approxeq::ApproxEqWrapper; + let a = $a; + let b = $b; + proptest::prop_assert_eq!(ApproxEqWrapper(&a, $ulps), b); + } + }; +} diff --git a/library/portable-simd/crates/test_helpers/src/lib.rs b/library/portable-simd/crates/test_helpers/src/lib.rs index 197c920e11eac..eb3d3f68bc2ea 100644 --- a/library/portable-simd/crates/test_helpers/src/lib.rs +++ b/library/portable-simd/crates/test_helpers/src/lib.rs @@ -12,6 +12,9 @@ pub mod wasm; #[macro_use] pub mod biteq; +#[macro_use] +pub mod approxeq; + pub mod subnormals; use subnormals::FlushSubnormals; @@ -185,6 +188,41 @@ pub fn test_unary_elementwise( + fv: &dyn Fn(Vector) -> VectorResult, + fs: &dyn Fn(Scalar) -> ScalarResult, + check: &dyn Fn([Scalar; LANES]) -> bool, + ulps: i64, +) where + Scalar: Copy + core::fmt::Debug + DefaultStrategy, + ScalarResult: Copy + approxeq::ApproxEq + core::fmt::Debug + DefaultStrategy, + Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy, + VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, +{ + test_1(&|x: [Scalar; LANES]| { + proptest::prop_assume!(check(x)); + let result_1: [ScalarResult; LANES] = fv(x.into()).into(); + let result_2: [ScalarResult; LANES] = x + .iter() + .copied() + .map(fs) + .collect::>() + .try_into() + .unwrap(); + crate::prop_assert_approxeq!(result_1, result_2, ulps); + Ok(()) + }); +} + /// Test a unary vector function against a unary scalar function, applied elementwise. /// /// Where subnormals are flushed, use approximate equality. @@ -290,6 +328,44 @@ pub fn test_binary_elementwise< }); } +/// Test a binary vector function against a binary scalar function, applied elementwise. +pub fn test_binary_elementwise_approx< + Scalar1, + Scalar2, + ScalarResult, + Vector1, + Vector2, + VectorResult, + const LANES: usize, +>( + fv: &dyn Fn(Vector1, Vector2) -> VectorResult, + fs: &dyn Fn(Scalar1, Scalar2) -> ScalarResult, + check: &dyn Fn([Scalar1; LANES], [Scalar2; LANES]) -> bool, + ulps: i64, +) where + Scalar1: Copy + core::fmt::Debug + DefaultStrategy, + Scalar2: Copy + core::fmt::Debug + DefaultStrategy, + ScalarResult: Copy + approxeq::ApproxEq + core::fmt::Debug + DefaultStrategy, + Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy, + Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy, + VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy, +{ + test_2(&|x: [Scalar1; LANES], y: [Scalar2; LANES]| { + proptest::prop_assume!(check(x, y)); + let result_1: [ScalarResult; LANES] = fv(x.into(), y.into()).into(); + let result_2: [ScalarResult; LANES] = x + .iter() + .copied() + .zip(y.iter().copied()) + .map(|(x, y)| fs(x, y)) + .collect::>() + .try_into() + .unwrap(); + crate::prop_assert_approxeq!(result_1, result_2, ulps); + Ok(()) + }); +} + /// Test a binary vector function against a binary scalar function, applied elementwise. /// /// Where subnormals are flushed, use approximate equality. @@ -528,8 +604,6 @@ macro_rules! test_lanes { use super::*; fn implementation() - where - core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount, $body #[cfg(target_arch = "wasm32")] @@ -628,8 +702,6 @@ macro_rules! test_lanes_panic { use super::*; fn implementation() - where - core_simd::simd::LaneCount<$lanes>: core_simd::simd::SupportedLaneCount, $body // test some odd and even non-power-of-2 lengths on miri diff --git a/library/portable-simd/rust-toolchain.toml b/library/portable-simd/rust-toolchain.toml index d17c6d2e88946..639d07df73374 100644 --- a/library/portable-simd/rust-toolchain.toml +++ b/library/portable-simd/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-01-16" +channel = "nightly-2026-01-26" components = ["rustfmt", "clippy", "miri", "rust-src"] diff --git a/library/proc_macro/Cargo.toml b/library/proc_macro/Cargo.toml index 0042a6e8ece58..1e5046ca61c39 100644 --- a/library/proc_macro/Cargo.toml +++ b/library/proc_macro/Cargo.toml @@ -9,8 +9,11 @@ std = { path = "../std" } # `core` when resolving doc links. Without this line a different `core` will be # loaded from sysroot causing duplicate lang items and other similar errors. core = { path = "../core" } -rustc-literal-escaper = { version = "0.0.5", features = ["rustc-dep-of-std"] } +rustc-literal-escaper = { version = "0.0.7", features = ["rustc-dep-of-std"] } [features] default = ["rustc-dep-of-std"] rustc-dep-of-std = [] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bootstrap)'] } diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index bdaa865a998d6..8f4a79b389f62 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -6,103 +6,70 @@ use std::sync::atomic::AtomicU32; use super::*; -macro_rules! define_client_handles { - ( - 'owned: $($oty:ident,)* - 'interned: $($ity:ident,)* - ) => { - #[repr(C)] - #[allow(non_snake_case)] - pub(super) struct HandleCounters { - $(pub(super) $oty: AtomicU32,)* - $(pub(super) $ity: AtomicU32,)* - } - - static COUNTERS: HandleCounters = HandleCounters { - $($oty: AtomicU32::new(1),)* - $($ity: AtomicU32::new(1),)* - }; - - $( - pub(crate) struct $oty { - handle: handle::Handle, - } +#[repr(C)] +pub(super) struct HandleCounters { + pub(super) token_stream: AtomicU32, + pub(super) span: AtomicU32, +} - impl !Send for $oty {} - impl !Sync for $oty {} +static COUNTERS: HandleCounters = + HandleCounters { token_stream: AtomicU32::new(1), span: AtomicU32::new(1) }; - // Forward `Drop::drop` to the inherent `drop` method. - impl Drop for $oty { - fn drop(&mut self) { - $oty { - handle: self.handle, - }.drop(); - } - } +pub(crate) struct TokenStream { + handle: handle::Handle, +} - impl Encode for $oty { - fn encode(self, w: &mut Writer, s: &mut S) { - mem::ManuallyDrop::new(self).handle.encode(w, s); - } - } +impl !Send for TokenStream {} +impl !Sync for TokenStream {} - impl Encode for &$oty { - fn encode(self, w: &mut Writer, s: &mut S) { - self.handle.encode(w, s); - } - } +// Forward `Drop::drop` to the inherent `drop` method. +impl Drop for TokenStream { + fn drop(&mut self) { + Methods::ts_drop(TokenStream { handle: self.handle }); + } +} - impl Encode for &mut $oty { - fn encode(self, w: &mut Writer, s: &mut S) { - self.handle.encode(w, s); - } - } +impl Encode for TokenStream { + fn encode(self, w: &mut Buffer, s: &mut S) { + mem::ManuallyDrop::new(self).handle.encode(w, s); + } +} - impl Decode<'_, '_, S> for $oty { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - $oty { - handle: handle::Handle::decode(r, s), - } - } - } - )* +impl Encode for &TokenStream { + fn encode(self, w: &mut Buffer, s: &mut S) { + self.handle.encode(w, s); + } +} - $( - #[derive(Copy, Clone, PartialEq, Eq, Hash)] - pub(crate) struct $ity { - handle: handle::Handle, - } +impl Decode<'_, '_, S> for TokenStream { + fn decode(r: &mut &[u8], s: &mut S) -> Self { + TokenStream { handle: handle::Handle::decode(r, s) } + } +} - impl !Send for $ity {} - impl !Sync for $ity {} +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) struct Span { + handle: handle::Handle, +} - impl Encode for $ity { - fn encode(self, w: &mut Writer, s: &mut S) { - self.handle.encode(w, s); - } - } +impl !Send for Span {} +impl !Sync for Span {} - impl Decode<'_, '_, S> for $ity { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - $ity { - handle: handle::Handle::decode(r, s), - } - } - } - )* +impl Encode for Span { + fn encode(self, w: &mut Buffer, s: &mut S) { + self.handle.encode(w, s); } } -with_api_handle_types!(define_client_handles); -// FIXME(eddyb) generate these impls by pattern-matching on the -// names of methods - also could use the presence of `fn drop` -// to distinguish between 'owned and 'interned, above. -// Alternatively, special "modes" could be listed of types in with_api -// instead of pattern matching on methods, here and in server decl. +impl Decode<'_, '_, S> for Span { + fn decode(r: &mut &[u8], s: &mut S) -> Self { + Span { handle: handle::Handle::decode(r, s) } + } +} impl Clone for TokenStream { fn clone(&self) -> Self { - self.clone() + Methods::ts_clone(self) } } @@ -122,23 +89,24 @@ impl Span { impl fmt::Debug for Span { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.debug()) + f.write_str(&Methods::span_debug(*self)) } } +pub(crate) use super::Methods; pub(crate) use super::symbol::Symbol; macro_rules! define_client_side { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* - }),* $(,)?) => { - $(impl $name { + ( + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + ) => { + impl Methods { $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)? { Bridge::with(|bridge| { let mut buf = bridge.cached_buffer.take(); buf.clear(); - api_tags::Method::$name(api_tags::$name::$method).encode(&mut buf, &mut ()); + ApiTags::$method.encode(&mut buf, &mut ()); $($arg.encode(&mut buf, &mut ());)* buf = bridge.dispatch.call(buf); @@ -150,10 +118,10 @@ macro_rules! define_client_side { r.unwrap_or_else(|e| panic::resume_unwind(e.into())) }) })* - })* + } } } -with_api!(self, self, define_client_side); +with_api!(self, define_client_side); struct Bridge<'a> { /// Reusable buffer (only `clear`-ed, never shrunk), primarily diff --git a/library/proc_macro/src/bridge/handle.rs b/library/proc_macro/src/bridge/handle.rs index 8c53bb609f60c..f0c01e39de32d 100644 --- a/library/proc_macro/src/bridge/handle.rs +++ b/library/proc_macro/src/bridge/handle.rs @@ -3,7 +3,7 @@ use std::collections::BTreeMap; use std::hash::Hash; use std::num::NonZero; -use std::ops::{Index, IndexMut}; +use std::ops::Index; use std::sync::atomic::{AtomicU32, Ordering}; use super::fxhash::FxHashMap; @@ -47,12 +47,6 @@ impl Index for OwnedStore { } } -impl IndexMut for OwnedStore { - fn index_mut(&mut self, h: Handle) -> &mut T { - self.data.get_mut(&h).expect("use-after-free in `proc_macro` handle") - } -} - /// Like `OwnedStore`, but avoids storing any value more than once. pub(super) struct InternedStore { owned: OwnedStore, diff --git a/library/proc_macro/src/bridge/mod.rs b/library/proc_macro/src/bridge/mod.rs index b0ee9c0cc3027..244ab7d81b022 100644 --- a/library/proc_macro/src/bridge/mod.rs +++ b/library/proc_macro/src/bridge/mod.rs @@ -13,111 +13,81 @@ use std::ops::{Bound, Range}; use std::sync::Once; use std::{fmt, marker, mem, panic, thread}; -use crate::{Delimiter, Level, Spacing}; +use crate::{Delimiter, Level}; /// Higher-order macro describing the server RPC API, allowing automatic /// generation of type-safe Rust APIs, both client-side and server-side. /// -/// `with_api!(MySelf, my_self, my_macro)` expands to: +/// `with_api!(MySelf, my_macro)` expands to: /// ```rust,ignore (pseudo-code) /// my_macro! { -/// // ... -/// Literal { -/// // ... -/// fn character(ch: char) -> MySelf::Literal; -/// // ... -/// fn span(my_self: &MySelf::Literal) -> MySelf::Span; -/// fn set_span(my_self: &mut MySelf::Literal, span: MySelf::Span); -/// }, +/// fn lit_character(ch: char) -> MySelf::Literal; +/// fn lit_span(lit: &MySelf::Literal) -> MySelf::Span; +/// fn lit_set_span(lit: &mut MySelf::Literal, span: MySelf::Span); /// // ... /// } /// ``` /// -/// The first two arguments serve to customize the arguments names -/// and argument/return types, to enable several different usecases: -/// -/// If `my_self` is just `self`, then each `fn` signature can be used -/// as-is for a method. If it's anything else (`self_` in practice), -/// then the signatures don't have a special `self` argument, and -/// can, therefore, have a different one introduced. +/// The first argument serves to customize the argument/return types, +/// to enable several different usecases: /// /// If `MySelf` is just `Self`, then the types are only valid inside /// a trait or a trait impl, where the trait has associated types /// for each of the API types. If non-associated types are desired, /// a module name (`self` in practice) can be used instead of `Self`. macro_rules! with_api { - ($S:ident, $self:ident, $m:ident) => { + ($S:ident, $m:ident) => { $m! { - FreeFunctions { - fn drop($self: $S::FreeFunctions); - fn injected_env_var(var: &str) -> Option; - fn track_env_var(var: &str, value: Option<&str>); - fn track_path(path: &str); - fn literal_from_str(s: &str) -> Result, ()>; - fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>); - }, - TokenStream { - fn drop($self: $S::TokenStream); - fn clone($self: &$S::TokenStream) -> $S::TokenStream; - fn is_empty($self: &$S::TokenStream) -> bool; - fn expand_expr($self: &$S::TokenStream) -> Result<$S::TokenStream, ()>; - fn from_str(src: &str) -> $S::TokenStream; - fn to_string($self: &$S::TokenStream) -> String; - fn from_token_tree( - tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>, - ) -> $S::TokenStream; - fn concat_trees( - base: Option<$S::TokenStream>, - trees: Vec>, - ) -> $S::TokenStream; - fn concat_streams( - base: Option<$S::TokenStream>, - streams: Vec<$S::TokenStream>, - ) -> $S::TokenStream; - fn into_trees( - $self: $S::TokenStream - ) -> Vec>; - }, - Span { - fn debug($self: $S::Span) -> String; - fn parent($self: $S::Span) -> Option<$S::Span>; - fn source($self: $S::Span) -> $S::Span; - fn byte_range($self: $S::Span) -> Range; - fn start($self: $S::Span) -> $S::Span; - fn end($self: $S::Span) -> $S::Span; - fn line($self: $S::Span) -> usize; - fn column($self: $S::Span) -> usize; - fn file($self: $S::Span) -> String; - fn local_file($self: $S::Span) -> Option; - fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>; - fn subspan($self: $S::Span, start: Bound, end: Bound) -> Option<$S::Span>; - fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span; - fn source_text($self: $S::Span) -> Option; - fn save_span($self: $S::Span) -> usize; - fn recover_proc_macro_span(id: usize) -> $S::Span; - }, - Symbol { - fn normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>; - }, + fn injected_env_var(var: &str) -> Option; + fn track_env_var(var: &str, value: Option<&str>); + fn track_path(path: &str); + fn literal_from_str(s: &str) -> Result, ()>; + fn emit_diagnostic(diagnostic: Diagnostic<$S::Span>); + + fn ts_drop(stream: $S::TokenStream); + fn ts_clone(stream: &$S::TokenStream) -> $S::TokenStream; + fn ts_is_empty(stream: &$S::TokenStream) -> bool; + fn ts_expand_expr(stream: &$S::TokenStream) -> Result<$S::TokenStream, ()>; + fn ts_from_str(src: &str) -> $S::TokenStream; + fn ts_to_string(stream: &$S::TokenStream) -> String; + fn ts_from_token_tree( + tree: TokenTree<$S::TokenStream, $S::Span, $S::Symbol>, + ) -> $S::TokenStream; + fn ts_concat_trees( + base: Option<$S::TokenStream>, + trees: Vec>, + ) -> $S::TokenStream; + fn ts_concat_streams( + base: Option<$S::TokenStream>, + streams: Vec<$S::TokenStream>, + ) -> $S::TokenStream; + fn ts_into_trees( + stream: $S::TokenStream + ) -> Vec>; + + fn span_debug(span: $S::Span) -> String; + fn span_parent(span: $S::Span) -> Option<$S::Span>; + fn span_source(span: $S::Span) -> $S::Span; + fn span_byte_range(span: $S::Span) -> Range; + fn span_start(span: $S::Span) -> $S::Span; + fn span_end(span: $S::Span) -> $S::Span; + fn span_line(span: $S::Span) -> usize; + fn span_column(span: $S::Span) -> usize; + fn span_file(span: $S::Span) -> String; + fn span_local_file(span: $S::Span) -> Option; + fn span_join(span: $S::Span, other: $S::Span) -> Option<$S::Span>; + fn span_subspan(span: $S::Span, start: Bound, end: Bound) -> Option<$S::Span>; + fn span_resolved_at(span: $S::Span, at: $S::Span) -> $S::Span; + fn span_source_text(span: $S::Span) -> Option; + fn span_save_span(span: $S::Span) -> usize; + fn span_recover_proc_macro_span(id: usize) -> $S::Span; + + fn symbol_normalize_and_validate_ident(string: &str) -> Result<$S::Symbol, ()>; } }; } -// Similar to `with_api`, but only lists the types requiring handles, and they -// are divided into the two storage categories. -macro_rules! with_api_handle_types { - ($m:ident) => { - $m! { - 'owned: - FreeFunctions, - TokenStream, - - 'interned: - Span, - // Symbol is handled manually - } - }; -} +pub(crate) struct Methods; #[allow(unsafe_code)] mod arena; @@ -143,7 +113,7 @@ mod symbol; use buffer::Buffer; pub use rpc::PanicMessage; -use rpc::{Decode, Encode, Reader, Writer}; +use rpc::{Decode, Encode}; /// Configuration for establishing an active connection between a server and a /// client. The server creates the bridge config (`run_server` in `server.rs`), @@ -165,30 +135,18 @@ pub struct BridgeConfig<'a> { impl !Send for BridgeConfig<'_> {} impl !Sync for BridgeConfig<'_> {} -#[forbid(unsafe_code)] -#[allow(non_camel_case_types)] -mod api_tags { - use super::rpc::{Decode, Encode, Reader, Writer}; - - macro_rules! declare_tags { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* - }),* $(,)?) => { - $( - pub(super) enum $name { - $($method),* - } - rpc_encode_decode!(enum $name { $($method),* }); - )* - - pub(super) enum Method { - $($name($name)),* - } - rpc_encode_decode!(enum Method { $($name(m)),* }); +macro_rules! declare_tags { + ( + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + ) => { + #[allow(non_camel_case_types)] + pub(super) enum ApiTags { + $($method),* } + rpc_encode_decode!(enum ApiTags { $($method),* }); } - with_api!(self, self, declare_tags); } +with_api!(self, declare_tags); /// Helper to wrap associated types to allow trait impl dispatch. /// That is, normally a pair of impls for `T::Foo` and `T::Bar` @@ -197,11 +155,6 @@ mod api_tags { trait Mark { type Unmarked; fn mark(unmarked: Self::Unmarked) -> Self; -} - -/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details). -trait Unmark { - type Unmarked; fn unmark(self) -> Self::Unmarked; } @@ -216,23 +169,17 @@ impl Mark for Marked { fn mark(unmarked: Self::Unmarked) -> Self { Marked { value: unmarked, _marker: marker::PhantomData } } -} -impl Unmark for Marked { - type Unmarked = T; fn unmark(self) -> Self::Unmarked { self.value } } -impl<'a, T, M> Unmark for &'a Marked { +impl<'a, T, M> Mark for &'a Marked { type Unmarked = &'a T; - fn unmark(self) -> Self::Unmarked { - &self.value + fn mark(_: Self::Unmarked) -> Self { + unreachable!() } -} -impl<'a, T, M> Unmark for &'a mut Marked { - type Unmarked = &'a mut T; fn unmark(self) -> Self::Unmarked { - &mut self.value + &self.value } } @@ -242,9 +189,6 @@ impl Mark for Vec { // Should be a no-op due to std's in-place collect optimizations. unmarked.into_iter().map(T::mark).collect() } -} -impl Unmark for Vec { - type Unmarked = Vec; fn unmark(self) -> Self::Unmarked { // Should be a no-op due to std's in-place collect optimizations. self.into_iter().map(T::unmark).collect() @@ -259,9 +203,6 @@ macro_rules! mark_noop { fn mark(unmarked: Self::Unmarked) -> Self { unmarked } - } - impl Unmark for $ty { - type Unmarked = Self; fn unmark(self) -> Self::Unmarked { self } @@ -272,8 +213,6 @@ macro_rules! mark_noop { mark_noop! { (), bool, - char, - &'_ [u8], &'_ str, String, u8, @@ -281,7 +220,6 @@ mark_noop! { Delimiter, LitKind, Level, - Spacing, } rpc_encode_decode!( @@ -300,12 +238,6 @@ rpc_encode_decode!( Help, } ); -rpc_encode_decode!( - enum Spacing { - Alone, - Joint, - } -); #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum LitKind { @@ -351,13 +283,9 @@ macro_rules! mark_compound { $($field: Mark::mark(unmarked.$field)),* } } - } - - impl<$($T: Unmark),+> Unmark for $name <$($T),+> { - type Unmarked = $name <$($T::Unmarked),+>; fn unmark(self) -> Self::Unmarked { $name { - $($field: Unmark::unmark(self.$field)),* + $($field: Mark::unmark(self.$field)),* } } } @@ -372,14 +300,10 @@ macro_rules! mark_compound { })* } } - } - - impl<$($T: Unmark),+> Unmark for $name <$($T),+> { - type Unmarked = $name <$($T::Unmarked),+>; fn unmark(self) -> Self::Unmarked { match self { $($name::$variant $(($field))? => { - $name::$variant $((Unmark::unmark($field)))? + $name::$variant $((Mark::unmark($field)))? })* } } diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index ed67674a74ab7..63329c8c02601 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -4,28 +4,26 @@ use std::any::Any; use std::io::Write; use std::num::NonZero; -pub(super) type Writer = super::buffer::Buffer; +use super::buffer::Buffer; pub(super) trait Encode: Sized { - fn encode(self, w: &mut Writer, s: &mut S); + fn encode(self, w: &mut Buffer, s: &mut S); } -pub(super) type Reader<'a> = &'a [u8]; - pub(super) trait Decode<'a, 's, S>: Sized { - fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self; + fn decode(r: &mut &'a [u8], s: &'s mut S) -> Self; } macro_rules! rpc_encode_decode { (le $ty:ty) => { impl Encode for $ty { - fn encode(self, w: &mut Writer, _: &mut S) { + fn encode(self, w: &mut Buffer, _: &mut S) { w.extend_from_array(&self.to_le_bytes()); } } impl Decode<'_, '_, S> for $ty { - fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { + fn decode(r: &mut &[u8], _: &mut S) -> Self { const N: usize = size_of::<$ty>(); let mut bytes = [0; N]; @@ -38,7 +36,7 @@ macro_rules! rpc_encode_decode { }; (struct $name:ident $(<$($T:ident),+>)? { $($field:ident),* $(,)? }) => { impl),+)?> Encode for $name $(<$($T),+>)? { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { $(self.$field.encode(w, s);)* } } @@ -46,7 +44,7 @@ macro_rules! rpc_encode_decode { impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S> for $name $(<$($T),+>)? { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { + fn decode(r: &mut &'a [u8], s: &mut S) -> Self { $name { $($field: Decode::decode(r, s)),* } @@ -55,10 +53,12 @@ macro_rules! rpc_encode_decode { }; (enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => { impl),+)?> Encode for $name $(<$($T),+>)? { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { // HACK(eddyb): `Tag` enum duplicated between the // two impls as there's no other place to stash it. - #[repr(u8)] enum Tag { $($variant),* } + #[allow(non_camel_case_types)] + #[repr(u8)] + enum Tag { $($variant),* } match self { $($name::$variant $(($field))* => { @@ -72,10 +72,10 @@ macro_rules! rpc_encode_decode { impl<'a, S, $($($T: for<'s> Decode<'a, 's, S>),+)?> Decode<'a, '_, S> for $name $(<$($T),+>)? { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { + fn decode(r: &mut &'a [u8], s: &mut S) -> Self { // HACK(eddyb): `Tag` enum duplicated between the // two impls as there's no other place to stash it. - #[allow(non_upper_case_globals)] + #[allow(non_upper_case_globals, non_camel_case_types)] mod tag { #[repr(u8)] enum Tag { $($variant),* } @@ -95,21 +95,21 @@ macro_rules! rpc_encode_decode { } impl Encode for () { - fn encode(self, _: &mut Writer, _: &mut S) {} + fn encode(self, _: &mut Buffer, _: &mut S) {} } impl Decode<'_, '_, S> for () { - fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {} + fn decode(_: &mut &[u8], _: &mut S) -> Self {} } impl Encode for u8 { - fn encode(self, w: &mut Writer, _: &mut S) { + fn encode(self, w: &mut Buffer, _: &mut S) { w.push(self); } } impl Decode<'_, '_, S> for u8 { - fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { + fn decode(r: &mut &[u8], _: &mut S) -> Self { let x = r[0]; *r = &r[1..]; x @@ -120,13 +120,13 @@ rpc_encode_decode!(le u32); rpc_encode_decode!(le usize); impl Encode for bool { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { (self as u8).encode(w, s); } } impl Decode<'_, '_, S> for bool { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + fn decode(r: &mut &[u8], s: &mut S) -> Self { match u8::decode(r, s) { 0 => false, 1 => true, @@ -135,32 +135,20 @@ impl Decode<'_, '_, S> for bool { } } -impl Encode for char { - fn encode(self, w: &mut Writer, s: &mut S) { - (self as u32).encode(w, s); - } -} - -impl Decode<'_, '_, S> for char { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - char::from_u32(u32::decode(r, s)).unwrap() - } -} - impl Encode for NonZero { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { self.get().encode(w, s); } } impl Decode<'_, '_, S> for NonZero { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + fn decode(r: &mut &[u8], s: &mut S) -> Self { Self::new(u32::decode(r, s)).unwrap() } } impl, B: Encode> Encode for (A, B) { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { self.0.encode(w, s); self.1.encode(w, s); } @@ -169,53 +157,42 @@ impl, B: Encode> Encode for (A, B) { impl<'a, S, A: for<'s> Decode<'a, 's, S>, B: for<'s> Decode<'a, 's, S>> Decode<'a, '_, S> for (A, B) { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { + fn decode(r: &mut &'a [u8], s: &mut S) -> Self { (Decode::decode(r, s), Decode::decode(r, s)) } } -impl Encode for &[u8] { - fn encode(self, w: &mut Writer, s: &mut S) { - self.len().encode(w, s); - w.write_all(self).unwrap(); +impl Encode for &str { + fn encode(self, w: &mut Buffer, s: &mut S) { + let bytes = self.as_bytes(); + bytes.len().encode(w, s); + w.write_all(bytes).unwrap(); } } -impl<'a, S> Decode<'a, '_, S> for &'a [u8] { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { +impl<'a, S> Decode<'a, '_, S> for &'a str { + fn decode(r: &mut &'a [u8], s: &mut S) -> Self { let len = usize::decode(r, s); let xs = &r[..len]; *r = &r[len..]; - xs - } -} - -impl Encode for &str { - fn encode(self, w: &mut Writer, s: &mut S) { - self.as_bytes().encode(w, s); - } -} - -impl<'a, S> Decode<'a, '_, S> for &'a str { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - str::from_utf8(<&[u8]>::decode(r, s)).unwrap() + str::from_utf8(xs).unwrap() } } impl Encode for String { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { self[..].encode(w, s); } } impl Decode<'_, '_, S> for String { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + fn decode(r: &mut &[u8], s: &mut S) -> Self { <&str>::decode(r, s).to_string() } } impl> Encode for Vec { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { self.len().encode(w, s); for x in self { x.encode(w, s); @@ -224,7 +201,7 @@ impl> Encode for Vec { } impl<'a, S, T: for<'s> Decode<'a, 's, S>> Decode<'a, '_, S> for Vec { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { + fn decode(r: &mut &'a [u8], s: &mut S) -> Self { let len = usize::decode(r, s); let mut vec = Vec::with_capacity(len); for _ in 0..len { @@ -278,13 +255,13 @@ impl PanicMessage { } impl Encode for PanicMessage { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { self.as_str().encode(w, s); } } impl Decode<'_, '_, S> for PanicMessage { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + fn decode(r: &mut &[u8], s: &mut S) -> Self { match Option::::decode(r, s) { Some(s) => PanicMessage::String(s), None => PanicMessage::Unknown, diff --git a/library/proc_macro/src/bridge/server.rs b/library/proc_macro/src/bridge/server.rs index e9ef26c07f24f..073ddb554994c 100644 --- a/library/proc_macro/src/bridge/server.rs +++ b/library/proc_macro/src/bridge/server.rs @@ -5,108 +5,68 @@ use std::marker::PhantomData; use super::*; -macro_rules! define_server_handles { - ( - 'owned: $($oty:ident,)* - 'interned: $($ity:ident,)* - ) => { - #[allow(non_snake_case)] - pub(super) struct HandleStore { - $($oty: handle::OwnedStore,)* - $($ity: handle::InternedStore,)* - } +pub(super) struct HandleStore { + token_stream: handle::OwnedStore>, + span: handle::InternedStore>, +} - impl HandleStore { - fn new(handle_counters: &'static client::HandleCounters) -> Self { - HandleStore { - $($oty: handle::OwnedStore::new(&handle_counters.$oty),)* - $($ity: handle::InternedStore::new(&handle_counters.$ity),)* - } - } +impl HandleStore { + fn new(handle_counters: &'static client::HandleCounters) -> Self { + HandleStore { + token_stream: handle::OwnedStore::new(&handle_counters.token_stream), + span: handle::InternedStore::new(&handle_counters.span), } + } +} - $( - impl Encode>> for Marked { - fn encode(self, w: &mut Writer, s: &mut HandleStore>) { - s.$oty.alloc(self).encode(w, s); - } - } - - impl Decode<'_, '_, HandleStore>> - for Marked - { - fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { - s.$oty.take(handle::Handle::decode(r, &mut ())) - } - } - - impl<'s, S: Types> Decode<'_, 's, HandleStore>> - for &'s Marked - { - fn decode(r: &mut Reader<'_>, s: &'s mut HandleStore>) -> Self { - &s.$oty[handle::Handle::decode(r, &mut ())] - } - } - - impl<'s, S: Types> Decode<'_, 's, HandleStore>> - for &'s mut Marked - { - fn decode( - r: &mut Reader<'_>, - s: &'s mut HandleStore> - ) -> Self { - &mut s.$oty[handle::Handle::decode(r, &mut ())] - } - } - )* +pub(super) type MarkedTokenStream = Marked<::TokenStream, client::TokenStream>; +pub(super) type MarkedSpan = Marked<::Span, client::Span>; +pub(super) type MarkedSymbol = Marked<::Symbol, client::Symbol>; - $( - impl Encode>> for Marked { - fn encode(self, w: &mut Writer, s: &mut HandleStore>) { - s.$ity.alloc(self).encode(w, s); - } - } +impl Encode> for MarkedTokenStream { + fn encode(self, w: &mut Buffer, s: &mut HandleStore) { + s.token_stream.alloc(self).encode(w, s); + } +} - impl Decode<'_, '_, HandleStore>> - for Marked - { - fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { - s.$ity.copy(handle::Handle::decode(r, &mut ())) - } - } - )* +impl Decode<'_, '_, HandleStore> for MarkedTokenStream { + fn decode(r: &mut &[u8], s: &mut HandleStore) -> Self { + s.token_stream.take(handle::Handle::decode(r, &mut ())) } } -with_api_handle_types!(define_server_handles); -pub trait Types { - type FreeFunctions: 'static; - type TokenStream: 'static + Clone; - type Span: 'static + Copy + Eq + Hash; - type Symbol: 'static; +impl<'s, S: Server> Decode<'_, 's, HandleStore> for &'s MarkedTokenStream { + fn decode(r: &mut &[u8], s: &'s mut HandleStore) -> Self { + &s.token_stream[handle::Handle::decode(r, &mut ())] + } } -/// Declare an associated fn of one of the traits below, adding necessary -/// default bodies. -macro_rules! associated_fn { - (fn drop(&mut self, $arg:ident: $arg_ty:ty)) => - (fn drop(&mut self, $arg: $arg_ty) { mem::drop($arg) }); +impl Encode> for MarkedSpan { + fn encode(self, w: &mut Buffer, s: &mut HandleStore) { + s.span.alloc(self).encode(w, s); + } +} - (fn clone(&mut self, $arg:ident: $arg_ty:ty) -> $ret_ty:ty) => - (fn clone(&mut self, $arg: $arg_ty) -> $ret_ty { $arg.clone() }); +impl Decode<'_, '_, HandleStore> for MarkedSpan { + fn decode(r: &mut &[u8], s: &mut HandleStore) -> Self { + s.span.copy(handle::Handle::decode(r, &mut ())) + } +} - ($($item:tt)*) => ($($item)*;) +struct Dispatcher { + handle_store: HandleStore, + server: S, } -macro_rules! declare_server_traits { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* - }),* $(,)?) => { - $(pub trait $name: Types { - $(associated_fn!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)* - })* +macro_rules! define_server_dispatcher_impl { + ( + $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* + ) => { + pub trait Server { + type TokenStream: 'static + Clone; + type Span: 'static + Copy + Eq + Hash; + type Symbol: 'static; - pub trait Server: Types $(+ $name)* { fn globals(&mut self) -> ExpnGlobals; /// Intern a symbol received from RPC @@ -114,95 +74,60 @@ macro_rules! declare_server_traits { /// Recover the string value of a symbol, and invoke a callback with it. fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)); - } - } -} -with_api!(Self, self_, declare_server_traits); - -pub(super) struct MarkedTypes(S); -impl Server for MarkedTypes { - fn globals(&mut self) -> ExpnGlobals { - <_>::mark(Server::globals(&mut self.0)) - } - fn intern_symbol(ident: &str) -> Self::Symbol { - <_>::mark(S::intern_symbol(ident)) - } - fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { - S::with_symbol_string(symbol.unmark(), f) - } -} - -macro_rules! define_mark_types_impls { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* - }),* $(,)?) => { - impl Types for MarkedTypes { - $(type $name = Marked;)* + $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?;)* } - $(impl $name for MarkedTypes { - $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)? { - <_>::mark($name::$method(&mut self.0, $($arg.unmark()),*)) - })* - })* - } -} -with_api!(Self, self_, define_mark_types_impls); - -struct Dispatcher { - handle_store: HandleStore, - server: S, -} - -macro_rules! define_dispatcher_impl { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* - }),* $(,)?) => { // FIXME(eddyb) `pub` only for `ExecutionStrategy` below. pub trait DispatcherTrait { // HACK(eddyb) these are here to allow `Self::$name` to work below. - $(type $name;)* + type TokenStream; + type Span; + type Symbol; fn dispatch(&mut self, buf: Buffer) -> Buffer; } - impl DispatcherTrait for Dispatcher> { - $(type $name = as Types>::$name;)* + impl DispatcherTrait for Dispatcher { + type TokenStream = MarkedTokenStream; + type Span = MarkedSpan; + type Symbol = MarkedSymbol; fn dispatch(&mut self, mut buf: Buffer) -> Buffer { let Dispatcher { handle_store, server } = self; let mut reader = &buf[..]; - match api_tags::Method::decode(&mut reader, &mut ()) { - $(api_tags::Method::$name(m) => match m { - $(api_tags::$name::$method => { - let mut call_method = || { - $(let $arg = <$arg_ty>::decode(&mut reader, handle_store);)* - $name::$method(server, $($arg),*) - }; - // HACK(eddyb) don't use `panic::catch_unwind` in a panic. - // If client and server happen to use the same `std`, - // `catch_unwind` asserts that the panic counter was 0, - // even when the closure passed to it didn't panic. - let r = if thread::panicking() { - Ok(call_method()) - } else { - panic::catch_unwind(panic::AssertUnwindSafe(call_method)) - .map_err(PanicMessage::from) - }; - - buf.clear(); - r.encode(&mut buf, handle_store); - })* - }),* + match ApiTags::decode(&mut reader, &mut ()) { + $(ApiTags::$method => { + let mut call_method = || { + $(let $arg = <$arg_ty>::decode(&mut reader, handle_store).unmark();)* + let r = server.$method($($arg),*); + $( + let r: $ret_ty = Mark::mark(r); + )* + r + }; + // HACK(eddyb) don't use `panic::catch_unwind` in a panic. + // If client and server happen to use the same `std`, + // `catch_unwind` asserts that the panic counter was 0, + // even when the closure passed to it didn't panic. + let r = if thread::panicking() { + Ok(call_method()) + } else { + panic::catch_unwind(panic::AssertUnwindSafe(call_method)) + .map_err(PanicMessage::from) + }; + + buf.clear(); + r.encode(&mut buf, handle_store); + })* } buf } } } } -with_api!(Self, self_, define_dispatcher_impl); +with_api!(Self, define_server_dispatcher_impl); pub trait ExecutionStrategy { fn run_bridge_and_client( @@ -354,8 +279,8 @@ pub trait MessagePipe: Sized { fn run_server< S: Server, - I: Encode>>, - O: for<'a, 's> Decode<'a, 's, HandleStore>>, + I: Encode>, + O: for<'a, 's> Decode<'a, 's, HandleStore>, >( strategy: &impl ExecutionStrategy, handle_counters: &'static client::HandleCounters, @@ -364,13 +289,13 @@ fn run_server< run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, force_show_panics: bool, ) -> Result { - let mut dispatcher = - Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) }; + let mut dispatcher = Dispatcher { handle_store: HandleStore::new(handle_counters), server }; let globals = dispatcher.server.globals(); let mut buf = Buffer::new(); - (globals, input).encode(&mut buf, &mut dispatcher.handle_store); + (> as Mark>::mark(globals), input) + .encode(&mut buf, &mut dispatcher.handle_store); buf = strategy.run_bridge_and_client(&mut dispatcher, buf, run_client, force_show_panics); @@ -394,11 +319,11 @@ impl client::Client { strategy, handle_counters, server, - as Types>::TokenStream::mark(input), + >::mark(input), run, force_show_panics, ) - .map(|s| as Types>::TokenStream>>::unmark(s).unwrap_or_default()) + .map(|s| >>::unmark(s).unwrap_or_default()) } } @@ -420,13 +345,10 @@ impl client::Client<(crate::TokenStream, crate::TokenStream), crate::TokenStream strategy, handle_counters, server, - ( - as Types>::TokenStream::mark(input), - as Types>::TokenStream::mark(input2), - ), + (>::mark(input), >::mark(input2)), run, force_show_panics, ) - .map(|s| as Types>::TokenStream>>::unmark(s).unwrap_or_default()) + .map(|s| >>::unmark(s).unwrap_or_default()) } } diff --git a/library/proc_macro/src/bridge/symbol.rs b/library/proc_macro/src/bridge/symbol.rs index 0d6a725fddd98..2a04f7d808bd5 100644 --- a/library/proc_macro/src/bridge/symbol.rs +++ b/library/proc_macro/src/bridge/symbol.rs @@ -46,7 +46,7 @@ impl Symbol { if string.is_ascii() { Err(()) } else { - client::Symbol::normalize_and_validate_ident(string) + client::Methods::symbol_normalize_and_validate_ident(string) } .unwrap_or_else(|_| panic!("`{:?}` is not a valid identifier", string)) } @@ -77,10 +77,7 @@ impl Symbol { // Mimics the behavior of `Symbol::can_be_raw` from `rustc_span` fn can_be_raw(string: &str) -> bool { - match string { - "_" | "super" | "self" | "Self" | "crate" | "$crate" => false, - _ => true, - } + !matches!(string, "_" | "super" | "self" | "Self" | "crate" | "$crate") } } @@ -97,29 +94,25 @@ impl fmt::Display for Symbol { } impl Encode for Symbol { - fn encode(self, w: &mut Writer, s: &mut S) { + fn encode(self, w: &mut Buffer, s: &mut S) { self.with(|sym| sym.encode(w, s)) } } -impl Decode<'_, '_, server::HandleStore>> - for Marked -{ - fn decode(r: &mut Reader<'_>, s: &mut server::HandleStore>) -> Self { +impl Decode<'_, '_, server::HandleStore> for server::MarkedSymbol { + fn decode(r: &mut &[u8], s: &mut server::HandleStore) -> Self { Mark::mark(S::intern_symbol(<&str>::decode(r, s))) } } -impl Encode>> - for Marked -{ - fn encode(self, w: &mut Writer, s: &mut server::HandleStore>) { +impl Encode> for server::MarkedSymbol { + fn encode(self, w: &mut Buffer, s: &mut server::HandleStore) { S::with_symbol_string(&self.unmark(), |sym| sym.encode(w, s)) } } impl Decode<'_, '_, S> for Symbol { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { + fn decode(r: &mut &[u8], s: &mut S) -> Self { Symbol::new(<&str>::decode(r, s)) } } diff --git a/library/proc_macro/src/diagnostic.rs b/library/proc_macro/src/diagnostic.rs index 5a209f7c7aa18..39a8ecb2ee52e 100644 --- a/library/proc_macro/src/diagnostic.rs +++ b/library/proc_macro/src/diagnostic.rs @@ -170,6 +170,6 @@ impl Diagnostic { } } - crate::bridge::client::FreeFunctions::emit_diagnostic(to_internal(self)); + crate::bridge::client::Methods::emit_diagnostic(to_internal(self)); } } diff --git a/library/proc_macro/src/escape.rs b/library/proc_macro/src/escape.rs index 87a4d1d50fd48..1f11a6633ff16 100644 --- a/library/proc_macro/src/escape.rs +++ b/library/proc_macro/src/escape.rs @@ -17,8 +17,7 @@ pub(crate) fn escape_bytes(bytes: &[u8], opt: EscapeOptions) -> String { escape_single_byte(byte, opt, &mut repr); } } else { - let mut chunks = bytes.utf8_chunks(); - while let Some(chunk) = chunks.next() { + for chunk in bytes.utf8_chunks() { for ch in chunk.valid().chars() { escape_single_char(ch, opt, &mut repr); } diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 4efdfcad924b5..49b6f2ae41f89 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -3,7 +3,7 @@ //! This library, provided by the standard distribution, provides the types //! consumed in the interfaces of procedurally defined macro definitions such as //! function-like macros `#[proc_macro]`, macro attributes `#[proc_macro_attribute]` and -//! custom derive attributes`#[proc_macro_derive]`. +//! custom derive attributes `#[proc_macro_derive]`. //! //! See [the book] for more. //! @@ -22,7 +22,6 @@ #![feature(staged_api)] #![feature(allow_internal_unstable)] #![feature(decl_macro)] -#![feature(maybe_uninit_write_slice)] #![feature(negative_impls)] #![feature(panic_can_unwind)] #![feature(restricted_std)] @@ -55,10 +54,13 @@ use std::{error, fmt}; pub use diagnostic::{Diagnostic, Level, MultiSpan}; #[unstable(feature = "proc_macro_value", issue = "136652")] pub use rustc_literal_escaper::EscapeError; -use rustc_literal_escaper::{MixedUnit, unescape_byte_str, unescape_c_str, unescape_str}; +use rustc_literal_escaper::{ + MixedUnit, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, unescape_str, +}; #[unstable(feature = "proc_macro_totokens", issue = "130977")] pub use to_tokens::ToTokens; +use crate::bridge::client::Methods as BridgeMethods; use crate::escape::{EscapeOptions, escape_bytes}; /// Errors returned when trying to retrieve a literal unescaped value. @@ -159,7 +161,7 @@ impl TokenStream { /// Checks if this `TokenStream` is empty. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn is_empty(&self) -> bool { - self.0.as_ref().map(|h| h.is_empty()).unwrap_or(true) + self.0.as_ref().map(|h| BridgeMethods::ts_is_empty(h)).unwrap_or(true) } /// Parses this `TokenStream` as an expression and attempts to expand any @@ -175,7 +177,7 @@ impl TokenStream { #[unstable(feature = "proc_macro_expand", issue = "90765")] pub fn expand_expr(&self) -> Result { let stream = self.0.as_ref().ok_or(ExpandError)?; - match bridge::client::TokenStream::expand_expr(stream) { + match BridgeMethods::ts_expand_expr(stream) { Ok(stream) => Ok(TokenStream(Some(stream))), Err(_) => Err(ExpandError), } @@ -194,7 +196,7 @@ impl FromStr for TokenStream { type Err = LexError; fn from_str(src: &str) -> Result { - Ok(TokenStream(Some(bridge::client::TokenStream::from_str(src)))) + Ok(TokenStream(Some(BridgeMethods::ts_from_str(src)))) } } @@ -211,16 +213,15 @@ impl FromStr for TokenStream { /// `TokenTree::Punct`, or `TokenTree::Literal`. #[stable(feature = "proc_macro_lib", since = "1.15.0")] impl fmt::Display for TokenStream { - #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 { - Some(ts) => write!(f, "{}", ts.to_string()), + Some(ts) => write!(f, "{}", BridgeMethods::ts_to_string(ts)), None => Ok(()), } } } -/// Prints token in a form convenient for debugging. +/// Prints tokens in a form convenient for debugging. #[stable(feature = "proc_macro_lib", since = "1.15.0")] impl fmt::Debug for TokenStream { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -254,7 +255,7 @@ fn tree_to_bridge_tree( #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl From for TokenStream { fn from(tree: TokenTree) -> TokenStream { - TokenStream(Some(bridge::client::TokenStream::from_token_tree(tree_to_bridge_tree(tree)))) + TokenStream(Some(BridgeMethods::ts_from_token_tree(tree_to_bridge_tree(tree)))) } } @@ -283,7 +284,7 @@ impl ConcatTreesHelper { if self.trees.is_empty() { TokenStream(None) } else { - TokenStream(Some(bridge::client::TokenStream::concat_trees(None, self.trees))) + TokenStream(Some(BridgeMethods::ts_concat_trees(None, self.trees))) } } @@ -291,7 +292,7 @@ impl ConcatTreesHelper { if self.trees.is_empty() { return; } - stream.0 = Some(bridge::client::TokenStream::concat_trees(stream.0.take(), self.trees)) + stream.0 = Some(BridgeMethods::ts_concat_trees(stream.0.take(), self.trees)) } } @@ -316,7 +317,7 @@ impl ConcatStreamsHelper { if self.streams.len() <= 1 { TokenStream(self.streams.pop()) } else { - TokenStream(Some(bridge::client::TokenStream::concat_streams(None, self.streams))) + TokenStream(Some(BridgeMethods::ts_concat_streams(None, self.streams))) } } @@ -328,7 +329,7 @@ impl ConcatStreamsHelper { if base.is_none() && self.streams.len() == 1 { stream.0 = self.streams.pop(); } else { - stream.0 = Some(bridge::client::TokenStream::concat_streams(base, self.streams)); + stream.0 = Some(BridgeMethods::ts_concat_streams(base, self.streams)); } } } @@ -379,7 +380,7 @@ impl Extend for TokenStream { macro_rules! extend_items { ($($item:ident)*) => { $( - #[stable(feature = "token_stream_extend_tt_items", since = "1.92.0")] + #[stable(feature = "token_stream_extend_ts_items", since = "1.92.0")] impl Extend<$item> for TokenStream { fn extend>(&mut self, iter: T) { self.extend(iter.into_iter().map(TokenTree::$item)); @@ -394,7 +395,7 @@ extend_items!(Group Literal Punct Ident); /// Public implementation details for the `TokenStream` type, such as iterators. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub mod token_stream { - use crate::{Group, Ident, Literal, Punct, TokenStream, TokenTree, bridge}; + use crate::{BridgeMethods, Group, Ident, Literal, Punct, TokenStream, TokenTree, bridge}; /// An iterator over `TokenStream`'s `TokenTree`s. /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups, @@ -439,7 +440,9 @@ pub mod token_stream { type IntoIter = IntoIter; fn into_iter(self) -> IntoIter { - IntoIter(self.0.map(|v| v.into_trees()).unwrap_or_default().into_iter()) + IntoIter( + self.0.map(|v| BridgeMethods::ts_into_trees(v)).unwrap_or_default().into_iter(), + ) } } } @@ -511,7 +514,7 @@ impl Span { /// `self` was generated from, if any. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn parent(&self) -> Option { - self.0.parent().map(Span) + BridgeMethods::span_parent(self.0).map(Span) } /// The span for the origin source code that `self` was generated from. If @@ -519,25 +522,25 @@ impl Span { /// value is the same as `*self`. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn source(&self) -> Span { - Span(self.0.source()) + Span(BridgeMethods::span_source(self.0)) } /// Returns the span's byte position range in the source file. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn byte_range(&self) -> Range { - self.0.byte_range() + BridgeMethods::span_byte_range(self.0) } /// Creates an empty span pointing to directly before this span. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn start(&self) -> Span { - Span(self.0.start()) + Span(BridgeMethods::span_start(self.0)) } /// Creates an empty span pointing to directly after this span. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn end(&self) -> Span { - Span(self.0.end()) + Span(BridgeMethods::span_end(self.0)) } /// The one-indexed line of the source file where the span starts. @@ -545,7 +548,7 @@ impl Span { /// To obtain the line of the span's end, use `span.end().line()`. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn line(&self) -> usize { - self.0.line() + BridgeMethods::span_line(self.0) } /// The one-indexed column of the source file where the span starts. @@ -553,7 +556,7 @@ impl Span { /// To obtain the column of the span's end, use `span.end().column()`. #[stable(feature = "proc_macro_span_location", since = "1.88.0")] pub fn column(&self) -> usize { - self.0.column() + BridgeMethods::span_column(self.0) } /// The path to the source file in which this span occurs, for display purposes. @@ -562,7 +565,7 @@ impl Span { /// It might be remapped (e.g. `"/src/lib.rs"`) or an artificial path (e.g. `""`). #[stable(feature = "proc_macro_span_file", since = "1.88.0")] pub fn file(&self) -> String { - self.0.file() + BridgeMethods::span_file(self.0) } /// The path to the source file in which this span occurs on the local file system. @@ -572,7 +575,7 @@ impl Span { /// This path should not be embedded in the output of the macro; prefer `file()` instead. #[stable(feature = "proc_macro_span_file", since = "1.88.0")] pub fn local_file(&self) -> Option { - self.0.local_file().map(|s| PathBuf::from(s)) + BridgeMethods::span_local_file(self.0).map(PathBuf::from) } /// Creates a new span encompassing `self` and `other`. @@ -580,14 +583,14 @@ impl Span { /// Returns `None` if `self` and `other` are from different files. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn join(&self, other: Span) -> Option { - self.0.join(other.0).map(Span) + BridgeMethods::span_join(self.0, other.0).map(Span) } /// Creates a new span with the same line/column information as `self` but /// that resolves symbols as though it were at `other`. #[stable(feature = "proc_macro_span_resolved_at", since = "1.45.0")] pub fn resolved_at(&self, other: Span) -> Span { - Span(self.0.resolved_at(other.0)) + Span(BridgeMethods::span_resolved_at(self.0, other.0)) } /// Creates a new span with the same name resolution behavior as `self` but @@ -612,21 +615,21 @@ impl Span { /// be used for diagnostics only. #[stable(feature = "proc_macro_source_text", since = "1.66.0")] pub fn source_text(&self) -> Option { - self.0.source_text() + BridgeMethods::span_source_text(self.0) } // Used by the implementation of `Span::quote` #[doc(hidden)] #[unstable(feature = "proc_macro_internals", issue = "27812")] pub fn save_span(&self) -> usize { - self.0.save_span() + BridgeMethods::span_save_span(self.0) } // Used by the implementation of `Span::quote` #[doc(hidden)] #[unstable(feature = "proc_macro_internals", issue = "27812")] pub fn recover_proc_macro_span(id: usize) -> Span { - Span(bridge::client::Span::recover_proc_macro_span(id)) + Span(BridgeMethods::span_recover_proc_macro_span(id)) } diagnostic_method!(error, Level::Error); @@ -751,7 +754,6 @@ impl From for TokenTree { /// `TokenTree::Punct`, or `TokenTree::Literal`. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl fmt::Display for TokenTree { - #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { TokenTree::Group(t) => write!(f, "{t}"), @@ -889,7 +891,6 @@ impl Group { /// with `Delimiter::None` delimiters. #[stable(feature = "proc_macro_lib2", since = "1.29.0")] impl fmt::Display for Group { - #[allow(clippy::recursive_format_impl)] // clippy doesn't see the specialization fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", TokenStream::from(TokenTree::from(self.clone()))) } @@ -1042,6 +1043,8 @@ impl Ident { /// The `string` argument must be a valid identifier permitted by the /// language (including keywords, e.g. `self` or `fn`). Otherwise, the function will panic. /// + /// The constructed identifier will be NFC-normalized. See the [Reference] for more info. + /// /// Note that `span`, currently in rustc, configures the hygiene information /// for this identifier. /// @@ -1056,6 +1059,8 @@ impl Ident { /// /// Due to the current importance of hygiene this constructor, unlike other /// tokens, requires a `Span` to be specified at construction. + /// + /// [Reference]: https://doc.rust-lang.org/nightly/reference/identifiers.html#r-ident.normalization #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn new(string: &str, span: Span) -> Ident { Ident(bridge::Ident { @@ -1389,7 +1394,12 @@ impl Literal { // was 'c' or whether it was '\u{63}'. #[unstable(feature = "proc_macro_span", issue = "54725")] pub fn subspan>(&self, range: R) -> Option { - self.0.span.subspan(range.start_bound().cloned(), range.end_bound().cloned()).map(Span) + BridgeMethods::span_subspan( + self.0.span, + range.start_bound().cloned(), + range.end_bound().cloned(), + ) + .map(Span) } fn with_symbol_and_suffix(&self, f: impl FnOnce(&str, &str) -> R) -> R { @@ -1443,6 +1453,28 @@ impl Literal { }) } + /// Returns the unescaped character value if the current literal is a byte character literal. + #[unstable(feature = "proc_macro_value", issue = "136652")] + pub fn byte_character_value(&self) -> Result { + self.0.symbol.with(|symbol| match self.0.kind { + bridge::LitKind::Char => { + unescape_byte(symbol).map_err(ConversionErrorKind::FailedToUnescape) + } + _ => Err(ConversionErrorKind::InvalidLiteralKind), + }) + } + + /// Returns the unescaped character value if the current literal is a character literal. + #[unstable(feature = "proc_macro_value", issue = "136652")] + pub fn character_value(&self) -> Result { + self.0.symbol.with(|symbol| match self.0.kind { + bridge::LitKind::Char => { + unescape_char(symbol).map_err(ConversionErrorKind::FailedToUnescape) + } + _ => Err(ConversionErrorKind::InvalidLiteralKind), + }) + } + /// Returns the unescaped string value if the current literal is a string or a string literal. #[unstable(feature = "proc_macro_value", issue = "136652")] pub fn str_value(&self) -> Result { @@ -1559,7 +1591,7 @@ impl FromStr for Literal { type Err = LexError; fn from_str(src: &str) -> Result { - match bridge::client::FreeFunctions::literal_from_str(src) { + match BridgeMethods::literal_from_str(src) { Ok(literal) => Ok(Literal(literal)), Err(()) => Err(LexError), } @@ -1594,11 +1626,18 @@ impl fmt::Debug for Literal { } } -/// Tracked access to environment variables. -#[unstable(feature = "proc_macro_tracked_env", issue = "99515")] -pub mod tracked_env { +#[unstable( + feature = "proc_macro_tracked_path", + issue = "99515", + implied_by = "proc_macro_tracked_env" +)] +/// Functionality for adding environment state to the build dependency info. +pub mod tracked { use std::env::{self, VarError}; use std::ffi::OsStr; + use std::path::Path; + + use crate::BridgeMethods; /// Retrieve an environment variable and add it to build dependency info. /// The build system executing the compiler will know that the variable was accessed during @@ -1606,25 +1645,19 @@ pub mod tracked_env { /// Besides the dependency tracking this function should be equivalent to `env::var` from the /// standard library, except that the argument must be UTF-8. #[unstable(feature = "proc_macro_tracked_env", issue = "99515")] - pub fn var + AsRef>(key: K) -> Result { + pub fn env_var + AsRef>(key: K) -> Result { let key: &str = key.as_ref(); - let value = crate::bridge::client::FreeFunctions::injected_env_var(key) - .map_or_else(|| env::var(key), Ok); - crate::bridge::client::FreeFunctions::track_env_var(key, value.as_deref().ok()); + let value = BridgeMethods::injected_env_var(key).map_or_else(|| env::var(key), Ok); + BridgeMethods::track_env_var(key, value.as_deref().ok()); value } -} - -/// Tracked access to additional files. -#[unstable(feature = "track_path", issue = "99515")] -pub mod tracked_path { - /// Track a file explicitly. + /// Track a file or directory explicitly. /// /// Commonly used for tracking asset preprocessing. - #[unstable(feature = "track_path", issue = "99515")] - pub fn path>(path: P) { - let path: &str = path.as_ref(); - crate::bridge::client::FreeFunctions::track_path(path); + #[unstable(feature = "proc_macro_tracked_path", issue = "99515")] + pub fn path>(path: P) { + let path: &str = path.as_ref().to_str().unwrap(); + BridgeMethods::track_path(path); } } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index bd5a5a553b0a3..b37787165062c 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -27,14 +27,14 @@ std_detect = { path = "../std_detect", public = true } safety = { path = "../contracts/safety" } # Dependencies of the `backtrace` crate -rustc-demangle = { version = "0.1.24", features = ['rustc-dep-of-std'] } +rustc-demangle = { version = "0.1.27", features = ['rustc-dep-of-std'] } [target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies] miniz_oxide = { version = "0.8.0", optional = true, default-features = false } addr2line = { version = "0.25.0", optional = true, default-features = false } [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies] -libc = { version = "0.2.177", default-features = false, features = [ +libc = { version = "0.2.178", default-features = false, features = [ 'rustc-dep-of-std', ], public = true } @@ -56,8 +56,8 @@ object = { version = "0.37.1", default-features = false, optional = true, featur 'archive', ] } -[target.'cfg(any(windows, target_os = "cygwin"))'.dependencies.windows-targets] -path = "../windows_targets" +[target.'cfg(any(windows, target_os = "cygwin"))'.dependencies.windows-link] +path = "../windows_link" [dev-dependencies] rand = { version = "0.9.0", default-features = false, features = ["alloc"] } @@ -72,14 +72,14 @@ fortanix-sgx-abi = { version = "0.6.1", features = [ ], public = true } [target.'cfg(target_os = "motor")'.dependencies] -moto-rt = { version = "0.15", features = ['rustc-dep-of-std'], public = true } +moto-rt = { version = "0.16", features = ['rustc-dep-of-std'], public = true } [target.'cfg(target_os = "hermit")'.dependencies] hermit-abi = { version = "0.5.0", features = [ 'rustc-dep-of-std', ], public = true } -[target.'cfg(target_os = "wasi")'.dependencies] +[target.'cfg(all(target_os = "wasi", target_env = "p1"))'.dependencies] wasi = { version = "0.11.0", features = [ 'rustc-dep-of-std', ], default-features = false } @@ -116,7 +116,6 @@ backtrace-trace-only = [] panic-unwind = ["dep:panic_unwind"] compiler-builtins-c = ["alloc/compiler-builtins-c"] compiler-builtins-mem = ["alloc/compiler-builtins-mem"] -compiler-builtins-no-f16-f128 = ["alloc/compiler-builtins-no-f16-f128"] llvm-libunwind = ["unwind/llvm-libunwind"] system-llvm-libunwind = ["unwind/system-llvm-libunwind"] @@ -129,13 +128,9 @@ debug_refcell = ["core/debug_refcell"] llvm_enzyme = ["core/llvm_enzyme"] -# Enable std_detect features: -std_detect_file_io = ["std_detect/std_detect_file_io"] -std_detect_dlsym_getauxval = ["std_detect/std_detect_dlsym_getauxval"] - # Enable using raw-dylib for Windows imports. # This will eventually be the default. -windows_raw_dylib = ["windows-targets/windows_raw_dylib"] +windows_raw_dylib = ["windows-link/windows_raw_dylib"] [package.metadata.fortanix-sgx] # Maximum possible number of threads when testing diff --git a/library/std/build.rs b/library/std/build.rs index bee28e88491d0..c0a6e30b38082 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -13,6 +13,9 @@ fn main() { println!("cargo:rustc-cfg=netbsd10"); } + // Needed for `#![doc(auto_cfg(hide(no_global_oom_handling)))]` attribute. + println!("cargo::rustc-check-cfg=cfg(no_global_oom_handling)"); + println!("cargo:rustc-check-cfg=cfg(restricted_std)"); if target_os == "linux" || target_os == "android" diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 56734028d75e9..27b92b6ee8458 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -11,7 +11,7 @@ //! //! This attribute allows configuring the choice of global allocator. //! You can use this to implement a completely custom global allocator -//! to route all default allocation requests to a custom object. +//! to route all[^system-alloc] default allocation requests to a custom object. //! //! ```rust //! use std::alloc::{GlobalAlloc, System, Layout}; @@ -52,6 +52,13 @@ //! //! The `#[global_allocator]` can only be used once in a crate //! or its recursive dependencies. +//! +//! [^system-alloc]: Note that the Rust standard library internals may still +//! directly call [`System`] when necessary (for example for the runtime +//! support typically required to implement a global allocator, see [re-entrance] on [`GlobalAlloc`] +//! for more details). +//! +//! [re-entrance]: trait.GlobalAlloc.html#re-entrance #![deny(unsafe_op_in_unsafe_fn)] #![stable(feature = "alloc_module", since = "1.28.0")] @@ -59,7 +66,7 @@ #[cfg(kani)] use core::kani; use core::ptr::NonNull; -use core::sync::atomic::{Atomic, AtomicPtr, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; use core::{hint, mem, ptr}; #[stable(feature = "alloc_module", since = "1.28.0")] @@ -138,7 +145,7 @@ impl System { #[inline] fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result, AllocError> { match layout.size() { - 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), + 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling_ptr(), 0)), // SAFETY: `layout` is non-zero in size, size => unsafe { let raw_ptr = if zeroed { @@ -270,7 +277,7 @@ unsafe impl Allocator for System { // SAFETY: conditions must be upheld by the caller 0 => unsafe { Allocator::deallocate(self, ptr, old_layout); - Ok(NonNull::slice_from_raw_parts(new_layout.dangling(), 0)) + Ok(NonNull::slice_from_raw_parts(new_layout.dangling_ptr(), 0)) }, // SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller @@ -298,7 +305,7 @@ unsafe impl Allocator for System { } } -static HOOK: Atomic<*mut ()> = AtomicPtr::new(ptr::null_mut()); +static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// Registers a custom allocation error hook, replacing any that was previously registered. /// @@ -355,26 +362,69 @@ pub fn take_alloc_error_hook() -> fn(Layout) { if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } } } +#[optimize(size)] fn default_alloc_error_hook(layout: Layout) { - unsafe extern "Rust" { - // This symbol is emitted by rustc next to __rust_alloc_error_handler. - // Its value depends on the -Zoom={panic,abort} compiler option. - #[rustc_std_internal_symbol] - fn __rust_alloc_error_handler_should_panic_v2() -> u8; + if cfg!(panic = "immediate-abort") { + return; } - if unsafe { __rust_alloc_error_handler_should_panic_v2() != 0 } { - panic!("memory allocation of {} bytes failed", layout.size()); + // This is the default path taken on OOM, and the only path taken on stable with std. + // Crucially, it does *not* call any user-defined code, and therefore users do not have to + // worry about allocation failure causing reentrancy issues. That makes it different from + // the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error + // handler that is called when there is no `#[alloc_error_handler]`), which triggers a + // regular panic and thus can invoke a user-defined panic hook, executing arbitrary + // user-defined code. + + static PREV_ALLOC_FAILURE: AtomicBool = AtomicBool::new(false); + if PREV_ALLOC_FAILURE.swap(true, Ordering::Relaxed) { + // Don't try to print a backtrace if a previous alloc error happened. This likely means + // there is not enough memory to print a backtrace, although it could also mean that two + // threads concurrently run out of memory. + rtprintpanic!( + "memory allocation of {} bytes failed\nskipping backtrace printing to avoid potential recursion\n", + layout.size() + ); + return; } else { - // This is the default path taken on OOM, and the only path taken on stable with std. - // Crucially, it does *not* call any user-defined code, and therefore users do not have to - // worry about allocation failure causing reentrancy issues. That makes it different from - // the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error - // handler that is called when there is no `#[alloc_error_handler]`), which triggers a - // regular panic and thus can invoke a user-defined panic hook, executing arbitrary - // user-defined code. rtprintpanic!("memory allocation of {} bytes failed\n", layout.size()); } + + let Some(mut out) = crate::sys::stdio::panic_output() else { + return; + }; + + // Use a lock to prevent mixed output in multithreading context. + // Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows. + // Make sure to not take this lock until after checking PREV_ALLOC_FAILURE to avoid deadlocks + // when there is too little memory to print a backtrace. + let mut lock = crate::sys::backtrace::lock(); + + match crate::panic::get_backtrace_style() { + Some(crate::panic::BacktraceStyle::Short) => { + drop(lock.print(&mut out, crate::backtrace_rs::PrintFmt::Short)) + } + Some(crate::panic::BacktraceStyle::Full) => { + drop(lock.print(&mut out, crate::backtrace_rs::PrintFmt::Full)) + } + Some(crate::panic::BacktraceStyle::Off) => { + use crate::io::Write; + let _ = writeln!( + out, + "note: run with `RUST_BACKTRACE=1` environment variable to display a \ + backtrace" + ); + if cfg!(miri) { + let _ = writeln!( + out, + "note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` \ + for the environment variable to have an effect" + ); + } + } + // If backtraces aren't supported or are forced-off, do nothing. + None => {} + } } #[cfg(not(test))] @@ -382,11 +432,13 @@ fn default_alloc_error_hook(layout: Layout) { #[alloc_error_handler] #[unstable(feature = "alloc_internals", issue = "none")] pub fn rust_oom(layout: Layout) -> ! { - let hook = HOOK.load(Ordering::Acquire); - let hook: fn(Layout) = - if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; - hook(layout); - crate::process::abort() + crate::sys::backtrace::__rust_end_short_backtrace(|| { + let hook = HOOK.load(Ordering::Acquire); + let hook: fn(Layout) = + if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; + hook(layout); + crate::process::abort() + }) } #[cfg(not(test))] diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 251d62bd2033a..b82beb3b8b2ef 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -4,6 +4,7 @@ mod tests; use hashbrown::hash_map as base; use self::Entry::*; +use crate::alloc::{Allocator, Global}; use crate::borrow::Borrow; use crate::collections::{TryReserveError, TryReserveErrorKind}; use crate::error::Error; @@ -243,8 +244,13 @@ use crate::ops::Index; #[cfg_attr(not(test), rustc_diagnostic_item = "HashMap")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_insignificant_dtor] -pub struct HashMap { - base: base::HashMap, +pub struct HashMap< + K, + V, + S = RandomState, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::HashMap, } impl HashMap { @@ -286,6 +292,46 @@ impl HashMap { } } +impl HashMap { + /// Creates an empty `HashMap` using the given allocator. + /// + /// The hash map is initially created with a capacity of 0, so it will not allocate until it + /// is first inserted into. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// let mut map: HashMap<&str, i32> = HashMap::new(); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn new_in(alloc: A) -> Self { + HashMap::with_hasher_in(Default::default(), alloc) + } + + /// Creates an empty `HashMap` with at least the specified capacity using + /// the given allocator. + /// + /// The hash map will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is zero, the hash map will not allocate. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// let mut map: HashMap<&str, i32> = HashMap::with_capacity(10); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { + HashMap::with_capacity_and_hasher_in(capacity, Default::default(), alloc) + } +} + impl HashMap { /// Creates an empty `HashMap` which will use the given hash builder to hash /// keys. @@ -347,6 +393,47 @@ impl HashMap { pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashMap { HashMap { base: base::HashMap::with_capacity_and_hasher(capacity, hasher) } } +} + +impl HashMap { + /// Creates an empty `HashMap` which will use the given hash builder and + /// allocator. + /// + /// The created map has the default initial capacity. + /// + /// Warning: `hash_builder` is normally randomly generated, and + /// is designed to allow HashMaps to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the `HashMap` to be useful, see its documentation for details. + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn with_hasher_in(hash_builder: S, alloc: A) -> Self { + HashMap { base: base::HashMap::with_hasher_in(hash_builder, alloc) } + } + + /// Creates an empty `HashMap` with at least the specified capacity, using + /// `hasher` to hash the keys and `alloc` to allocate memory. + /// + /// The hash map will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is zero, the hash map will not allocate. + /// + /// Warning: `hasher` is normally randomly generated, and + /// is designed to allow HashMaps to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hasher` passed should implement the [`BuildHasher`] trait for + /// the `HashMap` to be useful, see its documentation for details. + /// + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn with_capacity_and_hasher_in(capacity: usize, hash_builder: S, alloc: A) -> Self { + HashMap { base: base::HashMap::with_capacity_and_hasher_in(capacity, hash_builder, alloc) } + } /// Returns the number of elements the map can hold without reallocating. /// @@ -424,7 +511,7 @@ impl HashMap { #[inline] #[rustc_lint_query_instability] #[stable(feature = "map_into_keys_values", since = "1.54.0")] - pub fn into_keys(self) -> IntoKeys { + pub fn into_keys(self) -> IntoKeys { IntoKeys { inner: self.into_iter() } } @@ -519,7 +606,7 @@ impl HashMap { #[inline] #[rustc_lint_query_instability] #[stable(feature = "map_into_keys_values", since = "1.54.0")] - pub fn into_values(self) -> IntoValues { + pub fn into_values(self) -> IntoValues { IntoValues { inner: self.into_iter() } } @@ -648,7 +735,7 @@ impl HashMap { #[inline] #[rustc_lint_query_instability] #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self) -> Drain<'_, K, V> { + pub fn drain(&mut self) -> Drain<'_, K, V, A> { Drain { base: self.base.drain() } } @@ -688,7 +775,7 @@ impl HashMap { #[inline] #[rustc_lint_query_instability] #[stable(feature = "hash_extract_if", since = "1.88.0")] - pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, K, V, F> + pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, K, V, F, A> where F: FnMut(&K, &mut V) -> bool, { @@ -762,10 +849,11 @@ impl HashMap { } } -impl HashMap +impl HashMap where K: Eq + Hash, S: BuildHasher, + A: Allocator, { /// Reserves capacity for at least `additional` more elements to be inserted /// in the `HashMap`. The collection may reserve more space to speculatively @@ -884,7 +972,7 @@ where /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { + pub fn entry(&mut self, key: K) -> Entry<'_, K, V, A> { map_entry(self.base.rustc_entry(key)) } @@ -1232,7 +1320,7 @@ where /// assert_eq!(err.value, "b"); /// ``` #[unstable(feature = "map_try_insert", issue = "82766")] - pub fn try_insert(&mut self, key: K, value: V) -> Result<&mut V, OccupiedError<'_, K, V>> { + pub fn try_insert(&mut self, key: K, value: V) -> Result<&mut V, OccupiedError<'_, K, V, A>> { match self.entry(key) { Occupied(entry) => Err(OccupiedError { entry, value }), Vacant(entry) => Ok(entry.insert(value)), @@ -1298,11 +1386,12 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for HashMap +impl Clone for HashMap where K: Clone, V: Clone, S: Clone, + A: Allocator + Clone, { #[inline] fn clone(&self) -> Self { @@ -1316,13 +1405,14 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for HashMap +impl PartialEq for HashMap where K: Eq + Hash, V: PartialEq, S: BuildHasher, + A: Allocator, { - fn eq(&self, other: &HashMap) -> bool { + fn eq(&self, other: &HashMap) -> bool { if self.len() != other.len() { return false; } @@ -1332,19 +1422,21 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Eq for HashMap +impl Eq for HashMap where K: Eq + Hash, V: Eq, S: BuildHasher, + A: Allocator, { } #[stable(feature = "rust1", since = "1.0.0")] -impl Debug for HashMap +impl Debug for HashMap where K: Debug, V: Debug, + A: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_map().entries(self.iter()).finish() @@ -1352,9 +1444,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Default for HashMap +#[rustc_const_unstable(feature = "const_default", issue = "143894")] +impl const Default for HashMap where - S: Default, + S: [const] Default, { /// Creates an empty `HashMap`, with the `Default` value for the hasher. #[inline] @@ -1364,11 +1457,12 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Index<&Q> for HashMap +impl Index<&Q> for HashMap where K: Eq + Hash + Borrow, Q: Eq + Hash, S: BuildHasher, + A: Allocator, { type Output = V; @@ -1523,11 +1617,15 @@ impl Default for IterMut<'_, K, V> { /// let iter = map.into_iter(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter { - base: base::IntoIter, +pub struct IntoIter< + K, + V, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::IntoIter, } -impl IntoIter { +impl IntoIter { /// Returns an iterator of references over the remaining items. #[inline] pub(super) fn iter(&self) -> Iter<'_, K, V> { @@ -1656,11 +1754,16 @@ impl fmt::Debug for Values<'_, K, V> { /// ``` #[stable(feature = "drain", since = "1.6.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_drain_ty")] -pub struct Drain<'a, K: 'a, V: 'a> { - base: base::Drain<'a, K, V>, +pub struct Drain< + 'a, + K: 'a, + V: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::Drain<'a, K, V, A>, } -impl<'a, K, V> Drain<'a, K, V> { +impl<'a, K, V, A: Allocator> Drain<'a, K, V, A> { /// Returns an iterator of references over the remaining items. #[inline] pub(super) fn iter(&self) -> Iter<'_, K, V> { @@ -1687,8 +1790,14 @@ impl<'a, K, V> Drain<'a, K, V> { #[stable(feature = "hash_extract_if", since = "1.88.0")] #[must_use = "iterators are lazy and do nothing unless consumed; \ use `retain` to remove and discard elements"] -pub struct ExtractIf<'a, K, V, F> { - base: base::ExtractIf<'a, K, V, F>, +pub struct ExtractIf< + 'a, + K, + V, + F, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::ExtractIf<'a, K, V, F, A>, } /// A mutable iterator over the values of a `HashMap`. @@ -1740,8 +1849,12 @@ impl Default for ValuesMut<'_, K, V> { /// let iter_keys = map.into_keys(); /// ``` #[stable(feature = "map_into_keys_values", since = "1.54.0")] -pub struct IntoKeys { - inner: IntoIter, +pub struct IntoKeys< + K, + V, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + inner: IntoIter, } #[stable(feature = "default_iters_hash", since = "1.83.0")] @@ -1770,8 +1883,12 @@ impl Default for IntoKeys { /// let iter_keys = map.into_values(); /// ``` #[stable(feature = "map_into_keys_values", since = "1.54.0")] -pub struct IntoValues { - inner: IntoIter, +pub struct IntoValues< + K, + V, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + inner: IntoIter, } #[stable(feature = "default_iters_hash", since = "1.83.0")] @@ -1789,14 +1906,19 @@ impl Default for IntoValues { /// [`entry`]: HashMap::entry #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "HashMapEntry")] -pub enum Entry<'a, K: 'a, V: 'a> { +pub enum Entry< + 'a, + K: 'a, + V: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { /// An occupied entry. #[stable(feature = "rust1", since = "1.0.0")] - Occupied(#[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V>), + Occupied(#[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V, A>), /// A vacant entry. #[stable(feature = "rust1", since = "1.0.0")] - Vacant(#[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V>), + Vacant(#[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V, A>), } #[stable(feature = "debug_hash_map", since = "1.12.0")] @@ -1812,12 +1934,17 @@ impl Debug for Entry<'_, K, V> { /// A view into an occupied entry in a `HashMap`. /// It is part of the [`Entry`] enum. #[stable(feature = "rust1", since = "1.0.0")] -pub struct OccupiedEntry<'a, K: 'a, V: 'a> { - base: base::RustcOccupiedEntry<'a, K, V>, +pub struct OccupiedEntry< + 'a, + K: 'a, + V: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::RustcOccupiedEntry<'a, K, V, A>, } #[stable(feature = "debug_hash_map", since = "1.12.0")] -impl Debug for OccupiedEntry<'_, K, V> { +impl Debug for OccupiedEntry<'_, K, V, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OccupiedEntry") .field("key", self.key()) @@ -1829,12 +1956,17 @@ impl Debug for OccupiedEntry<'_, K, V> { /// A view into a vacant entry in a `HashMap`. /// It is part of the [`Entry`] enum. #[stable(feature = "rust1", since = "1.0.0")] -pub struct VacantEntry<'a, K: 'a, V: 'a> { - base: base::RustcVacantEntry<'a, K, V>, +pub struct VacantEntry< + 'a, + K: 'a, + V: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::RustcVacantEntry<'a, K, V, A>, } #[stable(feature = "debug_hash_map", since = "1.12.0")] -impl Debug for VacantEntry<'_, K, V> { +impl Debug for VacantEntry<'_, K, V, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("VacantEntry").field(self.key()).finish() } @@ -1844,15 +1976,20 @@ impl Debug for VacantEntry<'_, K, V> { /// /// Contains the occupied entry, and the value that was not inserted. #[unstable(feature = "map_try_insert", issue = "82766")] -pub struct OccupiedError<'a, K: 'a, V: 'a> { +pub struct OccupiedError< + 'a, + K: 'a, + V: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { /// The entry in the map that was already occupied. - pub entry: OccupiedEntry<'a, K, V>, + pub entry: OccupiedEntry<'a, K, V, A>, /// The value which was not inserted, because the entry was already occupied. pub value: V, } #[unstable(feature = "map_try_insert", issue = "82766")] -impl Debug for OccupiedError<'_, K, V> { +impl Debug for OccupiedError<'_, K, V, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OccupiedError") .field("key", self.entry.key()) @@ -1863,7 +2000,7 @@ impl Debug for OccupiedError<'_, K, V> { } #[unstable(feature = "map_try_insert", issue = "82766")] -impl<'a, K: Debug, V: Debug> fmt::Display for OccupiedError<'a, K, V> { +impl<'a, K: Debug, V: Debug, A: Allocator> fmt::Display for OccupiedError<'a, K, V, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, @@ -1876,10 +2013,10 @@ impl<'a, K: Debug, V: Debug> fmt::Display for OccupiedError<'a, K, V> { } #[unstable(feature = "map_try_insert", issue = "82766")] -impl<'a, K: Debug, V: Debug> Error for OccupiedError<'a, K, V> {} +impl<'a, K: Debug, V: Debug, A: Allocator> Error for OccupiedError<'a, K, V, A> {} #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V, S> IntoIterator for &'a HashMap { +impl<'a, K, V, S, A: Allocator> IntoIterator for &'a HashMap { type Item = (&'a K, &'a V); type IntoIter = Iter<'a, K, V>; @@ -1891,7 +2028,7 @@ impl<'a, K, V, S> IntoIterator for &'a HashMap { } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V, S> IntoIterator for &'a mut HashMap { +impl<'a, K, V, S, A: Allocator> IntoIterator for &'a mut HashMap { type Item = (&'a K, &'a mut V); type IntoIter = IterMut<'a, K, V>; @@ -1903,9 +2040,9 @@ impl<'a, K, V, S> IntoIterator for &'a mut HashMap { } #[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for HashMap { +impl IntoIterator for HashMap { type Item = (K, V); - type IntoIter = IntoIter; + type IntoIter = IntoIter; /// Creates a consuming iterator, that is, one that moves each key-value /// pair out of the map in arbitrary order. The map cannot be used after @@ -1927,7 +2064,7 @@ impl IntoIterator for HashMap { /// ``` #[inline] #[rustc_lint_query_instability] - fn into_iter(self) -> IntoIter { + fn into_iter(self) -> IntoIter { IntoIter { base: self.base.into_iter() } } } @@ -2015,7 +2152,7 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { +impl Iterator for IntoIter { type Item = (K, V); #[inline] @@ -2040,17 +2177,17 @@ impl Iterator for IntoIter { } } #[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { +impl ExactSizeIterator for IntoIter { #[inline] fn len(&self) -> usize { self.base.len() } } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} +impl FusedIterator for IntoIter {} #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for IntoIter { +impl fmt::Debug for IntoIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.iter()).finish() } @@ -2169,7 +2306,7 @@ impl fmt::Debug for ValuesMut<'_, K, V> { } #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl Iterator for IntoKeys { +impl Iterator for IntoKeys { type Item = K; #[inline] @@ -2194,24 +2331,24 @@ impl Iterator for IntoKeys { } } #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl ExactSizeIterator for IntoKeys { +impl ExactSizeIterator for IntoKeys { #[inline] fn len(&self) -> usize { self.inner.len() } } #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl FusedIterator for IntoKeys {} +impl FusedIterator for IntoKeys {} #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl fmt::Debug for IntoKeys { +impl fmt::Debug for IntoKeys { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.inner.iter().map(|(k, _)| k)).finish() } } #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl Iterator for IntoValues { +impl Iterator for IntoValues { type Item = V; #[inline] @@ -2236,24 +2373,24 @@ impl Iterator for IntoValues { } } #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl ExactSizeIterator for IntoValues { +impl ExactSizeIterator for IntoValues { #[inline] fn len(&self) -> usize { self.inner.len() } } #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl FusedIterator for IntoValues {} +impl FusedIterator for IntoValues {} #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl fmt::Debug for IntoValues { +impl fmt::Debug for IntoValues { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.inner.iter().map(|(_, v)| v)).finish() } } #[stable(feature = "drain", since = "1.6.0")] -impl<'a, K, V> Iterator for Drain<'a, K, V> { +impl<'a, K, V, A: Allocator> Iterator for Drain<'a, K, V, A> { type Item = (K, V); #[inline] @@ -2274,17 +2411,17 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> { } } #[stable(feature = "drain", since = "1.6.0")] -impl ExactSizeIterator for Drain<'_, K, V> { +impl ExactSizeIterator for Drain<'_, K, V, A> { #[inline] fn len(&self) -> usize { self.base.len() } } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Drain<'_, K, V> {} +impl FusedIterator for Drain<'_, K, V, A> {} #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Drain<'_, K, V> +impl fmt::Debug for Drain<'_, K, V, A> where K: fmt::Debug, V: fmt::Debug, @@ -2295,7 +2432,7 @@ where } #[stable(feature = "hash_extract_if", since = "1.88.0")] -impl Iterator for ExtractIf<'_, K, V, F> +impl Iterator for ExtractIf<'_, K, V, F, A> where F: FnMut(&K, &mut V) -> bool, { @@ -2312,10 +2449,13 @@ where } #[stable(feature = "hash_extract_if", since = "1.88.0")] -impl FusedIterator for ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} +impl FusedIterator for ExtractIf<'_, K, V, F, A> where + F: FnMut(&K, &mut V) -> bool +{ +} #[stable(feature = "hash_extract_if", since = "1.88.0")] -impl fmt::Debug for ExtractIf<'_, K, V, F> +impl fmt::Debug for ExtractIf<'_, K, V, F, A> where K: fmt::Debug, V: fmt::Debug, @@ -2325,7 +2465,7 @@ where } } -impl<'a, K, V> Entry<'a, K, V> { +impl<'a, K, V, A: Allocator> Entry<'a, K, V, A> { /// Ensures a value is in the entry by inserting the default if empty, and returns /// a mutable reference to the value in the entry. /// @@ -2473,7 +2613,7 @@ impl<'a, K, V> Entry<'a, K, V> { /// ``` #[inline] #[stable(feature = "entry_insert", since = "1.83.0")] - pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { + pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, A> { match self { Occupied(mut entry) => { entry.insert(value); @@ -2510,7 +2650,7 @@ impl<'a, K, V: Default> Entry<'a, K, V> { } } -impl<'a, K, V> OccupiedEntry<'a, K, V> { +impl<'a, K, V, A: Allocator> OccupiedEntry<'a, K, V, A> { /// Gets a reference to the key in the entry. /// /// # Examples @@ -2682,7 +2822,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { } } -impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { +impl<'a, K: 'a, V: 'a, A: Allocator> VacantEntry<'a, K, V, A> { /// Gets a reference to the key that would be used when inserting a value /// through the `VacantEntry`. /// @@ -2760,7 +2900,7 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { /// ``` #[inline] #[stable(feature = "entry_insert", since = "1.83.0")] - pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { + pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, A> { let base = self.base.insert_entry(value); OccupiedEntry { base } } @@ -2786,10 +2926,11 @@ where /// Inserts all new key-values from the iterator and replaces values with existing /// keys with new values returned from the iterator. #[stable(feature = "rust1", since = "1.0.0")] -impl Extend<(K, V)> for HashMap +impl Extend<(K, V)> for HashMap where K: Eq + Hash, S: BuildHasher, + A: Allocator, { #[inline] fn extend>(&mut self, iter: T) { @@ -2808,11 +2949,12 @@ where } #[stable(feature = "hash_extend_copy", since = "1.4.0")] -impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap +impl<'a, K, V, S, A> Extend<(&'a K, &'a V)> for HashMap where K: Eq + Hash + Copy, V: Copy, S: BuildHasher, + A: Allocator, { #[inline] fn extend>(&mut self, iter: T) { @@ -2831,7 +2973,9 @@ where } #[inline] -fn map_entry<'a, K: 'a, V: 'a>(raw: base::RustcEntry<'a, K, V>) -> Entry<'a, K, V> { +fn map_entry<'a, K: 'a, V: 'a, A: Allocator>( + raw: base::RustcEntry<'a, K, V, A>, +) -> Entry<'a, K, V, A> { match raw { base::RustcEntry::Occupied(base) => Entry::Occupied(OccupiedEntry { base }), base::RustcEntry::Vacant(base) => Entry::Vacant(VacantEntry { base }), diff --git a/library/std/src/collections/hash/map/tests.rs b/library/std/src/collections/hash/map/tests.rs index 9f7df20a1d7e4..fd7d81b2855f3 100644 --- a/library/std/src/collections/hash/map/tests.rs +++ b/library/std/src/collections/hash/map/tests.rs @@ -3,7 +3,7 @@ use realstd::collections::TryReserveErrorKind::*; use super::Entry::{Occupied, Vacant}; use super::HashMap; -use crate::assert_matches::assert_matches; +use crate::assert_matches; use crate::cell::RefCell; use crate::hash::{BuildHasher, BuildHasherDefault, DefaultHasher, RandomState}; use crate::test_helpers::test_rng; @@ -1053,4 +1053,7 @@ fn const_with_hasher() { assert_eq!(y.len(), 0); y.insert((), ()); assert_eq!(y.len(), 1); + + const Z: HashMap<(), (), BuildHasherDefault> = Default::default(); + assert_eq!(X, Z); } diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 6795da80aacb6..3f3d601e4d30c 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -4,6 +4,7 @@ mod tests; use hashbrown::hash_set as base; use super::map::map_try_reserve_error; +use crate::alloc::{Allocator, Global}; use crate::borrow::Borrow; use crate::collections::TryReserveError; use crate::fmt; @@ -122,8 +123,12 @@ use crate::ops::{BitAnd, BitOr, BitXor, Sub}; /// ``` #[cfg_attr(not(test), rustc_diagnostic_item = "HashSet")] #[stable(feature = "rust1", since = "1.0.0")] -pub struct HashSet { - base: base::HashSet, +pub struct HashSet< + T, + S = RandomState, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::HashSet, } impl HashSet { @@ -166,7 +171,141 @@ impl HashSet { } } +impl HashSet { + /// Creates an empty `HashSet` in the provided allocator. + /// + /// The hash set is initially created with a capacity of 0, so it will not allocate until it + /// is first inserted into. + #[inline] + #[must_use] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn new_in(alloc: A) -> HashSet { + HashSet::with_hasher_in(Default::default(), alloc) + } + + /// Creates an empty `HashSet` with at least the specified capacity. + /// + /// The hash set will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is zero, the hash set will not allocate. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// let set: HashSet = HashSet::with_capacity(10); + /// assert!(set.capacity() >= 10); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn with_capacity_in(capacity: usize, alloc: A) -> HashSet { + HashSet::with_capacity_and_hasher_in(capacity, Default::default(), alloc) + } +} + impl HashSet { + /// Creates a new empty hash set which will use the given hasher to hash + /// keys. + /// + /// The hash set is also created with the default initial capacity. + /// + /// Warning: `hasher` is normally randomly generated, and + /// is designed to allow `HashSet`s to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the `HashSet` to be useful, see its documentation for details. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// use std::hash::RandomState; + /// + /// let s = RandomState::new(); + /// let mut set = HashSet::with_hasher(s); + /// set.insert(2); + /// ``` + #[inline] + #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] + #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] + pub const fn with_hasher(hasher: S) -> HashSet { + HashSet { base: base::HashSet::with_hasher(hasher) } + } + + /// Creates an empty `HashSet` with at least the specified capacity, using + /// `hasher` to hash the keys. + /// + /// The hash set will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is zero, the hash set will not allocate. + /// + /// Warning: `hasher` is normally randomly generated, and + /// is designed to allow `HashSet`s to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the `HashSet` to be useful, see its documentation for details. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// use std::hash::RandomState; + /// + /// let s = RandomState::new(); + /// let mut set = HashSet::with_capacity_and_hasher(10, s); + /// set.insert(1); + /// ``` + #[inline] + #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] + pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet { + HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, hasher) } + } +} + +impl HashSet { + /// Creates a new empty hash set which will use the given hasher to hash + /// keys and will allocate memory using the provided allocator. + /// + /// The hash set is also created with the default initial capacity. + /// + /// Warning: `hasher` is normally randomly generated, and + /// is designed to allow `HashSet`s to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the `HashSet` to be useful, see its documentation for details. + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn with_hasher_in(hasher: S, alloc: A) -> HashSet { + HashSet { base: base::HashSet::with_hasher_in(hasher, alloc) } + } + + /// Creates an empty `HashSet` with at least the specified capacity, using + /// `hasher` to hash the keys and `alloc` to allocate memory. + /// + /// The hash set will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is zero, the hash set will not allocate. + /// + /// Warning: `hasher` is normally randomly generated, and + /// is designed to allow `HashSet`s to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the `HashSet` to be useful, see its documentation for details. + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn with_capacity_and_hasher_in(capacity: usize, hasher: S, alloc: A) -> HashSet { + HashSet { base: base::HashSet::with_capacity_and_hasher_in(capacity, hasher, alloc) } + } + /// Returns the number of elements the set can hold without reallocating. /// /// # Examples @@ -272,7 +411,7 @@ impl HashSet { #[inline] #[rustc_lint_query_instability] #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self) -> Drain<'_, T> { + pub fn drain(&mut self) -> Drain<'_, T, A> { Drain { base: self.base.drain() } } @@ -309,7 +448,7 @@ impl HashSet { #[inline] #[rustc_lint_query_instability] #[stable(feature = "hash_extract_if", since = "1.88.0")] - pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, T, F> + pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, T, F, A> where F: FnMut(&T) -> bool, { @@ -362,67 +501,6 @@ impl HashSet { self.base.clear() } - /// Creates a new empty hash set which will use the given hasher to hash - /// keys. - /// - /// The hash set is also created with the default initial capacity. - /// - /// Warning: `hasher` is normally randomly generated, and - /// is designed to allow `HashSet`s to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the `HashSet` to be useful, see its documentation for details. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// use std::hash::RandomState; - /// - /// let s = RandomState::new(); - /// let mut set = HashSet::with_hasher(s); - /// set.insert(2); - /// ``` - #[inline] - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] - pub const fn with_hasher(hasher: S) -> HashSet { - HashSet { base: base::HashSet::with_hasher(hasher) } - } - - /// Creates an empty `HashSet` with at least the specified capacity, using - /// `hasher` to hash the keys. - /// - /// The hash set will be able to hold at least `capacity` elements without - /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the hash set will not allocate. - /// - /// Warning: `hasher` is normally randomly generated, and - /// is designed to allow `HashSet`s to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the `HashSet` to be useful, see its documentation for details. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// use std::hash::RandomState; - /// - /// let s = RandomState::new(); - /// let mut set = HashSet::with_capacity_and_hasher(10, s); - /// set.insert(1); - /// ``` - #[inline] - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet { - HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, hasher) } - } - /// Returns a reference to the set's [`BuildHasher`]. /// /// # Examples @@ -442,10 +520,11 @@ impl HashSet { } } -impl HashSet +impl HashSet where T: Eq + Hash, S: BuildHasher, + A: Allocator, { /// Reserves capacity for at least `additional` more elements to be inserted /// in the `HashSet`. The collection may reserve more space to speculatively @@ -569,7 +648,7 @@ where #[inline] #[rustc_lint_query_instability] #[stable(feature = "rust1", since = "1.0.0")] - pub fn difference<'a>(&'a self, other: &'a HashSet) -> Difference<'a, T, S> { + pub fn difference<'a>(&'a self, other: &'a HashSet) -> Difference<'a, T, S, A> { Difference { iter: self.iter(), other } } @@ -599,8 +678,8 @@ where #[stable(feature = "rust1", since = "1.0.0")] pub fn symmetric_difference<'a>( &'a self, - other: &'a HashSet, - ) -> SymmetricDifference<'a, T, S> { + other: &'a HashSet, + ) -> SymmetricDifference<'a, T, S, A> { SymmetricDifference { iter: self.difference(other).chain(other.difference(self)) } } @@ -631,7 +710,7 @@ where #[inline] #[rustc_lint_query_instability] #[stable(feature = "rust1", since = "1.0.0")] - pub fn intersection<'a>(&'a self, other: &'a HashSet) -> Intersection<'a, T, S> { + pub fn intersection<'a>(&'a self, other: &'a HashSet) -> Intersection<'a, T, S, A> { if self.len() <= other.len() { Intersection { iter: self.iter(), other } } else { @@ -660,7 +739,7 @@ where #[inline] #[rustc_lint_query_instability] #[stable(feature = "rust1", since = "1.0.0")] - pub fn union<'a>(&'a self, other: &'a HashSet) -> Union<'a, T, S> { + pub fn union<'a>(&'a self, other: &'a HashSet) -> Union<'a, T, S, A> { if self.len() >= other.len() { Union { iter: self.iter().chain(other.difference(self)) } } else { @@ -812,7 +891,7 @@ where /// ``` #[inline] #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn entry(&mut self, value: T) -> Entry<'_, T, S> { + pub fn entry(&mut self, value: T) -> Entry<'_, T, S, A> { map_entry(self.base.entry(value)) } @@ -834,7 +913,7 @@ where /// assert_eq!(a.is_disjoint(&b), false); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_disjoint(&self, other: &HashSet) -> bool { + pub fn is_disjoint(&self, other: &HashSet) -> bool { if self.len() <= other.len() { self.iter().all(|v| !other.contains(v)) } else { @@ -860,7 +939,7 @@ where /// assert_eq!(set.is_subset(&sup), false); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_subset(&self, other: &HashSet) -> bool { + pub fn is_subset(&self, other: &HashSet) -> bool { if self.len() <= other.len() { self.iter().all(|v| other.contains(v)) } else { false } } @@ -886,7 +965,7 @@ where /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_superset(&self, other: &HashSet) -> bool { + pub fn is_superset(&self, other: &HashSet) -> bool { other.is_subset(self) } @@ -995,7 +1074,7 @@ where } #[inline] -fn map_entry<'a, K: 'a, V: 'a>(raw: base::Entry<'a, K, V>) -> Entry<'a, K, V> { +fn map_entry<'a, K: 'a, V: 'a, A: Allocator>(raw: base::Entry<'a, K, V, A>) -> Entry<'a, K, V, A> { match raw { base::Entry::Occupied(base) => Entry::Occupied(OccupiedEntry { base }), base::Entry::Vacant(base) => Entry::Vacant(VacantEntry { base }), @@ -1003,10 +1082,11 @@ fn map_entry<'a, K: 'a, V: 'a>(raw: base::Entry<'a, K, V>) -> Entry<'a, K, V> { } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for HashSet +impl Clone for HashSet where T: Clone, S: Clone, + A: Allocator + Clone, { #[inline] fn clone(&self) -> Self { @@ -1024,12 +1104,13 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for HashSet +impl PartialEq for HashSet where T: Eq + Hash, S: BuildHasher, + A: Allocator, { - fn eq(&self, other: &HashSet) -> bool { + fn eq(&self, other: &HashSet) -> bool { if self.len() != other.len() { return false; } @@ -1039,17 +1120,19 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Eq for HashSet +impl Eq for HashSet where T: Eq + Hash, S: BuildHasher, + A: Allocator, { } #[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for HashSet +impl fmt::Debug for HashSet where T: fmt::Debug, + A: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_set().entries(self.iter()).finish() @@ -1107,10 +1190,11 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Extend for HashSet +impl Extend for HashSet where T: Eq + Hash, S: BuildHasher, + A: Allocator, { #[inline] fn extend>(&mut self, iter: I) { @@ -1129,10 +1213,11 @@ where } #[stable(feature = "hash_extend_copy", since = "1.4.0")] -impl<'a, T, S> Extend<&'a T> for HashSet +impl<'a, T, S, A> Extend<&'a T> for HashSet where T: 'a + Eq + Hash + Copy, S: BuildHasher, + A: Allocator, { #[inline] fn extend>(&mut self, iter: I) { @@ -1151,14 +1236,15 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Default for HashSet +#[rustc_const_unstable(feature = "const_default", issue = "143894")] +impl const Default for HashSet where - S: Default, + S: [const] Default, { /// Creates an empty `HashSet` with the `Default` value for the hasher. #[inline] fn default() -> HashSet { - HashSet { base: Default::default() } + HashSet { base: base::HashSet::with_hasher(Default::default()) } } } @@ -1341,8 +1427,11 @@ impl Default for Iter<'_, K> { /// let mut iter = a.into_iter(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter { - base: base::IntoIter, +pub struct IntoIter< + K, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::IntoIter, } #[stable(feature = "default_iters_hash", since = "1.83.0")] @@ -1371,8 +1460,12 @@ impl Default for IntoIter { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "hashset_drain_ty")] -pub struct Drain<'a, K: 'a> { - base: base::Drain<'a, K>, +pub struct Drain< + 'a, + K: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::Drain<'a, K, A>, } /// A draining, filtering iterator over the items of a `HashSet`. @@ -1393,8 +1486,13 @@ pub struct Drain<'a, K: 'a> { #[stable(feature = "hash_extract_if", since = "1.88.0")] #[must_use = "iterators are lazy and do nothing unless consumed; \ use `retain` to remove and discard elements"] -pub struct ExtractIf<'a, K, F> { - base: base::ExtractIf<'a, K, F>, +pub struct ExtractIf< + 'a, + K, + F, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::ExtractIf<'a, K, F, A>, } /// A lazy iterator producing elements in the intersection of `HashSet`s. @@ -1417,11 +1515,16 @@ pub struct ExtractIf<'a, K, F> { #[must_use = "this returns the intersection as an iterator, \ without modifying either input set"] #[stable(feature = "rust1", since = "1.0.0")] -pub struct Intersection<'a, T: 'a, S: 'a> { +pub struct Intersection< + 'a, + T: 'a, + S: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { // iterator of the first set iter: Iter<'a, T>, // the second set - other: &'a HashSet, + other: &'a HashSet, } /// A lazy iterator producing elements in the difference of `HashSet`s. @@ -1444,11 +1547,16 @@ pub struct Intersection<'a, T: 'a, S: 'a> { #[must_use = "this returns the difference as an iterator, \ without modifying either input set"] #[stable(feature = "rust1", since = "1.0.0")] -pub struct Difference<'a, T: 'a, S: 'a> { +pub struct Difference< + 'a, + T: 'a, + S: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { // iterator of the first set iter: Iter<'a, T>, // the second set - other: &'a HashSet, + other: &'a HashSet, } /// A lazy iterator producing elements in the symmetric difference of `HashSet`s. @@ -1471,8 +1579,13 @@ pub struct Difference<'a, T: 'a, S: 'a> { #[must_use = "this returns the difference as an iterator, \ without modifying either input set"] #[stable(feature = "rust1", since = "1.0.0")] -pub struct SymmetricDifference<'a, T: 'a, S: 'a> { - iter: Chain, Difference<'a, T, S>>, +pub struct SymmetricDifference< + 'a, + T: 'a, + S: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + iter: Chain, Difference<'a, T, S, A>>, } /// A lazy iterator producing elements in the union of `HashSet`s. @@ -1495,12 +1608,17 @@ pub struct SymmetricDifference<'a, T: 'a, S: 'a> { #[must_use = "this returns the union as an iterator, \ without modifying either input set"] #[stable(feature = "rust1", since = "1.0.0")] -pub struct Union<'a, T: 'a, S: 'a> { - iter: Chain, Difference<'a, T, S>>, +pub struct Union< + 'a, + T: 'a, + S: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + iter: Chain, Difference<'a, T, S, A>>, } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> IntoIterator for &'a HashSet { +impl<'a, T, S, A: Allocator> IntoIterator for &'a HashSet { type Item = &'a T; type IntoIter = Iter<'a, T>; @@ -1512,9 +1630,9 @@ impl<'a, T, S> IntoIterator for &'a HashSet { } #[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for HashSet { +impl IntoIterator for HashSet { type Item = T; - type IntoIter = IntoIter; + type IntoIter = IntoIter; /// Creates a consuming iterator, that is, one that moves each value out /// of the set in arbitrary order. The set cannot be used after calling @@ -1538,7 +1656,7 @@ impl IntoIterator for HashSet { /// ``` #[inline] #[rustc_lint_query_instability] - fn into_iter(self) -> IntoIter { + fn into_iter(self) -> IntoIter { IntoIter { base: self.base.into_iter() } } } @@ -1593,7 +1711,7 @@ impl fmt::Debug for Iter<'_, K> { } #[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { +impl Iterator for IntoIter { type Item = K; #[inline] @@ -1618,24 +1736,24 @@ impl Iterator for IntoIter { } } #[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { +impl ExactSizeIterator for IntoIter { #[inline] fn len(&self) -> usize { self.base.len() } } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} +impl FusedIterator for IntoIter {} #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for IntoIter { +impl fmt::Debug for IntoIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.base, f) } } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K> Iterator for Drain<'a, K> { +impl<'a, K, A: Allocator> Iterator for Drain<'a, K, A> { type Item = K; #[inline] @@ -1656,24 +1774,24 @@ impl<'a, K> Iterator for Drain<'a, K> { } } #[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Drain<'_, K> { +impl ExactSizeIterator for Drain<'_, K, A> { #[inline] fn len(&self) -> usize { self.base.len() } } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Drain<'_, K> {} +impl FusedIterator for Drain<'_, K, A> {} #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Drain<'_, K> { +impl fmt::Debug for Drain<'_, K, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.base, f) } } #[stable(feature = "hash_extract_if", since = "1.88.0")] -impl Iterator for ExtractIf<'_, K, F> +impl Iterator for ExtractIf<'_, K, F, A> where F: FnMut(&K) -> bool, { @@ -1690,10 +1808,10 @@ where } #[stable(feature = "hash_extract_if", since = "1.88.0")] -impl FusedIterator for ExtractIf<'_, K, F> where F: FnMut(&K) -> bool {} +impl FusedIterator for ExtractIf<'_, K, F, A> where F: FnMut(&K) -> bool {} #[stable(feature = "hash_extract_if", since = "1.88.0")] -impl fmt::Debug for ExtractIf<'_, K, F> +impl fmt::Debug for ExtractIf<'_, K, F, A> where K: fmt::Debug, { @@ -1703,7 +1821,7 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Intersection<'_, T, S> { +impl Clone for Intersection<'_, T, S, A> { #[inline] fn clone(&self) -> Self { Intersection { iter: self.iter.clone(), ..*self } @@ -1711,10 +1829,11 @@ impl Clone for Intersection<'_, T, S> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> Iterator for Intersection<'a, T, S> +impl<'a, T, S, A> Iterator for Intersection<'a, T, S, A> where T: Eq + Hash, S: BuildHasher, + A: Allocator, { type Item = &'a T; @@ -1745,10 +1864,11 @@ where } #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Intersection<'_, T, S> +impl fmt::Debug for Intersection<'_, T, S, A> where T: fmt::Debug + Eq + Hash, S: BuildHasher, + A: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() @@ -1756,15 +1876,16 @@ where } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Intersection<'_, T, S> +impl FusedIterator for Intersection<'_, T, S, A> where T: Eq + Hash, S: BuildHasher, + A: Allocator, { } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Difference<'_, T, S> { +impl Clone for Difference<'_, T, S, A> { #[inline] fn clone(&self) -> Self { Difference { iter: self.iter.clone(), ..*self } @@ -1772,10 +1893,11 @@ impl Clone for Difference<'_, T, S> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> Iterator for Difference<'a, T, S> +impl<'a, T, S, A> Iterator for Difference<'a, T, S, A> where T: Eq + Hash, S: BuildHasher, + A: Allocator, { type Item = &'a T; @@ -1806,18 +1928,20 @@ where } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Difference<'_, T, S> +impl FusedIterator for Difference<'_, T, S, A> where T: Eq + Hash, S: BuildHasher, + A: Allocator, { } #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Difference<'_, T, S> +impl fmt::Debug for Difference<'_, T, S, A> where T: fmt::Debug + Eq + Hash, S: BuildHasher, + A: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() @@ -1825,7 +1949,7 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for SymmetricDifference<'_, T, S> { +impl Clone for SymmetricDifference<'_, T, S, A> { #[inline] fn clone(&self) -> Self { SymmetricDifference { iter: self.iter.clone() } @@ -1833,10 +1957,11 @@ impl Clone for SymmetricDifference<'_, T, S> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> Iterator for SymmetricDifference<'a, T, S> +impl<'a, T, S, A> Iterator for SymmetricDifference<'a, T, S, A> where T: Eq + Hash, S: BuildHasher, + A: Allocator, { type Item = &'a T; @@ -1859,18 +1984,20 @@ where } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for SymmetricDifference<'_, T, S> +impl FusedIterator for SymmetricDifference<'_, T, S, A> where T: Eq + Hash, S: BuildHasher, + A: Allocator, { } #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for SymmetricDifference<'_, T, S> +impl fmt::Debug for SymmetricDifference<'_, T, S, A> where T: fmt::Debug + Eq + Hash, S: BuildHasher, + A: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() @@ -1878,7 +2005,7 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Union<'_, T, S> { +impl Clone for Union<'_, T, S, A> { #[inline] fn clone(&self) -> Self { Union { iter: self.iter.clone() } @@ -1886,7 +2013,7 @@ impl Clone for Union<'_, T, S> { } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Union<'_, T, S> +impl FusedIterator for Union<'_, T, S, A> where T: Eq + Hash, S: BuildHasher, @@ -1894,10 +2021,11 @@ where } #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Union<'_, T, S> +impl fmt::Debug for Union<'_, T, S, A> where T: fmt::Debug + Eq + Hash, S: BuildHasher, + A: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() @@ -1905,10 +2033,11 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> Iterator for Union<'a, T, S> +impl<'a, T, S, A> Iterator for Union<'a, T, S, A> where T: Eq + Hash, S: BuildHasher, + A: Allocator, { type Item = &'a T; @@ -1973,7 +2102,12 @@ where /// assert_eq!(vec, ["a", "b", "c", "d", "e"]); /// ``` #[unstable(feature = "hash_set_entry", issue = "60896")] -pub enum Entry<'a, T, S> { +pub enum Entry< + 'a, + T, + S, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { /// An occupied entry. /// /// # Examples @@ -1990,7 +2124,7 @@ pub enum Entry<'a, T, S> { /// Entry::Occupied(_) => { } /// } /// ``` - Occupied(OccupiedEntry<'a, T, S>), + Occupied(OccupiedEntry<'a, T, S, A>), /// A vacant entry. /// @@ -2008,11 +2142,11 @@ pub enum Entry<'a, T, S> { /// Entry::Vacant(_) => { } /// } /// ``` - Vacant(VacantEntry<'a, T, S>), + Vacant(VacantEntry<'a, T, S, A>), } #[unstable(feature = "hash_set_entry", issue = "60896")] -impl fmt::Debug for Entry<'_, T, S> { +impl fmt::Debug for Entry<'_, T, S, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Entry::Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), @@ -2060,12 +2194,17 @@ impl fmt::Debug for Entry<'_, T, S> { /// assert_eq!(set.len(), 2); /// ``` #[unstable(feature = "hash_set_entry", issue = "60896")] -pub struct OccupiedEntry<'a, T, S> { - base: base::OccupiedEntry<'a, T, S>, +pub struct OccupiedEntry< + 'a, + T, + S, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::OccupiedEntry<'a, T, S, A>, } #[unstable(feature = "hash_set_entry", issue = "60896")] -impl fmt::Debug for OccupiedEntry<'_, T, S> { +impl fmt::Debug for OccupiedEntry<'_, T, S, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OccupiedEntry").field("value", self.get()).finish() } @@ -2100,18 +2239,23 @@ impl fmt::Debug for OccupiedEntry<'_, T, S> { /// assert!(set.contains("b") && set.len() == 2); /// ``` #[unstable(feature = "hash_set_entry", issue = "60896")] -pub struct VacantEntry<'a, T, S> { - base: base::VacantEntry<'a, T, S>, +pub struct VacantEntry< + 'a, + T, + S, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::VacantEntry<'a, T, S, A>, } #[unstable(feature = "hash_set_entry", issue = "60896")] -impl fmt::Debug for VacantEntry<'_, T, S> { +impl fmt::Debug for VacantEntry<'_, T, S, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("VacantEntry").field(self.get()).finish() } } -impl<'a, T, S> Entry<'a, T, S> { +impl<'a, T, S, A: Allocator> Entry<'a, T, S, A> { /// Sets the value of the entry, and returns an OccupiedEntry. /// /// # Examples @@ -2128,7 +2272,7 @@ impl<'a, T, S> Entry<'a, T, S> { /// ``` #[inline] #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn insert(self) -> OccupiedEntry<'a, T, S> + pub fn insert(self) -> OccupiedEntry<'a, T, S, A> where T: Hash, S: BuildHasher, @@ -2198,7 +2342,7 @@ impl<'a, T, S> Entry<'a, T, S> { } } -impl OccupiedEntry<'_, T, S> { +impl OccupiedEntry<'_, T, S, A> { /// Gets a reference to the value in the entry. /// /// # Examples @@ -2255,7 +2399,7 @@ impl OccupiedEntry<'_, T, S> { } } -impl<'a, T, S> VacantEntry<'a, T, S> { +impl<'a, T, S, A: Allocator> VacantEntry<'a, T, S, A> { /// Gets a reference to the value that would be used when inserting /// through the `VacantEntry`. /// @@ -2325,7 +2469,7 @@ impl<'a, T, S> VacantEntry<'a, T, S> { } #[inline] - fn insert_entry(self) -> OccupiedEntry<'a, T, S> + fn insert_entry(self) -> OccupiedEntry<'a, T, S, A> where T: Hash, S: BuildHasher, diff --git a/library/std/src/collections/hash/set/tests.rs b/library/std/src/collections/hash/set/tests.rs index 8ee8a3e8bf6ae..d7bbc7bdc6a5b 100644 --- a/library/std/src/collections/hash/set/tests.rs +++ b/library/std/src/collections/hash/set/tests.rs @@ -504,7 +504,9 @@ fn from_array() { #[test] fn const_with_hasher() { const X: HashSet<(), ()> = HashSet::with_hasher(()); + const Y: HashSet<(), ()> = Default::default(); assert_eq!(X.len(), 0); + assert_eq!(Y.len(), 0); } #[test] diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 5c068ad2471ad..1f0ced5d0fd0d 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -518,8 +518,8 @@ pub struct JoinPathsError { /// /// Returns an [`Err`] (containing an error message) if one of the input /// [`Path`]s contains an invalid character for constructing the `PATH` -/// variable (a double quote on Windows or a colon on Unix), or if the system -/// does not have a `PATH`-like variable (e.g. UEFI or WASI). +/// variable (a double quote on Windows or a colon on Unix or semicolon on +/// UEFI), or if the system does not have a `PATH`-like variable (e.g. WASI). /// /// # Examples /// @@ -728,7 +728,7 @@ pub fn temp_dir() -> PathBuf { /// /// You expected to safely execute the current executable, but you're /// instead executing something completely different. The code you -/// just executed run with your privileges. +/// just executed runs with your privileges. /// /// This sort of behavior has been known to [lead to privilege escalation] when /// used incorrectly. diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index f44e12d48addf..999bd5e63dc45 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -172,7 +172,7 @@ pub use core::ffi::c_void; all supported platforms", issue = "44930" )] -pub use core::ffi::{VaArgSafe, VaList, VaListImpl}; +pub use core::ffi::{VaArgSafe, VaList}; #[stable(feature = "core_ffi_c", since = "1.64.0")] pub use core::ffi::{ c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 09bd911aa769a..4e4d377ae2708 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -13,7 +13,7 @@ use crate::rc::Rc; use crate::str::FromStr; use crate::sync::Arc; use crate::sys::os_str::{Buf, Slice}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{cmp, fmt, slice}; /// A type that can represent owned, mutable platform-native strings, but is @@ -1278,6 +1278,17 @@ impl OsStr { pub fn display(&self) -> Display<'_> { Display { os_str: self } } + + /// Returns the same string as a string slice `&OsStr`. + /// + /// This method is redundant when used directly on `&OsStr`, but + /// it helps dereferencing other string-like types to string slices, + /// for example references to `Box` or `Arc`. + #[inline] + #[unstable(feature = "str_as_str", issue = "130366")] + pub const fn as_os_str(&self) -> &OsStr { + self + } } #[stable(feature = "box_from_os_str", since = "1.17.0")] @@ -1285,8 +1296,7 @@ impl From<&OsStr> for Box { /// Copies the string into a newly allocated [Box]<[OsStr]>. #[inline] fn from(s: &OsStr) -> Box { - let rw = Box::into_raw(s.inner.into_box()) as *mut OsStr; - unsafe { Box::from_raw(rw) } + Box::clone_from_ref(s) } } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 8b3e943b4ccd0..a24baad615012 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -46,8 +46,7 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, use crate::path::{Path, PathBuf}; use crate::sealed::Sealed; use crate::sync::Arc; -use crate::sys::fs as fs_imp; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, fs as fs_imp}; use crate::time::SystemTime; use crate::{error, fmt}; @@ -153,6 +152,43 @@ pub enum TryLockError { WouldBlock, } +/// An object providing access to a directory on the filesystem. +/// +/// Directories are automatically closed when they go out of scope. Errors detected +/// on closing are ignored by the implementation of `Drop`. +/// +/// # Platform-specific behavior +/// +/// On supported systems (including Windows and some UNIX-based OSes), this function acquires a +/// handle/file descriptor for the directory. This allows functions like [`Dir::open_file`] to +/// avoid [TOCTOU] errors when the directory itself is being moved. +/// +/// On other systems, it stores an absolute path (see [`canonicalize()`]). In the latter case, no +/// [TOCTOU] guarantees are made. +/// +/// # Examples +/// +/// Opens a directory and then a file inside it. +/// +/// ```no_run +/// #![feature(dirfd)] +/// use std::{fs::Dir, io}; +/// +/// fn main() -> std::io::Result<()> { +/// let dir = Dir::open("foo")?; +/// let mut file = dir.open_file("bar.txt")?; +/// let contents = io::read_to_string(file)?; +/// assert_eq!(contents, "Hello, world!"); +/// Ok(()) +/// } +/// ``` +/// +/// [TOCTOU]: self#time-of-check-to-time-of-use-toctou +#[unstable(feature = "dirfd", issue = "120426")] +pub struct Dir { + inner: fs_imp::Dir, +} + /// Metadata information about a file. /// /// This structure is returned from the [`metadata`] or @@ -1324,7 +1360,7 @@ impl Read for &File { /// /// # Platform-specific behavior /// - /// This function currently returns `true` on Unix an `false` on Windows. + /// This function currently returns `true` on Unix and `false` on Windows. /// Note that this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior @@ -1385,7 +1421,7 @@ impl Write for &File { /// /// # Platform-specific behavior /// - /// This function currently returns `true` on Unix an `false` on Windows. + /// This function currently returns `true` on Unix and `false` on Windows. /// Note that this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior @@ -1555,6 +1591,87 @@ impl Seek for Arc { } } +impl Dir { + /// Attempts to open a directory at `path` in read-only mode. + /// + /// # Errors + /// + /// This function will return an error if `path` does not point to an existing directory. + /// Other errors may also be returned according to [`OpenOptions::open`]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(dirfd)] + /// use std::{fs::Dir, io}; + /// + /// fn main() -> std::io::Result<()> { + /// let dir = Dir::open("foo")?; + /// let mut f = dir.open_file("bar.txt")?; + /// let contents = io::read_to_string(f)?; + /// assert_eq!(contents, "Hello, world!"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "dirfd", issue = "120426")] + pub fn open>(path: P) -> io::Result { + fs_imp::Dir::open(path.as_ref(), &OpenOptions::new().read(true).0) + .map(|inner| Self { inner }) + } + + /// Attempts to open a file in read-only mode relative to this directory. + /// + /// # Errors + /// + /// This function will return an error if `path` does not point to an existing file. + /// Other errors may also be returned according to [`OpenOptions::open`]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(dirfd)] + /// use std::{fs::Dir, io}; + /// + /// fn main() -> std::io::Result<()> { + /// let dir = Dir::open("foo")?; + /// let mut f = dir.open_file("bar.txt")?; + /// let contents = io::read_to_string(f)?; + /// assert_eq!(contents, "Hello, world!"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "dirfd", issue = "120426")] + pub fn open_file>(&self, path: P) -> io::Result { + self.inner + .open_file(path.as_ref(), &OpenOptions::new().read(true).0) + .map(|f| File { inner: f }) + } +} + +impl AsInner for Dir { + #[inline] + fn as_inner(&self) -> &fs_imp::Dir { + &self.inner + } +} +impl FromInner for Dir { + fn from_inner(f: fs_imp::Dir) -> Dir { + Dir { inner: f } + } +} +impl IntoInner for Dir { + fn into_inner(self) -> fs_imp::Dir { + self.inner + } +} + +#[unstable(feature = "dirfd", issue = "120426")] +impl fmt::Debug for Dir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + impl OpenOptions { /// Creates a blank new set of options ready for configuration. /// @@ -3329,30 +3446,48 @@ impl DirBuilder { } fn create_dir_all(&self, path: &Path) -> io::Result<()> { - if path == Path::new("") { + // if path's parent is None, it is "/" path, which should + // return Ok immediately + if path == Path::new("") || path.parent() == None { return Ok(()); } - match self.inner.mkdir(path) { - Ok(()) => return Ok(()), - Err(ref e) if e.kind() == io::ErrorKind::NotFound => {} - Err(_) if path.is_dir() => return Ok(()), - Err(e) => return Err(e), - } - match path.parent() { - Some(p) => self.create_dir_all(p)?, - None => { - return Err(io::const_error!( - io::ErrorKind::Uncategorized, - "failed to create whole tree", - )); + let ancestors = path.ancestors(); + let mut uncreated_dirs = 0; + + for ancestor in ancestors { + // for relative paths like "foo/bar", the parent of + // "foo" will be "" which there's no need to invoke + // a mkdir syscall on + if ancestor == Path::new("") || ancestor.parent() == None { + break; + } + + match self.inner.mkdir(ancestor) { + Ok(()) => break, + Err(e) if e.kind() == io::ErrorKind::NotFound => uncreated_dirs += 1, + // we check if the err is AlreadyExists for two reasons + // - in case the path exists as a *file* + // - and to avoid calls to .is_dir() in case of other errs + // (i.e. PermissionDenied) + Err(e) if e.kind() == io::ErrorKind::AlreadyExists && ancestor.is_dir() => break, + Err(e) => return Err(e), } } - match self.inner.mkdir(path) { - Ok(()) => Ok(()), - Err(_) if path.is_dir() => Ok(()), - Err(e) => Err(e), + + // collect only the uncreated directories w/o letting the vec resize + let mut uncreated_dirs_vec = Vec::with_capacity(uncreated_dirs); + uncreated_dirs_vec.extend(ancestors.take(uncreated_dirs)); + + for uncreated_dir in uncreated_dirs_vec.iter().rev() { + if let Err(e) = self.inner.mkdir(uncreated_dir) { + if e.kind() != io::ErrorKind::AlreadyExists || !uncreated_dir.is_dir() { + return Err(e); + } + } } + + Ok(()) } } diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 0a5d1153d860c..42f3ccc340b27 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1,7 +1,10 @@ use rand::RngCore; -use crate::assert_matches::assert_matches; +#[cfg(not(miri))] +use super::Dir; use crate::fs::{self, File, FileTimes, OpenOptions, TryLockError}; +#[cfg(not(miri))] +use crate::io; use crate::io::prelude::*; use crate::io::{BorrowedBuf, ErrorKind, SeekFrom}; use crate::mem::MaybeUninit; @@ -17,7 +20,7 @@ use crate::path::Path; use crate::sync::Arc; use crate::test_helpers::{TempDir, tmpdir}; use crate::time::{Duration, Instant, SystemTime}; -use crate::{env, str, thread}; +use crate::{assert_matches, env, str, thread}; macro_rules! check { ($e:expr) => { @@ -212,6 +215,7 @@ fn file_test_io_seek_and_write() { target_os = "cygwin", target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "illumos", target_os = "linux", target_os = "netbsd", @@ -244,6 +248,7 @@ fn file_lock_multiple_shared() { target_os = "cygwin", target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "illumos", target_os = "linux", target_os = "netbsd", @@ -277,6 +282,7 @@ fn file_lock_blocking() { target_os = "cygwin", target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "illumos", target_os = "linux", target_os = "netbsd", @@ -307,6 +313,7 @@ fn file_lock_drop() { target_os = "cygwin", target_os = "freebsd", target_os = "fuchsia", + target_os = "hurd", target_os = "illumos", target_os = "linux", target_os = "netbsd", @@ -2294,6 +2301,57 @@ fn test_fs_set_times() { } } +#[test] +fn test_fs_set_times_on_dir() { + #[cfg(target_vendor = "apple")] + use crate::os::darwin::fs::FileTimesExt; + #[cfg(windows)] + use crate::os::windows::fs::FileTimesExt; + + let tmp = tmpdir(); + let dir_path = tmp.join("testdir"); + fs::create_dir(&dir_path).unwrap(); + + let mut times = FileTimes::new(); + let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); + let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); + times = times.set_accessed(accessed).set_modified(modified); + + #[cfg(any(windows, target_vendor = "apple"))] + let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); + #[cfg(any(windows, target_vendor = "apple"))] + { + times = times.set_created(created); + } + + match fs::set_times(&dir_path, times) { + // Allow unsupported errors on platforms which don't support setting times. + #[cfg(not(any( + windows, + all( + unix, + not(any( + target_os = "android", + target_os = "redox", + target_os = "espidf", + target_os = "horizon" + )) + ) + )))] + Err(e) if e.kind() == ErrorKind::Unsupported => return, + Err(e) => panic!("error setting directory times: {e:?}"), + Ok(_) => {} + } + + let metadata = fs::metadata(&dir_path).unwrap(); + assert_eq!(metadata.accessed().unwrap(), accessed); + assert_eq!(metadata.modified().unwrap(), modified); + #[cfg(any(windows, target_vendor = "apple"))] + { + assert_eq!(metadata.created().unwrap(), created); + } +} + #[test] fn test_fs_set_times_follows_symlink() { #[cfg(target_vendor = "apple")] @@ -2461,3 +2519,30 @@ fn test_fs_set_times_nofollow() { assert_ne!(target_metadata.accessed().unwrap(), accessed); assert_ne!(target_metadata.modified().unwrap(), modified); } + +#[test] +// FIXME: libc calls fail on miri +#[cfg(not(miri))] +fn test_dir_smoke_test() { + let tmpdir = tmpdir(); + let dir = Dir::open(tmpdir.path()); + check!(dir); +} + +#[test] +// FIXME: libc calls fail on miri +#[cfg(not(miri))] +fn test_dir_read_file() { + let tmpdir = tmpdir(); + let mut f = check!(File::create(tmpdir.join("foo.txt"))); + check!(f.write(b"bar")); + check!(f.flush()); + drop(f); + let dir = check!(Dir::open(tmpdir.path())); + let f = check!(dir.open_file("foo.txt")); + let buf = check!(io::read_to_string(f)); + assert_eq!("bar", &buf); + let f = check!(dir.open_file(tmpdir.join("foo.txt"))); + let buf = check!(io::read_to_string(f)); + assert_eq!("bar", &buf); +} diff --git a/library/std/src/hash/random.rs b/library/std/src/hash/random.rs index 236803b24a2ec..3c1b21eec9759 100644 --- a/library/std/src/hash/random.rs +++ b/library/std/src/hash/random.rs @@ -7,7 +7,6 @@ //! //! [`collections`]: crate::collections -#[allow(deprecated)] use super::{BuildHasher, Hasher, SipHasher13}; use crate::cell::Cell; use crate::fmt; @@ -81,7 +80,6 @@ impl RandomState { impl BuildHasher for RandomState { type Hasher = DefaultHasher; #[inline] - #[allow(deprecated)] fn build_hasher(&self) -> DefaultHasher { DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1)) } @@ -91,7 +89,6 @@ impl BuildHasher for RandomState { /// /// The internal algorithm is not specified, and so it and its hashes should /// not be relied upon over releases. -#[allow(deprecated)] #[derive(Clone, Debug)] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub struct DefaultHasher(SipHasher13); @@ -104,15 +101,16 @@ impl DefaultHasher { /// instances created through `new` or `default`. #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] #[inline] - #[allow(deprecated)] + #[rustc_const_unstable(feature = "const_default", issue = "143894")] #[must_use] - pub fn new() -> DefaultHasher { + pub const fn new() -> DefaultHasher { DefaultHasher(SipHasher13::new_with_keys(0, 0)) } } #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] -impl Default for DefaultHasher { +#[rustc_const_unstable(feature = "const_default", issue = "143894")] +impl const Default for DefaultHasher { /// Creates a new `DefaultHasher` using [`new`]. /// See its documentation for more. /// diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 9b600cd55758b..ad8608bf61908 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -48,7 +48,7 @@ impl Buffer { #[inline] pub fn buffer(&self) -> &[u8] { - // SAFETY: self.pos and self.cap are valid, and self.cap => self.pos, and + // SAFETY: self.pos and self.filled are valid, and self.filled >= self.pos, and // that region is initialized because those are all invariants of this type. unsafe { self.buf.get_unchecked(self.pos..self.filled).assume_init_ref() } } diff --git a/library/std/src/io/buffered/linewritershim.rs b/library/std/src/io/buffered/linewritershim.rs index 5ebeada59bb53..967e24812b9ff 100644 --- a/library/std/src/io/buffered/linewritershim.rs +++ b/library/std/src/io/buffered/linewritershim.rs @@ -52,7 +52,7 @@ impl<'a, W: ?Sized + Write> LineWriterShim<'a, W> { } impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { - /// Writes some data into this BufReader with line buffering. + /// Writes some data into this BufWriter with line buffering. /// /// This means that, if any newlines are present in the data, the data up to /// the last newline is sent directly to the underlying writer, and data @@ -146,7 +146,7 @@ impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { self.buffer.flush() } - /// Writes some vectored data into this BufReader with line buffering. + /// Writes some vectored data into this BufWriter with line buffering. /// /// This means that, if any newlines are present in the data, the data up to /// and including the buffer containing the last newline is sent directly to @@ -256,7 +256,7 @@ impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { self.inner().is_write_vectored() } - /// Writes some data into this BufReader with line buffering. + /// Writes some data into this BufWriter with line buffering. /// /// This means that, if any newlines are present in the data, the data up to /// the last newline is sent directly to the underlying writer, and data diff --git a/library/std/src/io/buffered/tests.rs b/library/std/src/io/buffered/tests.rs index 17f6107aa030c..6ad4158b92904 100644 --- a/library/std/src/io/buffered/tests.rs +++ b/library/std/src/io/buffered/tests.rs @@ -172,7 +172,7 @@ fn test_buffered_reader_stream_position_panic() { // cause internal buffer to be filled but read only partially let mut buffer = [0, 0]; assert!(reader.read_exact(&mut buffer).is_ok()); - // rewinding the internal reader will cause buffer to loose sync + // rewinding the internal reader will cause buffer to lose sync let inner = reader.get_mut(); assert!(inner.seek(SeekFrom::Start(0)).is_ok()); // overflow when subtracting the remaining buffer size from current position diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 21e82d43a800c..e6c6f7d766c02 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -1,6 +1,13 @@ #[cfg(test)] mod tests; +// On 64-bit platforms, `io::Error` may use a bit-packed representation to +// reduce size. However, this representation assumes that error codes are +// always 32-bit wide. +// +// This assumption is invalid on 64-bit UEFI, where error codes are 64-bit. +// Therefore, the packed representation is explicitly disabled for UEFI +// targets, and the unpacked representation must be used instead. #[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] mod repr_bitpacked; #[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] @@ -139,7 +146,7 @@ enum ErrorData { /// /// [`into`]: Into::into #[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub type RawOsError = sys::RawOsError; +pub type RawOsError = sys::io::RawOsError; // `#[repr(align(4))]` is probably redundant, it should have that value or // higher already. We include it just because repr_bitpacked.rs's encoding @@ -180,7 +187,7 @@ pub struct SimpleMessage { /// Err(FAIL) /// } /// ``` -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] #[unstable(feature = "io_const_error", issue = "133448")] #[allow_internal_unstable(hint_must_use, io_const_error_internals)] pub macro const_error($kind:expr, $message:expr $(,)?) { @@ -646,7 +653,7 @@ impl Error { #[must_use] #[inline] pub fn last_os_error() -> Error { - Error::from_raw_os_error(sys::os::errno()) + Error::from_raw_os_error(sys::io::errno()) } /// Creates a new instance of an [`Error`] from a particular OS error code. @@ -950,19 +957,19 @@ impl Error { where E: error::Error + Send + Sync + 'static, { - match self.repr.into_data() { - ErrorData::Custom(b) if b.error.is::() => { - let res = (*b).error.downcast::(); - - // downcast is a really trivial and is marked as inline, so - // it's likely be inlined here. - // - // And the compiler should be able to eliminate the branch - // that produces `Err` here since b.error.is::() - // returns true. - Ok(*res.unwrap()) + if let ErrorData::Custom(c) = self.repr.data() + && c.error.is::() + { + if let ErrorData::Custom(b) = self.repr.into_data() + && let Ok(err) = b.error.downcast::() + { + Ok(*err) + } else { + // Safety: We have just checked that the condition is true + unsafe { crate::hint::unreachable_unchecked() } } - repr_data => Err(Self { repr: Repr::new(repr_data) }), + } else { + Err(self) } } @@ -997,7 +1004,7 @@ impl Error { #[inline] pub fn kind(&self) -> ErrorKind { match self.repr.data() { - ErrorData::Os(code) => sys::decode_error_kind(code), + ErrorData::Os(code) => sys::io::decode_error_kind(code), ErrorData::Custom(c) => c.kind, ErrorData::Simple(kind) => kind, ErrorData::SimpleMessage(m) => m.kind, @@ -1007,7 +1014,7 @@ impl Error { #[inline] pub(crate) fn is_interrupted(&self) -> bool { match self.repr.data() { - ErrorData::Os(code) => sys::is_interrupted(code), + ErrorData::Os(code) => sys::io::is_interrupted(code), ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, @@ -1021,8 +1028,8 @@ impl fmt::Debug for Repr { ErrorData::Os(code) => fmt .debug_struct("Os") .field("code", &code) - .field("kind", &sys::decode_error_kind(code)) - .field("message", &sys::os::error_string(code)) + .field("kind", &sys::io::decode_error_kind(code)) + .field("message", &sys::io::error_string(code)) .finish(), ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), @@ -1040,7 +1047,7 @@ impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self.repr.data() { ErrorData::Os(code) => { - let detail = sys::os::error_string(code); + let detail = sys::io::error_string(code); write!(fmt, "{detail} (os error {code})") } ErrorData::Custom(ref c) => c.error.fmt(fmt), diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index 716da37168d01..7353816a8171b 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -133,15 +133,6 @@ unsafe impl Send for Repr {} unsafe impl Sync for Repr {} impl Repr { - pub(super) fn new(dat: ErrorData>) -> Self { - match dat { - ErrorData::Os(code) => Self::new_os(code), - ErrorData::Simple(kind) => Self::new_simple(kind), - ErrorData::SimpleMessage(simple_message) => Self::new_simple_message(simple_message), - ErrorData::Custom(b) => Self::new_custom(b), - } - } - pub(super) fn new_custom(b: Box) -> Self { let p = Box::into_raw(b).cast::(); // Should only be possible if an allocator handed out a pointer with diff --git a/library/std/src/io/error/repr_unpacked.rs b/library/std/src/io/error/repr_unpacked.rs index dc8a95577c959..b3e7b5f024ea0 100644 --- a/library/std/src/io/error/repr_unpacked.rs +++ b/library/std/src/io/error/repr_unpacked.rs @@ -10,9 +10,6 @@ pub(super) struct Repr(Inner); impl Repr { #[inline] - pub(super) fn new(dat: ErrorData>) -> Self { - Self(dat) - } pub(super) fn new_custom(b: Box) -> Self { Self(Inner::Custom(b)) } diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index 3e4029768eb85..a8eef06381dae 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,8 +1,6 @@ use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_error}; -use crate::assert_matches::assert_matches; -use crate::sys::decode_error_kind; -use crate::sys::os::error_string; -use crate::{error, fmt}; +use crate::sys::io::{decode_error_kind, error_string}; +use crate::{assert_matches, error, fmt}; #[test] fn test_size() { diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index b7756befa11e9..623c34c6d2910 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2431,7 +2431,7 @@ pub trait BufRead: Read { /// } /// # std::io::Result::Ok(()) /// ``` - #[unstable(feature = "buf_read_has_data_left", reason = "recently added", issue = "86423")] + #[unstable(feature = "buf_read_has_data_left", issue = "86423")] fn has_data_left(&mut self) -> Result { self.fill_buf().map(|b| !b.is_empty()) } diff --git a/library/std/src/io/pipe.rs b/library/std/src/io/pipe.rs index 16727d445416c..61b81cf074a6e 100644 --- a/library/std/src/io/pipe.rs +++ b/library/std/src/io/pipe.rs @@ -1,6 +1,5 @@ use crate::io; -use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner}; -use crate::sys_common::{FromInner, IntoInner}; +use crate::sys::{FromInner, IntoInner, pipe as imp}; /// Creates an anonymous pipe. /// @@ -84,39 +83,39 @@ use crate::sys_common::{FromInner, IntoInner}; #[stable(feature = "anonymous_pipe", since = "1.87.0")] #[inline] pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { - pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) + imp::pipe().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer))) } /// Read end of an anonymous pipe. #[stable(feature = "anonymous_pipe", since = "1.87.0")] #[derive(Debug)] -pub struct PipeReader(pub(crate) AnonPipe); +pub struct PipeReader(pub(crate) imp::Pipe); /// Write end of an anonymous pipe. #[stable(feature = "anonymous_pipe", since = "1.87.0")] #[derive(Debug)] -pub struct PipeWriter(pub(crate) AnonPipe); +pub struct PipeWriter(pub(crate) imp::Pipe); -impl FromInner for PipeReader { - fn from_inner(inner: AnonPipe) -> Self { +impl FromInner for PipeReader { + fn from_inner(inner: imp::Pipe) -> Self { Self(inner) } } -impl IntoInner for PipeReader { - fn into_inner(self) -> AnonPipe { +impl IntoInner for PipeReader { + fn into_inner(self) -> imp::Pipe { self.0 } } -impl FromInner for PipeWriter { - fn from_inner(inner: AnonPipe) -> Self { +impl FromInner for PipeWriter { + fn from_inner(inner: imp::Pipe) -> Self { Self(inner) } } -impl IntoInner for PipeWriter { - fn into_inner(self) -> AnonPipe { +impl IntoInner for PipeWriter { + fn into_inner(self) -> imp::Pipe { self.0 } } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index b329b4a2488b5..724d4c68fb47d 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -260,14 +260,13 @@ all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) )] +#![cfg_attr(all(test, target_os = "uefi"), feature(uefi_std))] #![cfg_attr(target_family = "wasm", feature(stdarch_wasm_atomic_wait))] #![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] #![cfg_attr(kani, feature(kani))] // // Language features: // tidy-alphabetical-start - -// stabilization was reverted after it hit beta #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] @@ -277,6 +276,7 @@ #![feature(cfg_sanitizer_cfi)] #![feature(cfg_target_thread_local)] #![feature(cfi_encoding)] +#![feature(const_default)] #![feature(const_trait_impl)] #![feature(core_float_math)] #![feature(decl_macro)] @@ -311,6 +311,7 @@ #![feature(staged_api)] #![feature(stmt_expr_attributes)] #![feature(strict_provenance_lints)] +#![feature(target_feature_inline_always)] #![feature(thread_local)] #![feature(try_blocks)] #![feature(try_trait_v2)] @@ -326,9 +327,9 @@ #![feature(char_internals)] #![feature(clone_to_uninit)] #![feature(const_convert)] -#![feature(const_mul_add)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] +#![feature(cstr_display)] #![feature(drop_guard)] #![feature(duration_constants)] #![feature(error_generic_member_access)] @@ -347,10 +348,7 @@ #![feature(hint_must_use)] #![feature(int_from_ascii)] #![feature(ip)] -#![feature(lazy_get)] #![feature(maybe_uninit_array_assume_init)] -#![feature(maybe_uninit_slice)] -#![feature(maybe_uninit_write_slice)] #![feature(panic_can_unwind)] #![feature(panic_internals)] #![feature(pin_coerce_unsized_trait)] @@ -373,8 +371,8 @@ // // Library features (alloc): // tidy-alphabetical-start -#![feature(alloc_layout_extra)] #![feature(allocator_api)] +#![feature(clone_from_ref)] #![feature(get_mut_unchecked)] #![feature(map_try_insert)] #![feature(slice_concat_trait)] @@ -422,7 +420,7 @@ #![default_lib_allocator] // The Rust prelude -// The compiler expects the prelude definition to be defined before it's use statement. +// The compiler expects the prelude definition to be defined before its use statement. pub mod prelude; // Explicitly import the prelude. The compiler uses this same unstable attribute @@ -473,7 +471,9 @@ extern crate std as realstd; // The standard macros that are not built-in to the compiler. #[macro_use] -mod macros; +#[doc(hidden)] +#[unstable(feature = "std_internals", issue = "none")] +pub mod macros; // The runtime entry point and a few unstable public functions used by the // compiler @@ -672,7 +672,7 @@ pub mod arch { pub use std_detect::is_loongarch_feature_detected; #[unstable(feature = "is_riscv_feature_detected", issue = "111192")] pub use std_detect::is_riscv_feature_detected; - #[stable(feature = "stdarch_s390x_feature_detection", since = "CURRENT_RUSTC_VERSION")] + #[stable(feature = "stdarch_s390x_feature_detection", since = "1.93.0")] pub use std_detect::is_s390x_feature_detected; #[stable(feature = "simd_x86", since = "1.27.0")] pub use std_detect::is_x86_feature_detected; @@ -686,9 +686,7 @@ pub mod arch { #[stable(feature = "simd_x86", since = "1.27.0")] pub use std_detect::is_x86_feature_detected; -// Platform-abstraction modules mod sys; -mod sys_common; pub mod alloc; @@ -718,9 +716,9 @@ pub use core::todo; // Re-export built-in macros defined through core. #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] pub use core::{ - assert, assert_matches, cfg, column, compile_error, concat, const_format_args, env, file, - format_args, format_args_nl, include, include_bytes, include_str, line, log_syntax, - module_path, option_env, stringify, trace_macros, + assert, cfg, column, compile_error, concat, const_format_args, env, file, format_args, + format_args_nl, include, include_bytes, include_str, line, log_syntax, module_path, option_env, + stringify, trace_macros, }; // Re-export macros defined in core. #[stable(feature = "rust1", since = "1.0.0")] @@ -729,6 +727,8 @@ pub use core::{ assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, r#try, unimplemented, unreachable, write, writeln, }; +#[unstable(feature = "assert_matches", issue = "82775")] +pub use core::{assert_matches, debug_assert_matches}; // Re-export unstable derive macro defined through core. #[unstable(feature = "derive_from", issue = "144889")] diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 25e2b7ea13703..0bb14552432d5 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -347,35 +347,70 @@ macro_rules! eprintln { /// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html /// [`log`]: https://crates.io/crates/log #[macro_export] +#[allow_internal_unstable(std_internals)] #[cfg_attr(not(test), rustc_diagnostic_item = "dbg_macro")] #[stable(feature = "dbg_macro", since = "1.32.0")] macro_rules! dbg { - // NOTE: We cannot use `concat!` to make a static string as a format argument - // of `eprintln!` because `file!` could contain a `{` or - // `$val` expression could be a block (`{ .. }`), in which case the `eprintln!` - // will be malformed. () => { $crate::eprintln!("[{}:{}:{}]", $crate::file!(), $crate::line!(), $crate::column!()) }; - ($val:expr $(,)?) => { + ($($val:expr),+ $(,)?) => { + $crate::macros::dbg_internal!(() () ($($val),+)) + }; +} + +/// Internal macro that processes a list of expressions and produces a chain of +/// nested `match`es, one for each expression, before finally calling `eprint!` +/// with the collected information and returning all the evaluated expressions +/// in a tuple. +/// +/// E.g. `dbg_internal!(() () (1, 2))` expands into +/// ```rust, ignore +/// match 1 { +/// tmp_1 => match 2 { +/// tmp_2 => { +/// eprint!("...", &tmp_1, &tmp_2, /* some other arguments */); +/// (tmp_1, tmp_2) +/// } +/// } +/// } +/// ``` +/// +/// This is necessary so that `dbg!` outputs don't get torn, see #136703. +#[doc(hidden)] +#[rustc_macro_transparency = "semiopaque"] +pub macro dbg_internal { + (($($piece:literal),+) ($($processed:expr => $bound:expr),+) ()) => {{ + $crate::eprint!( + $crate::concat!($($piece),+), + $( + $crate::stringify!($processed), + // The `&T: Debug` check happens here (not in the format literal desugaring) + // to avoid format literal related messages and suggestions. + &&$bound as &dyn $crate::fmt::Debug + ),+, + // The location returned here is that of the macro invocation, so + // it will be the same for all expressions. Thus, label these + // arguments so that they can be reused in every piece of the + // formatting template. + file=$crate::file!(), + line=$crate::line!(), + column=$crate::column!() + ); + // Comma separate the variables only when necessary so that this will + // not yield a tuple for a single expression, but rather just parenthesize + // the expression. + ($($bound),+) + }}, + (($($piece:literal),*) ($($processed:expr => $bound:expr),*) ($val:expr $(,$rest:expr)*)) => { // Use of `match` here is intentional because it affects the lifetimes // of temporaries - https://stackoverflow.com/a/48732525/1063961 match $val { - tmp => { - $crate::eprintln!("[{}:{}:{}] {} = {:#?}", - $crate::file!(), - $crate::line!(), - $crate::column!(), - $crate::stringify!($val), - // The `&T: Debug` check happens here (not in the format literal desugaring) - // to avoid format literal related messages and suggestions. - &&tmp as &dyn $crate::fmt::Debug, - ); - tmp - } + tmp => $crate::macros::dbg_internal!( + ($($piece,)* "[{file}:{line}:{column}] {} = {:#?}\n") + ($($processed => $bound,)* $val => tmp) + ($($rest),*) + ), } - }; - ($($val:expr),+ $(,)?) => { - ($($crate::dbg!($val)),+,) - }; + }, } diff --git a/library/std/src/net/hostname.rs b/library/std/src/net/hostname.rs index b1010cec60058..4042496d534fd 100644 --- a/library/std/src/net/hostname.rs +++ b/library/std/src/net/hostname.rs @@ -8,10 +8,10 @@ use crate::ffi::OsString; /// /// # Underlying system calls /// -/// | Platform | System call | -/// |----------|---------------------------------------------------------------------------------------------------------| -/// | UNIX | [`gethostname`](https://www.man7.org/linux/man-pages/man2/gethostname.2.html) | -/// | Windows | [`GetHostNameW`](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-gethostnamew) | +/// | Platform | System call | +/// |--------------|---------------------------------------------------------------------------------------------------------| +/// | UNIX | [`gethostname`](https://www.man7.org/linux/man-pages/man2/gethostname.2.html) | +/// | Windows (8+) | [`GetHostNameW`](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-gethostnamew) | /// /// Note that platform-specific behavior [may change in the future][changes]. /// diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index ae50f531a7162..dac568e419f3f 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -16,8 +16,7 @@ use crate::io::prelude::*; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::iter::FusedIterator; use crate::net::{Shutdown, SocketAddr, ToSocketAddrs}; -use crate::sys::net as net_imp; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, net as net_imp}; use crate::time::Duration; /// A TCP stream between a local and a remote socket. diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 7c7ef7b2f7018..e4a30b80e3df4 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -37,7 +37,8 @@ fn connect_error() { e.kind() == ErrorKind::ConnectionRefused || e.kind() == ErrorKind::InvalidInput || e.kind() == ErrorKind::AddrInUse - || e.kind() == ErrorKind::AddrNotAvailable, + || e.kind() == ErrorKind::AddrNotAvailable + || e.kind() == ErrorKind::NetworkUnreachable, "bad error: {} {:?}", e, e.kind() diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 72e292e3d157c..136803ab16f1f 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -13,8 +13,7 @@ mod tests; use crate::fmt; use crate::io::{self, ErrorKind}; use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; -use crate::sys::net as net_imp; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, net as net_imp}; use crate::time::Duration; /// A UDP socket. diff --git a/library/std/src/num/f128.rs b/library/std/src/num/f128.rs index 3b787713afa24..6f1fd2975b714 100644 --- a/library/std/src/num/f128.rs +++ b/library/std/src/num/f128.rs @@ -19,6 +19,11 @@ use crate::sys::cmath; impl f128 { /// Raises a number to a floating point power. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f128::powf(f128::NAN, 0.0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, diff --git a/library/std/src/num/f16.rs b/library/std/src/num/f16.rs index 4af21c95c9baf..20d0b4e1e552b 100644 --- a/library/std/src/num/f16.rs +++ b/library/std/src/num/f16.rs @@ -19,6 +19,11 @@ use crate::sys::cmath; impl f16 { /// Raises a number to a floating point power. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f16::powf(f16::NAN, 0.0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index 09ced388a3399..77e6824784605 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -217,7 +217,7 @@ impl f32 { #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] - #[rustc_const_unstable(feature = "const_mul_add", issue = "146724")] + #[rustc_const_stable(feature = "const_mul_add", since = "1.94.0")] pub const fn mul_add(self, a: f32, b: f32) -> f32 { core::f32::math::mul_add(self, a, b) } @@ -252,7 +252,8 @@ impl f32 { core::f32::math::div_euclid(self, rhs) } - /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// Calculates the least nonnegative remainder of `self` when divided by + /// `rhs`. /// /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in /// most cases. However, due to a floating point round-off error it can @@ -295,6 +296,11 @@ impl f32 { /// It might have a different sequence of rounding operations than `powf`, /// so the results are not guaranteed to agree. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f32::powi(f32::NAN, 0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and @@ -320,6 +326,11 @@ impl f32 { /// Raises a number to a floating point power. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f32::powf(f32::NAN, 0.0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 79adf076e4b1a..e0b9948a924db 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -217,7 +217,7 @@ impl f64 { #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] - #[rustc_const_unstable(feature = "const_mul_add", issue = "146724")] + #[rustc_const_stable(feature = "const_mul_add", since = "1.94.0")] pub const fn mul_add(self, a: f64, b: f64) -> f64 { core::f64::math::mul_add(self, a, b) } @@ -252,7 +252,8 @@ impl f64 { core::f64::math::div_euclid(self, rhs) } - /// Calculates the least nonnegative remainder of `self (mod rhs)`. + /// Calculates the least nonnegative remainder of `self` when divided by + /// `rhs`. /// /// In particular, the return value `r` satisfies `0.0 <= r < rhs.abs()` in /// most cases. However, due to a floating point round-off error it can @@ -295,6 +296,11 @@ impl f64 { /// It might have a different sequence of rounding operations than `powf`, /// so the results are not guaranteed to agree. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f64::powi(f64::NAN, 0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and @@ -320,6 +326,11 @@ impl f64 { /// Raises a number to a floating point power. /// + /// Note that this function is special in that it can return non-NaN results for NaN inputs. For + /// example, `f64::powf(f64::NAN, 0.0)` returns `1.0`. However, if an input is a *signaling* + /// NaN, then the result is non-deterministically either a NaN or the result that the + /// corresponding quiet NaN would produce. + /// /// # Unspecified precision /// /// The precision of this function is non-deterministic. This means it varies by platform, Rust version, and diff --git a/library/std/src/os/aix/fs.rs b/library/std/src/os/aix/fs.rs index ac9dd45f05426..2a940c6b9c97c 100644 --- a/library/std/src/os/aix/fs.rs +++ b/library/std/src/os/aix/fs.rs @@ -5,7 +5,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/android/fs.rs b/library/std/src/os/android/fs.rs index 6b931e3816950..d67c5582fd915 100644 --- a/library/std/src/os/android/fs.rs +++ b/library/std/src/os/android/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::android::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/cygwin/fs.rs b/library/std/src/os/cygwin/fs.rs index 5533264fd515b..42623e6d47ad6 100644 --- a/library/std/src/os/cygwin/fs.rs +++ b/library/std/src/os/cygwin/fs.rs @@ -1,6 +1,6 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// /// [`fs::Metadata`]: crate::fs::Metadata diff --git a/library/std/src/os/darwin/fs.rs b/library/std/src/os/darwin/fs.rs index 5740c86e62183..af98e069b41a3 100644 --- a/library/std/src/os/darwin/fs.rs +++ b/library/std/src/os/darwin/fs.rs @@ -5,7 +5,7 @@ use crate::fs::{self, Metadata}; use crate::sealed::Sealed; -use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, IntoInner}; use crate::time::SystemTime; /// OS-specific extensions to [`fs::Metadata`]. diff --git a/library/std/src/os/dragonfly/fs.rs b/library/std/src/os/dragonfly/fs.rs index 0cd543b2ab97d..b47b5108c70e6 100644 --- a/library/std/src/os/dragonfly/fs.rs +++ b/library/std/src/os/dragonfly/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::dragonfly::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/emscripten/fs.rs b/library/std/src/os/emscripten/fs.rs index 81f9ef331a5fa..a5778812ad947 100644 --- a/library/std/src/os/emscripten/fs.rs +++ b/library/std/src/os/emscripten/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::emscripten::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/espidf/fs.rs b/library/std/src/os/espidf/fs.rs index ffff584cda02a..cd09efca2db5f 100644 --- a/library/std/src/os/espidf/fs.rs +++ b/library/std/src/os/espidf/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::espidf::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/fd/net.rs b/library/std/src/os/fd/net.rs index 34479ca0e190e..98faf0709153b 100644 --- a/library/std/src/os/fd/net.rs +++ b/library/std/src/os/fd/net.rs @@ -1,6 +1,6 @@ use crate::os::fd::owned::OwnedFd; use crate::os::fd::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{net, sys}; macro_rules! impl_as_raw_fd { diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 6a0e7a640028b..8a39fe073bf95 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -20,7 +20,7 @@ use crate::mem::ManuallyDrop; )))] use crate::sys::cvt; #[cfg(not(target_os = "trusty"))] -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{fmt, io}; type ValidRawFd = core::num::niche_types::NotAllOnes; @@ -77,7 +77,11 @@ impl BorrowedFd<'_> { /// # Safety /// /// The resource pointed to by `fd` must remain open for the duration of - /// the returned `BorrowedFd`, and it must not have the value `-1`. + /// the returned `BorrowedFd`. + /// + /// # Panics + /// + /// Panics if the raw file descriptor has the value `-1`. #[inline] #[track_caller] #[rustc_const_stable(feature = "io_safety", since = "1.63.0")] @@ -91,7 +95,7 @@ impl OwnedFd { /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `OwnedFd` instance. #[stable(feature = "io_safety", since = "1.63.0")] - pub fn try_clone(&self) -> crate::io::Result { + pub fn try_clone(&self) -> io::Result { self.as_fd().try_clone_to_owned() } } @@ -106,7 +110,7 @@ impl BorrowedFd<'_> { target_os = "motor" )))] #[stable(feature = "io_safety", since = "1.63.0")] - pub fn try_clone_to_owned(&self) -> crate::io::Result { + pub fn try_clone_to_owned(&self) -> io::Result { // We want to atomically duplicate this file descriptor and set the // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This // is a POSIX flag that was added to Linux in 2.6.24. @@ -129,15 +133,15 @@ impl BorrowedFd<'_> { /// description as the existing `BorrowedFd` instance. #[cfg(any(target_arch = "wasm32", target_os = "hermit", target_os = "trusty"))] #[stable(feature = "io_safety", since = "1.63.0")] - pub fn try_clone_to_owned(&self) -> crate::io::Result { - Err(crate::io::Error::UNSUPPORTED_PLATFORM) + pub fn try_clone_to_owned(&self) -> io::Result { + Err(io::Error::UNSUPPORTED_PLATFORM) } /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `BorrowedFd` instance. #[cfg(target_os = "motor")] #[stable(feature = "io_safety", since = "1.63.0")] - pub fn try_clone_to_owned(&self) -> crate::io::Result { + pub fn try_clone_to_owned(&self) -> io::Result { let fd = moto_rt::fs::duplicate(self.as_raw_fd()).map_err(crate::sys::map_motor_error)?; Ok(unsafe { OwnedFd::from_raw_fd(fd) }) } @@ -177,6 +181,10 @@ impl FromRawFd for OwnedFd { /// [ownership][io-safety]. The resource must not require any cleanup other than `close`. /// /// [io-safety]: io#io-safety + /// + /// # Panics + /// + /// Panics if the raw file descriptor has the value `-1`. #[inline] #[track_caller] unsafe fn from_raw_fd(fd: RawFd) -> Self { @@ -233,7 +241,7 @@ macro_rules! impl_is_terminal { impl crate::sealed::Sealed for $t {} #[stable(feature = "is_terminal", since = "1.70.0")] - impl crate::io::IsTerminal for $t { + impl io::IsTerminal for $t { #[inline] fn is_terminal(&self) -> bool { crate::sys::io::is_terminal(self) diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index c01e6b83cd366..39e374174646c 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -23,7 +23,7 @@ use crate::os::unix::io::OwnedFd; #[cfg(target_os = "wasi")] use crate::os::wasi::io::OwnedFd; #[cfg(not(target_os = "trusty"))] -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; /// Raw file descriptors. #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/os/freebsd/fs.rs b/library/std/src/os/freebsd/fs.rs index 34384a4bcb505..682a32a94c197 100644 --- a/library/std/src/os/freebsd/fs.rs +++ b/library/std/src/os/freebsd/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::freebsd::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/freebsd/net.rs b/library/std/src/os/freebsd/net.rs index fcfc5c1c83935..f759b91921b55 100644 --- a/library/std/src/os/freebsd/net.rs +++ b/library/std/src/os/freebsd/net.rs @@ -6,7 +6,7 @@ use crate::ffi::CStr; use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// FreeBSD-specific functionality for `AF_UNIX` sockets [`UnixDatagram`] /// and [`UnixStream`]. diff --git a/library/std/src/os/fuchsia/fs.rs b/library/std/src/os/fuchsia/fs.rs index b48a46f9124a9..8cded1db37d7b 100644 --- a/library/std/src/os/fuchsia/fs.rs +++ b/library/std/src/os/fuchsia/fs.rs @@ -1,7 +1,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/haiku/fs.rs b/library/std/src/os/haiku/fs.rs index 23f6493180b76..86624edcaef95 100644 --- a/library/std/src/os/haiku/fs.rs +++ b/library/std/src/os/haiku/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::haiku::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/hermit/io/net.rs b/library/std/src/os/hermit/io/net.rs index 233bc885fc733..d5baeafec6497 100644 --- a/library/std/src/os/hermit/io/net.rs +++ b/library/std/src/os/hermit/io/net.rs @@ -1,6 +1,6 @@ +use crate::net; use crate::os::hermit::io::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; -use crate::{net, sys}; +use crate::sys::{self, AsInner, FromInner, IntoInner}; macro_rules! impl_as_raw_fd { ($($t:ident)*) => {$( diff --git a/library/std/src/os/horizon/fs.rs b/library/std/src/os/horizon/fs.rs index 1325522105dc8..a5f3f791e6a1d 100644 --- a/library/std/src/os/horizon/fs.rs +++ b/library/std/src/os/horizon/fs.rs @@ -1,7 +1,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/hurd/fs.rs b/library/std/src/os/hurd/fs.rs index e3087fa8af1cc..e0fc544fed1db 100644 --- a/library/std/src/os/hurd/fs.rs +++ b/library/std/src/os/hurd/fs.rs @@ -5,7 +5,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/illumos/fs.rs b/library/std/src/os/illumos/fs.rs index 75dbe167785db..76a5871638a1e 100644 --- a/library/std/src/os/illumos/fs.rs +++ b/library/std/src/os/illumos/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::illumos::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/illumos/net.rs b/library/std/src/os/illumos/net.rs index 5ef4e1ec89e36..c21c887edbdf9 100644 --- a/library/std/src/os/illumos/net.rs +++ b/library/std/src/os/illumos/net.rs @@ -5,7 +5,7 @@ use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// illumos-specific functionality for `AF_UNIX` sockets [`UnixDatagram`] /// and [`UnixStream`]. diff --git a/library/std/src/os/l4re/fs.rs b/library/std/src/os/l4re/fs.rs index 0511ddcf19af6..1f0bacae1ca68 100644 --- a/library/std/src/os/l4re/fs.rs +++ b/library/std/src/os/l4re/fs.rs @@ -7,7 +7,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::l4re::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/linux/fs.rs b/library/std/src/os/linux/fs.rs index 20a7a161a2628..e52a63bc798ea 100644 --- a/library/std/src/os/linux/fs.rs +++ b/library/std/src/os/linux/fs.rs @@ -7,7 +7,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::linux::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/linux/process.rs b/library/std/src/os/linux/process.rs index 9195909479729..60851db831bf9 100644 --- a/library/std/src/os/linux/process.rs +++ b/library/std/src/os/linux/process.rs @@ -8,9 +8,9 @@ use crate::io::Result; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::process::{self, ExitStatus}; use crate::sealed::Sealed; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner}; #[cfg(not(doc))] use crate::sys::{fd::FileDesc, linux::pidfd::PidFd as InnerPidFd}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; #[cfg(doc)] struct InnerPidFd; @@ -67,8 +67,10 @@ impl PidFd { /// Waits for the child to exit completely, returning the status that it exited with. /// /// Unlike [`Child::wait`] it does not ensure that the stdin handle is closed. - /// Additionally it will not return an `ExitStatus` if the child - /// has already been reaped. Instead an error will be returned. + /// + /// Additionally on kernels prior to 6.15 only the first attempt to + /// reap a child will return an ExitStatus, further attempts + /// will return an Error. /// /// [`Child::wait`]: process::Child::wait pub fn wait(&self) -> Result { @@ -77,8 +79,8 @@ impl PidFd { /// Attempts to collect the exit status of the child if it has already exited. /// - /// Unlike [`Child::try_wait`] this method will return an Error - /// if the child has already been reaped. + /// On kernels prior to 6.15, and unlike [`Child::try_wait`], only the first attempt + /// to reap a child will return an ExitStatus, further attempts will return an Error. /// /// [`Child::try_wait`]: process::Child::try_wait pub fn try_wait(&self) -> Result> { diff --git a/library/std/src/os/motor/ffi.rs b/library/std/src/os/motor/ffi.rs index 10e8da392dcc5..e65a7736d3fa0 100644 --- a/library/std/src/os/motor/ffi.rs +++ b/library/std/src/os/motor/ffi.rs @@ -3,7 +3,7 @@ use crate::ffi::{OsStr, OsString}; use crate::sealed::Sealed; -use crate::sys_common::{AsInner, IntoInner}; +use crate::sys::{AsInner, IntoInner}; /// Motor OS–specific extensions to [`OsString`]. /// diff --git a/library/std/src/os/motor/mod.rs b/library/std/src/os/motor/mod.rs index 18da079c74a15..e8812f48a1209 100644 --- a/library/std/src/os/motor/mod.rs +++ b/library/std/src/os/motor/mod.rs @@ -2,3 +2,7 @@ pub mod ffi; pub mod process; + +pub fn rt_version() -> u64 { + moto_rt::RT_VERSION +} diff --git a/library/std/src/os/motor/process.rs b/library/std/src/os/motor/process.rs index 015fbcb97f97d..09f38a4721bbc 100644 --- a/library/std/src/os/motor/process.rs +++ b/library/std/src/os/motor/process.rs @@ -1,7 +1,7 @@ #![unstable(feature = "motor_ext", issue = "147456")] use crate::sealed::Sealed; -use crate::sys_common::AsInner; +use crate::sys::AsInner; pub trait ChildExt: Sealed { /// Extracts the main thread raw handle, without taking ownership diff --git a/library/std/src/os/net/linux_ext/socket.rs b/library/std/src/os/net/linux_ext/socket.rs index a15feb6bd9f28..ecc2758961e1d 100644 --- a/library/std/src/os/net/linux_ext/socket.rs +++ b/library/std/src/os/net/linux_ext/socket.rs @@ -3,7 +3,7 @@ use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// Linux-specific functionality for `AF_UNIX` sockets [`UnixDatagram`] /// and [`UnixStream`]. diff --git a/library/std/src/os/net/linux_ext/tcp.rs b/library/std/src/os/net/linux_ext/tcp.rs index 3f9b2bd3f4b43..c97063dd16505 100644 --- a/library/std/src/os/net/linux_ext/tcp.rs +++ b/library/std/src/os/net/linux_ext/tcp.rs @@ -3,7 +3,7 @@ //! [`std::net`]: crate::net use crate::sealed::Sealed; -use crate::sys_common::AsInner; +use crate::sys::AsInner; #[cfg(target_os = "linux")] use crate::time::Duration; use crate::{io, net}; diff --git a/library/std/src/os/netbsd/fs.rs b/library/std/src/os/netbsd/fs.rs index 74fbbabb17a5d..e61afd895debb 100644 --- a/library/std/src/os/netbsd/fs.rs +++ b/library/std/src/os/netbsd/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::netbsd::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/netbsd/net.rs b/library/std/src/os/netbsd/net.rs index e1950d349bf7d..9174fee2f9962 100644 --- a/library/std/src/os/netbsd/net.rs +++ b/library/std/src/os/netbsd/net.rs @@ -6,7 +6,7 @@ use crate::ffi::CStr; use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// NetBSD-specific functionality for `AF_UNIX` sockets [`UnixDatagram`] /// and [`UnixStream`]. diff --git a/library/std/src/os/nto/fs.rs b/library/std/src/os/nto/fs.rs index 8f915b08c9e2e..d371936c4336f 100644 --- a/library/std/src/os/nto/fs.rs +++ b/library/std/src/os/nto/fs.rs @@ -1,7 +1,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; #[stable(feature = "metadata_ext", since = "1.1.0")] pub trait MetadataExt { diff --git a/library/std/src/os/nuttx/fs.rs b/library/std/src/os/nuttx/fs.rs index 7d6d8d16eca79..f6c74bab9b45a 100644 --- a/library/std/src/os/nuttx/fs.rs +++ b/library/std/src/os/nuttx/fs.rs @@ -1,7 +1,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; #[stable(feature = "metadata_ext", since = "1.1.0")] pub trait MetadataExt { diff --git a/library/std/src/os/openbsd/fs.rs b/library/std/src/os/openbsd/fs.rs index e584098476a7b..d3ca2426507a5 100644 --- a/library/std/src/os/openbsd/fs.rs +++ b/library/std/src/os/openbsd/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::openbsd::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/redox/fs.rs b/library/std/src/os/redox/fs.rs index c6b813f0cc6ce..0451e91071bcf 100644 --- a/library/std/src/os/redox/fs.rs +++ b/library/std/src/os/redox/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::redox::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/rtems/fs.rs b/library/std/src/os/rtems/fs.rs index bec0d41e42d81..97a0c9004c00a 100644 --- a/library/std/src/os/rtems/fs.rs +++ b/library/std/src/os/rtems/fs.rs @@ -1,7 +1,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/solaris/fs.rs b/library/std/src/os/solaris/fs.rs index 9b0527d713891..d32f575228603 100644 --- a/library/std/src/os/solaris/fs.rs +++ b/library/std/src/os/solaris/fs.rs @@ -3,7 +3,7 @@ use crate::fs::Metadata; #[allow(deprecated)] use crate::os::solaris::raw; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/solaris/net.rs b/library/std/src/os/solaris/net.rs index ca841f15b0e72..cea65f6ee7019 100644 --- a/library/std/src/os/solaris/net.rs +++ b/library/std/src/os/solaris/net.rs @@ -5,7 +5,7 @@ use crate::io; use crate::os::unix::net; use crate::sealed::Sealed; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// solaris-specific functionality for `AF_UNIX` sockets [`UnixDatagram`] /// and [`UnixStream`]. diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index c23d842b238b8..ac112e739170d 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -48,8 +48,8 @@ use crate::marker::PhantomData; use crate::mem::ManuallyDrop; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::{fmt, net, sys}; +use crate::sys::{AsInner, FromInner, IntoInner}; +use crate::{fmt, io, net, sys}; /// Raw file descriptors. pub type RawFd = i32; @@ -110,7 +110,7 @@ impl BorrowedFd<'_> { impl OwnedFd { /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `OwnedFd` instance. - pub fn try_clone(&self) -> crate::io::Result { + pub fn try_clone(&self) -> io::Result { self.as_fd().try_clone_to_owned() } } @@ -118,7 +118,7 @@ impl OwnedFd { impl BorrowedFd<'_> { /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `BorrowedFd` instance. - pub fn try_clone_to_owned(&self) -> crate::io::Result { + pub fn try_clone_to_owned(&self) -> io::Result { let fd = sys::net::cvt(unsafe { crate::sys::abi::sockets::dup(self.as_raw_fd()) })?; Ok(unsafe { OwnedFd::from_raw_fd(fd) }) } @@ -184,7 +184,7 @@ macro_rules! impl_is_terminal { impl crate::sealed::Sealed for $t {} #[stable(feature = "is_terminal", since = "1.70.0")] - impl crate::io::IsTerminal for $t { + impl io::IsTerminal for $t { #[inline] fn is_terminal(&self) -> bool { crate::sys::io::is_terminal(self) diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index ab5406e605c6b..82e3fc9775cba 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -4,13 +4,25 @@ use crate::ffi::c_void; use crate::ptr::NonNull; -use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, Ordering}; +use crate::sync::atomic::Ordering; -static SYSTEM_TABLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); -static IMAGE_HANDLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); -// Flag to check if BootServices are still valid. -// Start with assuming that they are not available -static BOOT_SERVICES_FLAG: Atomic = AtomicBool::new(false); +#[doc(hidden)] +#[cfg(not(test))] +pub mod globals { + use crate::ffi::c_void; + use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr}; + + pub static SYSTEM_TABLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); + pub static IMAGE_HANDLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); + // Flag to check if BootServices are still valid. + // Start with assuming that they are not available + pub static BOOT_SERVICES_FLAG: Atomic = AtomicBool::new(false); +} + +#[cfg(not(test))] +use globals::*; +#[cfg(test)] +use realstd::os::uefi::env::globals::*; /// Initializes the global System Table and Image Handle pointers. /// diff --git a/library/std/src/os/unix/ffi/os_str.rs b/library/std/src/os/unix/ffi/os_str.rs index 650f712bc6eef..da47112f7cb47 100644 --- a/library/std/src/os/unix/ffi/os_str.rs +++ b/library/std/src/os/unix/ffi/os_str.rs @@ -2,7 +2,7 @@ use crate::ffi::{OsStr, OsString}; use crate::mem; use crate::sealed::Sealed; use crate::sys::os_str::Buf; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; // Note: this file is currently reused in other `std::os::{platform}::ffi` modules to reduce duplication. // Keep this in mind when applying changes to this file that only apply to `unix`. diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index 1d1a138b30250..219b340b92469 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -15,7 +15,7 @@ use crate::io::BorrowedCursor; use crate::os::unix::io::{AsFd, AsRawFd}; use crate::path::Path; use crate::sealed::Sealed; -use crate::sys_common::{AsInner, AsInnerMut, FromInner}; +use crate::sys::{AsInner, AsInnerMut, FromInner}; use crate::{io, sys}; // Tests for this module diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs index 6d4090ee31cfc..18b0f70c06877 100644 --- a/library/std/src/os/unix/io/mod.rs +++ b/library/std/src/os/unix/io/mod.rs @@ -92,9 +92,138 @@ #![stable(feature = "rust1", since = "1.0.0")] +use crate::io::{self, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, Write}; #[stable(feature = "rust1", since = "1.0.0")] pub use crate::os::fd::*; +#[allow(unused_imports)] // not used on all targets +use crate::sys::cvt; // Tests for this module #[cfg(test)] mod tests; + +#[unstable(feature = "stdio_swap", issue = "150667")] +pub trait StdioExt: crate::sealed::Sealed { + /// Redirects the stdio file descriptor to point to the file description underpinning `fd`. + /// + /// Rust std::io write buffers (if any) are flushed, but other runtimes + /// (e.g. C stdio) or libraries that acquire a clone of the file descriptor + /// will not be aware of this change. + /// + /// # Platform-specific behavior + /// + /// This is [currently] implemented using + /// + /// - `fd_renumber` on wasip1 + /// - `dup2` on most unixes + /// + /// [currently]: crate::io#platform-specific-behavior + /// + /// ``` + /// #![feature(stdio_swap)] + /// use std::io::{self, Read, Write}; + /// use std::os::unix::io::StdioExt; + /// + /// fn main() -> io::Result<()> { + /// let (reader, mut writer) = io::pipe()?; + /// let mut stdin = io::stdin(); + /// stdin.set_fd(reader)?; + /// writer.write_all(b"Hello, world!")?; + /// let mut buffer = vec![0; 13]; + /// assert_eq!(stdin.read(&mut buffer)?, 13); + /// assert_eq!(&buffer, b"Hello, world!"); + /// Ok(()) + /// } + /// ``` + fn set_fd>(&mut self, fd: T) -> io::Result<()>; + + /// Redirects the stdio file descriptor and returns a new `OwnedFd` + /// backed by the previous file description. + /// + /// See [`set_fd()`] for details. + /// + /// [`set_fd()`]: StdioExt::set_fd + fn replace_fd>(&mut self, replace_with: T) -> io::Result; + + /// Redirects the stdio file descriptor to the null device (`/dev/null`) + /// and returns a new `OwnedFd` backed by the previous file description. + /// + /// Programs that communicate structured data via stdio can use this early in `main()` to + /// extract the fds, treat them as other IO types (`File`, `UnixStream`, etc), + /// apply custom buffering or avoid interference from stdio use later in the program. + /// + /// See [`set_fd()`] for additional details. + /// + /// [`set_fd()`]: StdioExt::set_fd + fn take_fd(&mut self) -> io::Result; +} + +macro io_ext_impl($stdio_ty:ty, $stdio_lock_ty:ty, $writer:literal) { + #[unstable(feature = "stdio_swap", issue = "150667")] + impl StdioExt for $stdio_ty { + fn set_fd>(&mut self, fd: T) -> io::Result<()> { + self.lock().set_fd(fd) + } + + fn take_fd(&mut self) -> io::Result { + self.lock().take_fd() + } + + fn replace_fd>(&mut self, replace_with: T) -> io::Result { + self.lock().replace_fd(replace_with) + } + } + + #[unstable(feature = "stdio_swap", issue = "150667")] + impl StdioExt for $stdio_lock_ty { + fn set_fd>(&mut self, fd: T) -> io::Result<()> { + #[cfg($writer)] + self.flush()?; + replace_stdio_fd(self.as_fd(), fd.into()) + } + + fn take_fd(&mut self) -> io::Result { + let null = null_fd()?; + let cloned = self.as_fd().try_clone_to_owned()?; + self.set_fd(null)?; + Ok(cloned) + } + + fn replace_fd>(&mut self, replace_with: T) -> io::Result { + let cloned = self.as_fd().try_clone_to_owned()?; + self.set_fd(replace_with)?; + Ok(cloned) + } + } +} + +io_ext_impl!(Stdout, StdoutLock<'_>, true); +io_ext_impl!(Stdin, StdinLock<'_>, false); +io_ext_impl!(Stderr, StderrLock<'_>, true); + +fn null_fd() -> io::Result { + let null_dev = crate::fs::OpenOptions::new().read(true).write(true).open("/dev/null")?; + Ok(null_dev.into()) +} + +/// Replaces the underlying file descriptor with the one from `other`. +/// Does not set CLOEXEC. +fn replace_stdio_fd(this: BorrowedFd<'_>, other: OwnedFd) -> io::Result<()> { + cfg_select! { + all(target_os = "wasi", target_env = "p1") => { + cvt(unsafe { libc::__wasilibc_fd_renumber(other.as_raw_fd(), this.as_raw_fd()) }).map(|_| ()) + } + not(any( + target_arch = "wasm32", + target_os = "hermit", + target_os = "trusty", + target_os = "motor" + )) => { + cvt(unsafe {libc::dup2(other.as_raw_fd(), this.as_raw_fd())}).map(|_| ()) + } + _ => { + let _ = (this, other); + Err(io::Error::UNSUPPORTED_PLATFORM) + } + } +} diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index 25b95014e08b2..0748f6984a825 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -264,7 +264,7 @@ impl linux_ext::addr::SocketAddrExt for SocketAddr { if let AddressKind::Abstract(name) = self.address() { Some(name.as_bytes()) } else { None } } - fn from_abstract_name(name: N) -> crate::io::Result + fn from_abstract_name(name: N) -> io::Result where N: AsRef<[u8]>, { diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 163267be1e5c1..beda370006f73 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -22,9 +22,8 @@ use crate::net::Shutdown; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; use crate::sealed::Sealed; -use crate::sys::cvt; use crate::sys::net::Socket; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, cvt}; use crate::time::Duration; use crate::{fmt, io}; #[cfg(not(any( diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index 5b4659e261883..99eef7f4013d6 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -1,9 +1,8 @@ use super::{SocketAddr, UnixStream, sockaddr_un}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; -use crate::sys::cvt; use crate::sys::net::Socket; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, cvt}; use crate::{fmt, io, mem}; /// A structure representing a Unix domain socket server. diff --git a/library/std/src/os/unix/net/mod.rs b/library/std/src/os/unix/net/mod.rs index 94523d7d1e450..a44b23a77d2d4 100644 --- a/library/std/src/os/unix/net/mod.rs +++ b/library/std/src/os/unix/net/mod.rs @@ -47,5 +47,5 @@ pub use self::stream::*; target_vendor = "apple", target_os = "cygwin", ))] -#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")] +#[unstable(feature = "peer_credentials_unix_socket", issue = "42839")] pub use self::ucred::*; diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 851ff7f08795b..30124d96951eb 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -36,9 +36,8 @@ use crate::net::Shutdown; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; use crate::sealed::Sealed; -use crate::sys::cvt; use crate::sys::net::Socket; -use crate::sys_common::{AsInner, FromInner}; +use crate::sys::{AsInner, FromInner, cvt}; use crate::time::Duration; /// A Unix stream socket. @@ -59,6 +58,13 @@ use crate::time::Duration; /// } /// ``` /// +/// # `SOCK_CLOEXEC` +/// +/// On platforms that support it, we pass the close-on-exec flag to atomically create the socket and +/// set it as CLOEXEC. On Linux, this was added in 2.6.27. See [`socket(2)`] for more information. +/// +/// [`socket(2)`]: https://www.man7.org/linux/man-pages/man2/socket.2.html#:~:text=SOCK_CLOEXEC +/// /// # `SIGPIPE` /// /// Writes to the underlying socket in `SOCK_STREAM` mode are made with `MSG_NOSIGNAL` flag. @@ -245,7 +251,7 @@ impl UnixStream { /// Ok(()) /// } /// ``` - #[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")] + #[unstable(feature = "peer_credentials_unix_socket", issue = "42839")] #[cfg(any( target_os = "android", target_os = "linux", diff --git a/library/std/src/os/unix/net/ucred.rs b/library/std/src/os/unix/net/ucred.rs index 36fb9c46b4aba..1395d2ef4be3c 100644 --- a/library/std/src/os/unix/net/ucred.rs +++ b/library/std/src/os/unix/net/ucred.rs @@ -7,7 +7,7 @@ use libc::{gid_t, pid_t, uid_t}; /// Credentials for a UNIX process for credentials passing. -#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")] +#[unstable(feature = "peer_credentials_unix_socket", issue = "42839")] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct UCred { /// The UID part of the peer credential. This is the effective UID of the process at the domain diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index ee0c460f7dfa7..fab1b20b8c0e9 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -8,7 +8,8 @@ use crate::ffi::OsStr; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; use crate::sealed::Sealed; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; +use crate::sys::process::ChildPipe; +use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::{io, process, sys}; cfg_select! { @@ -511,7 +512,7 @@ impl From for OwnedFd { /// Takes ownership of a [`ChildStdin`](crate::process::ChildStdin)'s file descriptor. #[inline] fn from(child_stdin: crate::process::ChildStdin) -> OwnedFd { - child_stdin.into_inner().into_inner().into_inner() + child_stdin.into_inner().into_inner() } } @@ -523,8 +524,7 @@ impl From for OwnedFd { impl From for process::ChildStdin { #[inline] fn from(fd: OwnedFd) -> process::ChildStdin { - let fd = sys::fd::FileDesc::from_inner(fd); - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = ChildPipe::from_inner(fd); process::ChildStdin::from_inner(pipe) } } @@ -542,7 +542,7 @@ impl From for OwnedFd { /// Takes ownership of a [`ChildStdout`](crate::process::ChildStdout)'s file descriptor. #[inline] fn from(child_stdout: crate::process::ChildStdout) -> OwnedFd { - child_stdout.into_inner().into_inner().into_inner() + child_stdout.into_inner().into_inner() } } @@ -554,8 +554,7 @@ impl From for OwnedFd { impl From for process::ChildStdout { #[inline] fn from(fd: OwnedFd) -> process::ChildStdout { - let fd = sys::fd::FileDesc::from_inner(fd); - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = ChildPipe::from_inner(fd); process::ChildStdout::from_inner(pipe) } } @@ -573,7 +572,7 @@ impl From for OwnedFd { /// Takes ownership of a [`ChildStderr`](crate::process::ChildStderr)'s file descriptor. #[inline] fn from(child_stderr: crate::process::ChildStderr) -> OwnedFd { - child_stderr.into_inner().into_inner().into_inner() + child_stderr.into_inner().into_inner() } } @@ -585,8 +584,7 @@ impl From for OwnedFd { impl From for process::ChildStderr { #[inline] fn from(fd: OwnedFd) -> process::ChildStderr { - let fd = sys::fd::FileDesc::from_inner(fd); - let pipe = sys::pipe::AnonPipe::from_inner(fd); + let pipe = ChildPipe::from_inner(fd); process::ChildStderr::from_inner(pipe) } } diff --git a/library/std/src/os/unix/thread.rs b/library/std/src/os/unix/thread.rs index 03dcc3a4f9ba0..32085e525942e 100644 --- a/library/std/src/os/unix/thread.rs +++ b/library/std/src/os/unix/thread.rs @@ -6,7 +6,7 @@ #[allow(deprecated)] use crate::os::unix::raw::pthread_t; -use crate::sys_common::{AsInner, IntoInner}; +use crate::sys::{AsInner, IntoInner}; use crate::thread::JoinHandle; #[stable(feature = "thread_extensions", since = "1.9.0")] diff --git a/library/std/src/os/vita/fs.rs b/library/std/src/os/vita/fs.rs index a5a06764a4dd8..d796f02ba6a33 100644 --- a/library/std/src/os/vita/fs.rs +++ b/library/std/src/os/vita/fs.rs @@ -1,7 +1,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// OS-specific extensions to [`fs::Metadata`]. /// diff --git a/library/std/src/os/vxworks/fs.rs b/library/std/src/os/vxworks/fs.rs index 77e6238ca1f52..b88ed19b067a5 100644 --- a/library/std/src/os/vxworks/fs.rs +++ b/library/std/src/os/vxworks/fs.rs @@ -1,7 +1,7 @@ #![stable(feature = "metadata_ext", since = "1.1.0")] use crate::fs::Metadata; -use crate::sys_common::AsInner; +use crate::sys::AsInner; /// /// [`fs::Metadata`]: crate::fs::Metadata diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index 5ea91dd6521ad..248112cb369dc 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -1,4 +1,4 @@ -//! WASI-specific extensions to primitives in the [`std::fs`] module. +//! WASIp1-specific extensions to primitives in the [`std::fs`] module. //! //! [`std::fs`]: crate::fs @@ -8,11 +8,16 @@ #[allow(unused_imports)] use io::{Read, Write}; +#[cfg(target_env = "p1")] use crate::ffi::OsStr; -use crate::fs::{self, File, Metadata, OpenOptions}; -use crate::io::{self, IoSlice, IoSliceMut}; -use crate::path::{Path, PathBuf}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner}; +use crate::fs::{self, File, OpenOptions}; +use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +#[cfg(target_env = "p1")] +use crate::os::fd::AsRawFd; +use crate::path::Path; +#[cfg(target_env = "p1")] +use crate::sys::err2io; +use crate::sys::{AsInner, AsInnerMut}; /// WASI-specific extensions to [`File`]. pub trait FileExt { @@ -27,10 +32,7 @@ pub trait FileExt { /// /// Note that similar to [`File::read`], it is not an error to return with a /// short read. - fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - let bufs = &mut [IoSliceMut::new(buf)]; - self.read_vectored_at(bufs, offset) - } + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result; /// Reads a number of bytes starting from a given offset. /// @@ -45,6 +47,13 @@ pub trait FileExt { /// return with a short read. fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result; + /// Reads some bytes starting from a given offset into the buffer. + /// + /// This equivalent to the [`read_at`](FileExt::read_at) method, except that it is passed a + /// [`BorrowedCursor`] rather than `&mut [u8]` to allow use with uninitialized buffers. The new + /// data will be appended to any existing contents of `buf`. + fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()>; + /// Reads the exact number of byte required to fill `buf` from the given offset. /// /// The offset is relative to the start of the file and thus independent @@ -102,10 +111,7 @@ pub trait FileExt { /// /// Note that similar to [`File::write`], it is not an error to return a /// short write. - fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - let bufs = &[IoSlice::new(buf)]; - self.write_vectored_at(bufs, offset) - } + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result; /// Writes a number of bytes starting from a given offset. /// @@ -164,54 +170,49 @@ pub trait FileExt { /// /// This corresponds to the `fd_fdstat_set_flags` syscall. #[doc(alias = "fd_fdstat_set_flags")] + #[cfg(target_env = "p1")] fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>; /// Adjusts the rights associated with this file. /// /// This corresponds to the `fd_fdstat_set_rights` syscall. #[doc(alias = "fd_fdstat_set_rights")] + #[cfg(target_env = "p1")] fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>; /// Provides file advisory information on a file descriptor. /// /// This corresponds to the `fd_advise` syscall. #[doc(alias = "fd_advise")] + #[cfg(target_env = "p1")] fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>; /// Forces the allocation of space in a file. /// /// This corresponds to the `fd_allocate` syscall. #[doc(alias = "fd_allocate")] + #[cfg(target_env = "p1")] fn allocate(&self, offset: u64, len: u64) -> io::Result<()>; /// Creates a directory. /// /// This corresponds to the `path_create_directory` syscall. #[doc(alias = "path_create_directory")] + #[cfg(target_env = "p1")] fn create_directory>(&self, dir: P) -> io::Result<()>; - /// Reads the contents of a symbolic link. - /// - /// This corresponds to the `path_readlink` syscall. - #[doc(alias = "path_readlink")] - fn read_link>(&self, path: P) -> io::Result; - - /// Returns the attributes of a file or directory. - /// - /// This corresponds to the `path_filestat_get` syscall. - #[doc(alias = "path_filestat_get")] - fn metadata_at>(&self, lookup_flags: u32, path: P) -> io::Result; - /// Unlinks a file. /// /// This corresponds to the `path_unlink_file` syscall. #[doc(alias = "path_unlink_file")] + #[cfg(target_env = "p1")] fn remove_file>(&self, path: P) -> io::Result<()>; /// Removes a directory. /// /// This corresponds to the `path_remove_directory` syscall. #[doc(alias = "path_remove_directory")] + #[cfg(target_env = "p1")] fn remove_directory>(&self, path: P) -> io::Result<()>; } @@ -222,23 +223,41 @@ pub trait FileExt { // FIXME: bind poll_oneoff maybe? - probably should wait for I/O to settle // FIXME: bind random_get maybe? - on crates.io for unix -impl FileExt for fs::File { +impl FileExt for File { + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + self.as_inner().read_at(buf, offset) + } + + fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> { + self.as_inner().read_buf_at(buf, offset) + } + fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - self.as_inner().as_inner().pread(bufs, offset) + self.as_inner().read_vectored_at(bufs, offset) + } + + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + self.as_inner().write_at(buf, offset) } fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - self.as_inner().as_inner().pwrite(bufs, offset) + self.as_inner().write_vectored_at(bufs, offset) } + #[cfg(target_env = "p1")] fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> { - self.as_inner().as_inner().set_flags(flags) + unsafe { wasi::fd_fdstat_set_flags(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) } } + #[cfg(target_env = "p1")] fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> { - self.as_inner().as_inner().set_rights(rights, inheriting) + unsafe { + wasi::fd_fdstat_set_rights(self.as_raw_fd() as wasi::Fd, rights, inheriting) + .map_err(err2io) + } } + #[cfg(target_env = "p1")] fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> { let advice = match advice { a if a == wasi::ADVICE_NORMAL.raw() => wasi::ADVICE_NORMAL, @@ -255,149 +274,46 @@ impl FileExt for fs::File { } }; - self.as_inner().as_inner().advise(offset, len, advice) + unsafe { + wasi::fd_advise(self.as_raw_fd() as wasi::Fd, offset, len, advice).map_err(err2io) + } } + #[cfg(target_env = "p1")] fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { - self.as_inner().as_inner().allocate(offset, len) + unsafe { wasi::fd_allocate(self.as_raw_fd() as wasi::Fd, offset, len).map_err(err2io) } } + #[cfg(target_env = "p1")] fn create_directory>(&self, dir: P) -> io::Result<()> { - self.as_inner().as_inner().create_directory(osstr2str(dir.as_ref().as_ref())?) - } - - fn read_link>(&self, path: P) -> io::Result { - self.as_inner().read_link(path.as_ref()) - } - - fn metadata_at>(&self, lookup_flags: u32, path: P) -> io::Result { - let m = self.as_inner().metadata_at(lookup_flags, path.as_ref())?; - Ok(FromInner::from_inner(m)) + let path = osstr2str(dir.as_ref().as_ref())?; + unsafe { wasi::path_create_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } } + #[cfg(target_env = "p1")] fn remove_file>(&self, path: P) -> io::Result<()> { - self.as_inner().as_inner().unlink_file(osstr2str(path.as_ref().as_ref())?) + let path = osstr2str(path.as_ref().as_ref())?; + unsafe { wasi::path_unlink_file(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } } + #[cfg(target_env = "p1")] fn remove_directory>(&self, path: P) -> io::Result<()> { - self.as_inner().as_inner().remove_directory(osstr2str(path.as_ref().as_ref())?) + let path = osstr2str(path.as_ref().as_ref())?; + unsafe { wasi::path_remove_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } } } -/// WASI-specific extensions to [`fs::OpenOptions`]. +/// WASI-specific extensions to [`OpenOptions`]. pub trait OpenOptionsExt { - /// Pass custom `dirflags` argument to `path_open`. - /// - /// This option configures the `dirflags` argument to the - /// `path_open` syscall which `OpenOptions` will eventually call. The - /// `dirflags` argument configures how the file is looked up, currently - /// primarily affecting whether symlinks are followed or not. - /// - /// By default this value is `__WASI_LOOKUP_SYMLINK_FOLLOW`, or symlinks are - /// followed. You can call this method with 0 to disable following symlinks - fn lookup_flags(&mut self, flags: u32) -> &mut Self; - - /// Indicates whether `OpenOptions` must open a directory or not. - /// - /// This method will configure whether the `__WASI_O_DIRECTORY` flag is - /// passed when opening a file. When passed it will require that the opened - /// path is a directory. - /// - /// This option is by default `false` - fn directory(&mut self, dir: bool) -> &mut Self; - - /// Indicates whether `__WASI_FDFLAG_DSYNC` is passed in the `fs_flags` - /// field of `path_open`. - /// - /// This option is by default `false` - fn dsync(&mut self, dsync: bool) -> &mut Self; - - /// Indicates whether `__WASI_FDFLAG_NONBLOCK` is passed in the `fs_flags` - /// field of `path_open`. - /// - /// This option is by default `false` - fn nonblock(&mut self, nonblock: bool) -> &mut Self; - - /// Indicates whether `__WASI_FDFLAG_RSYNC` is passed in the `fs_flags` - /// field of `path_open`. - /// - /// This option is by default `false` - fn rsync(&mut self, rsync: bool) -> &mut Self; - - /// Indicates whether `__WASI_FDFLAG_SYNC` is passed in the `fs_flags` - /// field of `path_open`. - /// - /// This option is by default `false` - fn sync(&mut self, sync: bool) -> &mut Self; - - /// Indicates the value that should be passed in for the `fs_rights_base` - /// parameter of `path_open`. - /// - /// This option defaults based on the `read` and `write` configuration of - /// this `OpenOptions` builder. If this method is called, however, the - /// exact mask passed in will be used instead. - fn fs_rights_base(&mut self, rights: u64) -> &mut Self; - - /// Indicates the value that should be passed in for the - /// `fs_rights_inheriting` parameter of `path_open`. - /// - /// The default for this option is the same value as what will be passed - /// for the `fs_rights_base` parameter but if this method is called then - /// the specified value will be used instead. - fn fs_rights_inheriting(&mut self, rights: u64) -> &mut Self; - - /// Open a file or directory. - /// - /// This corresponds to the `path_open` syscall. - #[doc(alias = "path_open")] - fn open_at>(&self, file: &File, path: P) -> io::Result; + /// Pass custom flags to the `flags` argument of `open`. + fn custom_flags(&mut self, flags: i32) -> &mut Self; } impl OpenOptionsExt for OpenOptions { - fn lookup_flags(&mut self, flags: u32) -> &mut OpenOptions { - self.as_inner_mut().lookup_flags(flags); - self - } - - fn directory(&mut self, dir: bool) -> &mut OpenOptions { - self.as_inner_mut().directory(dir); - self - } - - fn dsync(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().dsync(enabled); - self - } - - fn nonblock(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().nonblock(enabled); - self - } - - fn rsync(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().rsync(enabled); - self - } - - fn sync(&mut self, enabled: bool) -> &mut OpenOptions { - self.as_inner_mut().sync(enabled); + fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions { + self.as_inner_mut().custom_flags(flags); self } - - fn fs_rights_base(&mut self, rights: u64) -> &mut OpenOptions { - self.as_inner_mut().fs_rights_base(rights); - self - } - - fn fs_rights_inheriting(&mut self, rights: u64) -> &mut OpenOptions { - self.as_inner_mut().fs_rights_inheriting(rights); - self - } - - fn open_at>(&self, file: &File, path: P) -> io::Result { - let inner = file.as_inner().open_at(path.as_ref(), self.as_inner())?; - Ok(File::from_inner(inner)) - } } /// WASI-specific extensions to [`fs::Metadata`]. @@ -408,37 +324,17 @@ pub trait MetadataExt { fn ino(&self) -> u64; /// Returns the `st_nlink` field of the internal `filestat_t` fn nlink(&self) -> u64; - /// Returns the `st_size` field of the internal `filestat_t` - fn size(&self) -> u64; - /// Returns the `st_atim` field of the internal `filestat_t` - fn atim(&self) -> u64; - /// Returns the `st_mtim` field of the internal `filestat_t` - fn mtim(&self) -> u64; - /// Returns the `st_ctim` field of the internal `filestat_t` - fn ctim(&self) -> u64; } impl MetadataExt for fs::Metadata { fn dev(&self) -> u64 { - self.as_inner().as_wasi().dev + self.as_inner().as_inner().st_dev } fn ino(&self) -> u64 { - self.as_inner().as_wasi().ino + self.as_inner().as_inner().st_ino } fn nlink(&self) -> u64 { - self.as_inner().as_wasi().nlink - } - fn size(&self) -> u64 { - self.as_inner().as_wasi().size - } - fn atim(&self) -> u64 { - self.as_inner().as_wasi().atim - } - fn mtim(&self) -> u64 { - self.as_inner().as_wasi().mtim - } - fn ctim(&self) -> u64 { - self.as_inner().as_wasi().ctim + self.as_inner().as_inner().st_nlink } } @@ -451,28 +347,19 @@ pub trait FileTypeExt { fn is_block_device(&self) -> bool; /// Returns `true` if this file type is a character device. fn is_char_device(&self) -> bool; - /// Returns `true` if this file type is a socket datagram. - fn is_socket_dgram(&self) -> bool; - /// Returns `true` if this file type is a socket stream. - fn is_socket_stream(&self) -> bool; /// Returns `true` if this file type is any type of socket. - fn is_socket(&self) -> bool { - self.is_socket_stream() || self.is_socket_dgram() - } + fn is_socket(&self) -> bool; } impl FileTypeExt for fs::FileType { fn is_block_device(&self) -> bool { - self.as_inner().bits() == wasi::FILETYPE_BLOCK_DEVICE + self.as_inner().is(libc::S_IFBLK) } fn is_char_device(&self) -> bool { - self.as_inner().bits() == wasi::FILETYPE_CHARACTER_DEVICE - } - fn is_socket_dgram(&self) -> bool { - self.as_inner().bits() == wasi::FILETYPE_SOCKET_DGRAM + self.as_inner().is(libc::S_IFCHR) } - fn is_socket_stream(&self) -> bool { - self.as_inner().bits() == wasi::FILETYPE_SOCKET_STREAM + fn is_socket(&self) -> bool { + self.as_inner().is(libc::S_IFSOCK) } } @@ -492,6 +379,7 @@ impl DirEntryExt for fs::DirEntry { /// /// This corresponds to the `path_link` syscall. #[doc(alias = "path_link")] +#[cfg(target_env = "p1")] pub fn link, U: AsRef>( old_fd: &File, old_flags: u32, @@ -499,43 +387,58 @@ pub fn link, U: AsRef>( new_fd: &File, new_path: U, ) -> io::Result<()> { - old_fd.as_inner().as_inner().link( - old_flags, - osstr2str(old_path.as_ref().as_ref())?, - new_fd.as_inner().as_inner(), - osstr2str(new_path.as_ref().as_ref())?, - ) + unsafe { + wasi::path_link( + old_fd.as_raw_fd() as wasi::Fd, + old_flags, + osstr2str(old_path.as_ref().as_ref())?, + new_fd.as_raw_fd() as wasi::Fd, + osstr2str(new_path.as_ref().as_ref())?, + ) + .map_err(err2io) + } } /// Renames a file or directory. /// /// This corresponds to the `path_rename` syscall. #[doc(alias = "path_rename")] +#[cfg(target_env = "p1")] pub fn rename, U: AsRef>( old_fd: &File, old_path: P, new_fd: &File, new_path: U, ) -> io::Result<()> { - old_fd.as_inner().as_inner().rename( - osstr2str(old_path.as_ref().as_ref())?, - new_fd.as_inner().as_inner(), - osstr2str(new_path.as_ref().as_ref())?, - ) + unsafe { + wasi::path_rename( + old_fd.as_raw_fd() as wasi::Fd, + osstr2str(old_path.as_ref().as_ref())?, + new_fd.as_raw_fd() as wasi::Fd, + osstr2str(new_path.as_ref().as_ref())?, + ) + .map_err(err2io) + } } /// Creates a symbolic link. /// /// This corresponds to the `path_symlink` syscall. #[doc(alias = "path_symlink")] +#[cfg(target_env = "p1")] pub fn symlink, U: AsRef>( old_path: P, fd: &File, new_path: U, ) -> io::Result<()> { - fd.as_inner() - .as_inner() - .symlink(osstr2str(old_path.as_ref().as_ref())?, osstr2str(new_path.as_ref().as_ref())?) + unsafe { + wasi::path_symlink( + osstr2str(old_path.as_ref().as_ref())?, + fd.as_raw_fd() as wasi::Fd, + osstr2str(new_path.as_ref().as_ref())?, + ) + .map_err(err2io) + } } /// Creates a symbolic link. @@ -546,6 +449,7 @@ pub fn symlink_path, U: AsRef>(old_path: P, new_path: U) -> crate::sys::fs::symlink(old_path.as_ref(), new_path.as_ref()) } +#[cfg(target_env = "p1")] fn osstr2str(f: &OsStr) -> io::Result<&str> { f.to_str().ok_or_else(|| io::const_error!(io::ErrorKind::Uncategorized, "input must be utf-8")) } diff --git a/library/std/src/os/wasi/net/mod.rs b/library/std/src/os/wasi/net/mod.rs index 4704dd574517a..9430cd3b05eee 100644 --- a/library/std/src/os/wasi/net/mod.rs +++ b/library/std/src/os/wasi/net/mod.rs @@ -2,7 +2,8 @@ #![unstable(feature = "wasi_ext", issue = "71213")] -use crate::sys_common::AsInner; +use crate::os::fd::AsRawFd; +use crate::sys::err2io; use crate::{io, net}; /// WASI-specific extensions to [`std::net::TcpListener`]. @@ -17,6 +18,6 @@ pub trait TcpListenerExt { impl TcpListenerExt for net::TcpListener { fn sock_accept(&self, flags: u16) -> io::Result { - self.as_inner().as_inner().as_inner().sock_accept(flags) + unsafe { wasi::sock_accept(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) } } } diff --git a/library/std/src/os/windows/ffi.rs b/library/std/src/os/windows/ffi.rs index 20e5383dc09e0..fa115385c5d4e 100644 --- a/library/std/src/os/windows/ffi.rs +++ b/library/std/src/os/windows/ffi.rs @@ -60,7 +60,7 @@ use crate::fmt; use crate::iter::FusedIterator; use crate::sealed::Sealed; use crate::sys::os_str::Buf; -use crate::sys_common::{AsInner, FromInner}; +use crate::sys::{AsInner, FromInner}; /// Windows-specific extensions to [`OsString`]. /// diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index b445f368aeb12..73f3e589e2432 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -8,7 +8,7 @@ use crate::fs::{self, Metadata, OpenOptions}; use crate::io::BorrowedCursor; use crate::path::Path; use crate::sealed::Sealed; -use crate::sys_common::{AsInner, AsInnerMut, IntoInner}; +use crate::sys::{AsInner, AsInnerMut, IntoInner}; use crate::time::SystemTime; use crate::{io, sys}; @@ -305,6 +305,18 @@ pub trait OpenOptionsExt { /// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level #[stable(feature = "open_options_ext", since = "1.10.0")] fn security_qos_flags(&mut self, flags: u32) -> &mut Self; + + /// If set to `true`, prevent the "last access time" of the file from being changed. + /// + /// Default to `false`. + #[unstable(feature = "windows_freeze_file_times", issue = "149715")] + fn freeze_last_access_time(&mut self, freeze: bool) -> &mut Self; + + /// If set to `true`, prevent the "last write time" of the file from being changed. + /// + /// Default to `false`. + #[unstable(feature = "windows_freeze_file_times", issue = "149715")] + fn freeze_last_write_time(&mut self, freeze: bool) -> &mut Self; } #[stable(feature = "open_options_ext", since = "1.10.0")] @@ -333,6 +345,16 @@ impl OpenOptionsExt for OpenOptions { self.as_inner_mut().security_qos_flags(flags); self } + + fn freeze_last_access_time(&mut self, freeze: bool) -> &mut Self { + self.as_inner_mut().freeze_last_access_time(freeze); + self + } + + fn freeze_last_write_time(&mut self, freeze: bool) -> &mut Self { + self.as_inner_mut().freeze_last_write_time(freeze); + self + } } /// Windows-specific extensions to [`fs::Metadata`]. diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index 4fc04b79315f9..13c0752b560b9 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -5,8 +5,7 @@ use super::raw::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; use crate::marker::PhantomData; use crate::mem::ManuallyDrop; -use crate::sys::cvt; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner, cvt}; use crate::{fmt, fs, io, ptr, sys}; /// A borrowed handle. @@ -185,7 +184,7 @@ impl OwnedHandle { /// Creates a new `OwnedHandle` instance that shares the same underlying /// object as the existing `OwnedHandle` instance. #[stable(feature = "io_safety", since = "1.63.0")] - pub fn try_clone(&self) -> crate::io::Result { + pub fn try_clone(&self) -> io::Result { self.as_handle().try_clone_to_owned() } } @@ -194,7 +193,7 @@ impl BorrowedHandle<'_> { /// Creates a new `OwnedHandle` instance that shares the same underlying /// object as the existing `BorrowedHandle` instance. #[stable(feature = "io_safety", since = "1.63.0")] - pub fn try_clone_to_owned(&self) -> crate::io::Result { + pub fn try_clone_to_owned(&self) -> io::Result { self.duplicate(0, false, sys::c::DUPLICATE_SAME_ACCESS) } @@ -410,7 +409,7 @@ macro_rules! impl_is_terminal { impl crate::sealed::Sealed for $t {} #[stable(feature = "is_terminal", since = "1.70.0")] - impl crate::io::IsTerminal for $t { + impl io::IsTerminal for $t { #[inline] fn is_terminal(&self) -> bool { crate::sys::io::is_terminal(self) @@ -547,7 +546,7 @@ impl From for fs::File { } #[stable(feature = "io_safety", since = "1.63.0")] -impl AsHandle for crate::io::Stdin { +impl AsHandle for io::Stdin { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } @@ -555,7 +554,7 @@ impl AsHandle for crate::io::Stdin { } #[stable(feature = "io_safety", since = "1.63.0")] -impl<'a> AsHandle for crate::io::StdinLock<'a> { +impl<'a> AsHandle for io::StdinLock<'a> { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } @@ -563,7 +562,7 @@ impl<'a> AsHandle for crate::io::StdinLock<'a> { } #[stable(feature = "io_safety", since = "1.63.0")] -impl AsHandle for crate::io::Stdout { +impl AsHandle for io::Stdout { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } @@ -571,7 +570,7 @@ impl AsHandle for crate::io::Stdout { } #[stable(feature = "io_safety", since = "1.63.0")] -impl<'a> AsHandle for crate::io::StdoutLock<'a> { +impl<'a> AsHandle for io::StdoutLock<'a> { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } @@ -579,7 +578,7 @@ impl<'a> AsHandle for crate::io::StdoutLock<'a> { } #[stable(feature = "io_safety", since = "1.63.0")] -impl AsHandle for crate::io::Stderr { +impl AsHandle for io::Stderr { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } @@ -587,7 +586,7 @@ impl AsHandle for crate::io::Stderr { } #[stable(feature = "io_safety", since = "1.63.0")] -impl<'a> AsHandle for crate::io::StderrLock<'a> { +impl<'a> AsHandle for io::StderrLock<'a> { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index a3ec7440338d2..0d3f24005f369 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -6,7 +6,7 @@ use crate::os::windows::io::{AsHandle, AsSocket}; use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys::{AsInner, FromInner, IntoInner}; use crate::{fs, io, net, ptr, sys}; /// Raw HANDLEs. diff --git a/library/std/src/os/windows/mod.rs b/library/std/src/os/windows/mod.rs index f452403ee8426..53c33d17a9f65 100644 --- a/library/std/src/os/windows/mod.rs +++ b/library/std/src/os/windows/mod.rs @@ -29,6 +29,8 @@ pub mod ffi; pub mod fs; pub mod io; +#[unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +pub mod net; pub mod process; pub mod raw; pub mod thread; diff --git a/library/std/src/os/windows/net/addr.rs b/library/std/src/os/windows/net/addr.rs new file mode 100644 index 0000000000000..ef2263edcf617 --- /dev/null +++ b/library/std/src/os/windows/net/addr.rs @@ -0,0 +1,173 @@ +#![unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +use crate::bstr::ByteStr; +use crate::ffi::OsStr; +use crate::path::Path; +#[cfg(not(doc))] +use crate::sys::c::{AF_UNIX, SOCKADDR, SOCKADDR_UN}; +use crate::sys::cvt_nz; +use crate::{fmt, io, mem, ptr}; + +#[cfg(not(doc))] +pub fn sockaddr_un(path: &Path) -> io::Result<(SOCKADDR_UN, usize)> { + // SAFETY: All zeros is a valid representation for `sockaddr_un`. + let mut addr: SOCKADDR_UN = unsafe { mem::zeroed() }; + addr.sun_family = AF_UNIX; + + // path to UTF-8 bytes + let bytes = path + .to_str() + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "path must be valid UTF-8"))? + .as_bytes(); + if bytes.len() >= addr.sun_path.len() { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "path too long")); + } + // SAFETY: `bytes` and `addr.sun_path` are not overlapping and + // both point to valid memory. + // NOTE: We zeroed the memory above, so the path is already null + // terminated. + unsafe { + ptr::copy_nonoverlapping(bytes.as_ptr(), addr.sun_path.as_mut_ptr().cast(), bytes.len()) + }; + + let len = SUN_PATH_OFFSET + bytes.len() + 1; + Ok((addr, len)) +} +#[cfg(not(doc))] +const SUN_PATH_OFFSET: usize = mem::offset_of!(SOCKADDR_UN, sun_path); +pub struct SocketAddr { + #[cfg(not(doc))] + pub(super) addr: SOCKADDR_UN, + pub(super) len: u32, // Use u32 here as same as libc::socklen_t +} +impl fmt::Debug for SocketAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.address() { + AddressKind::Unnamed => write!(fmt, "(unnamed)"), + AddressKind::Abstract(name) => write!(fmt, "{name:?} (abstract)"), + AddressKind::Pathname(path) => write!(fmt, "{path:?} (pathname)"), + } + } +} + +impl SocketAddr { + #[cfg(not(doc))] + pub(super) fn new(f: F) -> io::Result + where + F: FnOnce(*mut SOCKADDR, *mut i32) -> i32, + { + unsafe { + let mut addr: SOCKADDR_UN = mem::zeroed(); + let mut len = mem::size_of::() as i32; + cvt_nz(f(&raw mut addr as *mut _, &mut len))?; + SocketAddr::from_parts(addr, len) + } + } + #[cfg(not(doc))] + pub(super) fn from_parts(addr: SOCKADDR_UN, len: i32) -> io::Result { + if addr.sun_family != AF_UNIX { + Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid address family")) + } else if len < SUN_PATH_OFFSET as _ || len > mem::size_of::() as _ { + Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid address length")) + } else { + Ok(SocketAddr { addr, len: len as _ }) + } + } + + /// Returns the contents of this address if it is a `pathname` address. + /// + /// # Examples + /// + /// With a pathname: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// use std::path::Path; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixListener::bind("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); + /// Ok(()) + /// } + /// ``` + pub fn as_pathname(&self) -> Option<&Path> { + if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } + } + + /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. + /// + /// # Errors + /// + /// Returns an error if the path is longer than `SUN_LEN` or if it contains + /// NULL bytes. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::SocketAddr; + /// use std::path::Path; + /// + /// # fn main() -> std::io::Result<()> { + /// let address = SocketAddr::from_pathname("/path/to/socket")?; + /// assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); + /// # Ok(()) + /// # } + /// ``` + /// + /// Creating a `SocketAddr` with a NULL byte results in an error. + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::SocketAddr; + /// + /// assert!(SocketAddr::from_pathname("/path/with/\0/bytes").is_err()); + /// ``` + pub fn from_pathname

(&mut self, predicate: P) -> Option where P: FnMut(Self::Item) -> bool, @@ -3165,6 +3229,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn max(self) -> Option where Self: Sized, @@ -3201,6 +3266,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn min(self) -> Option where Self: Sized, @@ -3223,6 +3289,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iter_cmp_by_key", since = "1.6.0")] + #[rustc_non_const_trait_method] fn max_by_key(self, f: F) -> Option where Self: Sized, @@ -3256,6 +3323,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iter_max_by", since = "1.15.0")] + #[rustc_non_const_trait_method] fn max_by(self, compare: F) -> Option where Self: Sized, @@ -3283,6 +3351,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iter_cmp_by_key", since = "1.6.0")] + #[rustc_non_const_trait_method] fn min_by_key(self, f: F) -> Option where Self: Sized, @@ -3316,6 +3385,7 @@ pub trait Iterator { /// ``` #[inline] #[stable(feature = "iter_min_by", since = "1.15.0")] + #[rustc_non_const_trait_method] fn min_by(self, compare: F) -> Option where Self: Sized, @@ -3353,6 +3423,7 @@ pub trait Iterator { #[inline] #[doc(alias = "reverse")] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn rev(self) -> Rev where Self: Sized + DoubleEndedIterator, @@ -3389,6 +3460,7 @@ pub trait Iterator { /// assert_eq!(z, [3, 6]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_non_const_trait_method] fn unzip(self) -> (FromA, FromB) where FromA: Default + Extend, @@ -3420,6 +3492,7 @@ pub trait Iterator { /// ``` #[stable(feature = "iter_copied", since = "1.36.0")] #[rustc_diagnostic_item = "iter_copied"] + #[rustc_non_const_trait_method] fn copied<'a, T>(self) -> Copied where T: Copy + 'a, @@ -3468,6 +3541,7 @@ pub trait Iterator { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "iter_cloned"] + #[rustc_non_const_trait_method] fn cloned<'a, T>(self) -> Cloned where T: Clone + 'a, @@ -3499,6 +3573,7 @@ pub trait Iterator { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[rustc_non_const_trait_method] fn cycle(self) -> Cycle where Self: Sized + Clone, @@ -3541,7 +3616,8 @@ pub trait Iterator { /// } /// ``` #[track_caller] - #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] + #[unstable(feature = "iter_array_chunks", issue = "100450")] + #[rustc_non_const_trait_method] fn array_chunks(self) -> ArrayChunks where Self: Sized, @@ -3578,6 +3654,7 @@ pub trait Iterator { /// assert_eq!(sum, -0.0_f32); /// ``` #[stable(feature = "iter_arith", since = "1.11.0")] + #[rustc_non_const_trait_method] fn sum(self) -> S where Self: Sized, @@ -3610,6 +3687,7 @@ pub trait Iterator { /// assert_eq!(factorial(5), 120); /// ``` #[stable(feature = "iter_arith", since = "1.11.0")] + #[rustc_non_const_trait_method] fn product