From 47aaf79554744162601f6cdc5fabd6fd05acb2db Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 26 Jan 2022 15:58:45 -0800 Subject: [PATCH 01/20] Add documentation about `BorrowedFd::to_owned`. Following up on #88564, this adds documentation explaining why `BorrowedFd::to_owned` returns another `BorrowedFd` rather than an `OwnedFd`. And similar for `BorrowedHandle` and `BorrowedSocket`. --- library/std/src/os/fd/owned.rs | 4 ++++ library/std/src/os/windows/io/handle.rs | 4 ++++ library/std/src/os/windows/io/socket.rs | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 0b6588db92c83..6a2f804551df6 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -21,6 +21,10 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; /// descriptor, so it can be used in FFI in places where a file descriptor is /// passed as an argument, it is not captured or consumed, and it never has the /// value `-1`. +/// +/// This type's `.to_owned()` implementation returns another `BorrowedFd` +/// rather than an `OwnedFd`. It just makes a trivial copy of the raw file +/// descriptor, which is then borrowed under the same lifetime. #[derive(Copy, Clone)] #[repr(transparent)] #[rustc_layout_scalar_valid_range_start(0)] diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index e37ce633a129a..f7a67a54a1d7b 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -28,6 +28,10 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; /// And, it *may* have the value `NULL` (0), which can occur when consoles are /// detached from processes, or when `windows_subsystem` is used. /// +/// This type's `.to_owned()` implementation returns another `BorrowedHandle` +/// rather than an `OwnedHandle`. It just makes a trivial copy of the raw +/// handle, which is then borrowed under the same lifetime. +/// /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 #[derive(Copy, Clone)] #[repr(transparent)] diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 26b569bcdd362..a7c8eef93d891 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -21,6 +21,10 @@ use crate::sys::cvt; /// so it can be used in FFI in places where a socket is passed as an argument, /// it is not captured or consumed, and it never has the value /// `INVALID_SOCKET`. +/// +/// This type's `.to_owned()` implementation returns another `BorrowedSocket` +/// rather than an `OwnedSocket`. It just makes a trivial copy of the raw +/// socket, which is then borrowed under the same lifetime. #[derive(Copy, Clone)] #[repr(transparent)] #[rustc_layout_scalar_valid_range_start(0)] From ca42a1bece0494a86b80713c6d0f8dbc3db667fa Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 1 Feb 2022 13:46:05 -0800 Subject: [PATCH 02/20] Update the documentation for `{As,Into,From}Raw{Fd,Handle,Socket}`. This change weakens the descriptions of the `{as,into,from}_raw_{fd,handle,socket}` descriptions from saying that they *do* express ownership relations to say that they are *typically used* in ways that express ownership relations. This needed needed since, for example, std's own [`RawFd`] implements `{As,From,Into}Fd` without any of the ownership relationships. This adds proper `# Safety` comments to `from_raw_{fd,handle,socket}`, adds the requirement that raw handles be not opened with the `FILE_FLAG_OVERLAPPED` flag, and merges the `OwnedHandle::from_raw_handle` comment into the main `FromRawHandle::from_raw_handle` comment. And, this changes `HandleOrNull` and `HandleOrInvalid` to not implement `FromRawHandle`, since they are intended for limited use in FFI situations, and not for generic use, and they have constraints that are stronger than the those of `FromRawHandle`. [`RawFd`]: https://doc.rust-lang.org/stable/std/os/unix/io/type.RawFd.html --- library/std/src/os/fd/raw.rs | 38 ++++++---- library/std/src/os/windows/io/handle.rs | 34 +++------ library/std/src/os/windows/io/raw.rs | 92 ++++++++++++++++++------- 3 files changed, 101 insertions(+), 63 deletions(-) diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index f874cf0b42d74..6925269c8f179 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -24,9 +24,14 @@ pub type RawFd = raw::c_int; pub trait AsRawFd { /// Extracts the raw file descriptor. /// - /// This method does **not** pass ownership of the raw file descriptor - /// to the caller. The descriptor is only guaranteed to be valid while - /// the original object has not yet been destroyed. + /// This function is typically used to **borrow** an owned file descriptor. + /// When used in this way, this method does **not** pass ownership of the + /// raw file descriptor to the caller, and the file descriptor is only + /// guaranteed to be valid while the original object has not yet been + /// destroyed. + /// + /// However, borrowing is not strictly required. See [`AsFd::as_fd`] + /// for an API which strictly borrows a handle. /// /// # Example /// @@ -55,15 +60,17 @@ pub trait FromRawFd { /// Constructs a new instance of `Self` from the given raw file /// descriptor. /// - /// This function **consumes ownership** of the specified file - /// descriptor. The returned object will take responsibility for closing - /// it when the object goes out of scope. + /// This function is typically used to **consume ownership** of the + /// specified file descriptor. When used in this way, the returned object + /// will take responsibility for closing it when the object goes out of + /// scope. + /// + /// However, consuming ownership is not strictly required. See + /// [`FromFd::from_fd`] for an API which strictly consumes ownership. /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. + /// # Safety + /// + /// The `fd` passed in must be a valid an open file descriptor. /// /// # Example /// @@ -94,9 +101,12 @@ pub trait FromRawFd { pub trait IntoRawFd { /// Consumes this object, returning the raw underlying file descriptor. /// - /// This function **transfers ownership** of the underlying file descriptor - /// to the caller. Callers are then the unique owners of the file descriptor - /// and must close the descriptor once it's no longer needed. + /// This function is typically used to **transfer ownership** of the underlying + /// file descriptor to the caller. When used in this way, callers are then the unique + /// owners of the file descriptor and must close it once it's no longer needed. + /// + /// However, transferring ownership is not strictly required. See + /// [`IntoFd::into_fd`] for an API which strictly transfers ownership. /// /// # Example /// diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index e37ce633a129a..b9951a4249fae 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -210,29 +210,13 @@ impl IntoRawHandle for OwnedHandle { } impl FromRawHandle for OwnedHandle { - /// Constructs a new instance of `Self` from the given raw handle. - /// - /// # Safety - /// - /// The resource pointed to by `handle` must be open and suitable for - /// assuming ownership. The resource must not require any cleanup other - /// than `CloseHandle`. - /// - /// In particular, it must not be used with handles to open registry - /// keys which need to be closed with [`RegCloseKey`] instead. - /// - /// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is - /// sometimes a valid handle value. See [here] for the full story. - /// - /// [`RegCloseKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey - /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 #[inline] unsafe fn from_raw_handle(handle: RawHandle) -> Self { Self { handle } } } -impl FromRawHandle for HandleOrNull { +impl HandleOrNull { /// Constructs a new instance of `Self` from the given `RawHandle` returned /// from a Windows API that uses null to indicate failure, such as /// `CreateThread`. @@ -242,9 +226,9 @@ impl FromRawHandle for HandleOrNull { /// /// # Safety /// - /// The resource pointed to by `handle` must be either open and otherwise - /// unowned, or null. Note that not all Windows APIs use null for errors; - /// see [here] for the full story. + /// The passed `handle` value must either satisfy the safety requirements + /// of [`FromRawHandle::from_raw_handle`], or be null. Note that not all + /// Windows APIs use null for errors; see [here] for the full story. /// /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 #[inline] @@ -253,7 +237,7 @@ impl FromRawHandle for HandleOrNull { } } -impl FromRawHandle for HandleOrInvalid { +impl HandleOrInvalid { /// Constructs a new instance of `Self` from the given `RawHandle` returned /// from a Windows API that uses `INVALID_HANDLE_VALUE` to indicate /// failure, such as `CreateFileW`. @@ -263,10 +247,10 @@ impl FromRawHandle for HandleOrInvalid { /// /// # Safety /// - /// The resource pointed to by `handle` must be either open and otherwise - /// unowned, null, or equal to `INVALID_HANDLE_VALUE` (-1). Note that not - /// all Windows APIs use `INVALID_HANDLE_VALUE` for errors; see [here] for - /// the full story. + /// The passed `handle` value must either satisfy the safety requirements + /// of [`FromRawHandle::from_raw_handle`], or be + /// `INVALID_HANDLE_VALUE` (-1). Note that not all Windows APIs use + /// `INVALID_HANDLE_VALUE` for errors; see [here] for the full story. /// /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 #[inline] diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index c7f122048a198..580ebfe92d75d 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -22,7 +22,15 @@ pub type RawSocket = raw::SOCKET; /// Extracts raw handles. #[stable(feature = "rust1", since = "1.0.0")] pub trait AsRawHandle { - /// Extracts the raw handle, without taking any ownership. + /// Extracts the raw handle. + /// + /// This function is typically used to **borrow** an owned handle. + /// When used in this way, this method does **not** pass ownership of the + /// raw handle to the caller, and the handle is only guaranteed + /// to be valid while the original object has not yet been destroyed. + /// + /// However, borrowing is not strictly required. See [`AsHandle::as_handle`] + /// for an API which strictly borrows a handle. #[stable(feature = "rust1", since = "1.0.0")] fn as_raw_handle(&self) -> RawHandle; } @@ -32,15 +40,29 @@ pub trait AsRawHandle { pub trait FromRawHandle { /// Constructs a new I/O object from the specified raw handle. /// - /// This function will **consume ownership** of the handle given, - /// passing responsibility for closing the handle to the returned - /// object. + /// This function is typically used to **consume ownership** of the handle + /// given, passing responsibility for closing the handle to the returned + /// object. When used in this way, the returned object + /// will take responsibility for closing it when the object goes out of + /// scope. + /// + /// However, consuming ownership is not strictly required. See + /// [`FromHandle::from_handle`] for an API which strictly consumes ownership. + /// + /// # Safety /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. + /// The `handle` passed in must: + /// - be a valid an open handle, + /// - be a handle opened for synchronous I/O, *without* the + /// `FILE_FLAG_OVERLAPPED` flag, and + /// - be a handle for a resource that may be freed via [`CloseHandle`] + /// (as opposed to `RegCloseKey` or other close functions). + /// + /// Note that the handle *may* have the value `INVALID_HANDLE_VALUE` (-1), + /// which is sometimes a valid handle value. See [here] for the full story. + /// + /// [`CloseHandle`]: https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle + /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 #[stable(feature = "from_raw_os", since = "1.1.0")] unsafe fn from_raw_handle(handle: RawHandle) -> Self; } @@ -51,9 +73,12 @@ pub trait FromRawHandle { pub trait IntoRawHandle { /// Consumes this object, returning the raw underlying handle. /// - /// This function **transfers ownership** of the underlying handle to the - /// caller. Callers are then the unique owners of the handle and must close - /// it once it's no longer needed. + /// This function is typically used to **transfer ownership** of the underlying + /// handle to the caller. When used in this way, callers are then the unique + /// owners of the handle and must close it once it's no longer needed. + /// + /// However, transferring ownership is not strictly required. See + /// [`IntoHandle::into_handle`] for an API which strictly transfers ownership. #[stable(feature = "into_raw_os", since = "1.4.0")] fn into_raw_handle(self) -> RawHandle; } @@ -130,7 +155,15 @@ impl IntoRawHandle for fs::File { /// Extracts raw sockets. #[stable(feature = "rust1", since = "1.0.0")] pub trait AsRawSocket { - /// Extracts the underlying raw socket from this object. + /// Extracts the raw socket. + /// + /// This function is typically used to **borrow** an owned socket. + /// When used in this way, this method does **not** pass ownership of the + /// raw socket to the caller, and the socket is only guaranteed + /// to be valid while the original object has not yet been destroyed. + /// + /// However, borrowing is not strictly required. See [`AsSocket::as_socket`] + /// for an API which strictly borrows a socket. #[stable(feature = "rust1", since = "1.0.0")] fn as_raw_socket(&self) -> RawSocket; } @@ -138,16 +171,24 @@ pub trait AsRawSocket { /// Creates I/O objects from raw sockets. #[stable(feature = "from_raw_os", since = "1.1.0")] pub trait FromRawSocket { - /// Creates a new I/O object from the given raw socket. + /// Constructs a new I/O object from the specified raw socket. + /// + /// This function is typically used to **consume ownership** of the socket + /// given, passing responsibility for closing the socket to the returned + /// object. When used in this way, the returned object + /// will take responsibility for closing it when the object goes out of + /// scope. /// - /// This function will **consume ownership** of the socket provided and - /// it will be closed when the returned object goes out of scope. + /// However, consuming ownership is not strictly required. See + /// [`FromSocket::from_socket`] for an API which strictly consumes ownership. /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. + /// # Safety + /// + /// The `socket` passed in must: + /// - be a valid an open socket, + /// - be a handle for a resource that may be freed via [`closesocket`]. + /// + /// [`closesocket`]: https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-closesocket #[stable(feature = "from_raw_os", since = "1.1.0")] unsafe fn from_raw_socket(sock: RawSocket) -> Self; } @@ -158,9 +199,12 @@ pub trait FromRawSocket { pub trait IntoRawSocket { /// Consumes this object, returning the raw underlying socket. /// - /// This function **transfers ownership** of the underlying socket to the - /// caller. Callers are then the unique owners of the socket and must close - /// it once it's no longer needed. + /// This function is typically used to **transfer ownership** of the underlying + /// socket to the caller. When used in this way, callers are then the unique + /// owners of the socket and must close it once it's no longer needed. + /// + /// However, transferring ownership is not strictly required. See + /// [`IntoSocket::into_socket`] for an API which strictly transfers ownership. #[stable(feature = "into_raw_os", since = "1.4.0")] fn into_raw_socket(self) -> RawSocket; } From 713bb19ca3a838356324a447ad46d88e5ff002a2 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 1 Feb 2022 14:23:03 -0800 Subject: [PATCH 03/20] Add missing `pub` keywords. --- library/std/src/os/windows/io/handle.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index b9951a4249fae..117ba0b4ec608 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -232,7 +232,7 @@ impl HandleOrNull { /// /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 #[inline] - unsafe fn from_raw_handle(handle: RawHandle) -> Self { + pub unsafe fn from_raw_handle(handle: RawHandle) -> Self { Self(OwnedHandle::from_raw_handle(handle)) } } @@ -254,7 +254,7 @@ impl HandleOrInvalid { /// /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 #[inline] - unsafe fn from_raw_handle(handle: RawHandle) -> Self { + pub unsafe fn from_raw_handle(handle: RawHandle) -> Self { Self(OwnedHandle::from_raw_handle(handle)) } } From 8516895170a12710dbe68ed3ad4bf8273f7f375a Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 1 Feb 2022 14:27:54 -0800 Subject: [PATCH 04/20] Fix two copy+pastos. --- library/std/src/os/fd/raw.rs | 2 +- library/std/src/os/windows/io/raw.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 6925269c8f179..4af1c9bd8d750 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -31,7 +31,7 @@ pub trait AsRawFd { /// destroyed. /// /// However, borrowing is not strictly required. See [`AsFd::as_fd`] - /// for an API which strictly borrows a handle. + /// for an API which strictly borrows a file descriptor. /// /// # Example /// diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 580ebfe92d75d..240d14236e5c2 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -186,7 +186,7 @@ pub trait FromRawSocket { /// /// The `socket` passed in must: /// - be a valid an open socket, - /// - be a handle for a resource that may be freed via [`closesocket`]. + /// - be a socket that may be freed via [`closesocket`]. /// /// [`closesocket`]: https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-closesocket #[stable(feature = "from_raw_os", since = "1.1.0")] From 6ef7ee36c2103113d572a1ff6e8de68809639649 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 1 Feb 2022 14:37:15 -0800 Subject: [PATCH 05/20] Fix unresolved doc links. --- library/std/src/os/fd/raw.rs | 4 ++++ library/std/src/os/windows/io/raw.rs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 4af1c9bd8d750..192ada4a2552e 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -7,6 +7,10 @@ use crate::io; use crate::os::raw; #[cfg(unix)] use crate::os::unix::io::OwnedFd; +#[all(cfg(doc), unix)] +use crate::os::unix::io::{AsFd, FromFd, IntoFd}; +#[all(cfg(doc), target_os = "wasi")] +use crate::os::unix::io::{AsFd, FromFd, IntoFd}; #[cfg(target_os = "wasi")] use crate::os::wasi::io::OwnedFd; use crate::sys_common::{AsInner, IntoInner}; diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 240d14236e5c2..6ecffcb8a173e 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -5,6 +5,8 @@ use crate::fs; use crate::io; use crate::net; +#[all(cfg(doc), unix)] +use crate::os::windows::io::{AsHandle, AsSocket, FromHandle, FromSocket, IntoHandle, IntoSocket}; use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; use crate::sys; From 89544e900125aed4899706c78e8bbce5cb5f1023 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 1 Feb 2022 14:38:23 -0800 Subject: [PATCH 06/20] Fix errors. --- library/std/src/os/fd/raw.rs | 4 ++-- library/std/src/os/windows/io/raw.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 192ada4a2552e..bc3007da86a32 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -7,9 +7,9 @@ use crate::io; use crate::os::raw; #[cfg(unix)] use crate::os::unix::io::OwnedFd; -#[all(cfg(doc), unix)] +#[cfg(all(doc, unix))] use crate::os::unix::io::{AsFd, FromFd, IntoFd}; -#[all(cfg(doc), target_os = "wasi")] +#[cfg(all(doc, target_os = "wasi"))] use crate::os::unix::io::{AsFd, FromFd, IntoFd}; #[cfg(target_os = "wasi")] use crate::os::wasi::io::OwnedFd; diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 6ecffcb8a173e..285802c6a9ec4 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -5,7 +5,7 @@ use crate::fs; use crate::io; use crate::net; -#[all(cfg(doc), unix)] +#[cfg(doc)] use crate::os::windows::io::{AsHandle, AsSocket, FromHandle, FromSocket, IntoHandle, IntoSocket}; use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; From 656d2a3a12746d624b8ee9dada8a70d708e1526c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 1 Feb 2022 14:57:31 -0800 Subject: [PATCH 07/20] Use `From`/`Into` rather than the traits they replaced. --- library/std/src/os/fd/raw.rs | 14 ++++++++------ library/std/src/os/windows/io/raw.rs | 22 +++++++++++++--------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index bc3007da86a32..6feea737f3b5f 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -8,9 +8,9 @@ use crate::os::raw; #[cfg(unix)] use crate::os::unix::io::OwnedFd; #[cfg(all(doc, unix))] -use crate::os::unix::io::{AsFd, FromFd, IntoFd}; +use crate::os::unix::io::AsFd; #[cfg(all(doc, target_os = "wasi"))] -use crate::os::unix::io::{AsFd, FromFd, IntoFd}; +use crate::os::unix::io::AsFd; #[cfg(target_os = "wasi")] use crate::os::wasi::io::OwnedFd; use crate::sys_common::{AsInner, IntoInner}; @@ -69,8 +69,9 @@ pub trait FromRawFd { /// will take responsibility for closing it when the object goes out of /// scope. /// - /// However, consuming ownership is not strictly required. See - /// [`FromFd::from_fd`] for an API which strictly consumes ownership. + /// However, consuming ownership is not strictly required. Use a + /// [`From::from`] implementation for an API which strictly + /// consumes ownership. /// /// # Safety /// @@ -109,8 +110,9 @@ pub trait IntoRawFd { /// file descriptor to the caller. When used in this way, callers are then the unique /// owners of the file descriptor and must close it once it's no longer needed. /// - /// However, transferring ownership is not strictly required. See - /// [`IntoFd::into_fd`] for an API which strictly transfers ownership. + /// However, transferring ownership is not strictly required. Use a + /// [`Into::into`] implementation for an API which strictly + /// transfers ownership. /// /// # Example /// diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 285802c6a9ec4..dc84a38156ab8 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -6,7 +6,7 @@ use crate::fs; use crate::io; use crate::net; #[cfg(doc)] -use crate::os::windows::io::{AsHandle, AsSocket, FromHandle, FromSocket, IntoHandle, IntoSocket}; +use crate::os::windows::io::{AsHandle, AsSocket}; use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; use crate::sys; @@ -48,8 +48,9 @@ pub trait FromRawHandle { /// will take responsibility for closing it when the object goes out of /// scope. /// - /// However, consuming ownership is not strictly required. See - /// [`FromHandle::from_handle`] for an API which strictly consumes ownership. + /// However, consuming ownership is not strictly required. Use a + /// `From::from` implementation for an API which strictly + /// consumes ownership. /// /// # Safety /// @@ -79,8 +80,9 @@ pub trait IntoRawHandle { /// handle to the caller. When used in this way, callers are then the unique /// owners of the handle and must close it once it's no longer needed. /// - /// However, transferring ownership is not strictly required. See - /// [`IntoHandle::into_handle`] for an API which strictly transfers ownership. + /// However, transferring ownership is not strictly required. Use a + /// `Into::into` implementation for an API which strictly + /// transfers ownership. #[stable(feature = "into_raw_os", since = "1.4.0")] fn into_raw_handle(self) -> RawHandle; } @@ -181,8 +183,9 @@ pub trait FromRawSocket { /// will take responsibility for closing it when the object goes out of /// scope. /// - /// However, consuming ownership is not strictly required. See - /// [`FromSocket::from_socket`] for an API which strictly consumes ownership. + /// However, consuming ownership is not strictly required. Use a + /// `From::from` implementation for an API which strictly + /// consumes ownership. /// /// # Safety /// @@ -205,8 +208,9 @@ pub trait IntoRawSocket { /// socket to the caller. When used in this way, callers are then the unique /// owners of the socket and must close it once it's no longer needed. /// - /// However, transferring ownership is not strictly required. See - /// [`IntoSocket::into_socket`] for an API which strictly transfers ownership. + /// However, transferring ownership is not strictly required. Use a + /// `Into::into` implementation for an API which strictly + /// transfers ownership. #[stable(feature = "into_raw_os", since = "1.4.0")] fn into_raw_socket(self) -> RawSocket; } From f88fb2a9a5d5f3acf5ff284c411022e14c28207b Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 1 Feb 2022 15:10:59 -0800 Subject: [PATCH 08/20] x.py fmt --- library/std/src/os/fd/raw.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 6feea737f3b5f..35b8b95037292 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -5,12 +5,12 @@ use crate::fs; use crate::io; use crate::os::raw; -#[cfg(unix)] -use crate::os::unix::io::OwnedFd; #[cfg(all(doc, unix))] use crate::os::unix::io::AsFd; #[cfg(all(doc, target_os = "wasi"))] use crate::os::unix::io::AsFd; +#[cfg(unix)] +use crate::os::unix::io::OwnedFd; #[cfg(target_os = "wasi")] use crate::os::wasi::io::OwnedFd; use crate::sys_common::{AsInner, IntoInner}; From ba6050f7421bb6a1f00b6633257ce60cd25089b7 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 2 Feb 2022 16:23:23 -0800 Subject: [PATCH 09/20] Remove the documentation comment for `OwnedSocket::from_raw_socket`. This function is documented in more detail in the `FromRawSocket` trait. --- library/std/src/os/windows/io/socket.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 26b569bcdd362..c1bdef29f5318 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -168,13 +168,6 @@ impl IntoRawSocket for OwnedSocket { } impl FromRawSocket for OwnedSocket { - /// Constructs a new instance of `Self` from the given raw socket. - /// - /// # Safety - /// - /// The resource pointed to by `socket` must be open and suitable for - /// assuming ownership. The resource must not require cleanup other than - /// `closesocket`. #[inline] unsafe fn from_raw_socket(socket: RawSocket) -> Self { debug_assert_ne!(socket, c::INVALID_SOCKET as RawSocket); From 4c4e43035f8b8bcc171e4a359bb5b42108eadf41 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 4 Feb 2022 13:33:06 -0800 Subject: [PATCH 10/20] Rename `BorrowedFd::borrow_raw_fd` to `BorrowedFd::borrow_raw`. Also, rename `BorrowedHandle::borrow_raw_handle` and `BorrowedSocket::borrow_raw_socket` to `BorrowedHandle::borrow_raw` and `BorrowedSocket::borrow_raw`. This is just a minor rename to reduce redundancy in the user code calling these functions, and to eliminate an inessential difference between `BorrowedFd` code and `BorrowedHandle`/`BorrowedSocket` code. While here, add a simple test exercising `BorrowedFd::borrow_raw_fd`. --- library/std/src/os/fd/mod.rs | 3 +++ library/std/src/os/fd/owned.rs | 4 +-- library/std/src/os/fd/tests.rs | 34 +++++++++++++++++++++++++ library/std/src/os/windows/io/handle.rs | 24 ++++++++--------- library/std/src/os/windows/io/socket.rs | 10 ++++---- library/std/src/sys/unix/stdio.rs | 12 ++++----- 6 files changed, 62 insertions(+), 25 deletions(-) create mode 100644 library/std/src/os/fd/tests.rs diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs index df11dc21aa7a6..13bb079194fbe 100644 --- a/library/std/src/os/fd/mod.rs +++ b/library/std/src/os/fd/mod.rs @@ -11,3 +11,6 @@ pub mod owned; // Implementations for `AsRawFd` etc. for network types. mod net; + +#[cfg(test)] +mod tests; diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 0b6588db92c83..1b3739d33f395 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -62,7 +62,7 @@ impl BorrowedFd<'_> { /// the returned `BorrowedFd`, and it must not have the value `-1`. #[inline] #[unstable(feature = "io_safety", issue = "87074")] - pub unsafe fn borrow_raw_fd(fd: RawFd) -> Self { + pub unsafe fn borrow_raw(fd: RawFd) -> Self { assert_ne!(fd, u32::MAX as RawFd); // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) unsafe { Self { fd, _phantom: PhantomData } } @@ -215,7 +215,7 @@ impl AsFd for OwnedFd { // Safety: `OwnedFd` and `BorrowedFd` have the same validity // invariants, and the `BorrowdFd` is bounded by the lifetime // of `&self`. - unsafe { BorrowedFd::borrow_raw_fd(self.as_raw_fd()) } + unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) } } } diff --git a/library/std/src/os/fd/tests.rs b/library/std/src/os/fd/tests.rs new file mode 100644 index 0000000000000..be920a2b38c65 --- /dev/null +++ b/library/std/src/os/fd/tests.rs @@ -0,0 +1,34 @@ +#[cfg(any(unix, target_os = "wasi"))] +#[test] +fn test_raw_fd() { + #[cfg(unix)] + use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd, BorrowedFd}; + #[cfg(target_os = "wasi")] + use crate::os::wasi::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd, BorrowedFd}; + + let raw_fd: RawFd = crate::io::stdin().as_raw_fd(); + + let stdin_as_file = unsafe { crate::fs::File::from_raw_fd(raw_fd) }; + assert_eq!(stdin_as_file.as_raw_fd(), raw_fd); + assert_eq!(unsafe { BorrowedFd::borrow_raw(raw_fd).as_raw_fd() }, raw_fd); + assert_eq!(stdin_as_file.into_raw_fd(), 0); +} + +#[cfg(any(unix, target_os = "wasi"))] +#[test] +fn test_fd() { + #[cfg(unix)] + use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd, FromRawFd, IntoRawFd, RawFd, AsRawFd}; + #[cfg(target_os = "wasi")] + use crate::os::wasi::io::{AsFd, BorrowedFd, OwnedFd, FromRawFd, IntoRawFd, RawFd, AsRawFd}; + + let stdin = crate::io::stdin(); + let fd: BorrowedFd<'_> = stdin.as_fd(); + let raw_fd: RawFd = fd.as_raw_fd(); + let owned_fd: OwnedFd = unsafe { OwnedFd::from_raw_fd(raw_fd) }; + + let stdin_as_file = crate::fs::File::from(owned_fd); + + assert_eq!(stdin_as_file.as_fd().as_raw_fd(), raw_fd); + assert_eq!(Into::::into(stdin_as_file).into_raw_fd(), raw_fd); +} diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index e37ce633a129a..5c224f118b6ea 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -131,7 +131,7 @@ impl BorrowedHandle<'_> { /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 #[inline] #[unstable(feature = "io_safety", issue = "87074")] - pub unsafe fn borrow_raw_handle(handle: RawHandle) -> Self { + pub unsafe fn borrow_raw(handle: RawHandle) -> Self { Self { handle, _phantom: PhantomData } } } @@ -329,7 +329,7 @@ impl AsHandle for OwnedHandle { // Safety: `OwnedHandle` and `BorrowedHandle` have the same validity // invariants, and the `BorrowdHandle` is bounded by the lifetime // of `&self`. - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } @@ -357,49 +357,49 @@ impl From for fs::File { impl AsHandle for crate::io::Stdin { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } impl<'a> AsHandle for crate::io::StdinLock<'a> { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } impl AsHandle for crate::io::Stdout { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } impl<'a> AsHandle for crate::io::StdoutLock<'a> { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } impl AsHandle for crate::io::Stderr { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } impl<'a> AsHandle for crate::io::StderrLock<'a> { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } impl AsHandle for crate::process::ChildStdin { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } @@ -413,7 +413,7 @@ impl From for OwnedHandle { impl AsHandle for crate::process::ChildStdout { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } @@ -427,7 +427,7 @@ impl From for OwnedHandle { impl AsHandle for crate::process::ChildStderr { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } @@ -441,7 +441,7 @@ impl From for OwnedHandle { impl AsHandle for crate::thread::JoinHandle { #[inline] fn as_handle(&self) -> BorrowedHandle<'_> { - unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs index 26b569bcdd362..6ed44b4b11e61 100644 --- a/library/std/src/os/windows/io/socket.rs +++ b/library/std/src/os/windows/io/socket.rs @@ -67,7 +67,7 @@ impl BorrowedSocket<'_> { /// `INVALID_SOCKET`. #[inline] #[unstable(feature = "io_safety", issue = "87074")] - pub unsafe fn borrow_raw_socket(socket: RawSocket) -> Self { + pub unsafe fn borrow_raw(socket: RawSocket) -> Self { debug_assert_ne!(socket, c::INVALID_SOCKET as RawSocket); Self { socket, _phantom: PhantomData } } @@ -223,14 +223,14 @@ impl AsSocket for OwnedSocket { // Safety: `OwnedSocket` and `BorrowedSocket` have the same validity // invariants, and the `BorrowdSocket` is bounded by the lifetime // of `&self`. - unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) } + unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } } } impl AsSocket for crate::net::TcpStream { #[inline] fn as_socket(&self) -> BorrowedSocket<'_> { - unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) } + unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } } } @@ -251,7 +251,7 @@ impl From for crate::net::TcpStream { impl AsSocket for crate::net::TcpListener { #[inline] fn as_socket(&self) -> BorrowedSocket<'_> { - unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) } + unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } } } @@ -272,7 +272,7 @@ impl From for crate::net::TcpListener { impl AsSocket for crate::net::UdpSocket { #[inline] fn as_socket(&self) -> BorrowedSocket<'_> { - unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) } + unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } } } diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs index b359987595d30..e4d83ba0ffd13 100644 --- a/library/std/src/sys/unix/stdio.rs +++ b/library/std/src/sys/unix/stdio.rs @@ -96,7 +96,7 @@ pub fn panic_output() -> Option { impl AsFd for io::Stdin { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw_fd(libc::STDIN_FILENO) } + unsafe { BorrowedFd::borrow_raw(libc::STDIN_FILENO) } } } @@ -104,7 +104,7 @@ impl AsFd for io::Stdin { impl<'a> AsFd for io::StdinLock<'a> { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw_fd(libc::STDIN_FILENO) } + unsafe { BorrowedFd::borrow_raw(libc::STDIN_FILENO) } } } @@ -112,7 +112,7 @@ impl<'a> AsFd for io::StdinLock<'a> { impl AsFd for io::Stdout { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw_fd(libc::STDOUT_FILENO) } + unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) } } } @@ -120,7 +120,7 @@ impl AsFd for io::Stdout { impl<'a> AsFd for io::StdoutLock<'a> { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw_fd(libc::STDOUT_FILENO) } + unsafe { BorrowedFd::borrow_raw(libc::STDOUT_FILENO) } } } @@ -128,7 +128,7 @@ impl<'a> AsFd for io::StdoutLock<'a> { impl AsFd for io::Stderr { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw_fd(libc::STDERR_FILENO) } + unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) } } } @@ -136,6 +136,6 @@ impl AsFd for io::Stderr { impl<'a> AsFd for io::StderrLock<'a> { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { - unsafe { BorrowedFd::borrow_raw_fd(libc::STDERR_FILENO) } + unsafe { BorrowedFd::borrow_raw(libc::STDERR_FILENO) } } } From 7d603dc1b2330713a8739b933ec4fc564ee1127c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 4 Feb 2022 13:53:58 -0800 Subject: [PATCH 11/20] x.py fmt --- library/std/src/os/fd/tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/std/src/os/fd/tests.rs b/library/std/src/os/fd/tests.rs index be920a2b38c65..26ef93e3d7110 100644 --- a/library/std/src/os/fd/tests.rs +++ b/library/std/src/os/fd/tests.rs @@ -2,9 +2,9 @@ #[test] fn test_raw_fd() { #[cfg(unix)] - use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd, BorrowedFd}; + use crate::os::unix::io::{AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; #[cfg(target_os = "wasi")] - use crate::os::wasi::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd, BorrowedFd}; + use crate::os::wasi::io::{AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; let raw_fd: RawFd = crate::io::stdin().as_raw_fd(); @@ -18,9 +18,9 @@ fn test_raw_fd() { #[test] fn test_fd() { #[cfg(unix)] - use crate::os::unix::io::{AsFd, BorrowedFd, OwnedFd, FromRawFd, IntoRawFd, RawFd, AsRawFd}; + use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; #[cfg(target_os = "wasi")] - use crate::os::wasi::io::{AsFd, BorrowedFd, OwnedFd, FromRawFd, IntoRawFd, RawFd, AsRawFd}; + use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; let stdin = crate::io::stdin(); let fd: BorrowedFd<'_> = stdin.as_fd(); From 4c7fb9efb7804e489bcf4fe1d3cdf0f7df3b0eff Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 25 Feb 2022 19:01:44 +0300 Subject: [PATCH 12/20] Add helper function to suggest multiple constraints Add `rustc_middle::ty::suggest_constraining_type_params` that suggests adding multiple constraints. `suggest_constraining_type_param` now just forwards params to this new function. --- compiler/rustc_middle/src/lib.rs | 1 + compiler/rustc_middle/src/ty/diagnostics.rs | 398 ++++++++++++-------- 2 files changed, 236 insertions(+), 163 deletions(-) diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 7ca564f29e659..f977b0fffebb6 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -56,6 +56,7 @@ #![feature(nonzero_ops)] #![feature(unwrap_infallible)] #![feature(decl_macro)] +#![feature(drain_filter)] #![recursion_limit = "512"] #![allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 58cf9fa7a8943..99a3d4c7fe4f7 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -7,6 +7,7 @@ use crate::ty::{ ProjectionTy, Term, Ty, TyCtxt, TypeAndMut, }; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -157,9 +158,17 @@ pub fn suggest_arbitrary_trait_bound( true } +#[derive(Debug)] +enum SuggestChangingConstraintsMessage<'a> { + RestrictBoundFurther, + RestrictType { ty: &'a str }, + RestrictTypeFurther { ty: &'a str }, + RemovingQSized, +} + fn suggest_removing_unsized_bound( generics: &hir::Generics<'_>, - err: &mut Diagnostic, + suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>, param_name: &str, param: &hir::GenericParam<'_>, def_id: Option, @@ -221,13 +230,12 @@ fn suggest_removing_unsized_bound( // ^^^^^^^^^ (_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()), }; - err.span_suggestion_verbose( + + suggestions.push(( sp, - "consider removing the `?Sized` bound to make the \ - type parameter `Sized`", String::new(), - Applicability::MaybeIncorrect, - ); + SuggestChangingConstraintsMessage::RemovingQSized, + )); } } _ => {} @@ -249,13 +257,12 @@ fn suggest_removing_unsized_bound( // ^^^^^^^^^ (_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()), }; - err.span_suggestion_verbose( + + suggestions.push(( sp, - "consider removing the `?Sized` bound to make the type parameter \ - `Sized`", String::new(), - Applicability::MaybeIncorrect, - ); + SuggestChangingConstraintsMessage::RemovingQSized, + )); } _ => {} } @@ -271,184 +278,249 @@ pub fn suggest_constraining_type_param( constraint: &str, def_id: Option, ) -> bool { - let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); + suggest_constraining_type_params( + tcx, + generics, + err, + [(param_name, constraint, def_id)].into_iter(), + ) +} - let Some(param) = param else { - return false; - }; +/// Suggest restricting a type param with a new bound. +pub fn suggest_constraining_type_params<'a>( + tcx: TyCtxt<'_>, + generics: &hir::Generics<'_>, + err: &mut Diagnostic, + param_names_and_constraints: impl Iterator)>, +) -> bool { + let mut grouped = FxHashMap::default(); + param_names_and_constraints.for_each(|(param_name, constraint, def_id)| { + grouped.entry(param_name).or_insert(Vec::new()).push((constraint, def_id)) + }); - const MSG_RESTRICT_BOUND_FURTHER: &str = "consider further restricting this bound"; - let msg_restrict_type = format!("consider restricting type parameter `{}`", param_name); - let msg_restrict_type_further = - format!("consider further restricting type parameter `{}`", param_name); + let mut applicability = Applicability::MachineApplicable; + let mut suggestions = Vec::new(); - if def_id == tcx.lang_items().sized_trait() { - // Type parameters are already `Sized` by default. - err.span_label(param.span, &format!("this type parameter needs to be `{}`", constraint)); - suggest_removing_unsized_bound(generics, err, param_name, param, def_id); - return true; - } - let mut suggest_restrict = |span| { - err.span_suggestion_verbose( - span, - MSG_RESTRICT_BOUND_FURTHER, - format!(" + {}", constraint), - Applicability::MachineApplicable, - ); - }; + for (param_name, mut constraints) in grouped { + let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); + let Some(param) = param else { return false }; - if param_name.starts_with("impl ") { - // If there's an `impl Trait` used in argument position, suggest - // restricting it: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion for tools in this case is: - // - // fn foo(t: impl Foo) { ... } - // -------- - // | - // replace with: `impl Foo + Bar` - - suggest_restrict(param.span.shrink_to_hi()); - return true; - } + { + let mut sized_constraints = + constraints.drain_filter(|(_, def_id)| *def_id == tcx.lang_items().sized_trait()); + if let Some((constraint, def_id)) = sized_constraints.next() { + applicability = Applicability::MaybeIncorrect; - if generics.where_clause.predicates.is_empty() - // Given `trait Base: Super` where `T: Copy`, suggest restricting in the - // `where` clause instead of `trait Base: Super`. - && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) - { - if let Some(span) = param.bounds_span_for_suggestions() { - // If user has provided some bounds, suggest restricting them: + err.span_label( + param.span, + &format!("this type parameter needs to be `{}`", constraint), + ); + suggest_removing_unsized_bound( + generics, + &mut suggestions, + param_name, + param, + def_id, + ); + } + } + + if constraints.is_empty() { + continue; + } + + let constraint = constraints.iter().map(|&(c, _)| c).collect::>().join(" + "); + let mut suggest_restrict = |span| { + suggestions.push(( + span, + format!(" + {}", constraint), + SuggestChangingConstraintsMessage::RestrictBoundFurther, + )) + }; + + if param_name.starts_with("impl ") { + // If there's an `impl Trait` used in argument position, suggest + // restricting it: // - // fn foo(t: T) { ... } - // --- + // fn foo(t: impl Foo) { ... } + // -------- // | // help: consider further restricting this bound with `+ Bar` // // Suggestion for tools in this case is: // - // fn foo(t: T) { ... } - // -- - // | - // replace with: `T: Bar +` - suggest_restrict(span); - } else { - // If user hasn't provided any bounds, suggest adding a new one: - // - // fn foo(t: T) { ... } - // - help: consider restricting this type parameter with `T: Foo` - err.span_suggestion_verbose( - param.span.shrink_to_hi(), - &msg_restrict_type, - format!(": {}", constraint), - Applicability::MachineApplicable, - ); + // fn foo(t: impl Foo) { ... } + // -------- + // | + // replace with: `impl Foo + Bar` + + suggest_restrict(param.span.shrink_to_hi()); + continue; } - true - } else { - // This part is a bit tricky, because using the `where` clause user can - // provide zero, one or many bounds for the same type parameter, so we - // have following cases to consider: - // - // 1) When the type parameter has been provided zero bounds - // - // Message: - // fn foo(x: X, y: Y) where Y: Foo { ... } - // - help: consider restricting this type parameter with `where X: Bar` - // - // Suggestion: - // fn foo(x: X, y: Y) where Y: Foo { ... } - // - insert: `, X: Bar` - // - // - // 2) When the type parameter has been provided one bound - // - // Message: - // fn foo(t: T) where T: Foo { ... } - // ^^^^^^ - // | - // help: consider further restricting this bound with `+ Bar` - // - // Suggestion: - // fn foo(t: T) where T: Foo { ... } - // ^^ - // | - // replace with: `T: Bar +` - // - // - // 3) When the type parameter has been provided many bounds - // - // Message: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - help: consider further restricting this type parameter with `where T: Zar` - // - // Suggestion: - // fn foo(t: T) where T: Foo, T: Bar {... } - // - insert: `, T: Zar` - // - // Additionally, there may be no `where` clause whatsoever in the case that this was - // reached because the generic parameter has a default: - // - // Message: - // trait Foo {... } - // - help: consider further restricting this type parameter with `where T: Zar` - // - // Suggestion: - // trait Foo where T: Zar {... } - // - insert: `where T: Zar` - - if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) - && generics.where_clause.predicates.len() == 0 + if generics.where_clause.predicates.is_empty() + // Given `trait Base: Super` where `T: Copy`, suggest restricting in the + // `where` clause instead of `trait Base: Super`. + && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) { - // Suggest a bound, but there is no existing `where` clause *and* the type param has a - // default (``), so we suggest adding `where T: Bar`. - err.span_suggestion_verbose( - generics.where_clause.tail_span_for_suggestion(), - &msg_restrict_type_further, - format!(" where {}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); + if let Some(span) = param.bounds_span_for_suggestions() { + // If user has provided some bounds, suggest restricting them: + // + // fn foo(t: T) { ... } + // --- + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion for tools in this case is: + // + // fn foo(t: T) { ... } + // -- + // | + // replace with: `T: Bar +` + suggest_restrict(span); + } else { + // If user hasn't provided any bounds, suggest adding a new one: + // + // fn foo(t: T) { ... } + // - help: consider restricting this type parameter with `T: Foo` + suggestions.push(( + param.span.shrink_to_hi(), + format!(": {}", constraint), + SuggestChangingConstraintsMessage::RestrictType { ty: param_name }, + )); + } } else { - let mut param_spans = Vec::new(); + // This part is a bit tricky, because using the `where` clause user can + // provide zero, one or many bounds for the same type parameter, so we + // have following cases to consider: + // + // 1) When the type parameter has been provided zero bounds + // + // Message: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - help: consider restricting this type parameter with `where X: Bar` + // + // Suggestion: + // fn foo(x: X, y: Y) where Y: Foo { ... } + // - insert: `, X: Bar` + // + // + // 2) When the type parameter has been provided one bound + // + // Message: + // fn foo(t: T) where T: Foo { ... } + // ^^^^^^ + // | + // help: consider further restricting this bound with `+ Bar` + // + // Suggestion: + // fn foo(t: T) where T: Foo { ... } + // ^^ + // | + // replace with: `T: Bar +` + // + // + // 3) When the type parameter has been provided many bounds + // + // Message: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - help: consider further restricting this type parameter with `where T: Zar` + // + // Suggestion: + // fn foo(t: T) where T: Foo, T: Bar {... } + // - insert: `, T: Zar` + // + // Additionally, there may be no `where` clause whatsoever in the case that this was + // reached because the generic parameter has a default: + // + // Message: + // trait Foo {... } + // - help: consider further restricting this type parameter with `where T: Zar` + // + // Suggestion: + // trait Foo where T: Zar {... } + // - insert: `where T: Zar` - for predicate in generics.where_clause.predicates { - if let WherePredicate::BoundPredicate(WhereBoundPredicate { - span, - bounded_ty, - .. - }) = predicate - { - if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { - if let Some(segment) = path.segments.first() { - if segment.ident.to_string() == param_name { - param_spans.push(span); + if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) + && generics.where_clause.predicates.len() == 0 + { + // Suggest a bound, but there is no existing `where` clause *and* the type param has a + // default (``), so we suggest adding `where T: Bar`. + suggestions.push(( + generics.where_clause.tail_span_for_suggestion(), + format!(" where {}: {}", param_name, constraint), + SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name }, + )); + } else { + let mut param_spans = Vec::new(); + + for predicate in generics.where_clause.predicates { + if let WherePredicate::BoundPredicate(WhereBoundPredicate { + span, + bounded_ty, + .. + }) = predicate + { + if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind { + if let Some(segment) = path.segments.first() { + if segment.ident.to_string() == param_name { + param_spans.push(span); + } } } } } - } - match param_spans[..] { - [¶m_span] => suggest_restrict(param_span.shrink_to_hi()), - _ => { - err.span_suggestion_verbose( - generics.where_clause.tail_span_for_suggestion(), - &msg_restrict_type_further, - format!(", {}: {}", param_name, constraint), - Applicability::MachineApplicable, - ); + match param_spans[..] { + [¶m_span] => suggest_restrict(param_span.shrink_to_hi()), + _ => { + suggestions.push(( + generics.where_clause.tail_span_for_suggestion(), + constraints + .iter() + .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint)) + .collect::(), + SuggestChangingConstraintsMessage::RestrictTypeFurther { + ty: param_name, + }, + )); + } } } } + } - true + if suggestions.len() == 1 { + let (span, suggestion, msg) = suggestions.pop().unwrap(); + + let s; + let msg = match msg { + SuggestChangingConstraintsMessage::RestrictBoundFurther => { + "consider further restricting this bound" + } + SuggestChangingConstraintsMessage::RestrictType { ty } => { + s = format!("consider restricting type parameter `{}`", ty); + &s + } + SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => { + s = format!("consider further restricting type parameter `{}`", ty); + &s + } + SuggestChangingConstraintsMessage::RemovingQSized => { + "consider removing the `?Sized` bound to make the type parameter `Sized`" + } + }; + + err.span_suggestion_verbose(span, msg, suggestion, applicability); + } else { + err.multipart_suggestion_verbose( + "consider restricting type parameters", + suggestions.into_iter().map(|(span, suggestion, _)| (span, suggestion)).collect(), + applicability, + ); } + + true } /// Collect al types that have an implicit `'static` obligation that we could suggest `'_` for. From 400d343796065e57e78d92b4cf74c78bec38452a Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 25 Feb 2022 23:34:25 +0300 Subject: [PATCH 13/20] Suggest adding `Copy` bound when Adt is moved out Previously we've only suggested adding `Copy` bounds when the type being moved/copied is a type parameter (generic). With this commit we also suggest adding bounds when a type - Can be copy - All predicates that need to be satisfied for that are based on type params i.e. we will suggest `T: Copy` for `Option`, but won't suggest anything for `Option`. Future work: it would be nice to also suggest adding `.clone()` calls --- .../src/diagnostics/conflict_errors.rs | 63 ++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index eb906d5fde7b4..e8ecb3cb6f3e7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -5,16 +5,21 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::ObligationCause; use rustc_middle::mir::{ self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, }; -use rustc_middle::ty::{self, suggest_constraining_type_param, Ty}; +use rustc_middle::ty::{ + self, suggest_constraining_type_param, suggest_constraining_type_params, PredicateKind, Ty, +}; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::symbol::sym; use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::TraitEngineExt as _; use crate::borrow_set::TwoPhaseActivation; use crate::borrowck_errors; @@ -423,7 +428,63 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None, ); } + } else { + // Try to find predicates on *generic params* that would allow copying `ty` + + let tcx = self.infcx.tcx; + let generics = tcx.generics_of(self.mir_def_id()); + if let Some(hir_generics) = tcx + .typeck_root_def_id(self.mir_def_id().to_def_id()) + .as_local() + .and_then(|def_id| tcx.hir().get_generics(def_id)) + { + let predicates: Result, _> = tcx.infer_ctxt().enter(|infcx| { + let mut fulfill_cx = + >::new(infcx.tcx); + + let copy_did = infcx.tcx.lang_items().copy_trait().unwrap(); + let cause = ObligationCause::new( + span, + self.mir_hir_id(), + rustc_infer::traits::ObligationCauseCode::MiscObligation, + ); + fulfill_cx.register_bound(&infcx, self.param_env, ty, copy_did, cause); + let errors = fulfill_cx.select_where_possible(&infcx); + + // Only emit suggestion if all required predicates are on generic + errors + .into_iter() + .map(|err| match err.obligation.predicate.kind().skip_binder() { + PredicateKind::Trait(predicate) => { + match predicate.self_ty().kind() { + ty::Param(param_ty) => Ok(( + generics.type_param(param_ty, tcx), + predicate + .trait_ref + .print_only_trait_path() + .to_string(), + )), + _ => Err(()), + } + } + _ => Err(()), + }) + .collect() + }); + + if let Ok(predicates) = predicates { + suggest_constraining_type_params( + tcx, + hir_generics, + &mut err, + predicates.iter().map(|(param, constraint)| { + (param.name.as_str(), &**constraint, None) + }), + ); + } + } } + let span = if let Some(local) = place.as_local() { let decl = &self.body.local_decls[local]; Some(decl.source_info.span) From 879efa84516da086e7461d65fff6797c8ae1cff9 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 25 Feb 2022 23:38:00 +0300 Subject: [PATCH 14/20] Add a test for Adt copy suggestions --- .../use_of_moved_value_copy_suggestions.rs | 65 ++++++++ ...use_of_moved_value_copy_suggestions.stderr | 151 ++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 src/test/ui/moves/use_of_moved_value_copy_suggestions.rs create mode 100644 src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs new file mode 100644 index 0000000000000..0930927c49d66 --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs @@ -0,0 +1,65 @@ +fn duplicate_t(t: T) -> (T, T) { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_opt(t: Option) -> (Option, Option) { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom(t: S) -> (S, S) { + (t, t) //~ use of moved value: `t` +} + +struct S(T); +trait Trait {} +impl Clone for S { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +impl Copy for S {} + +trait A {} +trait B {} + +// Test where bounds are added with different bound placements +fn duplicate_custom_1(t: S) -> (S, S) where { + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_2(t: S) -> (S, S) +where + T: A, +{ + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_3(t: S) -> (S, S) +where + T: A, + T: B, +{ + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_4(t: S) -> (S, S) +where + T: B, +{ + (t, t) //~ use of moved value: `t` +} + +// `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint +fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { + (t, t) //~ use of moved value: `t` +} + +fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr new file mode 100644 index 0000000000000..c32ac2cd78aa0 --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr @@ -0,0 +1,151 @@ +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:2:9 + | +LL | fn duplicate_t(t: T) -> (T, T) { + | - move occurs because `t` has type `T`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_t(t: T) -> (T, T) { + | ++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:6:9 + | +LL | fn duplicate_opt(t: Option) -> (Option, Option) { + | - move occurs because `t` has type `Option`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_opt(t: Option) -> (Option, Option) { + | ++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:10:9 + | +LL | fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + | - move occurs because `t` has type `(T,)`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + | ++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:14:9 + | +LL | fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + | - move occurs because `t` has type `(A, B)`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameters + | +LL | fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + | ++++++ ++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:18:9 + | +LL | fn duplicate_custom(t: S) -> (S, S) { + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_custom(t: S) -> (S, S) { + | ++++++++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:35:9 + | +LL | fn duplicate_custom_1(t: S) -> (S, S) where { + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider restricting type parameter `T` + | +LL | fn duplicate_custom_1(t: S) -> (S, S) where { + | ++++++++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:42:9 + | +LL | fn duplicate_custom_2(t: S) -> (S, S) + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +... +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider further restricting this bound + | +LL | T: A + Trait + Copy, + | ++++++++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:50:9 + | +LL | fn duplicate_custom_3(t: S) -> (S, S) + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +... +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider further restricting type parameter `T` + | +LL | T: B, T: Trait, T: Copy + | ~~~~~~~~~~~~~~~~~~~ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:57:9 + | +LL | fn duplicate_custom_4(t: S) -> (S, S) + | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +... +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + | +help: consider further restricting this bound + | +LL | T: B + Trait + Copy, + | ++++++++++++++ + +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_copy_suggestions.rs:62:9 + | +LL | fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { + | - move occurs because `t` has type `Rc`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to 10 previous errors + +For more information about this error, try `rustc --explain E0382`. From f0a16b856011c8e63fac98fd6c127c2a0bbd532e Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Tue, 1 Mar 2022 14:11:42 +0300 Subject: [PATCH 15/20] Use rustfix in copy suggestion test --- .../use_of_moved_value_clone_suggestions.rs | 6 ++ ...se_of_moved_value_clone_suggestions.stderr | 13 ++++ .../use_of_moved_value_copy_suggestions.fixed | 72 +++++++++++++++++++ .../use_of_moved_value_copy_suggestions.rs | 17 +++-- ...use_of_moved_value_copy_suggestions.stderr | 36 +++++----- 5 files changed, 119 insertions(+), 25 deletions(-) create mode 100644 src/test/ui/moves/use_of_moved_value_clone_suggestions.rs create mode 100644 src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr create mode 100644 src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed diff --git a/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs b/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs new file mode 100644 index 0000000000000..d5c8d4e6bdf2b --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_clone_suggestions.rs @@ -0,0 +1,6 @@ +// `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint +fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { + (t, t) //~ use of moved value: `t` +} + +fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr new file mode 100644 index 0000000000000..c25981e6f8063 --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_clone_suggestions.stderr @@ -0,0 +1,13 @@ +error[E0382]: use of moved value: `t` + --> $DIR/use_of_moved_value_clone_suggestions.rs:3:9 + | +LL | fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { + | - move occurs because `t` has type `Rc`, which does not implement the `Copy` trait +LL | (t, t) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed b/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed new file mode 100644 index 0000000000000..d31046c77006e --- /dev/null +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.fixed @@ -0,0 +1,72 @@ +// run-rustfix +#![allow(dead_code)] + +fn duplicate_t(t: T) -> (T, T) { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +fn duplicate_opt(t: Option) -> (Option, Option) { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + //~^ HELP consider restricting type parameters + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom(t: S) -> (S, S) { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +struct S(T); +trait Trait {} +impl Clone for S { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +impl Copy for S {} + +trait A {} +trait B {} + +// Test where bounds are added with different bound placements +fn duplicate_custom_1(t: S) -> (S, S) where { + //~^ HELP consider restricting type parameter `T` + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_2(t: S) -> (S, S) +where + T: A + Trait + Copy, + //~^ HELP consider further restricting this bound +{ + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_3(t: S) -> (S, S) +where + T: A, + T: B, T: Trait, T: Copy + //~^ HELP consider further restricting type parameter `T` +{ + (t, t) //~ use of moved value: `t` +} + +fn duplicate_custom_4(t: S) -> (S, S) +where + T: B + Trait + Copy, + //~^ HELP consider further restricting this bound +{ + (t, t) //~ use of moved value: `t` +} + +fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs index 0930927c49d66..7cc5189fac017 100644 --- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.rs @@ -1,20 +1,28 @@ +// run-rustfix +#![allow(dead_code)] + fn duplicate_t(t: T) -> (T, T) { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } fn duplicate_opt(t: Option) -> (Option, Option) { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { + //~^ HELP consider restricting type parameters (t, t) //~ use of moved value: `t` } fn duplicate_custom(t: S) -> (S, S) { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } @@ -32,12 +40,14 @@ trait B {} // Test where bounds are added with different bound placements fn duplicate_custom_1(t: S) -> (S, S) where { + //~^ HELP consider restricting type parameter `T` (t, t) //~ use of moved value: `t` } fn duplicate_custom_2(t: S) -> (S, S) where T: A, + //~^ HELP consider further restricting this bound { (t, t) //~ use of moved value: `t` } @@ -46,6 +56,7 @@ fn duplicate_custom_3(t: S) -> (S, S) where T: A, T: B, + //~^ HELP consider further restricting type parameter `T` { (t, t) //~ use of moved value: `t` } @@ -53,13 +64,9 @@ where fn duplicate_custom_4(t: S) -> (S, S) where T: B, + //~^ HELP consider further restricting this bound { (t, t) //~ use of moved value: `t` } -// `Rc` is not ever `Copy`, we should not suggest adding `T: Copy` constraint -fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { - (t, t) //~ use of moved value: `t` -} - fn main() {} diff --git a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr index c32ac2cd78aa0..8e72697ca30bb 100644 --- a/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr +++ b/src/test/ui/moves/use_of_moved_value_copy_suggestions.stderr @@ -1,8 +1,9 @@ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:2:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:6:9 | LL | fn duplicate_t(t: T) -> (T, T) { | - move occurs because `t` has type `T`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -14,10 +15,11 @@ LL | fn duplicate_t(t: T) -> (T, T) { | ++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:6:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:11:9 | LL | fn duplicate_opt(t: Option) -> (Option, Option) { | - move occurs because `t` has type `Option`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -29,10 +31,11 @@ LL | fn duplicate_opt(t: Option) -> (Option, Option) { | ++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:10:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:16:9 | LL | fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { | - move occurs because `t` has type `(T,)`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -44,10 +47,11 @@ LL | fn duplicate_tup1(t: (T,)) -> ((T,), (T,)) { | ++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:14:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:21:9 | LL | fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { | - move occurs because `t` has type `(A, B)`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -59,10 +63,11 @@ LL | fn duplicate_tup2(t: (A, B)) -> ((A, B), (A, B)) { | ++++++ ++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:18:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:26:9 | LL | fn duplicate_custom(t: S) -> (S, S) { | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -74,10 +79,11 @@ LL | fn duplicate_custom(t: S) -> (S, S) { | ++++++++++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:35:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:44:9 | LL | fn duplicate_custom_1(t: S) -> (S, S) where { | - move occurs because `t` has type `S`, which does not implement the `Copy` trait +LL | LL | (t, t) | - ^ value used here after move | | @@ -89,7 +95,7 @@ LL | fn duplicate_custom_1(t: S) -> (S, S) where { | ++++++++++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:42:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:52:9 | LL | fn duplicate_custom_2(t: S) -> (S, S) | - move occurs because `t` has type `S`, which does not implement the `Copy` trait @@ -105,7 +111,7 @@ LL | T: A + Trait + Copy, | ++++++++++++++ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:50:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:61:9 | LL | fn duplicate_custom_3(t: S) -> (S, S) | - move occurs because `t` has type `S`, which does not implement the `Copy` trait @@ -121,7 +127,7 @@ LL | T: B, T: Trait, T: Copy | ~~~~~~~~~~~~~~~~~~~ error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:57:9 + --> $DIR/use_of_moved_value_copy_suggestions.rs:69:9 | LL | fn duplicate_custom_4(t: S) -> (S, S) | - move occurs because `t` has type `S`, which does not implement the `Copy` trait @@ -136,16 +142,6 @@ help: consider further restricting this bound LL | T: B + Trait + Copy, | ++++++++++++++ -error[E0382]: use of moved value: `t` - --> $DIR/use_of_moved_value_copy_suggestions.rs:62:9 - | -LL | fn duplicate_rc(t: std::rc::Rc) -> (std::rc::Rc, std::rc::Rc) { - | - move occurs because `t` has type `Rc`, which does not implement the `Copy` trait -LL | (t, t) - | - ^ value used here after move - | | - | value moved here - -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0382`. From f28786643e6ce7ec3104f96cd40a0baa8f8d3cac Mon Sep 17 00:00:00 2001 From: Edwin Amsler Date: Tue, 1 Mar 2022 15:47:11 -0600 Subject: [PATCH 16/20] Demote Windows XP to no_std only Modify the tier 3 non-ARM targets to show the standard library will no longer build for these and there is no work being done to change that. --- src/doc/rustc/src/platform-support.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index d7e0b9d50f084..4c05818440b09 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -139,7 +139,7 @@ target | std | notes `armv7r-none-eabi` | * | Bare ARMv7-R `armv7r-none-eabihf` | * | Bare ARMv7-R, hardfloat `asmjs-unknown-emscripten` | ✓ | asm.js via Emscripten -`i586-pc-windows-msvc` | ✓ | 32-bit Windows w/o SSE +`i586-pc-windows-msvc` | * | 32-bit Windows w/o SSE `i586-unknown-linux-gnu` | ✓ | 32-bit Linux w/o SSE (kernel 4.4, glibc 2.23) `i586-unknown-linux-musl` | ✓ | 32-bit Linux w/o SSE, MUSL `i686-linux-android` | ✓ | 32-bit x86 Android @@ -236,7 +236,7 @@ target | std | host | notes `hexagon-unknown-linux-musl` | ? | | `i386-apple-ios` | ✓ | | 32-bit x86 iOS `i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.7+, Lion+) -`i686-pc-windows-msvc` | ✓ | | 32-bit Windows XP support +`i686-pc-windows-msvc` | * | | 32-bit Windows XP support `i686-unknown-haiku` | ✓ | ✓ | 32-bit Haiku `i686-unknown-netbsd` | ✓ | ✓ | NetBSD/i386 with SSE2 [`i686-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 32-bit OpenBSD @@ -283,7 +283,7 @@ target | std | host | notes [`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? | | WebAssembly `x86_64-apple-ios-macabi` | ✓ | | Apple Catalyst on x86_64 `x86_64-apple-tvos` | * | | x86 64-bit tvOS -`x86_64-pc-windows-msvc` | ✓ | | 64-bit Windows XP support +`x86_64-pc-windows-msvc` | * | | 64-bit Windows XP support `x86_64-sun-solaris` | ? | | Deprecated target for 64-bit Solaris 10/11, illumos `x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD `x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku From 6739299d18a16851140bc6b24b8eeae31e3d5878 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 1 Mar 2022 20:02:59 -0500 Subject: [PATCH 17/20] Miri/CTFE: properly treat overflow in (signed) division/rem as UB --- .../src/interpret/intrinsics.rs | 12 ++------ .../src/interpret/operator.rs | 18 +++++++----- .../rustc_middle/src/mir/interpret/error.rs | 6 ++++ .../rustc_mir_transform/src/const_prop.rs | 11 +++++++- library/core/tests/num/wrapping.rs | 10 +++++++ src/test/ui/consts/const-int-unchecked.stderr | 4 +-- .../issue-8460-const.noopt.stderr | 28 +++++++++---------- .../issue-8460-const.opt.stderr | 28 +++++++++---------- ...8460-const.opt_with_overflow_checks.stderr | 28 +++++++++---------- .../ui/numbers-arithmetic/issue-8460-const.rs | 24 ++++++++-------- 10 files changed, 93 insertions(+), 76 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index d6f856a6f0a70..bf7e811c76f8e 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -500,15 +500,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`. // First, check x % y != 0 (or if that computation overflows). let (res, overflow, _ty) = self.overflowing_binary_op(BinOp::Rem, &a, &b)?; - if overflow || res.assert_bits(a.layout.size) != 0 { - // Then, check if `b` is -1, which is the "MIN / -1" case. - let minus1 = Scalar::from_int(-1, dest.layout.size); - let b_scalar = b.to_scalar().unwrap(); - if b_scalar == minus1 { - throw_ub_format!("exact_div: result of dividing MIN by -1 cannot be represented") - } else { - throw_ub_format!("exact_div: {} cannot be divided by {} without remainder", a, b,) - } + assert!(!overflow); // All overflow is UB, so this should never return on overflow. + if res.assert_bits(a.layout.size) != 0 { + throw_ub_format!("exact_div: {} cannot be divided by {} without remainder", a, b) } // `Rem` says this is all right, so we can let `Div` do its job. self.binop_ignore_overflow(BinOp::Div, &a, &b, dest) diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 48c90e1881a9a..079ce9f07b8e1 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -196,16 +196,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { _ => None, }; if let Some(op) = op { + let l = self.sign_extend(l, left_layout) as i128; let r = self.sign_extend(r, right_layout) as i128; - // We need a special check for overflowing remainder: - // "int_min % -1" overflows and returns 0, but after casting things to a larger int - // type it does *not* overflow nor give an unrepresentable result! - if bin_op == Rem { - if r == -1 && l == (1 << (size.bits() - 1)) { - return Ok((Scalar::from_int(0, size), true, left_layout.ty)); + + // We need a special check for overflowing Rem and Div since they are *UB* + // on overflow, which can happen with "int_min $OP -1". + if matches!(bin_op, Rem | Div) { + if l == size.signed_int_min() && r == -1 { + if bin_op == Rem { + throw_ub!(RemainderOverflow) + } else { + throw_ub!(DivisionOverflow) + } } } - let l = self.sign_extend(l, left_layout) as i128; let (result, oflo) = op(l, r); // This may be out-of-bounds for the result type, so we have to truncate ourselves. diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 31468ce73bfc5..389d31ea0c472 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -233,6 +233,10 @@ pub enum UndefinedBehaviorInfo<'tcx> { DivisionByZero, /// Something was "remainded" by 0 (x % 0). RemainderByZero, + /// Signed division overflowed (INT_MIN / -1). + DivisionOverflow, + /// Signed remainder overflowed (INT_MIN % -1). + RemainderOverflow, /// Overflowing inbounds pointer arithmetic. PointerArithOverflow, /// Invalid metadata in a wide pointer (using `str` to avoid allocations). @@ -310,6 +314,8 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { } DivisionByZero => write!(f, "dividing by zero"), RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"), + DivisionOverflow => write!(f, "overflow in signed division (dividing MIN by -1)"), + RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"), PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"), InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg), InvalidVtableDropFn(sig) => write!( diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 6075f572a651c..b724b527c7dfb 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -1200,12 +1200,21 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> { AssertKind::RemainderByZero(op) => { Some(AssertKind::RemainderByZero(eval_to_int(op))) } + AssertKind::Overflow(bin_op @ (BinOp::Div | BinOp::Rem), op1, op2) => { + // Division overflow is *UB* in the MIR, and different than the + // other overflow checks. + Some(AssertKind::Overflow( + *bin_op, + eval_to_int(op1), + eval_to_int(op2), + )) + } AssertKind::BoundsCheck { ref len, ref index } => { let len = eval_to_int(len); let index = eval_to_int(index); Some(AssertKind::BoundsCheck { len, index }) } - // Overflow is are already covered by checks on the binary operators. + // Remaining overflow errors are already covered by checks on the binary operators. AssertKind::Overflow(..) | AssertKind::OverflowNeg(_) => None, // Need proper const propagator for these. _ => None, diff --git a/library/core/tests/num/wrapping.rs b/library/core/tests/num/wrapping.rs index c4fb321ef26cf..8ded139a1809f 100644 --- a/library/core/tests/num/wrapping.rs +++ b/library/core/tests/num/wrapping.rs @@ -308,3 +308,13 @@ fn wrapping_int_api() { } } } + +#[test] +fn wrapping_const() { + // Specifically the wrapping behavior of division and remainder is subtle, + // see https://github.com/rust-lang/rust/pull/94512. + const _: () = { + assert!(i32::MIN.wrapping_div(-1) == i32::MIN); + assert!(i32::MIN.wrapping_rem(-1) == 0); + }; +} diff --git a/src/test/ui/consts/const-int-unchecked.stderr b/src/test/ui/consts/const-int-unchecked.stderr index 22e8c8dabc970..ad880d56d904d 100644 --- a/src/test/ui/consts/const-int-unchecked.stderr +++ b/src/test/ui/consts/const-int-unchecked.stderr @@ -266,7 +266,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:134:25 | LL | const _: i32 = unsafe { std::intrinsics::unchecked_div(i32::MIN, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_div` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow in signed division (dividing MIN by -1) error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:137:25 @@ -278,7 +278,7 @@ error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:139:25 | LL | const _: i32 = unsafe { std::intrinsics::unchecked_rem(i32::MIN, -1) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow executing `unchecked_rem` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ overflow in signed remainder (dividing MIN by -1) error[E0080]: evaluation of constant value failed --> $DIR/const-int-unchecked.rs:144:25 diff --git a/src/test/ui/numbers-arithmetic/issue-8460-const.noopt.stderr b/src/test/ui/numbers-arithmetic/issue-8460-const.noopt.stderr index d94c7742de301..e2eee1ccdc98c 100644 --- a/src/test/ui/numbers-arithmetic/issue-8460-const.noopt.stderr +++ b/src/test/ui/numbers-arithmetic/issue-8460-const.noopt.stderr @@ -1,36 +1,36 @@ -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:13:36 | LL | assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ attempt to compute `isize::MIN / -1_isize`, which would overflow | - = note: `#[deny(arithmetic_overflow)]` on by default + = note: `#[deny(unconditional_panic)]` on by default -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:15:36 | LL | assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^ attempt to compute `i8::MIN / -1_i8`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:17:36 | LL | assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i16::MIN / -1_i16`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:19:36 | LL | assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i32::MIN / -1_i32`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:21:36 | LL | assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i64::MIN / -1_i64`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:23:36 | LL | assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); @@ -41,8 +41,6 @@ error: this operation will panic at runtime | LL | assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); | ^^^^^^^^^^ attempt to divide `1_isize` by zero - | - = note: `#[deny(unconditional_panic)]` on by default error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:27:36 @@ -74,37 +72,37 @@ error: this operation will panic at runtime LL | assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); | ^^^^^^^^^ attempt to divide `1_i128` by zero -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:37:36 | LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ attempt to compute the remainder of `isize::MIN % -1_isize`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:39:36 | LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^ attempt to compute the remainder of `i8::MIN % -1_i8`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:41:36 | LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i16::MIN % -1_i16`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:43:36 | LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:45:36 | LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:47:36 | LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); diff --git a/src/test/ui/numbers-arithmetic/issue-8460-const.opt.stderr b/src/test/ui/numbers-arithmetic/issue-8460-const.opt.stderr index d94c7742de301..e2eee1ccdc98c 100644 --- a/src/test/ui/numbers-arithmetic/issue-8460-const.opt.stderr +++ b/src/test/ui/numbers-arithmetic/issue-8460-const.opt.stderr @@ -1,36 +1,36 @@ -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:13:36 | LL | assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ attempt to compute `isize::MIN / -1_isize`, which would overflow | - = note: `#[deny(arithmetic_overflow)]` on by default + = note: `#[deny(unconditional_panic)]` on by default -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:15:36 | LL | assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^ attempt to compute `i8::MIN / -1_i8`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:17:36 | LL | assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i16::MIN / -1_i16`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:19:36 | LL | assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i32::MIN / -1_i32`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:21:36 | LL | assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i64::MIN / -1_i64`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:23:36 | LL | assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); @@ -41,8 +41,6 @@ error: this operation will panic at runtime | LL | assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); | ^^^^^^^^^^ attempt to divide `1_isize` by zero - | - = note: `#[deny(unconditional_panic)]` on by default error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:27:36 @@ -74,37 +72,37 @@ error: this operation will panic at runtime LL | assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); | ^^^^^^^^^ attempt to divide `1_i128` by zero -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:37:36 | LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ attempt to compute the remainder of `isize::MIN % -1_isize`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:39:36 | LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^ attempt to compute the remainder of `i8::MIN % -1_i8`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:41:36 | LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i16::MIN % -1_i16`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:43:36 | LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:45:36 | LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:47:36 | LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); diff --git a/src/test/ui/numbers-arithmetic/issue-8460-const.opt_with_overflow_checks.stderr b/src/test/ui/numbers-arithmetic/issue-8460-const.opt_with_overflow_checks.stderr index d94c7742de301..e2eee1ccdc98c 100644 --- a/src/test/ui/numbers-arithmetic/issue-8460-const.opt_with_overflow_checks.stderr +++ b/src/test/ui/numbers-arithmetic/issue-8460-const.opt_with_overflow_checks.stderr @@ -1,36 +1,36 @@ -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:13:36 | LL | assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ attempt to compute `isize::MIN / -1_isize`, which would overflow | - = note: `#[deny(arithmetic_overflow)]` on by default + = note: `#[deny(unconditional_panic)]` on by default -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:15:36 | LL | assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^ attempt to compute `i8::MIN / -1_i8`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:17:36 | LL | assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i16::MIN / -1_i16`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:19:36 | LL | assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i32::MIN / -1_i32`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:21:36 | LL | assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute `i64::MIN / -1_i64`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:23:36 | LL | assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); @@ -41,8 +41,6 @@ error: this operation will panic at runtime | LL | assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); | ^^^^^^^^^^ attempt to divide `1_isize` by zero - | - = note: `#[deny(unconditional_panic)]` on by default error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:27:36 @@ -74,37 +72,37 @@ error: this operation will panic at runtime LL | assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); | ^^^^^^^^^ attempt to divide `1_i128` by zero -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:37:36 | LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ attempt to compute the remainder of `isize::MIN % -1_isize`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:39:36 | LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^ attempt to compute the remainder of `i8::MIN % -1_i8`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:41:36 | LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i16::MIN % -1_i16`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:43:36 | LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:45:36 | LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow -error: this arithmetic operation will overflow +error: this operation will panic at runtime --> $DIR/issue-8460-const.rs:47:36 | LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); diff --git a/src/test/ui/numbers-arithmetic/issue-8460-const.rs b/src/test/ui/numbers-arithmetic/issue-8460-const.rs index dc754666c8e1f..8cad6deb3db5e 100644 --- a/src/test/ui/numbers-arithmetic/issue-8460-const.rs +++ b/src/test/ui/numbers-arithmetic/issue-8460-const.rs @@ -11,17 +11,17 @@ use std::thread; fn main() { assert!(thread::spawn(move|| { isize::MIN / -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i8::MIN / -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i16::MIN / -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i32::MIN / -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); //~^ ERROR operation will panic assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); @@ -35,17 +35,17 @@ fn main() { assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); //~^ ERROR operation will panic assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); - //~^ ERROR arithmetic operation will overflow + //~^ ERROR operation will panic assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); //~^ ERROR operation will panic assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); From 3768f0b813cd6944b0893b76bc0add776eb8cae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Marie?= Date: Wed, 2 Mar 2022 10:33:50 +0000 Subject: [PATCH 18/20] update char signess for openbsd adds more archs for openbsd: arm, mips64, powerpc, powerpc64, and riscv64. --- library/core/src/ffi/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index e5255686ff984..81f04994c6f8d 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -130,7 +130,16 @@ mod c_char_definition { target_os = "netbsd", any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc") ), - all(target_os = "openbsd", target_arch = "aarch64"), + all( + target_os = "openbsd", + any( + target_arch = "aarch64", + target_arch = "arm", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "riscv64", + ), + ), all( target_os = "vxworks", any( From fa8e1bedd34d85d4cbe83b8c61a53d080a83e72d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Marie?= Date: Wed, 2 Mar 2022 12:12:28 +0000 Subject: [PATCH 19/20] merge the char signess list of archs with freebsd as it is the same --- library/core/src/ffi/mod.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 81f04994c6f8d..2b611e5aae276 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -117,7 +117,7 @@ mod c_char_definition { all(target_os = "android", any(target_arch = "aarch64", target_arch = "arm")), all(target_os = "l4re", target_arch = "x86_64"), all( - target_os = "freebsd", + any(target_os = "freebsd", target_os = "openbsd"), any( target_arch = "aarch64", target_arch = "arm", @@ -130,16 +130,6 @@ mod c_char_definition { target_os = "netbsd", any(target_arch = "aarch64", target_arch = "arm", target_arch = "powerpc") ), - all( - target_os = "openbsd", - any( - target_arch = "aarch64", - target_arch = "arm", - target_arch = "powerpc", - target_arch = "powerpc64", - target_arch = "riscv64", - ), - ), all( target_os = "vxworks", any( From 2d854f9c340df887e30896f49270ae81feb3e227 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Wed, 2 Mar 2022 15:22:32 +0100 Subject: [PATCH 20/20] Remove num_cpus dependency from bootstrap, build-manifest and rustc_session --- Cargo.lock | 3 --- compiler/rustc_session/Cargo.toml | 1 - compiler/rustc_session/src/options.rs | 2 +- src/bootstrap/Cargo.toml | 1 - src/bootstrap/config.rs | 2 +- src/bootstrap/flags.rs | 2 +- src/bootstrap/lib.rs | 4 +++- src/tools/build-manifest/Cargo.toml | 1 - src/tools/build-manifest/src/main.rs | 2 +- 9 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f0c6e371c38be..716f4d7501467 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,7 +221,6 @@ dependencies = [ "getopts", "ignore", "libc", - "num_cpus", "once_cell", "opener", "pretty_assertions", @@ -249,7 +248,6 @@ dependencies = [ "anyhow", "flate2", "hex 0.4.2", - "num_cpus", "rayon", "serde", "serde_json", @@ -4241,7 +4239,6 @@ name = "rustc_session" version = "0.0.0" dependencies = [ "getopts", - "num_cpus", "rustc_ast", "rustc_data_structures", "rustc_errors", diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index 37cfc4a0dc3c2..6b1eaa4d399d9 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -15,6 +15,5 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_span = { path = "../rustc_span" } rustc_fs_util = { path = "../rustc_fs_util" } -num_cpus = "1.0" rustc_ast = { path = "../rustc_ast" } rustc_lint_defs = { path = "../rustc_lint_defs" } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index c2b13346cd6c9..43123d30ad5c6 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -551,7 +551,7 @@ mod parse { crate fn parse_threads(slot: &mut usize, v: Option<&str>) -> bool { match v.and_then(|s| s.parse().ok()) { Some(0) => { - *slot = ::num_cpus::get(); + *slot = std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get); true } Some(i) => { diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 592a137e379de..02efc08cc791f 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -37,7 +37,6 @@ test = false build_helper = { path = "../build_helper" } cmake = "0.1.38" filetime = "0.2" -num_cpus = "1.0" getopts = "0.2.19" cc = "1.0.69" libc = "0.2" diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index d6f77fe6cd6d0..ce76ccd57551d 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -1187,7 +1187,7 @@ fn set(field: &mut T, val: Option) { fn threads_from_config(v: u32) -> u32 { match v { - 0 => num_cpus::get() as u32, + 0 => std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32, n => n, } } diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 9180c5f03af68..74528f2752f5f 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -208,7 +208,7 @@ To learn more about a subcommand, run `./x.py -h`", let j_msg = format!( "number of jobs to run in parallel; \ defaults to {} (this host's logical CPU count)", - num_cpus::get() + std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) ); opts.optopt("j", "jobs", &j_msg, "JOBS"); opts.optflag("h", "help", "print this help message"); diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 86339c8d7f88d..6a58de40181c2 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -917,7 +917,9 @@ impl Build { /// Returns the number of parallel jobs that have been configured for this /// build. fn jobs(&self) -> u32 { - self.config.jobs.unwrap_or_else(|| num_cpus::get() as u32) + self.config.jobs.unwrap_or_else(|| { + std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) as u32 + }) } fn debuginfo_map_to(&self, which: GitRepo) -> Option { diff --git a/src/tools/build-manifest/Cargo.toml b/src/tools/build-manifest/Cargo.toml index c022d3aa0acd7..c437bde5ae69a 100644 --- a/src/tools/build-manifest/Cargo.toml +++ b/src/tools/build-manifest/Cargo.toml @@ -13,4 +13,3 @@ tar = "0.4.29" sha2 = "0.10.1" rayon = "1.5.1" hex = "0.4.2" -num_cpus = "1.13.0" diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 8a62146abfc4e..378efeb644375 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -208,7 +208,7 @@ fn main() { let num_threads = if let Some(num) = env::var_os("BUILD_MANIFEST_NUM_THREADS") { num.to_str().unwrap().parse().expect("invalid number for BUILD_MANIFEST_NUM_THREADS") } else { - num_cpus::get() + std::thread::available_parallelism().map_or(1, std::num::NonZeroUsize::get) }; rayon::ThreadPoolBuilder::new() .num_threads(num_threads)