Skip to content

Commit 0f0a783

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

17 files changed

Lines changed: 1164 additions & 785 deletions

File tree

library/alloc/src/io/error.rs

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

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)