diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs new file mode 100644 index 0000000000000..f26a505702409 --- /dev/null +++ b/library/alloc/src/io/error.rs @@ -0,0 +1,204 @@ +#[cfg(test)] +mod tests; + +use crate::{alloc::Global, boxed::Box, string::String}; +use core::io::{ + const_io_error, + error_internals::{AllocVTable, Custom, ErrorBox, ErrorString}, + Error, ErrorKind, +}; +use core::{alloc::Allocator, error, ptr, result}; + +unsafe fn set_alloc_vtable() { + static ALLOC_VTABLE: AllocVTable = + AllocVTable { deallocate: |ptr, layout| unsafe { Global.deallocate(ptr, layout) } }; + unsafe { + ALLOC_VTABLE.install(); + } +} + +fn into_error_box(value: Box) -> ErrorBox { + unsafe { + set_alloc_vtable(); + ErrorBox::from_raw(Box::into_raw(value)) + } +} + +fn into_box(v: ErrorBox) -> Box { + unsafe { Box::from_raw(v.into_raw()) } +} + +impl From for ErrorString { + fn from(value: String) -> Self { + unsafe { + set_alloc_vtable(); + let (buf, length, capacity) = value.into_raw_parts(); + ErrorString::from_raw_parts( + ErrorBox::from_raw(ptr::slice_from_raw_parts_mut(buf.cast(), capacity)).into(), + length, + ) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From for Error { + /// Converts a [`crate::ffi::NulError`] into a [`Error`]. + fn from(_: crate::ffi::NulError) -> Error { + const_io_error!(ErrorKind::InvalidInput, "data provided contains a nul byte") + } +} + +impl Error { + /// Creates a new I/O error from a known kind of error as well as an + /// arbitrary error payload. + /// + /// This function is used to generically create I/O errors which do not + /// originate from the OS itself. The `error` argument is an arbitrary + /// payload which will be contained in this [`Error`]. + /// + /// Note that this function allocates memory on the heap. + /// If no extra payload is required, use the `From` conversion from + /// `ErrorKind`. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// // errors can be created from strings + /// let custom_error = Error::new(ErrorKind::Other, "oh no!"); + /// + /// // errors can also be created from other errors + /// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error); + /// + /// // creating an error without payload (and without memory allocation) + /// let eof_error = Error::from(ErrorKind::UnexpectedEof); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_allow_incoherent_impl] + pub fn new(kind: ErrorKind, error: E) -> Error + where + E: Into>, + { + Self::_new(kind, error.into()) + } + + /// Creates a new I/O error from an arbitrary error payload. + /// + /// This function is used to generically create I/O errors which do not + /// originate from the OS itself. It is a shortcut for [`Error::new`] + /// with [`ErrorKind::Other`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(io_error_other)] + /// + /// use std::io::Error; + /// + /// // errors can be created from strings + /// let custom_error = Error::other("oh no!"); + /// + /// // errors can also be created from other errors + /// let custom_error2 = Error::other(custom_error); + /// ``` + #[unstable(feature = "io_error_other", issue = "91946")] + #[rustc_allow_incoherent_impl] + pub fn other(error: E) -> Error + where + E: Into>, + { + Self::_new(ErrorKind::Other, error.into()) + } + + #[rustc_allow_incoherent_impl] + fn _new(kind: ErrorKind, error: Box) -> Error { + Error::_new_custom(into_error_box(Box::new(Custom { kind, error: into_error_box(error) }))) + } + + /// Consumes the `Error`, returning its inner error (if any). + /// + /// If this [`Error`] was constructed via [`new`] then this function will + /// return [`Some`], otherwise it will return [`None`]. + /// + /// [`new`]: Error::new + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: Error) { + /// if let Some(inner_err) = err.into_inner() { + /// println!("Inner error: {inner_err}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(Error::last_os_error()); + /// // Will print "Inner error: ...". + /// print_error(Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use = "`self` will be dropped if the result is not used"] + #[inline] + #[rustc_allow_incoherent_impl] + pub fn into_inner(self) -> Option> { + self.into_inner_impl().map(into_box) + } + + /// Attempt to downgrade the inner error to `E` if any. + /// + /// If this [`Error`] was constructed via [`new`] then this function will + /// attempt to perform downgrade on it, otherwise it will return [`Err`]. + /// + /// If downgrade succeeds, it will return [`Ok`], otherwise it will also + /// return [`Err`]. + /// + /// [`new`]: Error::new + /// + /// # Examples + /// + /// ``` + /// #![feature(io_error_downcast)] + /// + /// use std::fmt; + /// use std::io; + /// use std::error::Error; + /// + /// #[derive(Debug)] + /// enum E { + /// Io(io::Error), + /// SomeOtherVariant, + /// } + /// + /// impl fmt::Display for E { + /// // ... + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # todo!() + /// # } + /// } + /// impl Error for E {} + /// + /// impl From for E { + /// fn from(err: io::Error) -> E { + /// err.downcast::() + /// .map(|b| *b) + /// .unwrap_or_else(E::Io) + /// } + /// } + /// ``` + #[unstable(feature = "io_error_downcast", issue = "99262")] + #[rustc_allow_incoherent_impl] + pub fn downcast(self) -> result::Result, Self> + where + E: error::Error + Send + Sync + 'static, + { + self.downcast_impl::().map(|p| unsafe { Box::from_raw(p) }) + } +} diff --git a/library/alloc/src/io/error/tests.rs b/library/alloc/src/io/error/tests.rs new file mode 100644 index 0000000000000..ec395ebd9bc94 --- /dev/null +++ b/library/alloc/src/io/error/tests.rs @@ -0,0 +1,26 @@ +use core::error; +use core::fmt; +use core::io::{Error, ErrorKind}; + +#[test] +fn test_downcasting() { + #[derive(Debug)] + struct TestError; + + impl fmt::Display for TestError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("asdf") + } + } + + impl error::Error for TestError {} + + // we have to call all of these UFCS style right now since method + // resolution won't implicitly drop the Send+Sync bounds + let mut err = Error::new(ErrorKind::Other, TestError); + assert!(err.get_ref().unwrap().is::()); + assert_eq!("asdf", err.get_ref().unwrap().to_string()); + assert!(err.get_mut().unwrap().is::()); + let extracted = err.into_inner().unwrap(); + extracted.downcast::().unwrap(); +} diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs new file mode 100644 index 0000000000000..d924e8a5d4613 --- /dev/null +++ b/library/alloc/src/io/mod.rs @@ -0,0 +1,8 @@ +//! Traits, helpers, and type definitions for core I/O functionality. + +#![unstable(feature = "alloc_io", issue = "none")] + +pub use core::io::*; + +#[cfg(target_has_atomic_load_store = "ptr")] +mod error; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index d47f9de941cc8..3f30b879ca178 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -121,6 +121,8 @@ #![feature(const_size_of_val)] #![feature(const_waker)] #![feature(core_intrinsics)] +#![feature(core_io)] +#![feature(core_io_error_internals)] #![feature(core_panic)] #![feature(deprecated_suggestion)] #![feature(dispatch_from_dyn)] @@ -177,6 +179,7 @@ #![feature(associated_type_bounds)] #![feature(c_unwind)] #![feature(cfg_sanitize)] +#![feature(cfg_target_has_atomic)] #![feature(const_mut_refs)] #![feature(const_precise_live_drops)] #![feature(const_ptr_write)] @@ -246,6 +249,7 @@ pub mod collections; #[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))] pub mod ffi; pub mod fmt; +pub mod io; #[cfg(not(no_rc))] pub mod rc; pub mod slice; diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs new file mode 100644 index 0000000000000..675965fa3055f --- /dev/null +++ b/library/core/src/io/error.rs @@ -0,0 +1,1041 @@ +//! implementation detail of [`Error`] + +#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] +mod repr_bitpacked; +#[cfg(all(target_pointer_width = "64", bootstrap))] +#[unstable(feature = "core_io_error_internals", issue = "none")] +pub use repr_bitpacked::kind_from_prim; +#[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; + +use crate::alloc::Layout; +use crate::error; +use crate::fmt; +use crate::mem; +use crate::mem::ManuallyDrop; +use crate::ops::Deref; +use crate::ops::DerefMut; +use crate::ptr::NonNull; +use crate::ptr::{self, Unique}; +use crate::result; +use crate::str; +use crate::sync::atomic::{AtomicPtr, Ordering}; + +/// implementation detail of [`Error`] +#[unstable(feature = "core_io_error_internals", issue = "none")] +pub struct ErrorString { + bytes: ErrorBox<[mem::MaybeUninit]>, + length: usize, +} + +impl ErrorString { + /// implementation detail of [`Error`] + /// + /// Safety: `(*bytes)[..length]` must be initialized and valid UTF-8 + pub unsafe fn from_raw_parts(bytes: ErrorBox<[mem::MaybeUninit]>, length: usize) -> Self { + Self { bytes, length } + } +} + +impl Deref for ErrorString { + type Target = str; + fn deref(&self) -> &Self::Target { + // SAFETY: guaranteed safe by ErrorString's safety invariant + unsafe { + str::from_utf8_unchecked(mem::MaybeUninit::slice_assume_init_ref( + &(*self.bytes)[..self.length], + )) + } + } +} + +impl fmt::Debug for ErrorString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl fmt::Display for ErrorString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +/// implementation detail of [`Error`] +/// +/// Safety: `self.0` must point to allocated memory and +/// [`AllocVTable::install`] must have been run +#[unstable(feature = "core_io_error_internals", issue = "none")] +pub struct ErrorBox(Unique); + +impl fmt::Debug for ErrorBox { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + T::fmt(self, f) + } +} + +impl fmt::Display for ErrorBox { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + T::fmt(self, f) + } +} + +impl Deref for ErrorBox { + type Target = T; + + fn deref(&self) -> &Self::Target { + // SAFETY: guaranteed safe by ErrorBox's safety invariant + unsafe { self.0.as_ref() } + } +} + +impl DerefMut for ErrorBox { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: guaranteed safe by ErrorBox's safety invariant + unsafe { self.0.as_mut() } + } +} + +impl ErrorBox { + /// implementation detail of [`Error`] + /// + /// Safety: `v` must point to allocated memory and + /// [`AllocVTable::install`] must have been run + pub unsafe fn from_raw(v: *mut T) -> Self { + // SAFETY: guaranteed by caller + unsafe { Self(Unique::new_unchecked(v)) } + } + /// implementation detail of [`Error`] + pub fn into_raw(self) -> *mut T { + ManuallyDrop::new(self).0.as_ptr() + } + /// implementation detail of [`Error`] + pub fn into_inner(self) -> T + where + T: Sized, + { + let mut b = ManuallyDrop::new(self); + // SAFETY: guaranteed safe by ErrorBox's safety invariant + unsafe { + let _mem = ErrorBoxMem::new(&mut b); + ptr::read(b.0.as_ptr()) + } + } +} + +struct ErrorBoxMem { + layout: Layout, + ptr: NonNull, +} + +impl ErrorBoxMem { + /// Safety: `b` must not have had its internals dropped. `b` must not be dropped. + pub unsafe fn new(b: &mut ErrorBox) -> Self { + Self { + layout: Layout::for_value::(&**b), + // SAFETY: guaranteed safe by caller + ptr: unsafe { NonNull::new_unchecked(b.0.as_ptr().cast()) }, + } + } +} + +impl Drop for ErrorBoxMem { + fn drop(&mut self) { + // SAFETY: guaranteed safe by `ErrorBoxMem::new` + unsafe { (AllocVTable::get().deallocate)(self.ptr, self.layout) } + } +} + +impl Drop for ErrorBox { + fn drop(&mut self) { + // SAFETY: guaranteed safe by ErrorBox's safety invariant + unsafe { + let _mem = ErrorBoxMem::new(self); + ptr::drop_in_place(self.0.as_ptr()); + } + } +} + +/// implementation detail of [`Error`] +#[derive(Debug)] +#[unstable(feature = "core_io_error_internals", issue = "none")] +pub struct AllocVTable { + /// implementation detail of [`Error`] + /// + /// Safety: same as [`alloc::deallocate`] + pub deallocate: unsafe fn(ptr: NonNull, layout: Layout), +} + +static ALLOC_VTABLE: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + +impl AllocVTable { + /// implementation detail of [`Error`] + /// + /// Safety: `self` must be in a `static` variable that has never been + /// written. All members must be valid. + pub unsafe fn install(&'static self) { + // see `get` for why `Relaxed` is sufficient. + if ALLOC_VTABLE.load(Ordering::Relaxed).is_null() { + ALLOC_VTABLE.store(<*const _>::from(self).cast_mut(), Ordering::Relaxed); + } + } + /// implementation detail of [`Error`] + /// + /// Safety: `install` must have been called + pub unsafe fn get() -> &'static Self { + // SAFETY: `install` has been called and it set `ALLOC_VTABLE` to point + // to a `static` variable that has never been written, so no writes can + // possibly need to be communicated through other memory so `Relaxed` + // here and in `install` are sufficient. + unsafe { &*ALLOC_VTABLE.load(Ordering::Relaxed) } + } +} + +/// implementation detail of [`Error`] +#[derive(Debug)] +#[unstable(feature = "core_io_error_internals", issue = "none")] +pub struct StdVTable { + /// implementation detail of [`Error`] + pub decode_error_kind: fn(RawOsError) -> ErrorKind, + /// implementation detail of [`Error`] + pub error_string: fn(RawOsError) -> ErrorString, +} + +static STD_VTABLE: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + +impl StdVTable { + /// implementation detail of [`Error`] + /// + /// Safety: `self` must be in a `static` variable that has never been + /// written. All members must be valid. + pub unsafe fn install(&'static self) { + // see `get` for why `Relaxed` is sufficient. + if STD_VTABLE.load(Ordering::Relaxed).is_null() { + STD_VTABLE.store(<*const _>::from(self).cast_mut(), Ordering::Relaxed); + } + } + /// implementation detail of [`Error`] + /// + /// Safety: `install` must have been called + pub unsafe fn get() -> &'static Self { + // SAFETY: `install` has been called and it set `STD_VTABLE` to point + // to a `static` variable that has never been written, so no writes can + // possibly need to be communicated through other memory so `Relaxed` + // here and in `install` are sufficient. + unsafe { &*STD_VTABLE.load(Ordering::Relaxed) } + } +} + +/// A specialized [`Result`] type for I/O operations. +/// +/// This type is broadly used across [`std::io`] for any operation which may +/// produce an error. +/// +/// This typedef is generally used to avoid writing out [`io::Error`] directly and +/// is otherwise a direct mapping to [`Result`]. +/// +/// 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 +/// of [`std::result::Result`][`Result`]. +/// +/// [`std::io`]: crate::io +/// [`io::Error`]: Error +/// [`Result`]: crate::result::Result +/// [prelude]: crate::prelude +/// +/// # Examples +/// +/// A convenience function that bubbles an `io::Result` to its caller: +/// +/// ``` +/// use std::io; +/// +/// fn get_string() -> io::Result { +/// let mut buffer = String::new(); +/// +/// io::stdin().read_line(&mut buffer)?; +/// +/// Ok(buffer) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub type Result = result::Result; + +/// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and +/// associated traits. +/// +/// 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 +// Safety: [`StdVTable::install`] must have been run before any `Error` +// instances containing OS errors are created. +// [`AllocVTable::install`] must have been run before any instances containing +// `AllocBox` are created. +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_has_incoherent_inherent_impls] +pub struct Error { + repr: Repr, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.repr, f) + } +} + +// Only derive debug in tests, to make sure it +// doesn't accidentally get printed. +#[cfg_attr(test, derive(Debug))] +enum ErrorData { + Os(RawOsError), + Simple(ErrorKind), + SimpleMessage(&'static SimpleMessage), + Custom(C), +} + +#[cfg(not(target_os = "uefi"))] +type RawOsError_ = i32; + +#[cfg(target_os = "uefi")] +type RawOsError_ = usize; + +/// The type of raw OS error codes returned by [`Error::raw_os_error`]. +/// +/// 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 +/// [`usize`]. Use `as`or [`into`] conversions where applicable to ensure maximum +/// portability. +/// +/// [`into`]: Into::into +#[unstable(feature = "raw_os_error_ty", issue = "107792")] +pub type RawOsError = 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 +// requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the +// alignment required by the struct, only increase it). +// +// If we add more variants to ErrorData, this can be increased to 8, but it +// should probably be behind `#[cfg_attr(target_pointer_width = "64", ...)]` or +// whatever cfg we're using to enable the `repr_bitpacked` code, since only the +// that version needs the alignment, and 8 is higher than the alignment we'll +// have on 32 bit platforms. +// +// (For the sake of being explicit: the alignment requirement here only matters +// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't +// matter at all) +#[unstable(feature = "core_io_error_internals", issue = "none")] +#[repr(align(4))] +#[derive(Debug)] +/// implementation detail of [`Error`] +pub struct SimpleMessage { + kind: ErrorKind, + message: &'static str, +} + +impl SimpleMessage { + /// implementation detail of [`Error`] + #[unstable(feature = "core_io_error_internals", issue = "none")] + pub const fn new(kind: ErrorKind, message: &'static str) -> Self { + Self { kind, message } + } +} + +/// Create and return an `io::Error` for a given `ErrorKind` and constant +/// message. This doesn't allocate. +#[unstable(feature = "core_io_error_internals", issue = "none")] +pub macro const_io_error($kind:expr, $message:expr $(,)?) { + $crate::io::error::Error::from_static_message({ + const MESSAGE_DATA: $crate::io::error::SimpleMessage = + $crate::io::error::SimpleMessage::new($kind, $message); + &MESSAGE_DATA + }) +} + +// As with `SimpleMessage`: `#[repr(align(4))]` here is just because +// repr_bitpacked's encoding requires it. In practice it almost certainly be +// already be this high or higher. +#[derive(Debug)] +#[repr(align(4))] +#[unstable(feature = "core_io_error_internals", issue = "none")] +/// implementation detail of [`Error`] +pub struct Custom { + /// implementation detail of [`Error`] + pub kind: ErrorKind, + /// implementation detail of [`Error`] + pub error: ErrorBox, +} + +/// A list specifying general categories of I/O error. +/// +/// This list is intended to grow over time and it is not recommended to +/// exhaustively match against it. +/// +/// It is used with the [`io::Error`] type. +/// +/// [`io::Error`]: Error +/// +/// # Handling errors and matching on `ErrorKind` +/// +/// In application code, use `match` for the `ErrorKind` values you are +/// expecting; use `_` to match "all other errors". +/// +/// In comprehensive and thorough tests that want to verify that a test doesn't +/// return any known incorrect error kind, you may want to cut-and-paste the +/// current full list of errors from here into your test code, and then match +/// `_` as the correct case. This seems counterintuitive, but it will make your +/// tests more robust. In particular, if you want to verify that your code does +/// produce an unrecognized error kind, the robust solution is to check for all +/// the recognized error kinds and fail in those cases. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated)] +#[non_exhaustive] +pub enum ErrorKind { + /// An entity was not found, often a file. + #[stable(feature = "rust1", since = "1.0.0")] + NotFound, + /// The operation lacked the necessary privileges to complete. + #[stable(feature = "rust1", since = "1.0.0")] + PermissionDenied, + /// The connection was refused by the remote server. + #[stable(feature = "rust1", since = "1.0.0")] + ConnectionRefused, + /// The connection was reset by the remote server. + #[stable(feature = "rust1", since = "1.0.0")] + ConnectionReset, + /// The remote host is not reachable. + #[unstable(feature = "io_error_more", issue = "86442")] + HostUnreachable, + /// The network containing the remote host is not reachable. + #[unstable(feature = "io_error_more", issue = "86442")] + NetworkUnreachable, + /// The connection was aborted (terminated) by the remote server. + #[stable(feature = "rust1", since = "1.0.0")] + ConnectionAborted, + /// The network operation failed because it was not connected yet. + #[stable(feature = "rust1", since = "1.0.0")] + NotConnected, + /// A socket address could not be bound because the address is already in + /// use elsewhere. + #[stable(feature = "rust1", since = "1.0.0")] + AddrInUse, + /// A nonexistent interface was requested or the requested address was not + /// local. + #[stable(feature = "rust1", since = "1.0.0")] + AddrNotAvailable, + /// The system's networking is down. + #[unstable(feature = "io_error_more", issue = "86442")] + NetworkDown, + /// The operation failed because a pipe was closed. + #[stable(feature = "rust1", since = "1.0.0")] + BrokenPipe, + /// An entity already exists, often a file. + #[stable(feature = "rust1", since = "1.0.0")] + AlreadyExists, + /// The operation needs to block to complete, but the blocking operation was + /// requested to not occur. + #[stable(feature = "rust1", since = "1.0.0")] + WouldBlock, + /// A filesystem object is, unexpectedly, not a directory. + /// + /// For example, a filesystem path was specified where one of the intermediate directory + /// components was, in fact, a plain file. + #[unstable(feature = "io_error_more", issue = "86442")] + NotADirectory, + /// The filesystem object is, unexpectedly, a directory. + /// + /// A directory was specified when a non-directory was expected. + #[unstable(feature = "io_error_more", issue = "86442")] + IsADirectory, + /// A non-empty directory was specified where an empty directory was expected. + #[unstable(feature = "io_error_more", issue = "86442")] + DirectoryNotEmpty, + /// The filesystem or storage medium is read-only, but a write operation was attempted. + #[unstable(feature = "io_error_more", issue = "86442")] + ReadOnlyFilesystem, + /// Loop in the filesystem or IO subsystem; often, too many levels of symbolic links. + /// + /// There was a loop (or excessively long chain) resolving a filesystem object + /// or file IO object. + /// + /// On Unix this is usually the result of a symbolic link loop; or, of exceeding the + /// system-specific limit on the depth of symlink traversal. + #[unstable(feature = "io_error_more", issue = "86442")] + FilesystemLoop, + /// Stale network file handle. + /// + /// With some network filesystems, notably NFS, an open file (or directory) can be invalidated + /// by problems with the network or server. + #[unstable(feature = "io_error_more", issue = "86442")] + StaleNetworkFileHandle, + /// A parameter was incorrect. + #[stable(feature = "rust1", since = "1.0.0")] + InvalidInput, + /// Data not valid for the operation were encountered. + /// + /// Unlike [`InvalidInput`], this typically means that the operation + /// parameters were valid, however the error was caused by malformed + /// input data. + /// + /// For example, a function that reads a file into a string will error with + /// `InvalidData` if the file's contents are not valid UTF-8. + /// + /// [`InvalidInput`]: ErrorKind::InvalidInput + #[stable(feature = "io_invalid_data", since = "1.2.0")] + InvalidData, + /// The I/O operation's timeout expired, causing it to be canceled. + #[stable(feature = "rust1", since = "1.0.0")] + TimedOut, + /// An error returned when an operation could not be completed because a + /// 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, + /// The underlying storage (typically, a filesystem) is full. + /// + /// This does not include out of quota errors. + #[unstable(feature = "io_error_more", issue = "86442")] + StorageFull, + /// Seek on unseekable file. + /// + /// Seeking was attempted on an open file handle which is not suitable for seeking - for + /// example, on Unix, a named pipe opened with `File::open`. + #[unstable(feature = "io_error_more", issue = "86442")] + NotSeekable, + /// Filesystem quota was exceeded. + #[unstable(feature = "io_error_more", issue = "86442")] + FilesystemQuotaExceeded, + /// File larger than allowed or supported. + /// + /// This might arise from a hard limit of the underlying filesystem or file access API, or from + /// an administratively imposed resource limitation. Simple disk full, and out of quota, have + /// their own errors. + #[unstable(feature = "io_error_more", issue = "86442")] + FileTooLarge, + /// Resource is busy. + #[unstable(feature = "io_error_more", issue = "86442")] + ResourceBusy, + /// Executable file is busy. + /// + /// An attempt was made to write to a file which is also in use as a running program. (Not all + /// operating systems detect this situation.) + #[unstable(feature = "io_error_more", issue = "86442")] + ExecutableFileBusy, + /// Deadlock (avoided). + /// + /// A file locking operation would result in deadlock. This situation is typically detected, if + /// at all, on a best-effort basis. + #[unstable(feature = "io_error_more", issue = "86442")] + Deadlock, + /// Cross-device or cross-filesystem (hard) link or rename. + #[unstable(feature = "io_error_more", issue = "86442")] + CrossesDevices, + /// Too many (hard) links to the same filesystem object. + /// + /// The filesystem does not support making so many hardlinks to the same file. + #[unstable(feature = "io_error_more", issue = "86442")] + TooManyLinks, + /// A filename was invalid. + /// + /// This error can also cause if it exceeded the filename length limit. + #[unstable(feature = "io_error_more", issue = "86442")] + InvalidFilename, + /// Program argument list too long. + /// + /// When trying to run an external program, a system or process limit on the size of the + /// arguments would have been exceeded. + #[unstable(feature = "io_error_more", issue = "86442")] + ArgumentListTooLong, + /// This operation was interrupted. + /// + /// Interrupted operations can typically be retried. + #[stable(feature = "rust1", since = "1.0.0")] + Interrupted, + + /// This operation is unsupported on this platform. + /// + /// This means that the operation can never succeed. + #[stable(feature = "unsupported_error", since = "1.53.0")] + Unsupported, + + // ErrorKinds which are primarily categorisations for OS error + // codes should be added above. + // + /// An error returned when an operation could not be completed because an + /// "end of file" was reached prematurely. + /// + /// This typically means that an operation could only succeed if it read a + /// particular number of bytes but only a smaller number of bytes could be + /// read. + #[stable(feature = "read_exact", since = "1.6.0")] + UnexpectedEof, + + /// An operation could not be completed, because it failed + /// to allocate enough memory. + #[stable(feature = "out_of_memory_error", since = "1.54.0")] + OutOfMemory, + + // "Unusual" error kinds which do not correspond simply to (sets + // of) OS error codes, should be added just above this comment. + // `Other` and `Uncategorized` should remain at the end: + // + /// A custom error that does not fall under any other I/O error kind. + /// + /// This can be used to construct your own [`Error`]s that do not match any + /// [`ErrorKind`]. + /// + /// This [`ErrorKind`] is not used by the standard library. + /// + /// Errors from the standard library that do not fall under any of the I/O + /// error kinds cannot be `match`ed on, and will only match a wildcard (`_`) pattern. + /// New [`ErrorKind`]s might be added in the future for some of those. + #[stable(feature = "rust1", since = "1.0.0")] + Other, + + /// Any I/O error from the standard library that's not part of this list. + /// + /// Errors that are `Uncategorized` now may move to a different or a new + /// [`ErrorKind`] variant in the future. It is not recommended to match + /// an error against `Uncategorized`; use a wildcard match (`_`) instead. + #[unstable(feature = "io_error_uncategorized", issue = "none")] + #[doc(hidden)] + Uncategorized, +} + +impl ErrorKind { + /// implementation detail of [`Error`] + #[unstable(feature = "core_io_error_internals", issue = "none")] + pub fn as_str(&self) -> &'static str { + use ErrorKind::*; + // tidy-alphabetical-start + match *self { + AddrInUse => "address in use", + AddrNotAvailable => "address not available", + AlreadyExists => "entity already exists", + ArgumentListTooLong => "argument list too long", + BrokenPipe => "broken pipe", + ConnectionAborted => "connection aborted", + ConnectionRefused => "connection refused", + ConnectionReset => "connection reset", + CrossesDevices => "cross-device link or rename", + Deadlock => "deadlock", + DirectoryNotEmpty => "directory not empty", + ExecutableFileBusy => "executable file busy", + FileTooLarge => "file too large", + FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)", + FilesystemQuotaExceeded => "filesystem quota exceeded", + HostUnreachable => "host unreachable", + Interrupted => "operation interrupted", + InvalidData => "invalid data", + InvalidFilename => "invalid filename", + InvalidInput => "invalid input parameter", + IsADirectory => "is a directory", + NetworkDown => "network down", + NetworkUnreachable => "network unreachable", + NotADirectory => "not a directory", + NotConnected => "not connected", + NotFound => "entity not found", + NotSeekable => "seek on unseekable file", + Other => "other error", + OutOfMemory => "out of memory", + PermissionDenied => "permission denied", + ReadOnlyFilesystem => "read-only filesystem or storage medium", + ResourceBusy => "resource busy", + StaleNetworkFileHandle => "stale network file handle", + StorageFull => "no storage space", + TimedOut => "timed out", + TooManyLinks => "too many links", + Uncategorized => "uncategorized error", + UnexpectedEof => "unexpected end of file", + Unsupported => "unsupported", + WouldBlock => "operation would block", + WriteZero => "write zero", + } + // tidy-alphabetical-end + } +} + +#[stable(feature = "io_errorkind_display", since = "1.60.0")] +impl fmt::Display for ErrorKind { + /// Shows a human-readable description of the `ErrorKind`. + /// + /// This is similar to `impl Display for Error`, but doesn't require first converting to Error. + /// + /// # Examples + /// ``` + /// use std::io::ErrorKind; + /// assert_eq!("entity not found", ErrorKind::NotFound.to_string()); + /// ``` + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(self.as_str()) + } +} + +/// Intended for use for errors not exposed to the user, where allocating onto +/// the heap (for normal construction via Error::new) is too costly. +#[stable(feature = "io_error_from_errorkind", since = "1.14.0")] +impl From for Error { + /// Converts an [`ErrorKind`] into an [`Error`]. + /// + /// This conversion creates a new error with a simple representation of error kind. + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// let not_found = ErrorKind::NotFound; + /// let error = Error::from(not_found); + /// assert_eq!("entity not found", format!("{error}")); + /// ``` + #[inline] + fn from(kind: ErrorKind) -> Error { + Error { repr: Repr::new_simple(kind) } + } +} + +impl Error { + /// implementation detail of [`Error::new`] + #[unstable(feature = "core_io_error_internals", issue = "none")] + pub fn _new_custom(v: ErrorBox) -> Error { + Error { repr: Repr::new_custom(v) } + } + + /// Creates a new I/O error from a known kind of error as well as a constant + /// message. + /// + /// This function does not allocate. + /// + /// You should not use this directly, and instead use the `const_io_error!` + /// macro: `io::const_io_error!(ErrorKind::Something, "some_message")`. + /// + /// This function should maybe change to `from_static_message(kind: ErrorKind)` in the future, when const generics allow that. + #[inline] + #[unstable(feature = "core_io_error_internals", issue = "none")] + pub const fn from_static_message(msg: &'static SimpleMessage) -> Error { + Self { repr: Repr::new_simple_message(msg) } + } + + /// implementation detail of [`Error::from_raw_os_error`] + #[unstable(feature = "core_io_error_internals", issue = "none")] + #[must_use] + #[inline] + pub unsafe fn from_raw_os_error_impl(code: RawOsError) -> Error { + 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")] + #[must_use] + #[inline] + pub fn raw_os_error(&self) -> Option { + match self.repr.data() { + ErrorData::Os(i) => Some(i), + ErrorData::Custom(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + } + } + + /// Returns a reference to the inner error wrapped by this error (if any). + /// + /// If this [`Error`] was constructed via [`new`] then this function will + /// return [`Some`], otherwise it will return [`None`]. + /// + /// [`new`]: Error::new + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: &Error) { + /// if let Some(inner_err) = err.get_ref() { + /// println!("Inner error: {inner_err:?}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(&Error::last_os_error()); + /// // Will print "Inner error: ...". + /// print_error(&Error::new(ErrorKind::Other, "oh no!")); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use] + #[inline] + pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => Some(&*c.error), + } + } + + /// Returns a mutable reference to the inner error wrapped by this error + /// (if any). + /// + /// If this [`Error`] was constructed via [`new`] then this function will + /// return [`Some`], otherwise it will return [`None`]. + /// + /// [`new`]: Error::new + /// + /// # Examples + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// use std::{error, fmt}; + /// use std::fmt::Display; + /// + /// #[derive(Debug)] + /// struct MyError { + /// v: String, + /// } + /// + /// impl MyError { + /// fn new() -> MyError { + /// MyError { + /// v: "oh no!".to_string() + /// } + /// } + /// + /// fn change_message(&mut self, new_message: &str) { + /// self.v = new_message.to_string(); + /// } + /// } + /// + /// impl error::Error for MyError {} + /// + /// impl Display for MyError { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "MyError: {}", &self.v) + /// } + /// } + /// + /// fn change_error(mut err: Error) -> Error { + /// if let Some(inner_err) = err.get_mut() { + /// inner_err.downcast_mut::().unwrap().change_message("I've been changed!"); + /// } + /// err + /// } + /// + /// fn print_error(err: &Error) { + /// if let Some(inner_err) = err.get_ref() { + /// println!("Inner error: {inner_err}"); + /// } else { + /// println!("No inner error"); + /// } + /// } + /// + /// fn main() { + /// // Will print "No inner error". + /// print_error(&change_error(Error::last_os_error())); + /// // Will print "Inner error: ...". + /// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new()))); + /// } + /// ``` + #[stable(feature = "io_error_inner", since = "1.3.0")] + #[must_use] + #[inline] + pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { + match self.repr.data_mut() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => Some(&mut *c.error), + } + } + + /// implementation detail of [`Error::into_inner`] + #[unstable(feature = "core_io_error_internals", issue = "none")] + #[inline] + pub fn into_inner_impl(self) -> Option> { + match self.repr.into_data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => Some(c.into_inner().error), + } + } + + /// implementation detail of [`Error::downcast`] + #[unstable(feature = "core_io_error_internals", issue = "none")] + pub fn downcast_impl(self) -> result::Result<*mut E, Self> + where + E: error::Error + Send + Sync + 'static, + { + match self.repr.into_data() { + ErrorData::Custom(b) if b.error.is::() => Ok(b.into_inner().error.into_raw().cast()), + repr_data => Err(Self { repr: Repr::new(repr_data) }), + } + } + + /// Returns the corresponding [`ErrorKind`] for this 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 + /// + /// ``` + /// use std::io::{Error, ErrorKind}; + /// + /// fn print_error(err: Error) { + /// println!("{:?}", err.kind()); + /// } + /// + /// fn main() { + /// // As no error has (visibly) occurred, this may print anything! + /// // It likely prints a placeholder for unidentified (non-)errors. + /// print_error(Error::last_os_error()); + /// // Will print "AddrInUse". + /// print_error(Error::new(ErrorKind::AddrInUse, "oh no!")); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[must_use] + #[inline] + pub fn kind(&self) -> ErrorKind { + match self.repr.data() { + // SAFETY: `Error`'s safety invariant guarantees + // `StdVTable::install` has been run + ErrorData::Os(code) => unsafe { (StdVTable::get().decode_error_kind)(code) }, + ErrorData::Custom(c) => c.kind, + ErrorData::Simple(kind) => kind, + ErrorData::SimpleMessage(m) => m.kind, + } + } +} + +impl fmt::Debug for Repr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.data() { + ErrorData::Os(code) => { + // SAFETY: `Error`'s safety invariant guarantees + // `StdVTable::install` has been run + let std_vtable = unsafe { StdVTable::get() }; + fmt.debug_struct("Os") + .field("code", &code) + .field("kind", &(std_vtable.decode_error_kind)(code)) + .field("message", &(std_vtable.error_string)(code)) + .finish() + } + ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt), + ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(), + ErrorData::SimpleMessage(msg) => fmt + .debug_struct("Error") + .field("kind", &msg.kind) + .field("message", &msg.message) + .finish(), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.repr.data() { + ErrorData::Os(code) => { + // SAFETY: `Error`'s safety invariant guarantees + // `StdVTable::install` has been run + let detail = unsafe { (StdVTable::get().error_string)(code) }; + write!(fmt, "{detail} (os error {code})") + } + ErrorData::Custom(ref c) => c.error.fmt(fmt), + ErrorData::Simple(kind) => write!(fmt, "{}", kind.as_str()), + ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for Error { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + match self.repr.data() { + ErrorData::Os(..) | ErrorData::Simple(..) => self.kind().as_str(), + ErrorData::SimpleMessage(msg) => msg.message, + ErrorData::Custom(c) => c.error.description(), + } + } + + #[allow(deprecated)] + fn cause(&self) -> Option<&dyn error::Error> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => c.error.cause(), + } + } + + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self.repr.data() { + ErrorData::Os(..) => None, + ErrorData::Simple(..) => None, + ErrorData::SimpleMessage(..) => None, + ErrorData::Custom(c) => c.error.source(), + } + } +} + +fn _assert_error_is_sync_send() { + fn _is_sync_send() {} + _is_sync_send::(); +} diff --git a/library/core/src/io/error/repr_bitpacked.rs b/library/core/src/io/error/repr_bitpacked.rs new file mode 100644 index 0000000000000..09e37b3a31e45 --- /dev/null +++ b/library/core/src/io/error/repr_bitpacked.rs @@ -0,0 +1,419 @@ +//! This is a densely packed error representation which is used on targets with +//! 64-bit pointers. +//! +//! (Note that `bitpacked` vs `unpacked` here has no relationship to +//! `#[repr(packed)]`, it just refers to attempting to use any available bits in +//! a more clever manner than `rustc`'s default layout algorithm would). +//! +//! Conceptually, it stores the same data as the "unpacked" equivalent we use on +//! other targets. Specifically, you can imagine it as an optimized version of +//! the following enum (which is roughly equivalent to what's stored by +//! `repr_unpacked::Repr`, e.g. `super::ErrorData>`): +//! +//! ```ignore (exposition-only) +//! enum ErrorData { +//! Os(i32), +//! Simple(ErrorKind), +//! SimpleMessage(&'static SimpleMessage), +//! Custom(Box), +//! } +//! ``` +//! +//! However, it packs this data into a 64bit non-zero value. +//! +//! This optimization not only allows `io::Error` to occupy a single pointer, +//! but improves `io::Result` as well, especially for situations like +//! `io::Result<()>` (which is now 64 bits) or `io::Result` (which is now +//! 128 bits), which are quite common. +//! +//! # Layout +//! Tagged values are 64 bits, with the 2 least significant bits used for the +//! tag. This means there are there are 4 "variants": +//! +//! - **Tag 0b00**: The first variant is equivalent to +//! `ErrorData::SimpleMessage`, and holds a `&'static SimpleMessage` directly. +//! +//! `SimpleMessage` has an alignment >= 4 (which is requested with +//! `#[repr(align)]` and checked statically at the bottom of this file), which +//! means every `&'static SimpleMessage` should have the both tag bits as 0, +//! meaning its tagged and untagged representation are equivalent. +//! +//! This means we can skip tagging it, which is necessary as this variant can +//! be constructed from a `const fn`, which probably cannot tag pointers (or +//! at least it would be difficult). +//! +//! - **Tag 0b01**: The other pointer variant holds the data for +//! `ErrorData::Custom` and the remaining 62 bits are used to store a +//! `Box`. `Custom` also has alignment >= 4, so the bottom two bits +//! are free to use for the tag. +//! +//! The only important thing to note is that `ptr::wrapping_add` and +//! `ptr::wrapping_sub` are used to tag the pointer, rather than bitwise +//! operations. This should preserve the pointer's provenance, which would +//! otherwise be lost. +//! +//! - **Tag 0b10**: Holds the data for `ErrorData::Os(i32)`. We store the `i32` +//! in the pointer's most significant 32 bits, and don't use the bits `2..32` +//! for anything. Using the top 32 bits is just to let us easily recover the +//! `i32` code with the correct sign. +//! +//! - **Tag 0b11**: Holds the data for `ErrorData::Simple(ErrorKind)`. This +//! stores the `ErrorKind` in the top 32 bits as well, although it doesn't +//! occupy nearly that many. Most of the bits are unused here, but it's not +//! like we need them for anything else yet. +//! +//! # Use of `NonNull<()>` +//! +//! Everything is stored in a `NonNull<()>`, which is odd, but actually serves a +//! purpose. +//! +//! Conceptually you might think of this more like: +//! +//! ```ignore (exposition-only) +//! union Repr { +//! // holds integer (Simple/Os) variants, and +//! // provides access to the tag bits. +//! bits: NonZeroU64, +//! // Tag is 0, so this is stored untagged. +//! msg: &'static SimpleMessage, +//! // Tagged (offset) `Box` pointer. +//! tagged_custom: NonNull<()>, +//! } +//! ``` +//! +//! But there are a few problems with this: +//! +//! 1. Union access is equivalent to a transmute, so this representation would +//! require we transmute between integers and pointers in at least one +//! direction, which may be UB (and even if not, it is likely harder for a +//! compiler to reason about than explicit ptr->int operations). +//! +//! 2. Even if all fields of a union have a niche, the union itself doesn't, +//! although this may change in the future. This would make things like +//! `io::Result<()>` and `io::Result` larger, which defeats part of +//! the motivation of this bitpacking. +//! +//! Storing everything in a `NonZeroUsize` (or some other integer) would be a +//! bit more traditional for pointer tagging, but it would lose provenance +//! information, couldn't be constructed from a `const fn`, and would probably +//! run into other issues as well. +//! +//! The `NonNull<()>` seems like the only alternative, even if it's fairly odd +//! to use a pointer type to store something that may hold an integer, some of +//! the time. + +use super::{Custom, ErrorBox, ErrorData, ErrorKind, RawOsError, SimpleMessage}; +use core::marker::PhantomData; +use core::mem::{align_of, size_of}; +use core::ptr::{self, NonNull}; + +// The 2 least-significant bits are used as tag. +const TAG_MASK: usize = 0b11; +const TAG_SIMPLE_MESSAGE: usize = 0b00; +const TAG_CUSTOM: usize = 0b01; +const TAG_OS: usize = 0b10; +const TAG_SIMPLE: usize = 0b11; + +/// The internal representation. +/// +/// See the module docs for more, this is just a way to hack in a check that we +/// indeed are not unwind-safe. +/// +/// ```compile_fail,E0277 +/// fn is_unwind_safe() {} +/// is_unwind_safe::(); +/// ``` +#[repr(transparent)] +pub(super) struct Repr(NonNull<()>, PhantomData>>); + +// All the types `Repr` stores internally are Send + Sync, and so is it. +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: ErrorBox) -> Self { + let p = ErrorBox::into_raw(b).cast::(); + // Should only be possible if an allocator handed out a pointer with + // wrong alignment. + debug_assert_eq!(p.addr() & TAG_MASK, 0); + // Note: We know `TAG_CUSTOM <= size_of::()` (static_assert at + // end of file), and both the start and end of the expression must be + // valid without address space wraparound due to `Box`'s semantics. + // + // This means it would be correct to implement this using `ptr::add` + // (rather than `ptr::wrapping_add`), but it's unclear this would give + // any benefit, so we just use `wrapping_add` instead. + let tagged = p.wrapping_add(TAG_CUSTOM).cast::<()>(); + // SAFETY: `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`, + // because `p`'s alignment means it isn't allowed to have any of the + // `TAG_BITS` set (you can verify that addition and bitwise-or are the + // same when the operands have no bits in common using a truth table). + // + // Then, `TAG_CUSTOM | p` is not zero, as that would require + // `TAG_CUSTOM` and `p` both be zero, and neither is (as `p` came from a + // box, and `TAG_CUSTOM` just... isn't zero -- it's `0b01`). Therefore, + // `TAG_CUSTOM + p` isn't zero and so `tagged` can't be, and the + // `new_unchecked` is safe. + let res = Self(unsafe { NonNull::new_unchecked(tagged) }, PhantomData); + // quickly smoke-check we encoded the right thing (This generally will + // only run in std's tests, unless the user uses -Zbuild-std) + debug_assert!(matches!(res.data(), ErrorData::Custom(_)), "repr(custom) encoding failed"); + res + } + + #[inline] + pub(super) fn new_os(code: RawOsError) -> Self { + let utagged = ((code as usize) << 32) | TAG_OS; + // SAFETY: `TAG_OS` is not zero, so the result of the `|` is not 0. + let res = Self(unsafe { NonNull::new_unchecked(ptr::invalid_mut(utagged)) }, PhantomData); + // quickly smoke-check we encoded the right thing (This generally will + // only run in std's tests, unless the user uses -Zbuild-std) + debug_assert!( + matches!(res.data(), ErrorData::Os(c) if c == code), + "repr(os) encoding failed for {code}" + ); + res + } + + #[inline] + pub(super) fn new_simple(kind: ErrorKind) -> Self { + let utagged = ((kind as usize) << 32) | TAG_SIMPLE; + // SAFETY: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0. + let res = Self(unsafe { NonNull::new_unchecked(ptr::invalid_mut(utagged)) }, PhantomData); + // quickly smoke-check we encoded the right thing (This generally will + // only run in std's tests, unless the user uses -Zbuild-std) + debug_assert!( + matches!(res.data(), ErrorData::Simple(k) if k == kind), + "repr(simple) encoding failed {:?}", + kind, + ); + res + } + + #[inline] + pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self { + // SAFETY: References are never null. + Self(unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) }, PhantomData) + } + + #[inline] + pub(super) fn data(&self) -> ErrorData<&Custom> { + // SAFETY: We're a Repr, decode_repr is fine. + unsafe { decode_repr(self.0, |c| &*c) } + } + + #[inline] + pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> { + // SAFETY: We're a Repr, decode_repr is fine. + unsafe { decode_repr(self.0, |c| &mut *c) } + } + + #[inline] + pub(super) fn into_data(self) -> ErrorData> { + let this = core::mem::ManuallyDrop::new(self); + // SAFETY: We're a Repr, decode_repr is fine. The `Box::from_raw` is + // safe because we prevent double-drop using `ManuallyDrop`. + unsafe { decode_repr(this.0, |p| ErrorBox::from_raw(p)) } + } +} + +impl Drop for Repr { + #[inline] + fn drop(&mut self) { + // SAFETY: We're a Repr, decode_repr is fine. The `Box::from_raw` is + // safe because we're being dropped. + unsafe { + let _ = decode_repr(self.0, |p| ErrorBox::::from_raw(p)); + } + } +} + +// Shared helper to decode a `Repr`'s internal pointer into an ErrorData. +// +// Safety: `ptr`'s bits should be encoded as described in the document at the +// top (it should `some_repr.0`) +#[inline] +unsafe fn decode_repr(ptr: NonNull<()>, make_custom: F) -> ErrorData +where + F: FnOnce(*mut Custom) -> C, +{ + let bits = ptr.as_ptr().addr(); + match bits & TAG_MASK { + TAG_OS => { + let code = ((bits as i64) >> 32) as RawOsError; + ErrorData::Os(code) + } + TAG_SIMPLE => { + let kind_bits = (bits >> 32) as u32; + let kind = kind_from_prim(kind_bits).unwrap_or_else(|| { + debug_assert!(false, "Invalid io::error::Repr bits: `Repr({:#018x})`", bits); + // SAFETY: This means the `ptr` passed in was not valid, which + // violates the unsafe contract of `decode_repr`. + // + // Using this rather than unwrap meaningfully improves the code + // for callers which only care about one variant (usually + // `Custom`) + unsafe { + core::hint::unreachable_unchecked(); + } + }); + ErrorData::Simple(kind) + } + // SAFETY: ptr has the right lsb bits and is guaranteed valid by + // Repr's safety invariant + TAG_SIMPLE_MESSAGE => unsafe { + ErrorData::SimpleMessage(&*ptr.cast::().as_ptr()) + }, + TAG_CUSTOM => { + // It would be correct for us to use `ptr::byte_sub` here (see the + // comment above the `wrapping_add` call in `new_custom` for why), + // but it isn't clear that it makes a difference, so we don't. + let custom = ptr.as_ptr().wrapping_byte_sub(TAG_CUSTOM).cast::(); + ErrorData::Custom(make_custom(custom)) + } + _ => { + // Can't happen, and compiler can tell + unreachable!(); + } + } +} + +// This compiles to the same code as the check+transmute, but doesn't require +// unsafe, or to hard-code max ErrorKind or its size in a way the compiler +// couldn't verify. +/// implementation detail of [`Error`] +#[unstable(feature = "core_io_error_internals", issue = "none")] +#[inline] +pub fn kind_from_prim(ek: u32) -> Option { + macro_rules! from_prim { + ($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{ + // Force a compile error if the list gets out of date. + const _: fn(e: $Enum) = |e: $Enum| match e { + $($Enum::$Variant => ()),* + }; + match $prim { + $(v if v == ($Enum::$Variant as _) => Some($Enum::$Variant),)* + _ => None, + } + }} + } + from_prim!(ek => ErrorKind { + NotFound, + PermissionDenied, + ConnectionRefused, + ConnectionReset, + HostUnreachable, + NetworkUnreachable, + ConnectionAborted, + NotConnected, + AddrInUse, + AddrNotAvailable, + NetworkDown, + BrokenPipe, + AlreadyExists, + WouldBlock, + NotADirectory, + IsADirectory, + DirectoryNotEmpty, + ReadOnlyFilesystem, + FilesystemLoop, + StaleNetworkFileHandle, + InvalidInput, + InvalidData, + TimedOut, + WriteZero, + StorageFull, + NotSeekable, + FilesystemQuotaExceeded, + FileTooLarge, + ResourceBusy, + ExecutableFileBusy, + Deadlock, + CrossesDevices, + TooManyLinks, + InvalidFilename, + ArgumentListTooLong, + Interrupted, + Other, + UnexpectedEof, + Unsupported, + OutOfMemory, + Uncategorized, + }) +} + +// Some static checking to alert us if a change breaks any of the assumptions +// that our encoding relies on for correctness and soundness. (Some of these are +// a bit overly thorough/cautious, admittedly) +// +// If any of these are hit on a platform that std supports, we should likely +// just use `repr_unpacked.rs` there instead (unless the fix is easy). +macro_rules! static_assert { + ($condition:expr) => { + const _: () = assert!($condition); + }; + (@usize_eq: $lhs:expr, $rhs:expr) => { + const _: [(); $lhs] = [(); $rhs]; + }; +} + +// The bitpacking we use requires pointers be exactly 64 bits. +static_assert!(@usize_eq: size_of::>(), 8); + +// We also require pointers and usize be the same size. +static_assert!(@usize_eq: size_of::>(), size_of::()); + +// `Custom` and `SimpleMessage` need to be thin pointers. +static_assert!(@usize_eq: size_of::<&'static SimpleMessage>(), 8); +static_assert!(@usize_eq: size_of::>(), 8); + +static_assert!((TAG_MASK + 1).is_power_of_two()); +// And they must have sufficient alignment. +static_assert!(align_of::() >= TAG_MASK + 1); +static_assert!(align_of::() >= TAG_MASK + 1); + +// `RawOsError` must be an alias for `i32`. +const _: fn(RawOsError) -> i32 = |os| os; + +static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE_MESSAGE, TAG_SIMPLE_MESSAGE); +static_assert!(@usize_eq: TAG_MASK & TAG_CUSTOM, TAG_CUSTOM); +static_assert!(@usize_eq: TAG_MASK & TAG_OS, TAG_OS); +static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE, TAG_SIMPLE); + +// This is obviously true (`TAG_CUSTOM` is `0b01`), but in `Repr::new_custom` we +// offset a pointer by this value, and expect it to both be within the same +// object, and to not wrap around the address space. See the comment in that +// function for further details. +// +// Actually, at the moment we use `ptr::wrapping_add`, not `ptr::add`, so this +// check isn't needed for that one, although the assertion that we don't +// actually wrap around in that wrapping_add does simplify the safety reasoning +// elsewhere considerably. +static_assert!(size_of::() >= TAG_CUSTOM); + +// These two store a payload which is allowed to be zero, so they must be +// non-zero to preserve the `NonNull`'s range invariant. +static_assert!(TAG_OS != 0); +static_assert!(TAG_SIMPLE != 0); +// We can't tag `SimpleMessage`s, the tag must be 0. +static_assert!(@usize_eq: TAG_SIMPLE_MESSAGE, 0); + +// Check that the point of all of this still holds. +// +// We'd check against `io::Error`, but *technically* it's allowed to vary, +// as it's not `#[repr(transparent)]`/`#[repr(C)]`. We could add that, but +// the `#[repr()]` would show up in rustdoc, which might be seen as a stable +// commitment. +static_assert!(@usize_eq: size_of::(), 8); +static_assert!(@usize_eq: size_of::>(), 8); +static_assert!(@usize_eq: size_of::>(), 8); +static_assert!(@usize_eq: size_of::>(), 16); diff --git a/library/core/src/io/error/repr_unpacked.rs b/library/core/src/io/error/repr_unpacked.rs new file mode 100644 index 0000000000000..d1b7c5d7d41fc --- /dev/null +++ b/library/core/src/io/error/repr_unpacked.rs @@ -0,0 +1,53 @@ +//! This is a fairly simple unpacked error representation that's used on +//! non-64bit targets, where the packed 64 bit representation wouldn't work, and +//! would have no benefit. + +use super::{Custom, ErrorBox, ErrorData, ErrorKind, RawOsError, SimpleMessage}; + +type Inner = ErrorData>; + +pub(super) struct Repr(Inner); + +impl Repr { + #[inline] + pub(super) fn new(dat: ErrorData>) -> Self { + Self(dat) + } + pub(super) fn new_custom(b: ErrorBox) -> Self { + Self(Inner::Custom(b)) + } + #[inline] + pub(super) fn new_os(code: RawOsError) -> Self { + Self(Inner::Os(code)) + } + #[inline] + pub(super) fn new_simple(kind: ErrorKind) -> Self { + Self(Inner::Simple(kind)) + } + #[inline] + pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self { + Self(Inner::SimpleMessage(m)) + } + #[inline] + pub(super) fn into_data(self) -> ErrorData> { + self.0 + } + #[inline] + pub(super) fn data(&self) -> ErrorData<&Custom> { + match &self.0 { + Inner::Os(c) => ErrorData::Os(*c), + Inner::Simple(k) => ErrorData::Simple(*k), + Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m), + Inner::Custom(m) => ErrorData::Custom(&*m), + } + } + #[inline] + pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> { + match &mut self.0 { + Inner::Os(c) => ErrorData::Os(*c), + Inner::Simple(k) => ErrorData::Simple(*k), + Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m), + Inner::Custom(m) => ErrorData::Custom(&mut *m), + } + } +} diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs new file mode 100644 index 0000000000000..5e36b9a073fdd --- /dev/null +++ b/library/core/src/io/mod.rs @@ -0,0 +1,24 @@ +//! Traits, helpers, and type definitions for core I/O functionality. + +#![unstable(feature = "core_io", issue = "none")] + +#[unstable(feature = "raw_os_error_ty", issue = "107792")] +pub use self::error::RawOsError; +#[unstable(feature = "core_io_error", issue = "none")] +#[cfg(target_has_atomic_load_store = "ptr")] +pub use self::error::{Error, ErrorKind, Result}; + +#[cfg(target_has_atomic_load_store = "ptr")] +#[unstable(feature = "core_io_error_internals", issue = "none")] +pub use error::const_io_error; + +#[cfg(target_has_atomic_load_store = "ptr")] +#[unstable(feature = "core_io_error_internals", issue = "none")] +pub mod error_internals { + //! implementation detail of [`Error`] + + pub use super::error::*; +} + +#[cfg(target_has_atomic_load_store = "ptr")] +mod error; diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 906421327cbc5..3aa8b88d4cfc5 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -367,6 +367,7 @@ pub mod async_iter; pub mod cell; pub mod char; pub mod ffi; +pub mod io; pub mod iter; pub mod net; pub mod option; diff --git a/library/core/tests/io.rs b/library/core/tests/io.rs new file mode 100644 index 0000000000000..d4a42f71a19ae --- /dev/null +++ b/library/core/tests/io.rs @@ -0,0 +1 @@ +mod error; diff --git a/library/core/tests/io/error.rs b/library/core/tests/io/error.rs new file mode 100644 index 0000000000000..d491e761627fc --- /dev/null +++ b/library/core/tests/io/error.rs @@ -0,0 +1,144 @@ +use super::{const_io_error, Custom, Error, ErrorData, ErrorKind, SimpleMessage}; +use crate::assert_matches::assert_matches; +use crate::error; +use crate::fmt; +use crate::mem::size_of; + +#[test] +fn test_size() { + assert!(size_of::() <= size_of::<[usize; 2]>()); +} + +#[test] +fn test_const() { + const E: Error = const_io_error!(ErrorKind::NotFound, "hello"); + + assert_eq!(E.kind(), ErrorKind::NotFound); + assert_eq!(E.to_string(), "hello"); + assert!(format!("{E:?}").contains("\"hello\"")); + assert!(format!("{E:?}").contains("NotFound")); +} + +#[test] +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, + ); + } +} + +#[test] +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::*, SimpleMessage}; + 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 }) + ); + }}; + } + + let not_static = const_io_error!(Uncategorized, "not a constant!"); + check_simple_msg!(not_static, Uncategorized, "not a constant!"); + + const CONST: Error = const_io_error!(NotFound, "definitely a constant!"); + check_simple_msg!(CONST, NotFound, "definitely a constant!"); + + static STATIC: Error = const_io_error!(BrokenPipe, "a constant, sort of!"); + check_simple_msg!(STATIC, BrokenPipe, "a constant, sort of!"); +} + +#[derive(Debug, PartialEq)] +struct Bojji(bool); +impl error::Error for Bojji {} +impl fmt::Display for Bojji { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ah! {:?}", self) + } +} + +#[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; + +impl fmt::Display for E { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + Ok(()) + } +} + +impl error::Error for E {} + +#[test] +fn test_std_io_error_downcast() { + // Case 1: custom error, downcast succeeds + let io_error = Error::new(ErrorKind::Other, Bojji(true)); + let e: Box = io_error.downcast().unwrap(); + assert!(e.0); + + // Case 2: custom error, downcast fails + let io_error = Error::new(ErrorKind::Other, Bojji(true)); + let io_error = io_error.downcast::().unwrap_err(); + + // ensures that the custom error is intact + assert_eq!(ErrorKind::Other, io_error.kind()); + let e: Box = io_error.downcast().unwrap(); + assert!(e.0); + + // Case 3: os error + let errno = 20; + let io_error = Error::from_raw_os_error(errno); + let io_error = io_error.downcast::().unwrap_err(); + + assert_eq!(errno, io_error.raw_os_error().unwrap()); + + // Case 4: simple + let kind = ErrorKind::OutOfMemory; + let io_error: Error = kind.into(); + let io_error = io_error.downcast::().unwrap_err(); + + 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 = io_error.downcast::().unwrap_err(); + + assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind()); + assert_eq!(SIMPLE_MESSAGE.message, format!("{io_error}")); +} diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 5966416e32a19..d42c4ff59788c 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -1,20 +1,29 @@ #[cfg(test)] mod tests; +#[cfg(bootstrap)] #[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] mod repr_bitpacked; +#[cfg(bootstrap)] #[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] use repr_bitpacked::Repr; +#[cfg(bootstrap)] #[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] mod repr_unpacked; +#[cfg(bootstrap)] #[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] use repr_unpacked::Repr; -use crate::error; -use crate::fmt; -use crate::result; +#[cfg(not(bootstrap))] +use super::{Error, RawOsError}; use crate::sys; +#[cfg(bootstrap)] +use crate::{error, fmt, result}; +#[cfg(not(bootstrap))] +use core::io::error_internals::{ErrorString, StdVTable}; +#[cfg(bootstrap)] +use core::io::{ErrorKind, RawOsError}; /// A specialized [`Result`] type for I/O operations. /// @@ -51,6 +60,7 @@ use crate::sys; /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(bootstrap)] pub type Result = result::Result; /// The error type for I/O operations of the [`Read`], [`Write`], [`Seek`], and @@ -64,11 +74,13 @@ pub type Result = result::Result; /// [`Write`]: crate::io::Write /// [`Seek`]: crate::io::Seek #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(bootstrap)] pub struct Error { repr: Repr, } #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(bootstrap)] impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.repr, f) @@ -76,6 +88,7 @@ impl fmt::Debug for Error { } #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(bootstrap)] impl From for Error { /// Converts a [`alloc::ffi::NulError`] into a [`Error`]. fn from(_: alloc::ffi::NulError) -> Error { @@ -86,6 +99,7 @@ impl From for Error { // Only derive debug in tests, to make sure it // doesn't accidentally get printed. #[cfg_attr(test, derive(Debug))] +#[cfg(bootstrap)] enum ErrorData { Os(RawOsError), Simple(ErrorKind), @@ -93,17 +107,6 @@ enum ErrorData { Custom(C), } -/// The type of raw OS error codes returned by [`Error::raw_os_error`]. -/// -/// 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 -/// [`usize`]. Use `as`or [`into`] conversions where applicable to ensure maximum -/// portability. -/// -/// [`into`]: Into::into -#[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub type RawOsError = sys::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 // requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the @@ -120,11 +123,13 @@ pub type RawOsError = sys::RawOsError; // matter at all) #[repr(align(4))] #[derive(Debug)] +#[cfg(bootstrap)] pub(crate) struct SimpleMessage { kind: ErrorKind, message: &'static str, } +#[cfg(bootstrap)] impl SimpleMessage { pub(crate) const fn new(kind: ErrorKind, message: &'static str) -> Self { Self { kind, message } @@ -133,6 +138,7 @@ impl SimpleMessage { /// Create and return an `io::Error` for a given `ErrorKind` and constant /// message. This doesn't allocate. +#[cfg(bootstrap)] pub(crate) macro const_io_error($kind:expr, $message:expr $(,)?) { $crate::io::error::Error::from_static_message({ const MESSAGE_DATA: $crate::io::error::SimpleMessage = @@ -146,324 +152,16 @@ pub(crate) macro const_io_error($kind:expr, $message:expr $(,)?) { // already be this high or higher. #[derive(Debug)] #[repr(align(4))] +#[cfg(bootstrap)] struct Custom { kind: ErrorKind, error: Box, } -/// A list specifying general categories of I/O error. -/// -/// This list is intended to grow over time and it is not recommended to -/// exhaustively match against it. -/// -/// It is used with the [`io::Error`] type. -/// -/// [`io::Error`]: Error -/// -/// # Handling errors and matching on `ErrorKind` -/// -/// In application code, use `match` for the `ErrorKind` values you are -/// expecting; use `_` to match "all other errors". -/// -/// In comprehensive and thorough tests that want to verify that a test doesn't -/// return any known incorrect error kind, you may want to cut-and-paste the -/// current full list of errors from here into your test code, and then match -/// `_` as the correct case. This seems counterintuitive, but it will make your -/// tests more robust. In particular, if you want to verify that your code does -/// produce an unrecognized error kind, the robust solution is to check for all -/// the recognized error kinds and fail in those cases. -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -#[stable(feature = "rust1", since = "1.0.0")] -#[allow(deprecated)] -#[non_exhaustive] -pub enum ErrorKind { - /// An entity was not found, often a file. - #[stable(feature = "rust1", since = "1.0.0")] - NotFound, - /// The operation lacked the necessary privileges to complete. - #[stable(feature = "rust1", since = "1.0.0")] - PermissionDenied, - /// The connection was refused by the remote server. - #[stable(feature = "rust1", since = "1.0.0")] - ConnectionRefused, - /// The connection was reset by the remote server. - #[stable(feature = "rust1", since = "1.0.0")] - ConnectionReset, - /// The remote host is not reachable. - #[unstable(feature = "io_error_more", issue = "86442")] - HostUnreachable, - /// The network containing the remote host is not reachable. - #[unstable(feature = "io_error_more", issue = "86442")] - NetworkUnreachable, - /// The connection was aborted (terminated) by the remote server. - #[stable(feature = "rust1", since = "1.0.0")] - ConnectionAborted, - /// The network operation failed because it was not connected yet. - #[stable(feature = "rust1", since = "1.0.0")] - NotConnected, - /// A socket address could not be bound because the address is already in - /// use elsewhere. - #[stable(feature = "rust1", since = "1.0.0")] - AddrInUse, - /// A nonexistent interface was requested or the requested address was not - /// local. - #[stable(feature = "rust1", since = "1.0.0")] - AddrNotAvailable, - /// The system's networking is down. - #[unstable(feature = "io_error_more", issue = "86442")] - NetworkDown, - /// The operation failed because a pipe was closed. - #[stable(feature = "rust1", since = "1.0.0")] - BrokenPipe, - /// An entity already exists, often a file. - #[stable(feature = "rust1", since = "1.0.0")] - AlreadyExists, - /// The operation needs to block to complete, but the blocking operation was - /// requested to not occur. - #[stable(feature = "rust1", since = "1.0.0")] - WouldBlock, - /// A filesystem object is, unexpectedly, not a directory. - /// - /// For example, a filesystem path was specified where one of the intermediate directory - /// components was, in fact, a plain file. - #[unstable(feature = "io_error_more", issue = "86442")] - NotADirectory, - /// The filesystem object is, unexpectedly, a directory. - /// - /// A directory was specified when a non-directory was expected. - #[unstable(feature = "io_error_more", issue = "86442")] - IsADirectory, - /// A non-empty directory was specified where an empty directory was expected. - #[unstable(feature = "io_error_more", issue = "86442")] - DirectoryNotEmpty, - /// The filesystem or storage medium is read-only, but a write operation was attempted. - #[unstable(feature = "io_error_more", issue = "86442")] - ReadOnlyFilesystem, - /// Loop in the filesystem or IO subsystem; often, too many levels of symbolic links. - /// - /// There was a loop (or excessively long chain) resolving a filesystem object - /// or file IO object. - /// - /// On Unix this is usually the result of a symbolic link loop; or, of exceeding the - /// system-specific limit on the depth of symlink traversal. - #[unstable(feature = "io_error_more", issue = "86442")] - FilesystemLoop, - /// Stale network file handle. - /// - /// With some network filesystems, notably NFS, an open file (or directory) can be invalidated - /// by problems with the network or server. - #[unstable(feature = "io_error_more", issue = "86442")] - StaleNetworkFileHandle, - /// A parameter was incorrect. - #[stable(feature = "rust1", since = "1.0.0")] - InvalidInput, - /// Data not valid for the operation were encountered. - /// - /// Unlike [`InvalidInput`], this typically means that the operation - /// parameters were valid, however the error was caused by malformed - /// input data. - /// - /// For example, a function that reads a file into a string will error with - /// `InvalidData` if the file's contents are not valid UTF-8. - /// - /// [`InvalidInput`]: ErrorKind::InvalidInput - #[stable(feature = "io_invalid_data", since = "1.2.0")] - InvalidData, - /// The I/O operation's timeout expired, causing it to be canceled. - #[stable(feature = "rust1", since = "1.0.0")] - TimedOut, - /// An error returned when an operation could not be completed because a - /// 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, - /// The underlying storage (typically, a filesystem) is full. - /// - /// This does not include out of quota errors. - #[unstable(feature = "io_error_more", issue = "86442")] - StorageFull, - /// Seek on unseekable file. - /// - /// Seeking was attempted on an open file handle which is not suitable for seeking - for - /// example, on Unix, a named pipe opened with `File::open`. - #[unstable(feature = "io_error_more", issue = "86442")] - NotSeekable, - /// Filesystem quota was exceeded. - #[unstable(feature = "io_error_more", issue = "86442")] - FilesystemQuotaExceeded, - /// File larger than allowed or supported. - /// - /// This might arise from a hard limit of the underlying filesystem or file access API, or from - /// an administratively imposed resource limitation. Simple disk full, and out of quota, have - /// their own errors. - #[unstable(feature = "io_error_more", issue = "86442")] - FileTooLarge, - /// Resource is busy. - #[unstable(feature = "io_error_more", issue = "86442")] - ResourceBusy, - /// Executable file is busy. - /// - /// An attempt was made to write to a file which is also in use as a running program. (Not all - /// operating systems detect this situation.) - #[unstable(feature = "io_error_more", issue = "86442")] - ExecutableFileBusy, - /// Deadlock (avoided). - /// - /// A file locking operation would result in deadlock. This situation is typically detected, if - /// at all, on a best-effort basis. - #[unstable(feature = "io_error_more", issue = "86442")] - Deadlock, - /// Cross-device or cross-filesystem (hard) link or rename. - #[unstable(feature = "io_error_more", issue = "86442")] - CrossesDevices, - /// Too many (hard) links to the same filesystem object. - /// - /// The filesystem does not support making so many hardlinks to the same file. - #[unstable(feature = "io_error_more", issue = "86442")] - TooManyLinks, - /// A filename was invalid. - /// - /// This error can also cause if it exceeded the filename length limit. - #[unstable(feature = "io_error_more", issue = "86442")] - InvalidFilename, - /// Program argument list too long. - /// - /// When trying to run an external program, a system or process limit on the size of the - /// arguments would have been exceeded. - #[unstable(feature = "io_error_more", issue = "86442")] - ArgumentListTooLong, - /// This operation was interrupted. - /// - /// Interrupted operations can typically be retried. - #[stable(feature = "rust1", since = "1.0.0")] - Interrupted, - - /// This operation is unsupported on this platform. - /// - /// This means that the operation can never succeed. - #[stable(feature = "unsupported_error", since = "1.53.0")] - Unsupported, - - // ErrorKinds which are primarily categorisations for OS error - // codes should be added above. - // - /// An error returned when an operation could not be completed because an - /// "end of file" was reached prematurely. - /// - /// This typically means that an operation could only succeed if it read a - /// particular number of bytes but only a smaller number of bytes could be - /// read. - #[stable(feature = "read_exact", since = "1.6.0")] - UnexpectedEof, - - /// An operation could not be completed, because it failed - /// to allocate enough memory. - #[stable(feature = "out_of_memory_error", since = "1.54.0")] - OutOfMemory, - - // "Unusual" error kinds which do not correspond simply to (sets - // of) OS error codes, should be added just above this comment. - // `Other` and `Uncategorized` should remain at the end: - // - /// A custom error that does not fall under any other I/O error kind. - /// - /// This can be used to construct your own [`Error`]s that do not match any - /// [`ErrorKind`]. - /// - /// This [`ErrorKind`] is not used by the standard library. - /// - /// Errors from the standard library that do not fall under any of the I/O - /// error kinds cannot be `match`ed on, and will only match a wildcard (`_`) pattern. - /// New [`ErrorKind`]s might be added in the future for some of those. - #[stable(feature = "rust1", since = "1.0.0")] - Other, - - /// Any I/O error from the standard library that's not part of this list. - /// - /// Errors that are `Uncategorized` now may move to a different or a new - /// [`ErrorKind`] variant in the future. It is not recommended to match - /// an error against `Uncategorized`; use a wildcard match (`_`) instead. - #[unstable(feature = "io_error_uncategorized", issue = "none")] - #[doc(hidden)] - Uncategorized, -} - -impl ErrorKind { - pub(crate) fn as_str(&self) -> &'static str { - use ErrorKind::*; - // tidy-alphabetical-start - match *self { - AddrInUse => "address in use", - AddrNotAvailable => "address not available", - AlreadyExists => "entity already exists", - ArgumentListTooLong => "argument list too long", - BrokenPipe => "broken pipe", - ConnectionAborted => "connection aborted", - ConnectionRefused => "connection refused", - ConnectionReset => "connection reset", - CrossesDevices => "cross-device link or rename", - Deadlock => "deadlock", - DirectoryNotEmpty => "directory not empty", - ExecutableFileBusy => "executable file busy", - FileTooLarge => "file too large", - FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)", - FilesystemQuotaExceeded => "filesystem quota exceeded", - HostUnreachable => "host unreachable", - Interrupted => "operation interrupted", - InvalidData => "invalid data", - InvalidFilename => "invalid filename", - InvalidInput => "invalid input parameter", - IsADirectory => "is a directory", - NetworkDown => "network down", - NetworkUnreachable => "network unreachable", - NotADirectory => "not a directory", - NotConnected => "not connected", - NotFound => "entity not found", - NotSeekable => "seek on unseekable file", - Other => "other error", - OutOfMemory => "out of memory", - PermissionDenied => "permission denied", - ReadOnlyFilesystem => "read-only filesystem or storage medium", - ResourceBusy => "resource busy", - StaleNetworkFileHandle => "stale network file handle", - StorageFull => "no storage space", - TimedOut => "timed out", - TooManyLinks => "too many links", - Uncategorized => "uncategorized error", - UnexpectedEof => "unexpected end of file", - Unsupported => "unsupported", - WouldBlock => "operation would block", - WriteZero => "write zero", - } - // tidy-alphabetical-end - } -} - -#[stable(feature = "io_errorkind_display", since = "1.60.0")] -impl fmt::Display for ErrorKind { - /// Shows a human-readable description of the `ErrorKind`. - /// - /// This is similar to `impl Display for Error`, but doesn't require first converting to Error. - /// - /// # Examples - /// ``` - /// use std::io::ErrorKind; - /// assert_eq!("entity not found", ErrorKind::NotFound.to_string()); - /// ``` - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.write_str(self.as_str()) - } -} - /// Intended for use for errors not exposed to the user, where allocating onto /// the heap (for normal construction via Error::new) is too costly. #[stable(feature = "io_error_from_errorkind", since = "1.14.0")] +#[cfg(bootstrap)] impl From for Error { /// Converts an [`ErrorKind`] into an [`Error`]. /// @@ -484,6 +182,7 @@ impl From for Error { } } +#[cfg(bootstrap)] impl Error { /// Creates a new I/O error from a known kind of error as well as an /// arbitrary error payload. @@ -927,6 +626,7 @@ impl Error { } } +#[cfg(bootstrap)] impl fmt::Debug for Repr { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self.data() { @@ -948,6 +648,7 @@ impl fmt::Debug for Repr { } #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(bootstrap)] impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self.repr.data() { @@ -963,6 +664,7 @@ impl fmt::Display for Error { } #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(bootstrap)] impl error::Error for Error { #[allow(deprecated, deprecated_in_future)] fn description(&self) -> &str { @@ -993,7 +695,97 @@ impl error::Error for Error { } } +#[cfg(bootstrap)] fn _assert_error_is_sync_send() { fn _is_sync_send() {} _is_sync_send::(); } + +#[cfg(not(bootstrap))] +unsafe fn set_std_vtable() { + static STD_VTABLE: StdVTable = StdVTable { + decode_error_kind: sys::decode_error_kind, + error_string: |code| ErrorString::from(sys::os::error_string(code)), + }; + unsafe { + STD_VTABLE.install(); + } +} + +#[cfg(not(bootstrap))] +impl 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(sys::os::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] + #[rustc_allow_incoherent_impl] + pub fn from_raw_os_error(code: RawOsError) -> Error { + unsafe { + set_std_vtable(); + Self::from_raw_os_error_impl(code) + } + } + + #[inline] + #[rustc_allow_incoherent_impl] + pub(crate) fn is_interrupted(&self) -> bool { + if let Some(code) = self.raw_os_error() { + sys::is_interrupted(code) + } else { + self.kind() == ErrorKind::Interrupted + } + } +} diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index 6e7366b36355f..8d4ba809ab0d2 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -104,6 +104,7 @@ use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage}; use alloc::boxed::Box; +use core::io::error_internals::kind_from_prim; use core::marker::PhantomData; use core::mem::{align_of, size_of}; use core::ptr::{self, NonNull}; @@ -282,68 +283,6 @@ where } } -// This compiles to the same code as the check+transmute, but doesn't require -// unsafe, or to hard-code max ErrorKind or its size in a way the compiler -// couldn't verify. -#[inline] -fn kind_from_prim(ek: u32) -> Option { - macro_rules! from_prim { - ($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{ - // Force a compile error if the list gets out of date. - const _: fn(e: $Enum) = |e: $Enum| match e { - $($Enum::$Variant => ()),* - }; - match $prim { - $(v if v == ($Enum::$Variant as _) => Some($Enum::$Variant),)* - _ => None, - } - }} - } - from_prim!(ek => ErrorKind { - NotFound, - PermissionDenied, - ConnectionRefused, - ConnectionReset, - HostUnreachable, - NetworkUnreachable, - ConnectionAborted, - NotConnected, - AddrInUse, - AddrNotAvailable, - NetworkDown, - BrokenPipe, - AlreadyExists, - WouldBlock, - NotADirectory, - IsADirectory, - DirectoryNotEmpty, - ReadOnlyFilesystem, - FilesystemLoop, - StaleNetworkFileHandle, - InvalidInput, - InvalidData, - TimedOut, - WriteZero, - StorageFull, - NotSeekable, - FilesystemQuotaExceeded, - FileTooLarge, - ResourceBusy, - ExecutableFileBusy, - Deadlock, - CrossesDevices, - TooManyLinks, - InvalidFilename, - ArgumentListTooLong, - Interrupted, - Other, - UnexpectedEof, - Unsupported, - OutOfMemory, - Uncategorized, - }) -} - // Some static checking to alert us if a change breaks any of the assumptions // that our encoding relies on for correctness and soundness. (Some of these are // a bit overly thorough/cautious, admittedly) diff --git a/library/std/src/io/error/tests.rs b/library/std/src/io/error/tests.rs index 36d52aef03cb7..7ec65266a8a53 100644 --- a/library/std/src/io/error/tests.rs +++ b/library/std/src/io/error/tests.rs @@ -1,27 +1,13 @@ -use super::{const_io_error, Custom, Error, ErrorData, ErrorKind, Repr, SimpleMessage}; -use crate::assert_matches::assert_matches; -use crate::error; -use crate::fmt; -use crate::mem::size_of; +use super::{Error, ErrorKind}; use crate::sys::decode_error_kind; use crate::sys::os::error_string; -#[test] -fn test_size() { - assert!(size_of::() <= size_of::<[usize; 2]>()); -} - #[test] 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, \ @@ -35,160 +21,3 @@ fn test_debug_error() { ); assert_eq!(format!("{err:?}"), expected); } - -#[test] -fn test_downcasting() { - #[derive(Debug)] - struct TestError; - - impl fmt::Display for TestError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("asdf") - } - } - - impl error::Error for TestError {} - - // we have to call all of these UFCS style right now since method - // resolution won't implicitly drop the Send+Sync bounds - let mut err = Error::new(ErrorKind::Other, TestError); - assert!(err.get_ref().unwrap().is::()); - assert_eq!("asdf", err.get_ref().unwrap().to_string()); - assert!(err.get_mut().unwrap().is::()); - let extracted = err.into_inner().unwrap(); - extracted.downcast::().unwrap(); -} - -#[test] -fn test_const() { - const E: Error = const_io_error!(ErrorKind::NotFound, "hello"); - - assert_eq!(E.kind(), ErrorKind::NotFound); - assert_eq!(E.to_string(), "hello"); - assert!(format!("{E:?}").contains("\"hello\"")); - assert!(format!("{E:?}").contains("NotFound")); -} - -#[test] -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, - ); - } -} - -#[test] -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::*, SimpleMessage}; - 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 }) - ); - }}; - } - - let not_static = const_io_error!(Uncategorized, "not a constant!"); - check_simple_msg!(not_static, Uncategorized, "not a constant!"); - - const CONST: Error = const_io_error!(NotFound, "definitely a constant!"); - check_simple_msg!(CONST, NotFound, "definitely a constant!"); - - static STATIC: Error = const_io_error!(BrokenPipe, "a constant, sort of!"); - check_simple_msg!(STATIC, BrokenPipe, "a constant, sort of!"); -} - -#[derive(Debug, PartialEq)] -struct Bojji(bool); -impl error::Error for Bojji {} -impl fmt::Display for Bojji { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "ah! {:?}", self) - } -} - -#[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; - -impl fmt::Display for E { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) - } -} - -impl error::Error for E {} - -#[test] -fn test_std_io_error_downcast() { - // Case 1: custom error, downcast succeeds - let io_error = Error::new(ErrorKind::Other, Bojji(true)); - let e: Box = io_error.downcast().unwrap(); - assert!(e.0); - - // Case 2: custom error, downcast fails - let io_error = Error::new(ErrorKind::Other, Bojji(true)); - let io_error = io_error.downcast::().unwrap_err(); - - // ensures that the custom error is intact - assert_eq!(ErrorKind::Other, io_error.kind()); - let e: Box = io_error.downcast().unwrap(); - assert!(e.0); - - // Case 3: os error - let errno = 20; - let io_error = Error::from_raw_os_error(errno); - let io_error = io_error.downcast::().unwrap_err(); - - assert_eq!(errno, io_error.raw_os_error().unwrap()); - - // Case 4: simple - let kind = ErrorKind::OutOfMemory; - let io_error: Error = kind.into(); - let io_error = io_error.downcast::().unwrap_err(); - - 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 = io_error.downcast::().unwrap_err(); - - assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind()); - assert_eq!(SIMPLE_MESSAGE.message, format!("{io_error}")); -} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index e6431abcf8215..d04025720cc6e 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -308,8 +308,9 @@ use crate::sys_common::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; +#[cfg(bootstrap)] +#[stable(feature = "rust1", since = "1.0.0")] +pub use self::error::{Error, Result}; pub(crate) use self::stdio::attempt_print_to_stderr; #[unstable(feature = "internal_output_capture", issue = "none")] #[doc(no_inline, hidden)] @@ -323,14 +324,23 @@ pub use self::{ buffered::{BufReader, BufWriter, IntoInnerError, LineWriter}, copy::copy, cursor::Cursor, - error::{Error, ErrorKind, Result}, stdio::{stderr, stdin, stdout, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock}, util::{empty, repeat, sink, Empty, Repeat, Sink}, }; +#[stable(feature = "rust1", since = "1.0.0")] +pub use alloc::io::ErrorKind; +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(bootstrap))] +pub use alloc::io::{Error, Result}; +#[unstable(feature = "raw_os_error_ty", issue = "107792")] +pub use core::io::RawOsError; +#[cfg(bootstrap)] +pub(crate) use self::error::const_io_error; #[unstable(feature = "read_buf", issue = "78485")] pub use self::readbuf::{BorrowedBuf, BorrowedCursor}; -pub(crate) use error::const_io_error; +#[cfg(not(bootstrap))] +pub(crate) use core::io::const_io_error; mod buffered; pub(crate) mod copy; @@ -386,10 +396,7 @@ where let ret = f(g.buf); if str::from_utf8(&g.buf[g.len..]).is_err() { ret.and_then(|_| { - Err(error::const_io_error!( - ErrorKind::InvalidData, - "stream did not contain valid UTF-8" - )) + Err(const_io_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8")) }) } else { g.len = g.buf.len(); @@ -520,7 +527,7 @@ pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [ } } if !buf.is_empty() { - Err(error::const_io_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer")) + Err(const_io_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer")) } else { Ok(()) } @@ -1621,7 +1628,7 @@ pub trait Write { while !buf.is_empty() { match self.write(buf) { Ok(0) => { - return Err(error::const_io_error!( + return Err(const_io_error!( ErrorKind::WriteZero, "failed to write whole buffer", )); @@ -1689,7 +1696,7 @@ pub trait Write { while !bufs.is_empty() { match self.write_vectored(bufs) { Ok(0) => { - return Err(error::const_io_error!( + return Err(const_io_error!( ErrorKind::WriteZero, "failed to write whole buffer", )); @@ -1766,7 +1773,7 @@ pub trait Write { if output.error.is_err() { output.error } else { - Err(error::const_io_error!(ErrorKind::Uncategorized, "formatter error")) + Err(const_io_error!(ErrorKind::Uncategorized, "formatter error")) } } } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index aaf20875129c4..0f6234c630d19 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -308,8 +308,12 @@ // // Library features (core): // tidy-alphabetical-start +#![feature(alloc_io)] #![feature(char_internals)] #![feature(core_intrinsics)] +#![feature(core_io)] +#![feature(core_io_error)] +#![feature(core_io_error_internals)] #![feature(duration_constants)] #![feature(error_generic_member_access)] #![feature(error_in_core)] @@ -322,6 +326,9 @@ #![feature(float_next_up_down)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] +#![feature(int_roundings)] +#![feature(io_error_more)] +#![feature(io_error_uncategorized)] #![feature(ip)] #![feature(ip_in_core)] #![feature(maybe_uninit_slice)] @@ -336,6 +343,7 @@ #![feature(portable_simd)] #![feature(prelude_2024)] #![feature(ptr_as_uninit)] +#![feature(raw_os_error_ty)] #![feature(raw_os_nonzero)] #![feature(round_ties_even)] #![feature(slice_internals)] diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 159ffe7ac9635..de9f37af89f91 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -116,6 +116,3 @@ pub fn log_wrapper f64>(n: f64, log_fn: F) -> f64 { pub fn log_wrapper f64>(n: f64, log_fn: F) -> f64 { log_fn(n) } - -#[cfg(not(target_os = "uefi"))] -pub type RawOsError = i32; diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs index 097396ae99396..a67e4462af1a2 100644 --- a/library/std/src/sys/uefi/mod.rs +++ b/library/std/src/sys/uefi/mod.rs @@ -51,12 +51,11 @@ mod helpers; #[cfg(test)] mod tests; -pub type RawOsError = usize; - use crate::io as std_io; use crate::os::uefi; use crate::ptr::NonNull; use crate::sync::atomic::{AtomicPtr, Ordering}; +use core::io::RawOsError; pub mod memchr { pub use core::slice::memchr::{memchr, memrchr}; diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 5f6b63a67fda7..0469bc2c84fa0 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -46,6 +46,8 @@ const EXCEPTION_PATHS: &[&str] = &[ // we must use `#[cfg(windows)]` to conditionally compile the // correct `VaList` structure for windows. "library/core/src/ffi/mod.rs", + // need platform-specific code to select correct RawOsError type and Repr mod. + "library/core/src/io/error.rs", "library/std/src/sys/", // Platform-specific code for std lives here. "library/std/src/os", // Platform-specific public interfaces // Temporary `std` exceptions