From 9bead2bcdd0962e23696da226e6dedff4c795f53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Sat, 21 Feb 2026 13:13:02 +0100 Subject: [PATCH 1/3] Make `alloc::ffi` compile with `no_rc` and `no_sync` --- library/alloc/src/ffi/c_str.rs | 13 +++++++++---- library/alloc/src/lib.rs | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index b2f4277458f1c..19f7426e4ee68 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -9,9 +9,10 @@ use core::{fmt, mem, ops, ptr, slice}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; +#[cfg(not(no_rc))] use crate::rc::Rc; use crate::string::String; -#[cfg(target_has_atomic = "ptr")] +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] use crate::sync::Arc; use crate::vec::Vec; @@ -898,7 +899,7 @@ impl<'a> From<&'a CString> for Cow<'a, CStr> { } } -#[cfg(target_has_atomic = "ptr")] +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Arc { /// Converts a [`CString`] into an [Arc]<[CStr]> by moving the [`CString`] @@ -910,7 +911,7 @@ impl From for Arc { } } -#[cfg(target_has_atomic = "ptr")] +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<&CStr> for Arc { /// Converts a `&CStr` into a `Arc`, @@ -922,7 +923,7 @@ impl From<&CStr> for Arc { } } -#[cfg(target_has_atomic = "ptr")] +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] #[stable(feature = "shared_from_mut_slice", since = "1.84.0")] impl From<&mut CStr> for Arc { /// Converts a `&mut CStr` into a `Arc`, @@ -933,6 +934,7 @@ impl From<&mut CStr> for Arc { } } +#[cfg(not(no_rc))] #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Rc { /// Converts a [`CString`] into an [Rc]<[CStr]> by moving the [`CString`] @@ -944,6 +946,7 @@ impl From for Rc { } } +#[cfg(not(no_rc))] #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From<&CStr> for Rc { /// Converts a `&CStr` into a `Rc`, @@ -955,6 +958,7 @@ impl From<&CStr> for Rc { } } +#[cfg(not(no_rc))] #[stable(feature = "shared_from_mut_slice", since = "1.84.0")] impl From<&mut CStr> for Rc { /// Converts a `&mut CStr` into a `Rc`, @@ -965,6 +969,7 @@ impl From<&mut CStr> for Rc { } } +#[cfg(not(no_rc))] #[cfg(not(no_global_oom_handling))] #[stable(feature = "more_rc_default_impls", since = "1.80.0")] impl Default for Rc { diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 7ac9cdc3833d3..5d23a90b41005 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -220,7 +220,7 @@ pub mod boxed; #[unstable(feature = "bstr", issue = "134915")] pub mod bstr; pub mod collections; -#[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))] +#[cfg(not(no_global_oom_handling))] pub mod ffi; pub mod fmt; pub mod intrinsics; From e787a98f7a1a3e7cd1a9345e67d3c6fcaa59057e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Sat, 17 Jan 2026 16:37:15 +0100 Subject: [PATCH 2/3] Move `io::Error` to `alloc` --- library/{std => alloc}/src/io/error.rs | 270 +++++++++--------- .../src/io/error/repr_bitpacked.rs | 1 + .../src/io/error/repr_unpacked.rs | 2 + library/alloc/src/io/mod.rs | 18 ++ library/alloc/src/lib.rs | 9 +- library/std/src/io/error/mod.rs | 115 ++++++++ library/std/src/io/error/tests.rs | 67 ++--- library/std/src/io/mod.rs | 17 +- library/std/src/lib.rs | 5 + library/std/src/sys/io/error/mod.rs | 5 - library/std/src/sys/io/mod.rs | 2 +- src/tools/linkchecker/main.rs | 2 + tests/rustdoc-html/intra-doc/deprecated.rs | 4 +- tests/rustdoc-html/intra-doc/field.rs | 2 +- .../binary-op-not-allowed-issue-125631.stderr | 6 +- 15 files changed, 326 insertions(+), 199 deletions(-) rename library/{std => alloc}/src/io/error.rs (87%) rename library/{std => alloc}/src/io/error/repr_bitpacked.rs (99%) rename library/{std => alloc}/src/io/error/repr_unpacked.rs (97%) create mode 100644 library/alloc/src/io/mod.rs create mode 100644 library/std/src/io/error/mod.rs diff --git a/library/std/src/io/error.rs b/library/alloc/src/io/error.rs similarity index 87% rename from library/std/src/io/error.rs rename to library/alloc/src/io/error.rs index e6c6f7d766c02..bb81a6564a2d1 100644 --- a/library/std/src/io/error.rs +++ b/library/alloc/src/io/error.rs @@ -1,5 +1,6 @@ -#[cfg(test)] -mod tests; +use core::{error, fmt, result}; + +use crate::boxed::Box; // On 64-bit platforms, `io::Error` may use a bit-packed representation to // reduce size. However, this representation assumes that error codes are @@ -8,17 +9,94 @@ mod tests; // 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")))] -use repr_bitpacked::Repr; -#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] -mod repr_unpacked; -#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] -use repr_unpacked::Repr; +cfg_select! { + all(target_pointer_width = "64", not(target_os = "uefi")) => { + use repr_bitpacked::Repr; + mod repr_bitpacked; + } + _ => { + mod repr_unpacked; + use repr_unpacked::Repr; + } +} + +#[cfg(target_has_atomic = "ptr")] +pub(super) mod os { + //! OS-dependent functions + //! + //! `Error` needs OS functionalities to work interpret raw OS errors, but + //! we can't link to anythink here in `alloc`. Therefore, we restrict + //! creation of `Error` from raw OS errors in `std`, and require providing + //! a vtable of operations when creating one. + + // FIXME: replace this with externally implementable items once they are more stable + + use core::sync::atomic; + + use super::{ErrorKind, RawOsError}; + use crate::string::String; + + #[doc(hidden)] + #[derive(Debug)] + pub struct OsFunctions { + pub error_string: fn(_: RawOsError) -> String, + pub decode_error_kind: fn(_: RawOsError) -> ErrorKind, + pub is_interrupted: fn(_: RawOsError) -> bool, + } + + /// These default functions are not reachable, but have them just to be safe. + const DEFAULT_FUNCTIONS: &'static OsFunctions = &OsFunctions { + error_string: |_| String::new(), + decode_error_kind: |_| ErrorKind::Uncategorized, + is_interrupted: |_| false, + }; + static OS_FUNCTIONS: atomic::AtomicPtr = + atomic::AtomicPtr::new(DEFAULT_FUNCTIONS as *const _ as *mut _); + + #[inline] + pub(super) fn set_functions(f: &'static OsFunctions) { + OS_FUNCTIONS.store(f as *const _ as *mut _, atomic::Ordering::Relaxed); + } -use crate::{error, fmt, result, sys}; + #[inline] + pub(super) fn error_string(errno: RawOsError) -> String { + let f = unsafe { &*OS_FUNCTIONS.load(atomic::Ordering::Relaxed) }; + (f.error_string)(errno) + } + + #[inline] + pub(super) fn decode_error_kind(errno: RawOsError) -> ErrorKind { + let f = unsafe { &*OS_FUNCTIONS.load(atomic::Ordering::Relaxed) }; + (f.decode_error_kind)(errno) + } + + #[inline] + pub(super) fn is_interrupted(errno: RawOsError) -> bool { + let f = unsafe { &*OS_FUNCTIONS.load(atomic::Ordering::Relaxed) }; + (f.is_interrupted)(errno) + } +} + +// Disable these on target without atomics, they don't support `std` anyway +#[cfg(not(target_has_atomic = "ptr"))] +pub(super) mod os { + use super::{ErrorKind, RawOsError}; + use crate::string::String; + + #[inline] + pub(super) fn error_string(_: RawOsError) -> String { + String::new() + } + #[inline] + pub(super) fn decode_error_kind(_: RawOsError) -> ErrorKind { + ErrorKind::Uncategorized + } + #[inline] + pub(super) fn is_interrupted(_: RawOsError) -> bool { + false + } +} /// A specialized [`Result`] type for I/O operations. /// @@ -31,13 +109,12 @@ use crate::{error, fmt, result, sys}; /// While usual Rust style is to import types directly, aliases of [`Result`] /// often are not, to make it easier to distinguish between them. [`Result`] is /// generally assumed to be [`std::result::Result`][`Result`], and so users of this alias -/// will generally use `io::Result` instead of shadowing the [prelude]'s import +/// will generally use `io::Result` instead of shadowing the prelude's import /// of [`std::result::Result`][`Result`]. /// /// [`std::io`]: crate::io /// [`io::Error`]: Error -/// [`Result`]: crate::result::Result -/// [prelude]: crate::prelude +/// [`Result`]: core::result::Result /// /// # Examples /// @@ -58,17 +135,13 @@ use crate::{error, fmt, result, sys}; #[doc(search_unbox)] pub type Result = result::Result; -/// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and -/// associated traits. +/// The error type for I/O operations. /// /// Errors mostly originate from the underlying OS, but custom instances of /// `Error` can be created with crafted error messages and a particular value of /// [`ErrorKind`]. -/// -/// [`Read`]: crate::io::Read -/// [`Write`]: crate::io::Write -/// [`Seek`]: crate::io::Seek #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_has_incoherent_inherent_impls] pub struct Error { repr: Repr, } @@ -81,47 +154,48 @@ impl fmt::Debug for Error { } /// Common errors constants for use in std -#[allow(dead_code)] +#[doc(hidden)] +#[unstable(feature = "io_error_internals", issue = "none")] impl Error { - pub(crate) const INVALID_UTF8: Self = + pub const INVALID_UTF8: Self = const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8"); - pub(crate) const READ_EXACT_EOF: Self = + pub const READ_EXACT_EOF: Self = const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"); - pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_error!( + pub const UNKNOWN_THREAD_COUNT: Self = const_error!( ErrorKind::NotFound, "the number of hardware threads is not known for the target platform", ); - pub(crate) const UNSUPPORTED_PLATFORM: Self = + pub const UNSUPPORTED_PLATFORM: Self = const_error!(ErrorKind::Unsupported, "operation not supported on this platform"); - pub(crate) const WRITE_ALL_EOF: Self = + pub const WRITE_ALL_EOF: Self = const_error!(ErrorKind::WriteZero, "failed to write whole buffer"); - pub(crate) const ZERO_TIMEOUT: Self = + pub const ZERO_TIMEOUT: Self = const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); - pub(crate) const NO_ADDRESSES: Self = + pub const NO_ADDRESSES: Self = const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); } #[stable(feature = "rust1", since = "1.0.0")] -impl From for Error { - /// Converts a [`alloc::ffi::NulError`] into a [`Error`]. - fn from(_: alloc::ffi::NulError) -> Error { +impl From for Error { + /// Converts a [`crate::ffi::NulError`] into a [`Error`]. + fn from(_: crate::ffi::NulError) -> Error { const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") } } #[stable(feature = "io_error_from_try_reserve", since = "1.78.0")] -impl From for Error { +impl From for Error { /// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`]. /// /// `TryReserveError` won't be available as the error `source()`, /// but this may change in the future. - fn from(_: alloc::collections::TryReserveError) -> Error { + fn from(_: crate::collections::TryReserveError) -> Error { // ErrorData::Custom allocates, which isn't great for handling OOM errors. ErrorKind::OutOfMemory.into() } @@ -137,7 +211,7 @@ enum ErrorData { Custom(C), } -/// The type of raw OS error codes returned by [`Error::raw_os_error`]. +/// The type of raw OS error codes. /// /// This is an [`i32`] on all currently supported platforms, but platforms /// added in the future (such as UEFI) may use a different primitive type like @@ -145,8 +219,11 @@ enum ErrorData { /// portability. /// /// [`into`]: Into::into -#[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub type RawOsError = sys::io::RawOsError; +#[allow(unreachable_pub)] +pub type RawOsError = cfg_select! { + target_os = "uefi" => usize, + _ => i32, +}; // `#[repr(align(4))]` is probably redundant, it should have that value or // higher already. We include it just because repr_bitpacked.rs's encoding @@ -189,9 +266,9 @@ pub struct SimpleMessage { /// ``` #[rustc_macro_transparency = "semiopaque"] #[unstable(feature = "io_const_error", issue = "133448")] -#[allow_internal_unstable(hint_must_use, io_const_error_internals)] +#[allow_internal_unstable(alloc_io, hint_must_use, io_const_error_internals, liballoc_internals)] pub macro const_error($kind:expr, $message:expr $(,)?) { - $crate::hint::must_use($crate::io::Error::from_static_message( + $crate::__export::must_use($crate::io::Error::from_static_message( const { &$crate::io::SimpleMessage { kind: $kind, message: $message } }, )) } @@ -328,14 +405,14 @@ pub enum ErrorKind { /// The I/O operation's timeout expired, causing it to be canceled. #[stable(feature = "rust1", since = "1.0.0")] TimedOut, + // FIXME: restore links to `write` when trait is moved to `alloc` /// An error returned when an operation could not be completed because a - /// call to [`write`] returned [`Ok(0)`]. + /// call to `write` returned [`Ok(0)`]. /// /// This typically means that an operation could only succeed if it wrote a /// particular number of bytes but only a smaller number of bytes could be /// written. /// - /// [`write`]: crate::io::Write::write /// [`Ok(0)`]: Ok #[stable(feature = "rust1", since = "1.0.0")] WriteZero, @@ -628,99 +705,21 @@ impl Error { Self { repr: Repr::new_simple_message(msg) } } - /// Returns an error representing the last OS error which occurred. - /// - /// This function reads the value of `errno` for the target platform (e.g. - /// `GetLastError` on Windows) and will return a corresponding instance of - /// [`Error`] for the error code. - /// - /// This should be called immediately after a call to a platform function, - /// otherwise the state of the error value is indeterminate. In particular, - /// other standard library functions may call platform functions that may - /// (or may not) reset the error value even if they succeed. - /// - /// # Examples - /// - /// ``` - /// use std::io::Error; - /// - /// let os_error = Error::last_os_error(); - /// println!("last OS error: {os_error:?}"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[doc(alias = "GetLastError")] - #[doc(alias = "errno")] - #[must_use] + #[unstable(feature = "io_error_internals", issue = "none")] #[inline] - pub fn last_os_error() -> Error { - Error::from_raw_os_error(sys::io::errno()) - } - - /// Creates a new instance of an [`Error`] from a particular OS error code. - /// - /// # Examples - /// - /// On Linux: - /// - /// ``` - /// # if cfg!(target_os = "linux") { - /// use std::io; - /// - /// let error = io::Error::from_raw_os_error(22); - /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); - /// # } - /// ``` - /// - /// On Windows: - /// - /// ``` - /// # if cfg!(windows) { - /// use std::io; - /// - /// let error = io::Error::from_raw_os_error(10022); - /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); - /// # } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] #[must_use] - #[inline] - pub fn from_raw_os_error(code: RawOsError) -> Error { + #[doc(hidden)] + #[cfg(target_has_atomic = "ptr")] + pub fn _from_raw_os_error(code: RawOsError, os: &'static os::OsFunctions) -> Error { + os::set_functions(os); Error { repr: Repr::new_os(code) } } - /// Returns the OS error that this error represents (if any). - /// - /// If this [`Error`] was constructed via [`last_os_error`] or - /// [`from_raw_os_error`], then this function will return [`Some`], otherwise - /// it will return [`None`]. - /// - /// [`last_os_error`]: Error::last_os_error - /// [`from_raw_os_error`]: Error::from_raw_os_error - /// - /// # Examples - /// - /// ``` - /// use std::io::{Error, ErrorKind}; - /// - /// fn print_os_error(err: &Error) { - /// if let Some(raw_os_err) = err.raw_os_error() { - /// println!("raw OS error: {raw_os_err:?}"); - /// } else { - /// println!("Not an OS error"); - /// } - /// } - /// - /// fn main() { - /// // Will print "raw OS error: ...". - /// print_os_error(&Error::last_os_error()); - /// // Will print "Not an OS error". - /// print_os_error(&Error::new(ErrorKind::Other, "oh no!")); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] + #[unstable(feature = "io_error_internals", issue = "none")] #[must_use] #[inline] - pub fn raw_os_error(&self) -> Option { + #[doc(hidden)] + pub fn _raw_os_error(&self) -> Option { match self.repr.data() { ErrorData::Os(i) => Some(i), ErrorData::Custom(..) => None, @@ -966,7 +965,7 @@ impl Error { Ok(*err) } else { // Safety: We have just checked that the condition is true - unsafe { crate::hint::unreachable_unchecked() } + unsafe { core::hint::unreachable_unchecked() } } } else { Err(self) @@ -978,9 +977,6 @@ impl Error { /// This may be a value set by Rust code constructing custom `io::Error`s, /// or if this `io::Error` was sourced from the operating system, /// it will be a value inferred from the system's error encoding. - /// See [`last_os_error`] for more details. - /// - /// [`last_os_error`]: Error::last_os_error /// /// # Examples /// @@ -1004,17 +1000,19 @@ impl Error { #[inline] pub fn kind(&self) -> ErrorKind { match self.repr.data() { - ErrorData::Os(code) => sys::io::decode_error_kind(code), + ErrorData::Os(code) => os::decode_error_kind(code), ErrorData::Custom(c) => c.kind, ErrorData::Simple(kind) => kind, ErrorData::SimpleMessage(m) => m.kind, } } + #[unstable(feature = "io_error_internals", issue = "none")] + #[doc(hidden)] #[inline] - pub(crate) fn is_interrupted(&self) -> bool { + pub fn is_interrupted(&self) -> bool { match self.repr.data() { - ErrorData::Os(code) => sys::io::is_interrupted(code), + ErrorData::Os(code) => os::is_interrupted(code), ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, @@ -1028,8 +1026,8 @@ impl fmt::Debug for Repr { ErrorData::Os(code) => fmt .debug_struct("Os") .field("code", &code) - .field("kind", &sys::io::decode_error_kind(code)) - .field("message", &sys::io::error_string(code)) + .field("kind", &os::decode_error_kind(code)) + .field("message", &os::error_string(code)) .finish(), ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), @@ -1047,7 +1045,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::io::error_string(code); + let detail = os::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/alloc/src/io/error/repr_bitpacked.rs similarity index 99% rename from library/std/src/io/error/repr_bitpacked.rs rename to library/alloc/src/io/error/repr_bitpacked.rs index 7353816a8171b..9759bd33e654e 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/alloc/src/io/error/repr_bitpacked.rs @@ -107,6 +107,7 @@ use core::num::NonZeroUsize; use core::ptr::NonNull; use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; +use crate::boxed::Box; // The 2 least-significant bits are used as tag. const TAG_MASK: usize = 0b11; diff --git a/library/std/src/io/error/repr_unpacked.rs b/library/alloc/src/io/error/repr_unpacked.rs similarity index 97% rename from library/std/src/io/error/repr_unpacked.rs rename to library/alloc/src/io/error/repr_unpacked.rs index b3e7b5f024ea0..59e51e97951cc 100644 --- a/library/std/src/io/error/repr_unpacked.rs +++ b/library/alloc/src/io/error/repr_unpacked.rs @@ -3,6 +3,7 @@ //! would have no benefit. use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; +use crate::boxed::Box; type Inner = ErrorData>; @@ -13,6 +14,7 @@ impl Repr { pub(super) fn new_custom(b: Box) -> Self { Self(Inner::Custom(b)) } + #[allow(dead_code)] #[inline] pub(super) fn new_os(code: RawOsError) -> Self { Self(Inner::Os(code)) diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs new file mode 100644 index 0000000000000..c3cb8ff62f938 --- /dev/null +++ b/library/alloc/src/io/mod.rs @@ -0,0 +1,18 @@ +//! Traits, helpers, and type definitions for core I/O functionality. + +#[unstable(feature = "read_buf", issue = "78485")] +pub use core::io::{BorrowedBuf, BorrowedCursor}; + +#[doc(hidden)] +#[unstable(feature = "io_const_error_internals", issue = "none")] +pub use self::error::SimpleMessage; +#[unstable(feature = "io_const_error", issue = "133448")] +pub use self::error::const_error; +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::error::{Error, ErrorKind, Result}; +#[unstable(feature = "io_error_internals", issue = "none")] +#[doc(hidden)] +#[cfg(target_has_atomic = "ptr")] +pub use self::error::{RawOsError, os::OsFunctions}; + +mod error; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 5d23a90b41005..1250c052d088b 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -63,7 +63,10 @@ #![doc( html_playground_url = "https://play.rust-lang.org/", issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", - test(no_crate_inject, attr(allow(unused_variables, duplicate_features), deny(warnings))) + test( + no_crate_inject, + attr(allow(unused_variables, duplicate_features, dead_code), deny(warnings)) + ) )] #![doc(auto_cfg(hide(no_global_oom_handling, no_rc, no_sync, target_has_atomic = "ptr")))] #![doc(rust_logo)] @@ -86,6 +89,7 @@ // // Library features: // tidy-alphabetical-start +#![cfg_attr(not(no_global_oom_handling), feature(io_const_error))] #![feature(allocator_api)] #![feature(array_into_iter_constructors)] #![feature(ascii_char)] @@ -224,6 +228,9 @@ pub mod collections; pub mod ffi; pub mod fmt; pub mod intrinsics; +#[unstable(feature = "alloc_io", issue = "154046")] +#[cfg(not(no_global_oom_handling))] +pub mod io; #[cfg(not(no_rc))] pub mod rc; pub mod slice; diff --git a/library/std/src/io/error/mod.rs b/library/std/src/io/error/mod.rs new file mode 100644 index 0000000000000..74649b747c56b --- /dev/null +++ b/library/std/src/io/error/mod.rs @@ -0,0 +1,115 @@ +//! OS-dependent public methods of `Error` + +#[cfg(test)] +mod tests; + +#[cfg(not(test))] +use alloc::io::{Error, RawOsError}; + +// FIXME: for some reason, enabling these in `test` config makes them defined twice, +// but it does not seem to be the case with other incoherent items. +#[cfg(not(test))] +impl Error { + /// Creates a new instance of an [`Error`] from a particular OS error code. + /// + /// # Examples + /// + /// On Linux: + /// + /// ``` + /// # if cfg!(target_os = "linux") { + /// use std::io; + /// + /// let error = io::Error::from_raw_os_error(22); + /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); + /// # } + /// ``` + /// + /// On Windows: + /// + /// ``` + /// # if cfg!(windows) { + /// use std::io; + /// + /// let error = io::Error::from_raw_os_error(10022); + /// assert_eq!(error.kind(), io::ErrorKind::InvalidInput); + /// # } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + #[rustc_allow_incoherent_impl] + pub fn from_raw_os_error(code: RawOsError) -> Error { + Error::_from_raw_os_error( + code, + &alloc::io::OsFunctions { + error_string: crate::sys::io::error_string, + decode_error_kind: crate::sys::io::decode_error_kind, + is_interrupted: crate::sys::io::is_interrupted, + }, + ) + } + + /// Returns the OS error that this error represents (if any). + /// + /// If this [`Error`] was constructed via `last_os_error` or + /// `from_raw_os_error`, then this function will return [`Some`], otherwise + /// it will return [`None`]. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_os_error(err: &Error) { + /// if let Some(raw_os_err) = err.raw_os_error() { + /// println!("raw OS error: {raw_os_err:?}"); + /// } else { + /// println!("Not an OS error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "raw OS error: ...". + /// print_os_error(&Error::last_os_error()); + /// // Will print "Not an OS error". + /// print_os_error(&Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + #[rustc_allow_incoherent_impl] + pub fn raw_os_error(&self) -> Option { + self._raw_os_error() + } + + /// Returns an error representing the last OS error which occurred. + /// + /// This function reads the value of `errno` for the target platform (e.g. + /// `GetLastError` on Windows) and will return a corresponding instance of + /// [`Error`] for the error code. + /// + /// This should be called immediately after a call to a platform function, + /// otherwise the state of the error value is indeterminate. In particular, + /// other standard library functions may call platform functions that may + /// (or may not) reset the error value even if they succeed. + /// + /// # Examples + /// + /// ``` + /// use std::io::Error; + /// + /// let os_error = Error::last_os_error(); + /// println!("last OS error: {os_error:?}"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[doc(alias = "GetLastError")] + #[doc(alias = "errno")] + #[must_use] + #[inline] + #[rustc_allow_incoherent_impl] + pub fn last_os_error() -> Error { + Error::from_raw_os_error(crate::sys::io::errno()) + } +} diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index a8eef06381dae..772c74b2ed6ba 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,6 +1,12 @@ -use super::{Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage, const_error}; +use crate::io::{Error, ErrorKind, const_error}; use crate::sys::io::{decode_error_kind, error_string}; -use crate::{assert_matches, error, fmt}; +use crate::{error, fmt}; + +// Make sure that all these are used even when running tests +#[cfg(test)] +const _: () = { + let _ = (crate::sys::io::errno, crate::sys::io::is_interrupted); +}; #[test] fn test_size() { @@ -12,12 +18,9 @@ fn test_debug_error() { let code = 6; let msg = error_string(code); let kind = decode_error_kind(code); - let err = Error { - repr: Repr::new_custom(Box::new(Custom { - kind: ErrorKind::InvalidInput, - error: Box::new(Error { repr: super::Repr::new_os(code) }), - })), - }; + + let err = Error::new(ErrorKind::InvalidInput, Error::from_raw_os_error(code)); + let expected = format!( "Custom {{ \ kind: InvalidInput, \ @@ -30,6 +33,15 @@ fn test_debug_error() { code, kind, msg ); assert_eq!(format!("{err:?}"), expected); + + let err = Error::from(ErrorKind::AddrInUse); + assert_eq!(format!("{err:?}"), "Kind(AddrInUse)"); + + let err = Error::READ_EXACT_EOF; + assert_eq!( + format!("{err:?}"), + "Error { kind: UnexpectedEof, message: \"failed to fill whole buffer\" }" + ); } #[test] @@ -70,10 +82,6 @@ fn test_os_packing() { for code in -20..20 { let e = Error::from_raw_os_error(code); assert_eq!(e.raw_os_error(), Some(code)); - assert_matches!( - e.repr.data(), - ErrorData::Os(c) if c == code, - ); } } @@ -82,28 +90,18 @@ fn test_errorkind_packing() { assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound); assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied); assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized); - // Check that the innards look like what we want. - assert_matches!( - Error::from(ErrorKind::OutOfMemory).repr.data(), - ErrorData::Simple(ErrorKind::OutOfMemory), - ); } #[test] fn test_simple_message_packing() { - use super::ErrorKind::*; - use super::SimpleMessage; + use crate::io::ErrorKind::*; macro_rules! check_simple_msg { ($err:expr, $kind:ident, $msg:literal) => {{ let e = &$err; // Check that the public api is right. assert_eq!(e.kind(), $kind); assert!(format!("{e:?}").contains($msg)); - // and we got what we expected - assert_matches!( - e.repr.data(), - ErrorData::SimpleMessage(SimpleMessage { kind: $kind, message: $msg }) - ); + assert!(format!("{e}").contains($msg)); }}; } @@ -126,19 +124,6 @@ impl fmt::Display for Bojji { } } -#[test] -fn test_custom_error_packing() { - use super::Custom; - let test = Error::new(ErrorKind::Uncategorized, Bojji(true)); - assert_matches!( - test.repr.data(), - ErrorData::Custom(Custom { - kind: ErrorKind::Uncategorized, - error, - }) if error.downcast_ref::().as_deref() == Some(&Bojji(true)), - ); -} - #[derive(Debug)] struct E; @@ -181,11 +166,9 @@ fn test_std_io_error_downcast() { assert_eq!(kind, io_error.kind()); // Case 5: simple message - const SIMPLE_MESSAGE: SimpleMessage = - SimpleMessage { kind: ErrorKind::Other, message: "simple message error test" }; - let io_error = Error::from_static_message(&SIMPLE_MESSAGE); + let io_error = const_error!(ErrorKind::Other, "simple message error test"); let io_error = io_error.downcast::().unwrap_err(); - assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind()); - assert_eq!(SIMPLE_MESSAGE.message, format!("{io_error}")); + assert_eq!(io_error.kind(), ErrorKind::Other); + assert_eq!(format!("{io_error}"), "simple message error test"); } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 623c34c6d2910..380036b06a07e 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -297,19 +297,21 @@ #[cfg(test)] mod tests; +#[unstable(feature = "raw_os_error_ty", issue = "107792")] +pub use alloc::io::RawOsError; +#[doc(hidden)] +#[unstable(feature = "io_const_error_internals", issue = "none")] +pub use alloc::io::SimpleMessage; +#[unstable(feature = "io_const_error", issue = "133448")] +pub use alloc::io::const_error; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc::io::{Error, ErrorKind, Result}; #[unstable(feature = "read_buf", issue = "78485")] pub use core::io::{BorrowedBuf, BorrowedCursor}; use core::slice::memchr; #[stable(feature = "bufwriter_into_parts", since = "1.56.0")] pub use self::buffered::WriterPanicked; -#[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub use self::error::RawOsError; -#[doc(hidden)] -#[unstable(feature = "io_const_error_internals", issue = "none")] -pub use self::error::SimpleMessage; -#[unstable(feature = "io_const_error", issue = "133448")] -pub use self::error::const_error; #[stable(feature = "anonymous_pipe", since = "1.87.0")] pub use self::pipe::{PipeReader, PipeWriter, pipe}; #[stable(feature = "is_terminal", since = "1.70.0")] @@ -326,7 +328,6 @@ pub use self::{ buffered::{BufReader, BufWriter, IntoInnerError, LineWriter}, copy::copy, cursor::Cursor, - error::{Error, ErrorKind, Result}, stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, util::{Empty, Repeat, Sink, empty, repeat, sink}, }; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 6fcb28edc7d84..7969352186f68 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -369,9 +369,14 @@ // // Library features (alloc): // tidy-alphabetical-start +#![feature(alloc_io)] #![feature(allocator_api)] #![feature(clone_from_ref)] #![feature(get_mut_unchecked)] +#![feature(io_error_inprogress)] +#![feature(io_error_internals)] +#![feature(io_error_more)] +#![feature(io_error_uncategorized)] #![feature(map_try_insert)] #![feature(slice_concat_trait)] #![feature(thin_box)] diff --git a/library/std/src/sys/io/error/mod.rs b/library/std/src/sys/io/error/mod.rs index d7a0b9b4b301d..4fca658a7dcaa 100644 --- a/library/std/src/sys/io/error/mod.rs +++ b/library/std/src/sys/io/error/mod.rs @@ -48,8 +48,3 @@ cfg_select! { pub use generic::*; } } - -pub type RawOsError = cfg_select! { - target_os = "uefi" => usize, - _ => i32, -}; diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index b3587ab63696a..445bcdef0aa1f 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -62,7 +62,7 @@ pub use error::errno_location; target_os = "wasi", ))] pub use error::set_errno; -pub use error::{RawOsError, decode_error_kind, errno, error_string, is_interrupted}; +pub use error::{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/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index e07a0784cdb3a..44a232bcc4216 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -51,6 +51,8 @@ const LINKCHECK_EXCEPTIONS: &[(&str, &[&str])] = &[ ("alloc/slice/trait.Concat.html", &["#method.concat"]), ("alloc/slice/index.html", &["#method.concat", "#method.join"]), ("alloc/vec/struct.Vec.html", &["#method.sort_by_key", "#method.sort_by_cached_key"]), + ("alloc/io/struct.IoSlice.html", &["#method.sort_by_key"]), + ("alloc/io/struct.IoSliceMut.html", &["#method.sort_by_key"]), ("alloc/bstr/struct.ByteStr.html", &[ "#method.to_ascii_uppercase", "#method.to_ascii_lowercase", diff --git a/tests/rustdoc-html/intra-doc/deprecated.rs b/tests/rustdoc-html/intra-doc/deprecated.rs index 6f8639593a2d4..86be33fe80cdc 100644 --- a/tests/rustdoc-html/intra-doc/deprecated.rs +++ b/tests/rustdoc-html/intra-doc/deprecated.rs @@ -1,6 +1,6 @@ //@ has deprecated/struct.A.html '//a[@href="{{channel}}/core/ops/range/struct.Range.html#structfield.start"]' 'start' -//@ has deprecated/struct.B1.html '//a[@href="{{channel}}/std/io/error/enum.ErrorKind.html#variant.NotFound"]' 'not_found' -//@ has deprecated/struct.B2.html '//a[@href="{{channel}}/std/io/error/enum.ErrorKind.html#variant.NotFound"]' 'not_found' +//@ has deprecated/struct.B1.html '//a[@href="{{channel}}/alloc/io/error/enum.ErrorKind.html#variant.NotFound"]' 'not_found' +//@ has deprecated/struct.B2.html '//a[@href="{{channel}}/alloc/io/error/enum.ErrorKind.html#variant.NotFound"]' 'not_found' #[deprecated = "[start][std::ops::Range::start]"] pub struct A; diff --git a/tests/rustdoc-html/intra-doc/field.rs b/tests/rustdoc-html/intra-doc/field.rs index e98419618e23f..ba8f09bc90510 100644 --- a/tests/rustdoc-html/intra-doc/field.rs +++ b/tests/rustdoc-html/intra-doc/field.rs @@ -1,5 +1,5 @@ //@ has field/index.html '//a[@href="{{channel}}/core/ops/range/struct.Range.html#structfield.start"]' 'start' -//@ has field/index.html '//a[@href="{{channel}}/std/io/error/enum.ErrorKind.html#variant.NotFound"]' 'not_found' +//@ has field/index.html '//a[@href="{{channel}}/alloc/io/error/enum.ErrorKind.html#variant.NotFound"]' 'not_found' //@ has field/index.html '//a[@href="struct.FieldAndMethod.html#structfield.x"]' 'x' //@ has field/index.html '//a[@href="enum.VariantAndMethod.html#variant.X"]' 'X' //! [start][std::ops::Range::start] diff --git a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr index a997fbee1f2a0..6ce86680c6fe8 100644 --- a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr +++ b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr @@ -12,7 +12,7 @@ note: an implementation of `PartialEq` might be missing for `T1` LL | struct T1; | ^^^^^^^^^ must implement `PartialEq` note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/alloc/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate help: consider annotating `T1` with `#[derive(PartialEq)]` @@ -34,7 +34,7 @@ note: `Thread` does not implement `PartialEq` | = note: `Thread` is defined in another crate note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/alloc/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate @@ -58,7 +58,7 @@ note: `Thread` does not implement `PartialEq` | = note: `Thread` is defined in another crate note: `std::io::Error` does not implement `PartialEq` - --> $SRC_DIR/std/src/io/error.rs:LL:COL + --> $SRC_DIR/alloc/src/io/error.rs:LL:COL | = note: `std::io::Error` is defined in another crate help: consider annotating `T1` with `#[derive(PartialEq)]` From 53895d328c08bbc3575ac7c8889b84a01e041443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20du=20Garreau?= Date: Fri, 20 Feb 2026 16:12:29 +0100 Subject: [PATCH 3/3] Move `io::IoSlice[Mut]` to `alloc` --- .../sys => alloc/src}/io/io_slice/iovec.rs | 18 +- library/alloc/src/io/io_slice/mod.rs | 28 ++ .../src/sys => alloc/src}/io/io_slice/uefi.rs | 4 +- .../src}/io/io_slice/unsupported.rs | 4 +- .../sys => alloc/src}/io/io_slice/windows.rs | 23 +- library/alloc/src/io/mod.rs | 321 +++++++++++++++++ library/std/src/io/mod.rs | 324 +----------------- library/std/src/lib.rs | 1 + library/std/src/sys/io/mod.rs | 22 -- 9 files changed, 379 insertions(+), 366 deletions(-) rename library/{std/src/sys => alloc/src}/io/io_slice/iovec.rs (86%) create mode 100644 library/alloc/src/io/io_slice/mod.rs rename library/{std/src/sys => alloc/src}/io/io_slice/uefi.rs (97%) rename library/{std/src/sys => alloc/src}/io/io_slice/unsupported.rs (93%) rename library/{std/src/sys => alloc/src}/io/io_slice/windows.rs (82%) diff --git a/library/std/src/sys/io/io_slice/iovec.rs b/library/alloc/src/io/io_slice/iovec.rs similarity index 86% rename from library/std/src/sys/io/io_slice/iovec.rs rename to library/alloc/src/io/io_slice/iovec.rs index d549aca250d5f..13fedb050fe01 100644 --- a/library/std/src/sys/io/io_slice/iovec.rs +++ b/library/alloc/src/io/io_slice/iovec.rs @@ -1,13 +1,13 @@ -#[cfg(target_os = "hermit")] -use hermit_abi::iovec; -#[cfg(any(target_family = "unix", target_os = "trusty", target_os = "wasi"))] -use libc::iovec; +use core::ffi::c_void; +use core::marker::PhantomData; +use core::slice; -use crate::ffi::c_void; -use crate::marker::PhantomData; -use crate::slice; -#[cfg(target_os = "solid_asp3")] -use crate::sys::pal::abi::sockets::iovec; +#[derive(Copy, Clone)] +#[repr(C)] +struct iovec { + iov_base: *mut c_void, + iov_len: usize, +} #[derive(Copy, Clone)] #[repr(transparent)] diff --git a/library/alloc/src/io/io_slice/mod.rs b/library/alloc/src/io/io_slice/mod.rs new file mode 100644 index 0000000000000..05ac400c9dd97 --- /dev/null +++ b/library/alloc/src/io/io_slice/mod.rs @@ -0,0 +1,28 @@ +//! Target-dependant definition of `IoSlice` and `IoSliceMut` +//! +//! This is necessary to do it in `alloc` because other parts of the crate need +//! them even though they must have different layouts depending on the platform. +//! +//! However, we take great care to not leak platform-specific details and to not +//! link to any library here. + +#![allow(unreachable_pub)] + +cfg_select! { + any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty", target_os = "wasi") => { + mod iovec; + pub use iovec::*; + } + target_os = "windows" => { + mod windows; + pub use windows::*; + } + target_os = "uefi" => { + mod uefi; + pub use uefi::*; + } + _ => { + mod unsupported; + pub use unsupported::*; + } +} diff --git a/library/std/src/sys/io/io_slice/uefi.rs b/library/alloc/src/io/io_slice/uefi.rs similarity index 97% rename from library/std/src/sys/io/io_slice/uefi.rs rename to library/alloc/src/io/io_slice/uefi.rs index 909cfbea0b7ba..739a98a4cea31 100644 --- a/library/std/src/sys/io/io_slice/uefi.rs +++ b/library/alloc/src/io/io_slice/uefi.rs @@ -1,8 +1,8 @@ //! A buffer type used with `Write::write_vectored` for UEFI Networking APIs. Vectored writing to //! File is not supported as of UEFI Spec 2.11. -use crate::marker::PhantomData; -use crate::slice; +use core::marker::PhantomData; +use core::slice; #[derive(Copy, Clone)] #[repr(C)] diff --git a/library/std/src/sys/io/io_slice/unsupported.rs b/library/alloc/src/io/io_slice/unsupported.rs similarity index 93% rename from library/std/src/sys/io/io_slice/unsupported.rs rename to library/alloc/src/io/io_slice/unsupported.rs index 1572cac6cd771..dd429a89d9a40 100644 --- a/library/std/src/sys/io/io_slice/unsupported.rs +++ b/library/alloc/src/io/io_slice/unsupported.rs @@ -1,5 +1,3 @@ -use crate::mem; - #[derive(Copy, Clone)] pub struct IoSlice<'a>(&'a [u8]); @@ -30,7 +28,7 @@ impl<'a> IoSliceMut<'a> { #[inline] pub fn advance(&mut self, n: usize) { - let slice = mem::take(&mut self.0); + let slice = core::mem::take(&mut self.0); let (_, remaining) = slice.split_at_mut(n); self.0 = remaining; } diff --git a/library/std/src/sys/io/io_slice/windows.rs b/library/alloc/src/io/io_slice/windows.rs similarity index 82% rename from library/std/src/sys/io/io_slice/windows.rs rename to library/alloc/src/io/io_slice/windows.rs index c3d8ec87c19e3..69cf68aabe23b 100644 --- a/library/std/src/sys/io/io_slice/windows.rs +++ b/library/alloc/src/io/io_slice/windows.rs @@ -1,11 +1,17 @@ -use crate::marker::PhantomData; -use crate::slice; -use crate::sys::c; +use core::marker::PhantomData; +use core::slice; + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct WSABUF { + pub len: u32, + pub buf: *mut u8, +} #[derive(Copy, Clone)] #[repr(transparent)] pub struct IoSlice<'a> { - vec: c::WSABUF, + vec: WSABUF, _p: PhantomData<&'a [u8]>, } @@ -14,7 +20,7 @@ impl<'a> IoSlice<'a> { pub fn new(buf: &'a [u8]) -> IoSlice<'a> { assert!(buf.len() <= u32::MAX as usize); IoSlice { - vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 }, + vec: WSABUF { len: buf.len() as u32, buf: buf.as_ptr() as *mut u8 }, _p: PhantomData, } } @@ -39,7 +45,7 @@ impl<'a> IoSlice<'a> { #[repr(transparent)] pub struct IoSliceMut<'a> { - vec: c::WSABUF, + vec: WSABUF, _p: PhantomData<&'a mut [u8]>, } @@ -47,10 +53,7 @@ impl<'a> IoSliceMut<'a> { #[inline] pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { assert!(buf.len() <= u32::MAX as usize); - IoSliceMut { - vec: c::WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() }, - _p: PhantomData, - } + IoSliceMut { vec: WSABUF { len: buf.len() as u32, buf: buf.as_mut_ptr() }, _p: PhantomData } } #[inline] diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index c3cb8ff62f938..e633f4d3726a4 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -2,6 +2,8 @@ #[unstable(feature = "read_buf", issue = "78485")] pub use core::io::{BorrowedBuf, BorrowedCursor}; +use core::mem::take; +use core::ops::{Deref, DerefMut}; #[doc(hidden)] #[unstable(feature = "io_const_error_internals", issue = "none")] @@ -14,5 +16,324 @@ pub use self::error::{Error, ErrorKind, Result}; #[doc(hidden)] #[cfg(target_has_atomic = "ptr")] pub use self::error::{RawOsError, os::OsFunctions}; +use crate::fmt; mod error; +mod io_slice; + +/// A buffer type used with `Read::read_vectored`. +/// +/// It is semantically a wrapper around a `&mut [u8]`, but is guaranteed to be +/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on +/// Windows. +#[stable(feature = "iovec", since = "1.36.0")] +#[repr(transparent)] +pub struct IoSliceMut<'a>(io_slice::IoSliceMut<'a>); + +#[stable(feature = "iovec_send_sync", since = "1.44.0")] +unsafe impl<'a> Send for IoSliceMut<'a> {} + +#[stable(feature = "iovec_send_sync", since = "1.44.0")] +unsafe impl<'a> Sync for IoSliceMut<'a> {} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> fmt::Debug for IoSliceMut<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.0.as_slice(), fmt) + } +} + +impl<'a> IoSliceMut<'a> { + /// Creates a new `IoSliceMut` wrapping a byte slice. + /// + /// # Panics + /// + /// Panics on Windows if the slice is larger than 4GB. + #[stable(feature = "iovec", since = "1.36.0")] + #[inline] + pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { + IoSliceMut(io_slice::IoSliceMut::new(buf)) + } + + /// Advance the internal cursor of the slice. + /// + /// Also see [`IoSliceMut::advance_slices`] to advance the cursors of + /// multiple buffers. + /// + /// # Panics + /// + /// Panics when trying to advance beyond the end of the slice. + /// + /// # Examples + /// + /// ``` + /// use std::io::IoSliceMut; + /// use std::ops::Deref; + /// + /// let mut data = [1; 8]; + /// let mut buf = IoSliceMut::new(&mut data); + /// + /// // Mark 3 bytes as read. + /// buf.advance(3); + /// assert_eq!(buf.deref(), [1; 5].as_ref()); + /// ``` + #[stable(feature = "io_slice_advance", since = "1.81.0")] + #[inline] + pub fn advance(&mut self, n: usize) { + self.0.advance(n) + } + + /// Advance a slice of slices. + /// + /// Shrinks the slice to remove any `IoSliceMut`s that are fully advanced over. + /// If the cursor ends up in the middle of an `IoSliceMut`, it is modified + /// to start at that cursor. + /// + /// For example, if we have a slice of two 8-byte `IoSliceMut`s, and we advance by 10 bytes, + /// the result will only include the second `IoSliceMut`, advanced by 2 bytes. + /// + /// # Panics + /// + /// Panics when trying to advance beyond the end of the slices. + /// + /// # Examples + /// + /// ``` + /// use std::io::IoSliceMut; + /// use std::ops::Deref; + /// + /// let mut buf1 = [1; 8]; + /// let mut buf2 = [2; 16]; + /// let mut buf3 = [3; 8]; + /// let mut bufs = &mut [ + /// IoSliceMut::new(&mut buf1), + /// IoSliceMut::new(&mut buf2), + /// IoSliceMut::new(&mut buf3), + /// ][..]; + /// + /// // Mark 10 bytes as read. + /// IoSliceMut::advance_slices(&mut bufs, 10); + /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); + /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); + /// ``` + #[stable(feature = "io_slice_advance", since = "1.81.0")] + #[inline] + pub fn advance_slices(bufs: &mut &mut [IoSliceMut<'a>], n: usize) { + // Number of buffers to remove. + let mut remove = 0; + // Remaining length before reaching n. + let mut left = n; + for buf in bufs.iter() { + if let Some(remainder) = left.checked_sub(buf.len()) { + left = remainder; + remove += 1; + } else { + break; + } + } + + *bufs = &mut take(bufs)[remove..]; + if bufs.is_empty() { + assert!(left == 0, "advancing io slices beyond their length"); + } else { + bufs[0].advance(left); + } + } + + /// Get the underlying bytes as a mutable slice with the original lifetime. + /// + /// # Examples + /// + /// ``` + /// #![feature(io_slice_as_bytes)] + /// use std::io::IoSliceMut; + /// + /// let mut data = *b"abcdef"; + /// let io_slice = IoSliceMut::new(&mut data); + /// io_slice.into_slice()[0] = b'A'; + /// + /// assert_eq!(&data, b"Abcdef"); + /// ``` + #[unstable(feature = "io_slice_as_bytes", issue = "132818")] + pub const fn into_slice(self) -> &'a mut [u8] { + self.0.into_slice() + } +} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> Deref for IoSliceMut<'a> { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + self.0.as_slice() + } +} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> DerefMut for IoSliceMut<'a> { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + self.0.as_mut_slice() + } +} + +/// A buffer type used with `Write::write_vectored`. +/// +/// It is semantically a wrapper around a `&[u8]`, but is guaranteed to be +/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on +/// Windows. +#[stable(feature = "iovec", since = "1.36.0")] +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct IoSlice<'a>(io_slice::IoSlice<'a>); + +#[stable(feature = "iovec_send_sync", since = "1.44.0")] +unsafe impl<'a> Send for IoSlice<'a> {} + +#[stable(feature = "iovec_send_sync", since = "1.44.0")] +unsafe impl<'a> Sync for IoSlice<'a> {} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> fmt::Debug for IoSlice<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.0.as_slice(), fmt) + } +} + +impl<'a> IoSlice<'a> { + /// Creates a new `IoSlice` wrapping a byte slice. + /// + /// # Panics + /// + /// Panics on Windows if the slice is larger than 4GB. + #[stable(feature = "iovec", since = "1.36.0")] + #[must_use] + #[inline] + pub fn new(buf: &'a [u8]) -> IoSlice<'a> { + IoSlice(io_slice::IoSlice::new(buf)) + } + + /// Advance the internal cursor of the slice. + /// + /// Also see [`IoSlice::advance_slices`] to advance the cursors of multiple + /// buffers. + /// + /// # Panics + /// + /// Panics when trying to advance beyond the end of the slice. + /// + /// # Examples + /// + /// ``` + /// use std::io::IoSlice; + /// use std::ops::Deref; + /// + /// let data = [1; 8]; + /// let mut buf = IoSlice::new(&data); + /// + /// // Mark 3 bytes as read. + /// buf.advance(3); + /// assert_eq!(buf.deref(), [1; 5].as_ref()); + /// ``` + #[stable(feature = "io_slice_advance", since = "1.81.0")] + #[inline] + pub fn advance(&mut self, n: usize) { + self.0.advance(n) + } + + /// Advance a slice of slices. + /// + /// Shrinks the slice to remove any `IoSlice`s that are fully advanced over. + /// If the cursor ends up in the middle of an `IoSlice`, it is modified + /// to start at that cursor. + /// + /// For example, if we have a slice of two 8-byte `IoSlice`s, and we advance by 10 bytes, + /// the result will only include the second `IoSlice`, advanced by 2 bytes. + /// + /// # Panics + /// + /// Panics when trying to advance beyond the end of the slices. + /// + /// # Examples + /// + /// ``` + /// use std::io::IoSlice; + /// use std::ops::Deref; + /// + /// let buf1 = [1; 8]; + /// let buf2 = [2; 16]; + /// let buf3 = [3; 8]; + /// let mut bufs = &mut [ + /// IoSlice::new(&buf1), + /// IoSlice::new(&buf2), + /// IoSlice::new(&buf3), + /// ][..]; + /// + /// // Mark 10 bytes as written. + /// IoSlice::advance_slices(&mut bufs, 10); + /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); + /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); + #[stable(feature = "io_slice_advance", since = "1.81.0")] + #[inline] + pub fn advance_slices(bufs: &mut &mut [IoSlice<'a>], n: usize) { + // Number of buffers to remove. + let mut remove = 0; + // Remaining length before reaching n. This prevents overflow + // that could happen if the length of slices in `bufs` were instead + // accumulated. Those slice may be aliased and, if they are large + // enough, their added length may overflow a `usize`. + let mut left = n; + for buf in bufs.iter() { + if let Some(remainder) = left.checked_sub(buf.len()) { + left = remainder; + remove += 1; + } else { + break; + } + } + + *bufs = &mut take(bufs)[remove..]; + if bufs.is_empty() { + assert!(left == 0, "advancing io slices beyond their length"); + } else { + bufs[0].advance(left); + } + } + + /// Get the underlying bytes as a slice with the original lifetime. + /// + /// This doesn't borrow from `self`, so is less restrictive than calling + /// `.deref()`, which does. + /// + /// # Examples + /// + /// ``` + /// #![feature(io_slice_as_bytes)] + /// use std::io::IoSlice; + /// + /// let data = b"abcdef"; + /// + /// let mut io_slice = IoSlice::new(data); + /// let tail = &io_slice.as_slice()[3..]; + /// + /// // This works because `tail` doesn't borrow `io_slice` + /// io_slice = IoSlice::new(tail); + /// + /// assert_eq!(io_slice.as_slice(), b"def"); + /// ``` + #[unstable(feature = "io_slice_as_bytes", issue = "132818")] + pub const fn as_slice(self) -> &'a [u8] { + self.0.as_slice() + } +} + +#[stable(feature = "iovec", since = "1.36.0")] +impl<'a> Deref for IoSlice<'a> { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + self.0.as_slice() + } +} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 380036b06a07e..0a48bb670ac1e 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -306,6 +306,8 @@ pub use alloc::io::SimpleMessage; pub use alloc::io::const_error; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc::io::{Error, ErrorKind, Result}; +#[stable(feature = "iovec", since = "1.36.0")] +pub use alloc::io::{IoSlice, IoSliceMut}; #[unstable(feature = "read_buf", issue = "78485")] pub use core::io::{BorrowedBuf, BorrowedCursor}; use core::slice::memchr; @@ -331,9 +333,8 @@ pub use self::{ stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, util::{Empty, Repeat, Sink, empty, repeat, sink}, }; -use crate::mem::{MaybeUninit, take}; -use crate::ops::{Deref, DerefMut}; -use crate::{cmp, fmt, slice, str, sys}; +use crate::mem::MaybeUninit; +use crate::{cmp, fmt, slice, str}; mod buffered; pub(crate) mod copy; @@ -1350,323 +1351,6 @@ pub fn read_to_string(mut reader: R) -> Result { Ok(buf) } -/// A buffer type used with `Read::read_vectored`. -/// -/// It is semantically a wrapper around a `&mut [u8]`, but is guaranteed to be -/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on -/// Windows. -#[stable(feature = "iovec", since = "1.36.0")] -#[repr(transparent)] -pub struct IoSliceMut<'a>(sys::io::IoSliceMut<'a>); - -#[stable(feature = "iovec_send_sync", since = "1.44.0")] -unsafe impl<'a> Send for IoSliceMut<'a> {} - -#[stable(feature = "iovec_send_sync", since = "1.44.0")] -unsafe impl<'a> Sync for IoSliceMut<'a> {} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> fmt::Debug for IoSliceMut<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.0.as_slice(), fmt) - } -} - -impl<'a> IoSliceMut<'a> { - /// Creates a new `IoSliceMut` wrapping a byte slice. - /// - /// # Panics - /// - /// Panics on Windows if the slice is larger than 4GB. - #[stable(feature = "iovec", since = "1.36.0")] - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut(sys::io::IoSliceMut::new(buf)) - } - - /// Advance the internal cursor of the slice. - /// - /// Also see [`IoSliceMut::advance_slices`] to advance the cursors of - /// multiple buffers. - /// - /// # Panics - /// - /// Panics when trying to advance beyond the end of the slice. - /// - /// # Examples - /// - /// ``` - /// use std::io::IoSliceMut; - /// use std::ops::Deref; - /// - /// let mut data = [1; 8]; - /// let mut buf = IoSliceMut::new(&mut data); - /// - /// // Mark 3 bytes as read. - /// buf.advance(3); - /// assert_eq!(buf.deref(), [1; 5].as_ref()); - /// ``` - #[stable(feature = "io_slice_advance", since = "1.81.0")] - #[inline] - pub fn advance(&mut self, n: usize) { - self.0.advance(n) - } - - /// Advance a slice of slices. - /// - /// Shrinks the slice to remove any `IoSliceMut`s that are fully advanced over. - /// If the cursor ends up in the middle of an `IoSliceMut`, it is modified - /// to start at that cursor. - /// - /// For example, if we have a slice of two 8-byte `IoSliceMut`s, and we advance by 10 bytes, - /// the result will only include the second `IoSliceMut`, advanced by 2 bytes. - /// - /// # Panics - /// - /// Panics when trying to advance beyond the end of the slices. - /// - /// # Examples - /// - /// ``` - /// use std::io::IoSliceMut; - /// use std::ops::Deref; - /// - /// let mut buf1 = [1; 8]; - /// let mut buf2 = [2; 16]; - /// let mut buf3 = [3; 8]; - /// let mut bufs = &mut [ - /// IoSliceMut::new(&mut buf1), - /// IoSliceMut::new(&mut buf2), - /// IoSliceMut::new(&mut buf3), - /// ][..]; - /// - /// // Mark 10 bytes as read. - /// IoSliceMut::advance_slices(&mut bufs, 10); - /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); - /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - /// ``` - #[stable(feature = "io_slice_advance", since = "1.81.0")] - #[inline] - pub fn advance_slices(bufs: &mut &mut [IoSliceMut<'a>], n: usize) { - // Number of buffers to remove. - let mut remove = 0; - // Remaining length before reaching n. - let mut left = n; - for buf in bufs.iter() { - if let Some(remainder) = left.checked_sub(buf.len()) { - left = remainder; - remove += 1; - } else { - break; - } - } - - *bufs = &mut take(bufs)[remove..]; - if bufs.is_empty() { - assert!(left == 0, "advancing io slices beyond their length"); - } else { - bufs[0].advance(left); - } - } - - /// Get the underlying bytes as a mutable slice with the original lifetime. - /// - /// # Examples - /// - /// ``` - /// #![feature(io_slice_as_bytes)] - /// use std::io::IoSliceMut; - /// - /// let mut data = *b"abcdef"; - /// let io_slice = IoSliceMut::new(&mut data); - /// io_slice.into_slice()[0] = b'A'; - /// - /// assert_eq!(&data, b"Abcdef"); - /// ``` - #[unstable(feature = "io_slice_as_bytes", issue = "132818")] - pub const fn into_slice(self) -> &'a mut [u8] { - self.0.into_slice() - } -} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> Deref for IoSliceMut<'a> { - type Target = [u8]; - - #[inline] - fn deref(&self) -> &[u8] { - self.0.as_slice() - } -} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> DerefMut for IoSliceMut<'a> { - #[inline] - fn deref_mut(&mut self) -> &mut [u8] { - self.0.as_mut_slice() - } -} - -/// A buffer type used with `Write::write_vectored`. -/// -/// It is semantically a wrapper around a `&[u8]`, but is guaranteed to be -/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on -/// Windows. -#[stable(feature = "iovec", since = "1.36.0")] -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a>(sys::io::IoSlice<'a>); - -#[stable(feature = "iovec_send_sync", since = "1.44.0")] -unsafe impl<'a> Send for IoSlice<'a> {} - -#[stable(feature = "iovec_send_sync", since = "1.44.0")] -unsafe impl<'a> Sync for IoSlice<'a> {} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> fmt::Debug for IoSlice<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.0.as_slice(), fmt) - } -} - -impl<'a> IoSlice<'a> { - /// Creates a new `IoSlice` wrapping a byte slice. - /// - /// # Panics - /// - /// Panics on Windows if the slice is larger than 4GB. - #[stable(feature = "iovec", since = "1.36.0")] - #[must_use] - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice(sys::io::IoSlice::new(buf)) - } - - /// Advance the internal cursor of the slice. - /// - /// Also see [`IoSlice::advance_slices`] to advance the cursors of multiple - /// buffers. - /// - /// # Panics - /// - /// Panics when trying to advance beyond the end of the slice. - /// - /// # Examples - /// - /// ``` - /// use std::io::IoSlice; - /// use std::ops::Deref; - /// - /// let data = [1; 8]; - /// let mut buf = IoSlice::new(&data); - /// - /// // Mark 3 bytes as read. - /// buf.advance(3); - /// assert_eq!(buf.deref(), [1; 5].as_ref()); - /// ``` - #[stable(feature = "io_slice_advance", since = "1.81.0")] - #[inline] - pub fn advance(&mut self, n: usize) { - self.0.advance(n) - } - - /// Advance a slice of slices. - /// - /// Shrinks the slice to remove any `IoSlice`s that are fully advanced over. - /// If the cursor ends up in the middle of an `IoSlice`, it is modified - /// to start at that cursor. - /// - /// For example, if we have a slice of two 8-byte `IoSlice`s, and we advance by 10 bytes, - /// the result will only include the second `IoSlice`, advanced by 2 bytes. - /// - /// # Panics - /// - /// Panics when trying to advance beyond the end of the slices. - /// - /// # Examples - /// - /// ``` - /// use std::io::IoSlice; - /// use std::ops::Deref; - /// - /// let buf1 = [1; 8]; - /// let buf2 = [2; 16]; - /// let buf3 = [3; 8]; - /// let mut bufs = &mut [ - /// IoSlice::new(&buf1), - /// IoSlice::new(&buf2), - /// IoSlice::new(&buf3), - /// ][..]; - /// - /// // Mark 10 bytes as written. - /// IoSlice::advance_slices(&mut bufs, 10); - /// assert_eq!(bufs[0].deref(), [2; 14].as_ref()); - /// assert_eq!(bufs[1].deref(), [3; 8].as_ref()); - #[stable(feature = "io_slice_advance", since = "1.81.0")] - #[inline] - pub fn advance_slices(bufs: &mut &mut [IoSlice<'a>], n: usize) { - // Number of buffers to remove. - let mut remove = 0; - // Remaining length before reaching n. This prevents overflow - // that could happen if the length of slices in `bufs` were instead - // accumulated. Those slice may be aliased and, if they are large - // enough, their added length may overflow a `usize`. - let mut left = n; - for buf in bufs.iter() { - if let Some(remainder) = left.checked_sub(buf.len()) { - left = remainder; - remove += 1; - } else { - break; - } - } - - *bufs = &mut take(bufs)[remove..]; - if bufs.is_empty() { - assert!(left == 0, "advancing io slices beyond their length"); - } else { - bufs[0].advance(left); - } - } - - /// Get the underlying bytes as a slice with the original lifetime. - /// - /// This doesn't borrow from `self`, so is less restrictive than calling - /// `.deref()`, which does. - /// - /// # Examples - /// - /// ``` - /// #![feature(io_slice_as_bytes)] - /// use std::io::IoSlice; - /// - /// let data = b"abcdef"; - /// - /// let mut io_slice = IoSlice::new(data); - /// let tail = &io_slice.as_slice()[3..]; - /// - /// // This works because `tail` doesn't borrow `io_slice` - /// io_slice = IoSlice::new(tail); - /// - /// assert_eq!(io_slice.as_slice(), b"def"); - /// ``` - #[unstable(feature = "io_slice_as_bytes", issue = "132818")] - pub const fn as_slice(self) -> &'a [u8] { - self.0.as_slice() - } -} - -#[stable(feature = "iovec", since = "1.36.0")] -impl<'a> Deref for IoSlice<'a> { - type Target = [u8]; - - #[inline] - fn deref(&self) -> &[u8] { - self.0.as_slice() - } -} - /// A trait for objects which are byte-oriented sinks. /// /// Implementors of the `Write` trait are sometimes called 'writers'. diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 7969352186f68..4253e0c956fb6 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -377,6 +377,7 @@ #![feature(io_error_internals)] #![feature(io_error_more)] #![feature(io_error_uncategorized)] +#![feature(io_slice_as_bytes)] #![feature(map_try_insert)] #![feature(slice_concat_trait)] #![feature(thin_box)] diff --git a/library/std/src/sys/io/mod.rs b/library/std/src/sys/io/mod.rs index 445bcdef0aa1f..0158137174087 100644 --- a/library/std/src/sys/io/mod.rs +++ b/library/std/src/sys/io/mod.rs @@ -2,27 +2,6 @@ mod error; -mod io_slice { - cfg_select! { - any(target_family = "unix", target_os = "hermit", target_os = "solid_asp3", target_os = "trusty", target_os = "wasi") => { - mod iovec; - pub use iovec::*; - } - target_os = "windows" => { - mod windows; - pub use windows::*; - } - target_os = "uefi" => { - mod uefi; - pub use uefi::*; - } - _ => { - mod unsupported; - pub use unsupported::*; - } - } -} - mod is_terminal { cfg_select! { any(target_family = "unix", target_os = "wasi") => { @@ -63,7 +42,6 @@ pub use error::errno_location; ))] pub use error::set_errno; pub use error::{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};