Auto merge of #78880 - CDirkx:not_supported, r=joshtriplett

Add `Unsupported` to `std::io::ErrorKind`

I noticed a significant portion of the uses of `ErrorKind::Other` in std is for unsupported operations.
The notion that a specific operation is not available on a target (and will thus never succeed) seems semantically distinct enough from just "an unspecified error occurred", which is why I am proposing to add the variant `Unsupported` to `std::io::ErrorKind`.

**Implementation**:

The following variant will be added to `std::io::ErrorKind`:

```rust
/// This operation is unsupported on this platform.
Unsupported
```
`std::io::ErrorKind::Unsupported` is an error returned when a given operation is not supported on a platform, and will thus never succeed; there is no way for the software to recover. It will be used instead of `Other` where appropriate, e.g. on wasm for file and network operations.

`decode_error_kind` will be updated  to decode operating system errors to `Unsupported`:
- Unix and VxWorks: `libc::ENOSYS`
- Windows: `c::ERROR_CALL_NOT_IMPLEMENTED`
- WASI: `wasi::ERRNO_NOSYS`

**Stability**:
This changes the kind of error returned by some functions on some platforms, which I think is not covered by the stability guarantees of the std? User code could depend on this behavior, expecting `ErrorKind::Other`, however the docs already mention:

> Errors that are `Other` now may move to a different or a new `ErrorKind` variant in the future. It is not recommended to match an error against `Other` and to expect any additional characteristics, e.g., a specific `Error::raw_os_error` return value.

The most recent variant added to `ErrorKind` was `UnexpectedEof` in `1.6.0` (almost 5 years ago), but `ErrorKind` is marked as `#[non_exhaustive]` and the docs warn about exhaustively matching on it, so adding a new variant per se should not be a breaking change.

The variant `Unsupported` itself could be marked as `#[unstable]`, however, because this PR also immediately uses this new variant and changes the errors returned by functions I'm inclined to agree with the others in this thread that the variant should be insta-stabilized.
This commit is contained in:
bors 2021-04-18 20:03:54 +00:00
commit 5a4ab26459
20 changed files with 81 additions and 56 deletions

View file

@ -1329,7 +1329,9 @@ fn metadata_access_times() {
match (a.created(), b.created()) {
(Ok(t1), Ok(t2)) => assert!(t1 <= t2),
(Err(e1), Err(e2))
if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other => {}
if e1.kind() == ErrorKind::Other && e2.kind() == ErrorKind::Other
|| e1.kind() == ErrorKind::Unsupported
&& e2.kind() == ErrorKind::Unsupported => {}
(a, b) => {
panic!("creation time must be always supported or not supported: {:?} {:?}", a, b,)
}

View file

@ -180,6 +180,12 @@ pub enum ErrorKind {
/// read.
#[stable(feature = "read_exact", since = "1.6.0")]
UnexpectedEof,
/// This operation is unsupported on this platform.
///
/// This means that the operation can never succeed.
#[stable(feature = "unsupported_error", since = "1.53.0")]
Unsupported,
}
impl ErrorKind {
@ -203,6 +209,7 @@ impl ErrorKind {
ErrorKind::Interrupted => "operation interrupted",
ErrorKind::Other => "other os error",
ErrorKind::UnexpectedEof => "unexpected end of file",
ErrorKind::Unsupported => "unsupported",
}
}
}

View file

@ -1,9 +1,10 @@
#![unstable(reason = "not public", issue = "none", feature = "fd")]
use crate::io::{self, ErrorKind, Read};
use crate::io::{self, Read};
use crate::mem;
use crate::sys::cvt;
use crate::sys::hermit::abi;
use crate::sys::unsupported;
use crate::sys_common::AsInner;
#[derive(Debug)]
@ -46,7 +47,7 @@ impl FileDesc {
self.duplicate_path(&[])
}
pub fn duplicate_path(&self, _path: &[u8]) -> io::Result<FileDesc> {
Err(io::Error::new_const(ErrorKind::Other, &"duplicate isn't supported"))
unsupported()
}
pub fn nonblocking(&self) -> io::Result<bool> {
@ -54,11 +55,11 @@ impl FileDesc {
}
pub fn set_cloexec(&self) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"cloexec isn't supported"))
unsupported()
}
pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"nonblocking isn't supported"))
unsupported()
}
}

