Skip to content

Commit 281a1ae

Browse files
committed
Auto merge of #155625 - bushrat011899:core_io_error, r=Mark-Simulacrum
Move `std::io::Error` into `core` ACP: rust-lang/libs-team#755 Tracking issue: #154046 Related: #155574 Related: #152918 ## Description Moves `std::io::Error` into `core`, deferring `Box`-adjacent methods to incoherent implementations in `alloc`, and `RawOsError` methods to `std`. This requires some substantial changes to the internals of `Error`, but none of them are breaking changes or externally visible. Notably, I've replaced usage of `Box` with a wrapper around a pointer and an appropriate drop function. This requires the addition of quite a few lines of unsafe, but is required to work around `Box` only being accessible from `alloc`. Additionally, an atomic pointer to a VTable is used for working with `RawOsError` in `core`, since we cannot know the required implementations without `std`. As mention in [this comment](#155625 (comment)), there may be concern around having a static `AtomicPtr` in `core` for certain users. I've added a configuration option `no_io_statics` which (similar to `no_sync`/etc. in `alloc`) can be used to prevent their inclusion in `core`. When active, the fallback default implementation will always be used. --- ## Notes * This PR adopts the VTable technique from #152918 * This PR builds on my previous PR #155574 * No AI tooling of any kind was used during the creation of this PR.
2 parents df6ee90 + 0a1de1d commit 281a1ae

18 files changed

Lines changed: 1226 additions & 798 deletions

File tree

