Skip to content

Commit c538e5d

Browse files
committed
WIP: Smear std::io::Error across core and alloc
1 parent 7653e5e commit c538e5d

15 files changed

Lines changed: 1124 additions & 764 deletions

File tree

library/alloc/src/io/error.rs

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

library/alloc/src/io/mod.rs

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

library/alloc/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@
111111
#![feature(const_try)]
112112
#![feature(copied_into_inner)]
113113
#![feature(core_intrinsics)]
114+
#![feature(core_io)]
115+
#![feature(core_io_borrowed_buf)]
116+
#![feature(core_io_internals)]
114117
#![feature(deprecated_suggestion)]
115118
#![feature(deref_pure_trait)]
116119
#![feature(diagnostic_on_move)]
@@ -128,6 +131,8 @@
128131
#![feature(generic_atomic)]
129132
#![feature(hasher_prefixfree_extras)]
130133
#![feature(inplace_iteration)]
134+
#![feature(io_const_error)]
135+
#![feature(io_const_error_internals)]
131136
#![feature(iter_advance_by)]
132137
#![feature(iter_next_chunk)]
133138
#![feature(layout_for_ptr)]
@@ -141,6 +146,7 @@
141146
#![feature(ptr_alignment_type)]
142147
#![feature(ptr_internals)]
143148
#![feature(ptr_metadata)]
149+
#![feature(raw_os_error_ty)]
144150
#![feature(rev_into_inner)]
145151
#![feature(set_ptr_value)]
146152
#![feature(sized_type_properties)]
@@ -228,6 +234,8 @@ pub mod collections;
228234
pub mod ffi;
229235
pub mod fmt;
230236
pub mod intrinsics;
237+
#[unstable(feature = "alloc_io", issue = "154046")]
238+
pub mod io;
231239
#[cfg(not(no_rc))]
232240
pub mod rc;
233241
pub mod slice;

0 commit comments

Comments
 (0)