View file

@ -56,7 +56,7 @@ pub fn unsupported<T>() -> crate::io::Result<T> {
pub fn unsupported_err() -> crate::io::Error {
crate::io::Error::new_const(
crate::io::ErrorKind::Other,
crate::io::ErrorKind::Unsupported,
&"operation not supported on HermitCore yet",
)
}

View file

@ -166,7 +166,7 @@ impl TcpStream {
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
Err(io::Error::new_const(ErrorKind::Other, &"socket_addr isn't supported"))
unsupported()
}
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
@ -199,7 +199,7 @@ impl TcpStream {
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
Err(io::Error::new_const(ErrorKind::Other, &"take_error isn't supported"))
unsupported()
}
pub fn set_nonblocking(&self, mode: bool) -> io::Result<()> {
@ -247,27 +247,27 @@ impl TcpListener {
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn ttl(&self) -> io::Result<u32> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn only_v6(&self) -> io::Result<bool> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
}
@ -281,127 +281,127 @@ pub struct UdpSocket(abi::Handle);
impl UdpSocket {
pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn socket_addr(&self) -> io::Result<SocketAddr> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn duplicate(&self) -> io::Result<UdpSocket> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn broadcast(&self) -> io::Result<bool> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn multicast_loop_v6(&self) -> io::Result<bool> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn set_ttl(&self, _: u32) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn ttl(&self) -> io::Result<u32> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn send(&self, _: &[u8]) -> io::Result<usize> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
Err(io::Error::new_const(ErrorKind::Other, &"not supported"))
unsupported()
}
}

View file

@ -50,7 +50,7 @@ pub fn unsupported<T>() -> crate::io::Result<T> {
}
pub fn unsupported_err() -> crate::io::Error {
crate::io::Error::new_const(ErrorKind::Other, &"operation not supported on SGX yet")
crate::io::Error::new_const(ErrorKind::Unsupported, &"operation not supported on SGX yet")
}
/// This function is used to implement various functions that doesn't exist,

View file

@ -366,7 +366,7 @@ impl FileAttr {
}
Err(io::Error::new_const(
io::ErrorKind::Other,
io::ErrorKind::Unsupported,
&"creation time is not available on this platform \
currently",
))

View file

@ -1,6 +1,9 @@
macro_rules! unimpl {
() => {
return Err(io::Error::new_const(io::ErrorKind::Other, &"No networking available on L4Re."));
return Err(io::Error::new_const(
io::ErrorKind::Unsupported,
&"No networking available on L4Re.",
));
};
}

View file

@ -148,6 +148,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
libc::EINVAL => ErrorKind::InvalidInput,
libc::ETIMEDOUT => ErrorKind::TimedOut,
libc::EEXIST => ErrorKind::AlreadyExists,
libc::ENOSYS => ErrorKind::Unsupported,
// These two constants can have the same value on some systems,
// but different values on others, so we can't use a match

View file

@ -447,7 +447,7 @@ pub fn current_exe() -> io::Result<PathBuf> {
#[cfg(any(target_os = "fuchsia", target_os = "l4re"))]
pub fn current_exe() -> io::Result<PathBuf> {
use crate::io::ErrorKind;
Err(io::Error::new_const(ErrorKind::Other, &"Not yet implemented!"))
Err(io::Error::new_const(ErrorKind::Unsupported, &"Not yet implemented!"))
}
#[cfg(target_os = "vxworks")]

View file

@ -18,7 +18,10 @@ pub fn unsupported<T>() -> std_io::Result<T> {
}
pub fn unsupported_err() -> std_io::Error {
std_io::Error::new_const(std_io::ErrorKind::Other, &"operation not supported on this platform")
std_io::Error::new_const(
std_io::ErrorKind::Unsupported,
&"operation not supported on this platform",
)
}
pub fn decode_error_kind(_code: i32) -> crate::io::ErrorKind {

View file

@ -80,11 +80,11 @@ pub fn getenv(_: &OsStr) -> io::Result<Option<OsString>> {
}
pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
Err(io::Error::new_const(io::ErrorKind::Other, &"cannot set env vars on this platform"))
Err(io::Error::new_const(io::ErrorKind::Unsupported, &"cannot set env vars on this platform"))
}
pub fn unsetenv(_: &OsStr) -> io::Result<()> {
Err(io::Error::new_const(io::ErrorKind::Other, &"cannot unset env vars on this platform"))
Err(io::Error::new_const(io::ErrorKind::Unsupported, &"cannot unset env vars on this platform"))
}
pub fn temp_dir() -> PathBuf {

View file

@ -83,6 +83,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
libc::EINVAL => ErrorKind::InvalidInput,
libc::ETIMEDOUT => ErrorKind::TimedOut,
libc::EEXIST => ErrorKind::AlreadyExists,
libc::ENOSYS => ErrorKind::Unsupported,
// These two constants can have the same value on some systems,
// but different values on others, so we can't use a match

View file

@ -78,6 +78,7 @@ pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind {
wasi::ERRNO_TIMEDOUT => TimedOut,
wasi::ERRNO_EXIST => AlreadyExists,
wasi::ERRNO_AGAIN => WouldBlock,
wasi::ERRNO_NOSYS => Unsupported,
_ => Other,
}
}

View file

@ -802,7 +802,10 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> {
#[cfg(target_vendor = "uwp")]
pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
return Err(io::Error::new_const(io::ErrorKind::Other, &"hard link are not supported on UWP"));
return Err(io::Error::new_const(
io::ErrorKind::Unsupported,
&"hard link are not supported on UWP",
));
}
pub fn stat(path: &Path) -> io::Result<FileAttr> {

View file

@ -78,6 +78,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
| c::ERROR_IPSEC_IKE_TIMED_OUT
| c::ERROR_RUNLEVEL_SWITCH_TIMEOUT
| c::ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT => return ErrorKind::TimedOut,
c::ERROR_CALL_NOT_IMPLEMENTED => return ErrorKind::Unsupported,
_ => {}
}

View file

@ -370,7 +370,7 @@ impl Socket {
#[cfg(target_vendor = "uwp")]
fn set_no_inherit(&self) -> io::Result<()> {
Err(io::Error::new_const(io::ErrorKind::Other, &"Unavailable on UWP"))
Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Unavailable on UWP"))
}
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {

View file

@ -77,7 +77,7 @@ fn main() {
let error_kind = ErrorKind::NotFound;
match error_kind {
ErrorKind::NotFound => {},
ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _ => {},
ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | _ => {},
}
match error_kind {
ErrorKind::NotFound => {},
@ -98,6 +98,7 @@ fn main() {
ErrorKind::Interrupted => {},
ErrorKind::Other => {},
ErrorKind::UnexpectedEof => {},
ErrorKind::Unsupported => {},
_ => {},
}
}

View file

@ -98,6 +98,7 @@ fn main() {
ErrorKind::Interrupted => {},
ErrorKind::Other => {},
ErrorKind::UnexpectedEof => {},
ErrorKind::Unsupported => {},
_ => {},
}
}

View file

@ -32,7 +32,7 @@ error: wildcard matches known variants and will also match future added variants
--> $DIR/wildcard_enum_match_arm.rs:80:9
|
LL | _ => {},
| ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _`
| ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | ErrorKind::Unsupported | _`
error: aborting due to 5 previous errors