library/alloc/src/io/error.rs

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
use core::io::Custom;
2+
#[cfg_attr(no_global_oom_handling, expect(unused_imports))]
3+
use core::io::CustomOwner;
4+
use core::{error, result};
5+
6+
use crate::boxed::Box;
7+
#[cfg_attr(any(no_rc, no_sync, no_global_oom_handling), expect(unused_imports))]
8+
use crate::io::const_error;
9+
use crate::io::{Error, ErrorKind};
10+
11+
impl Error {
12+
/// Creates a new I/O error from a known kind of error as well as an
13+
/// arbitrary error payload.
14+
///
15+
/// This function is used to generically create I/O errors which do not
16+
/// originate from the OS itself. The `error` argument is an arbitrary
17+
/// payload which will be contained in this [`Error`].
18+
///
19+
/// Note that this function allocates memory on the heap.
20+
/// If no extra payload is required, use the `From` conversion from
21+
/// `ErrorKind`.
22+
///
23+
/// # Examples
24+
///
25+
/// ```
26+
/// use std::io::{Error, ErrorKind};
27+
///
28+
/// // errors can be created from strings
29+
/// let custom_error = Error::new(ErrorKind::Other, "oh no!");
30+
///
31+
/// // errors can also be created from other errors
32+
/// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error);
33+
///
34+
/// // creating an error without payload (and without memory allocation)
35+
/// let eof_error = Error::from(ErrorKind::UnexpectedEof);
36+
/// ```
37+
#[cfg(not(no_global_oom_handling))]
38+
#[stable(feature = "rust1", since = "1.0.0")]
39+
#[cfg_attr(not(test), rustc_diagnostic_item = "io_error_new")]
40+
#[inline(never)]
41+
#[rustc_allow_incoherent_impl]
42+
pub fn new<E>(kind: ErrorKind, error: E) -> Error
43+
where
44+
E: Into<Box<dyn error::Error + Send + Sync>>,
45+
{
46+
let custom = custom_owner_from_box(kind, error.into());
47+
48+
// SAFETY: `custom_owner` has been constructed from a `Box` from the `alloc` crate.
49+
unsafe { Self::from_custom_owner(custom) }
50+
}
51+
52+
/// Creates a new I/O error from an arbitrary error payload.
53+
///
54+
/// This function is used to generically create I/O errors which do not
55+
/// originate from the OS itself. It is a shortcut for [`Error::new`][new]
56+
/// with [`ErrorKind::Other`].
57+
///
58+
/// [new]: struct.Error.html#method.new
59+
///
60+
/// # Examples
61+
///
62+
/// ```
63+
/// use std::io::Error;
64+
///
65+
/// // errors can be created from strings
66+
/// let custom_error = Error::other("oh no!");
67+
///
68+
/// // errors can also be created from other errors
69+
/// let custom_error2 = Error::other(custom_error);
70+
/// ```
71+
#[cfg(not(no_global_oom_handling))]
72+
#[stable(feature = "io_error_other", since = "1.74.0")]
73+
#[rustc_allow_incoherent_impl]
74+
pub fn other<E>(error: E) -> Error
75+
where
76+
E: Into<Box<dyn error::Error + Send + Sync>>,
77+
{
78+
Self::new(ErrorKind::Other, error)
79+
}
80+
81+
/// Consumes the `Error`, returning its inner error (if any).
82+
///
83+
/// If this [`Error`] was constructed via [`new`][new] or [`other`][other],
84+
/// then this function will return [`Some`],
85+
/// otherwise it will return [`None`].
86+
///
87+
/// [new]: struct.Error.html#method.new
88+
/// [other]: struct.Error.html#method.other
89+
///
90+
/// # Examples
91+
///
92+
/// ```
93+
/// use std::io::{Error, ErrorKind};
94+
///
95+
/// fn print_error(err: Error) {
96+
/// if let Some(inner_err) = err.into_inner() {
97+
/// println!("Inner error: {inner_err}");
98+
/// } else {
99+
/// println!("No inner error");
100+
/// }
101+
/// }
102+
///
103+
/// fn main() {
104+
/// // Will print "No inner error".
105+
/// print_error(Error::last_os_error());
106+
/// // Will print "Inner error: ...".
107+
/// print_error(Error::new(ErrorKind::Other, "oh no!"));
108+
/// }
109+
/// ```
110+
#[stable(feature = "io_error_inner", since = "1.3.0")]
111+
#[must_use = "`self` will be dropped if the result is not used"]
112+
#[inline]
113+
#[rustc_allow_incoherent_impl]
114+
pub fn into_inner(self) -> Option<Box<dyn error::Error + Send + Sync>> {
115+
let custom_owner = self.into_custom_owner().ok()?;
116+
117+
let ptr = custom_owner.into_raw().as_ptr();
118+
119+
// SAFETY:
120+
// `Error` can only contain a `CustomOwner` if it was constructed using `Box::into_raw`.
121+
let custom = unsafe { Box::<Custom>::from_raw(ptr) };
122+
123+
let ptr = custom.into_raw().as_ptr();
124+
125+
// SAFETY:
126+
// Any `CustomOwner` from an `Error` was constructed by the `alloc` crate
127+
// to contain a `Custom` which itself was constructed with `Box::into_raw`.
128+
Some(unsafe { Box::from_raw(ptr) })
129+
}
130+
131+
/// Attempts to downcast the custom boxed error to `E`.
132+
///
133+
/// If this [`Error`] contains a custom boxed error,
134+
/// then it would attempt downcasting on the boxed error,
135+
/// otherwise it will return [`Err`].
136+
///
137+
/// If the custom boxed error has the same type as `E`, it will return [`Ok`],
138+
/// otherwise it will also return [`Err`].
139+
///
140+
/// This method is meant to be a convenience routine for calling
141+
/// `Box<dyn Error + Sync + Send>::downcast` on the custom boxed error, returned by
142+
/// [`Error::into_inner`][into_inner].
143+
///
144+
/// [into_inner]: struct.Error.html#method.into_inner
145+
///
146+
/// # Examples
147+
///
148+
/// ```
149+
/// use std::fmt;
150+
/// use std::io;
151+
/// use std::error::Error;
152+
///
153+
/// #[derive(Debug)]
154+
/// enum E {
155+
/// Io(io::Error),
156+
/// SomeOtherVariant,
157+
/// }
158+
///
159+
/// impl fmt::Display for E {
160+
/// // ...
161+
/// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162+
/// # todo!()
163+
/// # }
164+
/// }
165+
/// impl Error for E {}
166+
///
167+
/// impl From<io::Error> for E {
168+
/// fn from(err: io::Error) -> E {
169+
/// err.downcast::<E>()
170+
/// .unwrap_or_else(E::Io)
171+
/// }
172+
/// }
173+
///
174+
/// impl From<E> for io::Error {
175+
/// fn from(err: E) -> io::Error {
176+
/// match err {
177+
/// E::Io(io_error) => io_error,
178+
/// e => io::Error::new(io::ErrorKind::Other, e),
179+
/// }
180+
/// }
181+
/// }
182+
///
183+
/// # fn main() {
184+
/// let e = E::SomeOtherVariant;
185+
/// // Convert it to an io::Error
186+
/// let io_error = io::Error::from(e);
187+
/// // Cast it back to the original variant
188+
/// let e = E::from(io_error);
189+
/// assert!(matches!(e, E::SomeOtherVariant));
190+
///
191+
/// let io_error = io::Error::from(io::ErrorKind::AlreadyExists);
192+
/// // Convert it to E
193+
/// let e = E::from(io_error);
194+
/// // Cast it back to the original variant
195+
/// let io_error = io::Error::from(e);
196+
/// assert_eq!(io_error.kind(), io::ErrorKind::AlreadyExists);
197+
/// assert!(io_error.get_ref().is_none());
198+
/// assert!(io_error.raw_os_error().is_none());
199+
/// # }
200+
/// ```
201+
#[stable(feature = "io_error_downcast", since = "1.79.0")]
202+
#[rustc_allow_incoherent_impl]
203+
pub fn downcast<E>(self) -> result::Result<E, Self>
204+
where
205+
E: error::Error + Send + Sync + 'static,
206+
{
207+
if let Some(e) = self.get_ref()
208+
&& e.is::<E>()
209+
{
210+
if let Some(b) = self.into_inner()
211+
&& let Ok(err) = b.downcast::<E>()
212+
{
213+
Ok(*err)
214+
} else {
215+
// Safety: We have just checked that the condition is true
216+
unsafe { core::hint::unreachable_unchecked() }
217+
}
218+
} else {
219+
Err(self)
220+
}
221+
}
222+
}
223+
224+
#[cfg(all(not(no_rc), not(no_sync), not(no_global_oom_handling)))]
225+
#[stable(feature = "rust1", since = "1.0.0")]
226+
impl From<crate::ffi::NulError> for Error {
227+
/// Converts a [`crate::ffi::NulError`] into a [`Error`].
228+
fn from(_: crate::ffi::NulError) -> Error {
229+
const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte")
230+
}
231+
}
232+
233+
#[stable(feature = "io_error_from_try_reserve", since = "1.78.0")]
234+
impl From<crate::collections::TryReserveError> for Error {
235+
/// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`].
236+
///
237+
/// `TryReserveError` won't be available as the error `source()`,
238+
/// but this may change in the future.
239+
fn from(_: crate::collections::TryReserveError) -> Error {
240+
// ErrorData::Custom allocates, which isn't great for handling OOM errors.
241+
ErrorKind::OutOfMemory.into()
242+
}
243+
}
244+
245+
#[cfg(not(no_global_oom_handling))]
246+
fn custom_owner_from_box(
247+
kind: ErrorKind,
248+
error: Box<dyn core::error::Error + Send + Sync>,
249+
) -> CustomOwner {
250+
/// # Safety
251+
///
252+
/// `ptr` must be valid to pass into `Box::from_raw`.
253+
unsafe fn drop_box_raw<T: ?Sized>(ptr: *mut T) {
254+
// SAFETY
255+
// Caller ensures `ptr` is valid to pass into `Box::from_raw`.
256+
drop(unsafe { Box::from_raw(ptr) })
257+
}
258+
259+
// SAFETY: the pointer returned by Box::into_raw is non-null.
260+
let error = unsafe { core::ptr::NonNull::new_unchecked(Box::into_raw(error)) };
261+
262+
// SAFETY:
263+
// * `error` is valid up to a static lifetime, and owns its pointee.
264+
// * `drop_box_raw` is safe to call for the pointer `error` exactly once.
265+
// * `drop_box_raw` is safe to call on a pointer to this instance of `Custom`,
266+
// and will be stored in a `CustomOwner`.
267+
let custom = unsafe { Custom::from_raw(kind, error, drop_box_raw, drop_box_raw) };
268+
269+
// SAFETY: the pointer returned by Box::into_raw is non-null.
270+
let custom = unsafe { core::ptr::NonNull::new_unchecked(Box::into_raw(Box::new(custom))) };
271+
272+
// SAFETY: the `outer_drop` provided to `custom` is valid for itself.
273+
unsafe { CustomOwner::from_raw(custom) }
274+
}

library/alloc/src/io/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//! Traits, helpers, and type definitions for core I/O functionality.
2+
3+
mod error;
4+
5+
#[unstable(feature = "raw_os_error_ty", issue = "107792")]
6+
pub use core::io::RawOsError;
7+
#[unstable(feature = "io_const_error_internals", issue = "none")]
8+
pub use core::io::SimpleMessage;
9+
#[unstable(feature = "io_const_error", issue = "133448")]
10+
pub use core::io::const_error;
11+
#[unstable(feature = "core_io_borrowed_buf", issue = "117693")]
12+
pub use core::io::{BorrowedBuf, BorrowedCursor};
13+
#[unstable(feature = "alloc_io", issue = "154046")]
14+
pub use core::io::{
15+
Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Sink, Take, empty,
16+
repeat, sink,
17+
};
18+
#[doc(hidden)]
19+
#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")]
20+
pub use core::io::{OsFunctions, chain, take};

library/alloc/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@
116116
#![feature(const_try)]
117117
#![feature(copied_into_inner)]
118118
#![feature(core_intrinsics)]
119+
#![feature(core_io)]
120+
#![feature(core_io_borrowed_buf)]
121+
#![feature(core_io_internals)]
119122
#![feature(deprecated_suggestion)]
120123
#![feature(deref_pure_trait)]
121124
#![feature(diagnostic_on_move)]
@@ -133,6 +136,8 @@
133136
#![feature(generic_atomic)]
134137
#![feature(hasher_prefixfree_extras)]
135138
#![feature(inplace_iteration)]
139+
#![feature(io_const_error)]
140+
#![feature(io_const_error_internals)]
136141
#![feature(iter_advance_by)]
137142
#![feature(iter_next_chunk)]
138143
#![feature(layout_for_ptr)]
@@ -147,6 +152,7 @@
147152
#![feature(ptr_cast_slice)]
148153
#![feature(ptr_internals)]
149154
#![feature(ptr_metadata)]
155+
#![feature(raw_os_error_ty)]
150156
#![feature(rev_into_inner)]
151157
#![feature(set_ptr_value)]
152158
#![feature(share_trait)]
@@ -236,6 +242,8 @@ pub mod collections;
236242
pub mod ffi;
237243
pub mod fmt;
238244
pub mod intrinsics;
245+
#[unstable(feature = "alloc_io", issue = "154046")]
246+
pub mod io;
239247
#[cfg(not(no_rc))]
240248
pub mod rc;
241249
pub mod slice;

library/core/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,8 @@ check-cfg = [
4040
'cfg(target_has_reliable_f128)',
4141
'cfg(target_has_reliable_f128_math)',
4242
'cfg(llvm_enzyme)',
43+
# Prevents use of a static variable for providing platform specific RawOsError
44+
# functionality
45+
'cfg(no_io_statics)',
4346

4447
]

0 commit comments

Comments
 (0)