From fbaa7fc85e1462b9fd8ca6c091327f765a9f4821 Mon Sep 17 00:00:00 2001 From: WiktorPrzetacznik Date: Tue, 18 Jul 2023 17:42:28 +0200 Subject: [PATCH 01/30] std::error::Error -> Trait Implementations: lifetimes consistency improvement --- library/alloc/src/boxed.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 8697a77db3bc8..bd341976cd4f8 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -2276,7 +2276,7 @@ impl<'a, E: Error + Send + Sync + 'a> From for Box for Box { +impl<'a> From for Box { /// Converts a [`String`] into a box of dyn [`Error`] + [`Send`] + [`Sync`]. /// /// # Examples @@ -2291,7 +2291,7 @@ impl From for Box { /// mem::size_of::>() == mem::size_of_val(&a_boxed_error)) /// ``` #[inline] - fn from(err: String) -> Box { + fn from(err: String) -> Box { struct StringError(String); impl Error for StringError { @@ -2320,7 +2320,7 @@ impl From for Box { #[cfg(not(no_global_oom_handling))] #[stable(feature = "string_box_error", since = "1.6.0")] -impl From for Box { +impl<'a> From for Box { /// Converts a [`String`] into a box of dyn [`Error`]. /// /// # Examples @@ -2333,7 +2333,7 @@ impl From for Box { /// let a_boxed_error = Box::::from(a_string_error); /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) /// ``` - fn from(str_err: String) -> Box { + fn from(str_err: String) -> Box { let err1: Box = From::from(str_err); let err2: Box = err1; err2 @@ -2366,7 +2366,7 @@ impl<'a> From<&str> for Box { #[cfg(not(no_global_oom_handling))] #[stable(feature = "string_box_error", since = "1.6.0")] -impl From<&str> for Box { +impl<'a> From<&str> for Box { /// Converts a [`str`] into a box of dyn [`Error`]. /// /// [`str`]: prim@str @@ -2381,7 +2381,7 @@ impl From<&str> for Box { /// let a_boxed_error = Box::::from(a_str_error); /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) /// ``` - fn from(err: &str) -> Box { + fn from(err: &str) -> Box { From::from(String::from(err)) } } @@ -2410,7 +2410,7 @@ impl<'a, 'b> From> for Box { #[cfg(not(no_global_oom_handling))] #[stable(feature = "cow_box_error", since = "1.22.0")] -impl<'a> From> for Box { +impl<'a, 'b> From> for Box { /// Converts a [`Cow`] into a box of dyn [`Error`]. /// /// # Examples @@ -2424,7 +2424,7 @@ impl<'a> From> for Box { /// let a_boxed_error = Box::::from(a_cow_str_error); /// assert!(mem::size_of::>() == mem::size_of_val(&a_boxed_error)) /// ``` - fn from(err: Cow<'a, str>) -> Box { + fn from(err: Cow<'b, str>) -> Box { From::from(String::from(err)) } } From 3f1ad47b88f973a660dfc99d6f51e77e4e4e4d5e Mon Sep 17 00:00:00 2001 From: Wiktor Przetacznik <85874198+WiktorPrzetacznik@users.noreply.github.com> Date: Tue, 29 Aug 2023 14:09:41 +0200 Subject: [PATCH 02/30] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8a6c559b0b312..59592341d088a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +PR #113833 - https://github.com/rust-lang/rust/pull/113833 + # The Rust Programming Language [![Rust Community](https://img.shields.io/badge/Rust_Community%20-Join_us-brightgreen?style=plastic&logo=rust)](https://www.rust-lang.org/community) From 1da87ee4b31fd1238fe73a42179e5460120d5e2e Mon Sep 17 00:00:00 2001 From: Wiktor Przetacznik Date: Tue, 29 Aug 2023 14:49:09 +0200 Subject: [PATCH 03/30] Revert "Update README.md" This reverts commit 3f1ad47b88f973a660dfc99d6f51e77e4e4e4d5e. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 59592341d088a..8a6c559b0b312 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -PR #113833 - https://github.com/rust-lang/rust/pull/113833 - # The Rust Programming Language [![Rust Community](https://img.shields.io/badge/Rust_Community%20-Join_us-brightgreen?style=plastic&logo=rust)](https://www.rust-lang.org/community) From a63880c4d3a7e7620f25283999eb4a27516f4816 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 20 Nov 2023 21:55:17 +0100 Subject: [PATCH 04/30] PartialEq: handle longer transitive chains --- library/core/src/cmp.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index bffd3b2af971a..ba7950a709771 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -61,11 +61,13 @@ use self::Ordering::*; /// The equality relation `==` must satisfy the following conditions /// (for all `a`, `b`, `c` of type `A`, `B`, `C`): /// -/// - **Symmetric**: if `A: PartialEq` and `B: PartialEq`, then **`a == b` +/// - **Symmetry**: if `A: PartialEq` and `B: PartialEq`, then **`a == b` /// implies `b == a`**; and /// -/// - **Transitive**: if `A: PartialEq` and `B: PartialEq` and `A: +/// - **Transitivity**: if `A: PartialEq` and `B: PartialEq` and `A: /// PartialEq`, then **`a == b` and `b == c` implies `a == c`**. +/// This must also work for longer chains, such as when `A: PartialEq`, `B: PartialEq`, +/// `C: PartialEq`, and `A: PartialEq` all exist. /// /// Note that the `B: PartialEq` (symmetric) and `A: PartialEq` /// (transitive) impls are not forced to exist, but these requirements apply From 3e389ef6d50c2cae6b561a04adb9224e666eb964 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 20 Nov 2023 21:45:51 +0100 Subject: [PATCH 05/30] PartialOrd: transitivity and duality are required only if the corresponding impls exist --- library/core/src/cmp.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index ba7950a709771..e95410ba648b5 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -921,14 +921,18 @@ pub macro Ord($item:item) { /// easy to accidentally make them disagree by deriving some of the traits and manually /// implementing others. /// -/// The comparison must satisfy, for all `a`, `b` and `c`: +/// The comparison relations must satisfy the following conditions +/// (for all `a`, `b`, `c` of type `A`, `B`, `C`): /// -/// - transitivity: `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. -/// - duality: `a < b` if and only if `b > a`. +/// - **Transitivity**: if `A: PartialOrd` and `B: PartialOrd` and `A: +/// PartialOrd`, then `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. +/// This must also work for longer chains, such as when `A: PartialOrd`, `B: PartialOrd`, +/// `C: PartialOrd`, and `A: PartialOrd` all exist. +/// - **Duality**: if `A: PartialOrd` and `B: PartialOrd`, then `a < b` if and only if `b > a`. /// -/// Note that these requirements mean that the trait itself must be implemented symmetrically and -/// transitively: if `T: PartialOrd` and `U: PartialOrd` then `U: PartialOrd` and `T: -/// PartialOrd`. +/// Note that the `B: PartialOrd` (dual) and `A: PartialOrd` +/// (transitive) impls are not forced to exist, but these requirements apply +/// whenever they do exist. /// /// Violating these requirements is a logic error. The behavior resulting from a logic error is not /// specified, but users of the trait must ensure that such logic errors do *not* result in From baaf6d706ac3cb8b306ebede165706b7a0038415 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 26 Dec 2023 19:52:20 +0100 Subject: [PATCH 06/30] explain what crates should do when adding comparison with foreign types --- library/core/src/cmp.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index e95410ba648b5..9c18d5573e627 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -78,6 +78,25 @@ use self::Ordering::*; /// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these /// methods. /// +/// ## Cross-crate considerations +/// +/// Upholding the requirements stated above can become tricky when one crate implements `PartialEq` +/// for a type of another crate (i.e., to allow comparing one of its own types with a type from the +/// standard library). The recommendation is to never implement this trait for a foreign type. In +/// other words, such a crate should do `impl PartialEq for LocalType`, but it should +/// *not* do `impl PartialEq for ForeignType`. +/// +/// This avoids the problem of transitive chains that criss-cross crate boundaries: for all local +/// types `T`, you may assue that no other crate will add `impl`s that allow comparing `T == U`. In +/// other words, if other crates add `impl`s that allow building longer transitive chains `U1 == ... +/// == T == V1 == ...`, then all the types that appear to the right of `T` must be types that the +/// crate defining `T` already knows about. This rules out transitive chains where downstream crates +/// can add new `impl`s that "stitch together" comparisons of foreign types in ways that violate +/// transitivity. +/// +/// Not having such foreign `impl`s also avoids forward compatibility issues where one crate adding +/// more `PartialEq` implementations can cause build failures in downstream crates. +/// /// ## Derivable /// /// This trait can be used with `#[derive]`. When `derive`d on structs, two @@ -939,6 +958,25 @@ pub macro Ord($item:item) { /// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these /// methods. /// +/// ## Cross-crate considerations +/// +/// Upholding the requirements stated above can become tricky when one crate implements `PartialOrd` +/// for a type of another crate (i.e., to allow comparing one of its own types with a type from the +/// standard library). The recommendation is to never implement this trait for a foreign type. In +/// other words, such a crate should do `impl PartialOrd for LocalType`, but it should +/// *not* do `impl PartialOrd for ForeignType`. +/// +/// This avoids the problem of transitive chains that criss-cross crate boundaries: for all local +/// types `T`, you may assue that no other crate will add `impl`s that allow comparing `T < U`. In +/// other words, if other crates add `impl`s that allow building longer transitive chains `U1 < ... +/// < T < V1 < ...`, then all the types that appear to the right of `T` must be types that the crate +/// defining `T` already knows about. This rules out transitive chains where downstream crates can +/// add new `impl`s that "stitch together" comparisons of foreign types in ways that violate +/// transitivity. +/// +/// Not having such foreign `impl`s also avoids forward compatibility issues where one crate adding +/// more `PartialOrd` implementations can cause build failures in downstream crates. +/// /// ## Corollaries /// /// The following corollaries follow from the above requirements: From 60a08196b6f473da17fc280a8545f1b62097b4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Thu, 30 Nov 2023 18:11:02 -0300 Subject: [PATCH 07/30] feat: add LocalWaker type, ContextBuilder type, and LocalWake trait. --- library/alloc/src/lib.rs | 1 + library/alloc/src/task.rs | 165 +++++++++++++++- library/core/src/task/mod.rs | 2 +- library/core/src/task/wake.rs | 361 +++++++++++++++++++++++++++++++--- 4 files changed, 504 insertions(+), 25 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 78629b39d34b1..c8b4cebdf8987 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -135,6 +135,7 @@ #![feature(iter_next_chunk)] #![feature(iter_repeat_n)] #![feature(layout_for_ptr)] +#![feature(local_waker)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_uninit_array_transpose)] diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 5d9772b878b03..16ae5241da8a3 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -7,8 +7,9 @@ //! `#[cfg(target_has_atomic = "ptr")]`. use core::mem::ManuallyDrop; -use core::task::{RawWaker, RawWakerVTable, Waker}; +use core::task::{LocalWaker, RawWaker, RawWakerVTable, Waker}; +use crate::rc::Rc; use crate::sync::Arc; /// The implementation of waking a task on an executor. @@ -152,3 +153,165 @@ fn raw_waker(waker: Arc) -> RawWaker { &RawWakerVTable::new(clone_waker::, wake::, wake_by_ref::, drop_waker::), ) } + +/// An analogous trait to `Wake` but used to construct a `LocalWaker`. This API +/// works in exactly the same way as `Wake`, except that it uses an `Rc` instead +/// of an `Arc`, and the result is a `LocalWaker` instead of a `Waker`. +/// +/// The benefits of using `LocalWaker` over `Waker` are that it allows the local waker +/// to hold data that does not implement `Send` and `Sync`. Additionally, it saves calls +/// to `Arc::clone`, which requires atomic synchronization. +/// + +/// # Examples +/// +/// A +/// +/// This is a simplified example of a `spawn` and a `block_on` function. The `spawn` function +/// is used to push new tasks onto the run queue, while the block on function will remove them +/// and poll them. When a task is woken, it will put itself back on the run queue to be polled by the executor. +/// +/// **Note:** A real world example would interlieve poll calls with calls to an io reactor to wait for events instead +/// of spinning on a loop. +/// +/// ```rust +/// use std::task::{LocalWake, ContextBuilder, LocalWaker}; +/// use std::future::Future; +/// use std::pin::Pin; +/// use std::rc::Rc; +/// use std::cell::RefCell; +/// use std::collections::VecDeque; +/// +/// +/// thread_local! { +/// // A queue containing all tasks ready to do progress +/// static RUN_QUEUE: RefCell>> = RefCell::default(); +/// } +/// +/// type BoxedFuture = Pin>>; +/// +/// struct Task(RefCell); +/// +/// impl LocalWake for Task { +/// fn wake(self: Rc) { +/// RUN_QUEUE.with_borrow_mut(|queue| { +/// queue.push_back(self) +/// }) +/// } +/// } +/// +/// fn spawn(future: F) +/// where +/// F: Future + 'static + Send + Sync +/// { +/// let task = Rc::new(Box::pin(future)); +/// RUN_QUEUE.with_borrow_mut(|queue| { +/// queue.push_back(task) +/// }); +/// } +/// +/// fn block_on(future: F) +/// where +/// F: Future + 'static + Sync + Send +/// { +/// spawn(future); +/// loop { +/// let Some(task) = RUN_QUEUE.with_borrow_mut(|queue|queue.pop_front()) else { +/// // we exit, since there are no more tasks remaining on the queue +/// return; +/// }; +/// // cast the Rc into a `LocalWaker` +/// let waker: LocalWaker = task.into(); +/// // Build the context using `ContextBuilder` +/// let mut cx = ContextBuilder::new() +/// .local_waker(&waker) +/// .build(); +/// +/// // Poll the task +/// task.0 +/// .borrow_mut() +/// .as_mut() +/// .poll(&mut cx); +/// } +/// } +/// ``` +/// +#[unstable(feature = "local_waker", issue = "none")] +pub trait LocalWake { + /// Wake this task. + #[unstable(feature = "local_waker", issue = "none")] + fn wake(self: Rc); + + /// Wake this task without consuming the local waker. + /// + /// If an executor supports a cheaper way to wake without consuming the + /// waker, it should override this method. By default, it clones the + /// [`Rc`] and calls [`wake`] on the clone. + /// + /// [`wake`]: Rc::wake + #[unstable(feature = "local_waker", issue = "none")] + fn wake_by_ref(self: &Rc) { + self.clone().wake(); + } +} + +#[unstable(feature = "local_waker", issue = "none")] +impl From> for LocalWaker { + /// Use a `Wake`-able type as a `LocalWaker`. + /// + /// No heap allocations or atomic operations are used for this conversion. + fn from(waker: Rc) -> LocalWaker { + // SAFETY: This is safe because raw_waker safely constructs + // a RawWaker from Rc. + unsafe { LocalWaker::from_raw(local_raw_waker(waker)) } + } +} +#[allow(ineffective_unstable_trait_impl)] +#[unstable(feature = "local_waker", issue = "none")] +impl From> for RawWaker { + /// Use a `Wake`-able type as a `RawWaker`. + /// + /// No heap allocations or atomic operations are used for this conversion. + fn from(waker: Rc) -> RawWaker { + local_raw_waker(waker) + } +} + +// NB: This private function for constructing a RawWaker is used, rather than +// inlining this into the `From> for RawWaker` impl, to ensure that +// the safety of `From> for Waker` does not depend on the correct +// trait dispatch - instead both impls call this function directly and +// explicitly. +#[inline(always)] +fn local_raw_waker(waker: Rc) -> RawWaker { + // Increment the reference count of the Rc to clone it. + unsafe fn clone_waker(waker: *const ()) -> RawWaker { + unsafe { Rc::increment_strong_count(waker as *const W) }; + RawWaker::new( + waker as *const (), + &RawWakerVTable::new(clone_waker::, wake::, wake_by_ref::, drop_waker::), + ) + } + + // Wake by value, moving the Rc into the LocalWake::wake function + unsafe fn wake(waker: *const ()) { + let waker = unsafe { Rc::from_raw(waker as *const W) }; + ::wake(waker); + } + + // Wake by reference, wrap the waker in ManuallyDrop to avoid dropping it + unsafe fn wake_by_ref(waker: *const ()) { + let waker = unsafe { ManuallyDrop::new(Rc::from_raw(waker as *const W)) }; + ::wake_by_ref(&waker); + } + + // Decrement the reference count of the Rc on drop + unsafe fn drop_waker(waker: *const ()) { + unsafe { Rc::decrement_strong_count(waker as *const W) }; + } + + RawWaker::new( + Rc::into_raw(waker) as *const (), + &RawWakerVTable::new(clone_waker::, wake::, wake_by_ref::, drop_waker::), + ) +} diff --git a/library/core/src/task/mod.rs b/library/core/src/task/mod.rs index 3f0080e3832e1..f1a789e32a7a7 100644 --- a/library/core/src/task/mod.rs +++ b/library/core/src/task/mod.rs @@ -8,7 +8,7 @@ pub use self::poll::Poll; mod wake; #[stable(feature = "futures_api", since = "1.36.0")] -pub use self::wake::{Context, RawWaker, RawWakerVTable, Waker}; +pub use self::wake::{Context, ContextBuilder, LocalWaker, RawWaker, RawWakerVTable, Waker}; mod ready; #[stable(feature = "ready_macro", since = "1.64.0")] diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 077852b0120c4..4dedcbe47a02f 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -1,5 +1,7 @@ #![stable(feature = "futures_api", since = "1.36.0")] +use crate::mem::transmute; + use crate::fmt; use crate::marker::PhantomData; use crate::ptr; @@ -60,6 +62,21 @@ impl RawWaker { pub fn vtable(&self) -> &'static RawWakerVTable { self.vtable } + + #[unstable(feature = "noop_waker", issue = "98286")] + const NOOP: RawWaker = { + const VTABLE: RawWakerVTable = RawWakerVTable::new( + // Cloning just returns a new no-op raw waker + |_| RawWaker::NOOP, + // `wake` does nothing + |_| {}, + // `wake_by_ref` does nothing + |_| {}, + // Dropping does nothing as we don't allocate anything + |_| {}, + ); + RawWaker::new(ptr::null(), &VTABLE) + }; } /// A virtual function pointer table (vtable) that specifies the behavior @@ -177,7 +194,8 @@ impl RawWakerVTable { #[stable(feature = "futures_api", since = "1.36.0")] #[lang = "Context"] pub struct Context<'a> { - waker: &'a Waker, + waker: Option<&'a Waker>, + local_waker: Option<&'a LocalWaker>, // Ensure we future-proof against variance changes by forcing // the lifetime to be invariant (argument-position lifetimes // are contravariant while return-position lifetimes are @@ -195,16 +213,36 @@ impl<'a> Context<'a> { #[must_use] #[inline] pub const fn from_waker(waker: &'a Waker) -> Self { - Context { waker, _marker: PhantomData, _marker2: PhantomData } + ContextBuilder::new().waker(waker).build() } /// Returns a reference to the [`Waker`] for the current task. + /// + /// Note that if the waker does not need to be sent across threads, it + /// is preferable to call `local_waker`, which is more portable and + /// potentially more efficient. + /// + /// # Panics + /// This function will panic if no `Waker` was set on the context. This happens if + /// the executor does not support working with thread safe wakers. An alternative + /// may be to call [`.local_waker()`](Context::local_waker) instead. #[stable(feature = "futures_api", since = "1.36.0")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] #[must_use] #[inline] pub const fn waker(&self) -> &'a Waker { - &self.waker + &self + .waker + .expect("no waker was set on this context, consider calling `local_waker` instead.") + } + /// Returns a reference to the [`LocalWaker`] for the current task. + #[unstable(feature = "local_waker", issue = "none")] + pub fn local_waker(&self) -> &'a LocalWaker { + // Safety: + // It is safe to transmute a `&Waker` into a `&LocalWaker` since both are a transparent + // wrapper around a local waker. Also, the Option<&Waker> here cannot be None since it is + // impossible to construct a Context without any waker set. + self.local_waker.unwrap_or_else(|| unsafe { transmute(self.waker) }) } } @@ -215,6 +253,94 @@ impl fmt::Debug for Context<'_> { } } +/// A Builder used to construct a `Context` instance +/// with support for `LocalWaker`. +/// +/// # Examples +/// ``` +/// #![feature(local_waker)] +/// #![feature(noop_waker)] +/// use std::task::{ContextBuilder, LocalWaker, Waker}; +/// +/// let local_waker = LocalWaker::noop(); +/// let waker = Waker::noop(); +/// +/// let context = ContextBuilder::default() +/// .local_waker(&local_waker) +/// .waker(&waker) +/// .build(); +/// ``` +#[unstable(feature = "local_waker", issue = "none")] +#[derive(Default, Debug)] +pub struct ContextBuilder<'a> { + waker: Option<&'a Waker>, + local_waker: Option<&'a LocalWaker>, +} + +impl<'a> ContextBuilder<'a> { + /// Creates a new empty `ContextBuilder`. + #[inline] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[unstable(feature = "local_waker", issue = "none")] + pub const fn new() -> Self { + ContextBuilder { waker: None, local_waker: None } + } + + /// This field is used to set the value of the waker on `Context`. + #[inline] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[unstable(feature = "local_waker", issue = "none")] + pub const fn waker(self, waker: &'a Waker) -> Self { + Self { waker: Some(waker), ..self } + } + + /// This method is used to set the value for the local waker on `Context`. + /// + /// # Examples + /// ``` + /// #![feature(local_waker)] + /// #![feature(noop_waker)] + /// + /// use std::task; + /// use std::pin; + /// use std::future::Future; + /// + /// let local_waker = task::LocalWaker::noop(); + /// + /// let mut context = task::ContextBuilder::new() + /// .local_waker(&local_waker) + /// .build(); + /// + /// let future = pin::pin!(async { 20 }); + /// + /// let poll = future.poll(&mut context); + /// + /// assert_eq!(poll, task::Poll::Ready(20)); + /// ``` + #[inline] + #[unstable(feature = "local_waker", issue = "none")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn local_waker(self, local_waker: &'a LocalWaker) -> Self { + Self { local_waker: Some(local_waker), ..self } + } + + /// Builds the `Context`. + /// + /// # Panics + /// Panics if no `Waker` or `LocalWaker` is set. + #[inline] + #[unstable(feature = "local_waker", issue = "none")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn build(self) -> Context<'a> { + let ContextBuilder { waker, local_waker } = self; + assert!( + waker.is_some() || local_waker.is_some(), + "at least one waker must be set with either the `local_waker` or `waker` methods on `ContextBuilder`." + ); + Context { waker, local_waker, _marker: PhantomData, _marker2: PhantomData } + } +} + /// A `Waker` is a handle for waking up a task by notifying its executor that it /// is ready to be run. /// @@ -229,7 +355,8 @@ impl fmt::Debug for Context<'_> { /// Implements [`Clone`], [`Send`], and [`Sync`]; therefore, a waker may be invoked /// from any thread, including ones not in any way managed by the executor. For example, /// this might be done to wake a future when a blocking function call completes on another -/// thread. +/// thread. If the waker does not need to be moved across threads, it is better to use +/// [`LocalWaker`], which the executor may use to skip unnecessary memory synchronization. /// /// Note that it is preferable to use `waker.clone_from(&new_waker)` instead /// of `*waker = new_waker.clone()`, as the former will avoid cloning the waker @@ -354,25 +481,8 @@ impl Waker { #[must_use] #[unstable(feature = "noop_waker", issue = "98286")] pub const fn noop() -> &'static Waker { - // Ideally all this data would be explicitly `static` because it is used by reference and - // only ever needs one copy. But `const fn`s (and `const` items) cannot refer to statics, - // even though their values can be promoted to static. (That might change; see #119618.) - // An alternative would be a `pub static NOOP: &Waker`, but associated static items are not - // currently allowed either, and making it non-associated would be unergonomic. - const VTABLE: RawWakerVTable = RawWakerVTable::new( - // Cloning just returns a new no-op raw waker - |_| RAW, - // `wake` does nothing - |_| {}, - // `wake_by_ref` does nothing - |_| {}, - // Dropping does nothing as we don't allocate anything - |_| {}, - ); - const RAW: RawWaker = RawWaker::new(ptr::null(), &VTABLE); - const WAKER_REF: &Waker = &Waker { waker: RAW }; - - WAKER_REF + const WAKER: &Waker = &Waker { waker: RawWaker::NOOP }; + WAKER } /// Get a reference to the underlying [`RawWaker`]. @@ -425,3 +535,208 @@ impl fmt::Debug for Waker { .finish() } } + +/// A `LocalWaker` is analogous to a [`Waker`], but it does not implement [`Send`] or [`Sync`]. +/// This handle encapsulates a [`RawWaker`] instance, which defines the +/// executor-specific wakeup behavior. +/// +/// Local wakers can be requested from a `Context` with the [`local_waker`] method. +/// +/// The typical life of a `LocalWaker` is that it is constructed by an executor, wrapped in a +/// [`Context`], then passed to [`Future::poll()`]. Then, if the future chooses to return +/// [`Poll::Pending`], it must also store the waker somehow and call [`Waker::wake()`] when +/// the future should be polled again. +/// +/// Implements [`Clone`], but neither [`Send`] nor [`Sync`]; therefore, a local waker may +/// not be moved to other threads. In general, when deciding to use wakers or local wakers, +/// local wakers are preferable unless the waker needs to be sent across threads. This is because +/// wakers can incur in additional cost related to memory synchronization, and not all executors +/// may support wakers. +/// +/// Note that it is preferable to use `local_waker.clone_from(&new_waker)` instead +/// of `*local_waker = new_waker.clone()`, as the former will avoid cloning the waker +/// unnecessarily if the two wakers [wake the same task](Self::will_wake). +/// +/// # Examples +/// +/// ``` +/// #![feature(local_waker)] +/// use std::future::{Future, poll_fn}; +/// use std::task::Poll; +/// +/// // a future that returns pending once. +/// fn yield_now() -> impl Future + Unpin { +/// let mut yielded = false; +/// poll_fn(move |cx| { +/// if !yielded { +/// yielded = true; +/// cx.local_waker().wake_by_ref(); +/// return Poll::Pending; +/// } +/// return Poll::Ready(()) +/// }) +/// } +/// # async { +/// yield_now().await; +/// # }; +/// ``` +/// +/// [`Future::poll()`]: core::future::Future::poll +/// [`Poll::Pending`]: core::task::Poll::Pending +/// [`local_waker`]: core::task::Context::local_waker +#[unstable(feature = "local_waker", issue = "none")] +#[repr(transparent)] +pub struct LocalWaker { + waker: RawWaker, +} + +#[unstable(feature = "local_waker", issue = "none")] +impl Unpin for LocalWaker {} + +impl LocalWaker { + /// Creates a new `LocalWaker` from [`RawWaker`]. + /// + /// The behavior of the returned `Waker` is undefined if the contract defined + /// in [`RawWaker`]'s and [`RawWakerVTable`]'s documentation is not upheld. + /// Therefore this method is unsafe. + #[inline] + #[must_use] + #[stable(feature = "futures_api", since = "1.36.0")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const unsafe fn from_raw(waker: RawWaker) -> LocalWaker { + Self { waker } + } + + /// Wake up the task associated with this `LocalWaker`. + /// + /// As long as the executor keeps running and the task is not finished, it is + /// guaranteed that each invocation of [`wake()`](Self::wake) (or + /// [`wake_by_ref()`](Self::wake_by_ref)) will be followed by at least one + /// [`poll()`] of the task to which this `Waker` belongs. This makes + /// it possible to temporarily yield to other tasks while running potentially + /// unbounded processing loops. + /// + /// Note that the above implies that multiple wake-ups may be coalesced into a + /// single [`poll()`] invocation by the runtime. + /// + /// Also note that yielding to competing tasks is not guaranteed: it is the + /// executor’s choice which task to run and the executor may choose to run the + /// current task again. + /// + /// [`poll()`]: crate::future::Future::poll + #[inline] + #[stable(feature = "futures_api", since = "1.36.0")] + pub fn wake(self) { + // The actual wakeup call is delegated through a virtual function call + // to the implementation which is defined by the executor. + let wake = self.waker.vtable.wake; + let data = self.waker.data; + + // Don't call `drop` -- the waker will be consumed by `wake`. + crate::mem::forget(self); + + // SAFETY: This is safe because `Waker::from_raw` is the only way + // to initialize `wake` and `data` requiring the user to acknowledge + // that the contract of `RawWaker` is upheld. + unsafe { (wake)(data) }; + } + + /// Creates a new `LocalWaker` that does nothing when `wake` is called. + /// + /// This is mostly useful for writing tests that need a [`Context`] to poll + /// some futures, but are not expecting those futures to wake the waker or + /// do not need to do anything specific if it happens. + /// + /// # Examples + /// + /// ``` + /// #![feature(local_waker)] + /// #![feature(noop_waker)] + /// + /// use std::future::Future; + /// use std::task::{ContextBuilder, LocalWaker}; + /// + /// let mut cx = task::ContextBuilder::new() + /// .local_waker(LocalWaker::noop()) + /// .build(); + /// + /// let mut future = Box::pin(async { 10 }); + /// assert_eq!(future.as_mut().poll(&mut cx), task::Poll::Ready(10)); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "noop_waker", issue = "98286")] + pub const fn noop() -> &'static LocalWaker { + const WAKER: &LocalWaker = &LocalWaker { waker: RawWaker::NOOP }; + WAKER + } + + /// Get a reference to the underlying [`RawWaker`]. + #[inline] + #[must_use] + #[unstable(feature = "waker_getters", issue = "87021")] + pub fn as_raw(&self) -> &RawWaker { + &self.waker + } + + /// Returns `true` if this `LocalWaker` and another `LocalWaker` would awake the same task. + /// + /// This function works on a best-effort basis, and may return false even + /// when the `Waker`s would awaken the same task. However, if this function + /// returns `true`, it is guaranteed that the `Waker`s will awaken the same task. + /// + /// This function is primarily used for optimization purposes — for example, + /// this type's [`clone_from`](Self::clone_from) implementation uses it to + /// avoid cloning the waker when they would wake the same task anyway. + #[inline] + #[must_use] + #[stable(feature = "futures_api", since = "1.36.0")] + pub fn will_wake(&self, other: &LocalWaker) -> bool { + self.waker == other.waker + } + + /// Wake up the task associated with this `LocalWaker` without consuming the `LocalWaker`. + /// + /// This is similar to [`wake()`](Self::wake), but may be slightly less efficient in + /// the case where an owned `Waker` is available. This method should be preferred to + /// calling `waker.clone().wake()`. + #[inline] + #[stable(feature = "futures_api", since = "1.36.0")] + pub fn wake_by_ref(&self) { + // The actual wakeup call is delegated through a virtual function call + // to the implementation which is defined by the executor. + + // SAFETY: see `wake` + unsafe { (self.waker.vtable.wake_by_ref)(self.waker.data) } + } +} +#[unstable(feature = "local_waker", issue = "none")] +impl Clone for LocalWaker { + #[inline] + fn clone(&self) -> Self { + LocalWaker { + // SAFETY: This is safe because `Waker::from_raw` is the only way + // to initialize `clone` and `data` requiring the user to acknowledge + // that the contract of [`RawWaker`] is upheld. + waker: unsafe { (self.waker.vtable.clone)(self.waker.data) }, + } + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + if !self.will_wake(source) { + *self = source.clone(); + } + } +} + +#[stable(feature = "futures_api", since = "1.36.0")] +impl fmt::Debug for LocalWaker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let vtable_ptr = self.waker.vtable as *const RawWakerVTable; + f.debug_struct("LocalWaker") + .field("data", &self.waker.data) + .field("vtable", &vtable_ptr) + .finish() + } +} From 0cb5e2fe5f080f40059e673cec6f0bf27f4c7596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Thu, 30 Nov 2023 19:05:12 -0300 Subject: [PATCH 08/30] perf: move null check from local_wake() to build() --- library/core/src/task/wake.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 4dedcbe47a02f..fa2b730b31c44 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -195,7 +195,7 @@ impl RawWakerVTable { #[lang = "Context"] pub struct Context<'a> { waker: Option<&'a Waker>, - local_waker: Option<&'a LocalWaker>, + local_waker: &'a LocalWaker, // Ensure we future-proof against variance changes by forcing // the lifetime to be invariant (argument-position lifetimes // are contravariant while return-position lifetimes are @@ -238,11 +238,7 @@ impl<'a> Context<'a> { /// Returns a reference to the [`LocalWaker`] for the current task. #[unstable(feature = "local_waker", issue = "none")] pub fn local_waker(&self) -> &'a LocalWaker { - // Safety: - // It is safe to transmute a `&Waker` into a `&LocalWaker` since both are a transparent - // wrapper around a local waker. Also, the Option<&Waker> here cannot be None since it is - // impossible to construct a Context without any waker set. - self.local_waker.unwrap_or_else(|| unsafe { transmute(self.waker) }) + &self.local_waker } } @@ -325,7 +321,7 @@ impl<'a> ContextBuilder<'a> { } /// Builds the `Context`. - /// + /// /// # Panics /// Panics if no `Waker` or `LocalWaker` is set. #[inline] @@ -337,6 +333,16 @@ impl<'a> ContextBuilder<'a> { waker.is_some() || local_waker.is_some(), "at least one waker must be set with either the `local_waker` or `waker` methods on `ContextBuilder`." ); + let local_waker = match local_waker { + Some(local_waker) => local_waker, + None => { + // SAFETY: + // It is safe to transmute a `&Waker` into a `&LocalWaker` since both are a transparent + // wrapper around a local waker. Also, the Option<&Waker> here cannot be None because + // of the previous assert. + unsafe { transmute(self.waker) } + } + }; Context { waker, local_waker, _marker: PhantomData, _marker2: PhantomData } } } @@ -576,9 +582,10 @@ impl fmt::Debug for Waker { /// return Poll::Ready(()) /// }) /// } -/// # async { +/// # #[allow(unused_must_use)] +/// # async fn __() { /// yield_now().await; -/// # }; +/// # } /// ``` /// /// [`Future::poll()`]: core::future::Future::poll From 232cc2b4e4ba0a3c0e5e45fed64f0783201805f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Wed, 6 Dec 2023 12:11:28 -0300 Subject: [PATCH 09/30] refactor: remove in favor of and to make the API infallible. --- library/core/src/task/wake.rs | 81 +++++++++++++---------------------- 1 file changed, 29 insertions(+), 52 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index fa2b730b31c44..da36194d462d3 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -213,7 +213,7 @@ impl<'a> Context<'a> { #[must_use] #[inline] pub const fn from_waker(waker: &'a Waker) -> Self { - ContextBuilder::new().waker(waker).build() + ContextBuilder::from_waker(waker).build() } /// Returns a reference to the [`Waker`] for the current task. @@ -261,28 +261,43 @@ impl fmt::Debug for Context<'_> { /// let local_waker = LocalWaker::noop(); /// let waker = Waker::noop(); /// -/// let context = ContextBuilder::default() -/// .local_waker(&local_waker) +/// let context = ContextBuilder::from_local_waker(&local_waker) /// .waker(&waker) /// .build(); +/// +/// let future = pin::pin!(async { 20 }); +/// let poll = future.poll(&mut context); +/// assert_eq!(poll, task::Poll::Ready(20)); +/// /// ``` #[unstable(feature = "local_waker", issue = "none")] -#[derive(Default, Debug)] +#[derive(Debug)] pub struct ContextBuilder<'a> { waker: Option<&'a Waker>, - local_waker: Option<&'a LocalWaker>, + local_waker: &'a LocalWaker, } impl<'a> ContextBuilder<'a> { - /// Creates a new empty `ContextBuilder`. + /// Create a ContextBuilder from a Waker. + #[inline] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[unstable(feature = "local_waker", issue = "none")] + pub const fn from_waker(waker: &'a Waker) -> Self { + // SAFETY: LocalWaker is just Waker without thread safety + let local_waker = unsafe { transmute(waker) }; + Self { waker: Some(waker), local_waker } + } + + /// Create a ContextBuilder from a LocalWaker. #[inline] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] #[unstable(feature = "local_waker", issue = "none")] - pub const fn new() -> Self { - ContextBuilder { waker: None, local_waker: None } + pub const fn from_local_waker(local_waker: &'a LocalWaker) -> Self { + Self { local_waker, waker: None } } /// This field is used to set the value of the waker on `Context`. + #[inline] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] #[unstable(feature = "local_waker", issue = "none")] @@ -291,58 +306,19 @@ impl<'a> ContextBuilder<'a> { } /// This method is used to set the value for the local waker on `Context`. - /// - /// # Examples - /// ``` - /// #![feature(local_waker)] - /// #![feature(noop_waker)] - /// - /// use std::task; - /// use std::pin; - /// use std::future::Future; - /// - /// let local_waker = task::LocalWaker::noop(); - /// - /// let mut context = task::ContextBuilder::new() - /// .local_waker(&local_waker) - /// .build(); - /// - /// let future = pin::pin!(async { 20 }); - /// - /// let poll = future.poll(&mut context); - /// - /// assert_eq!(poll, task::Poll::Ready(20)); - /// ``` #[inline] #[unstable(feature = "local_waker", issue = "none")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn local_waker(self, local_waker: &'a LocalWaker) -> Self { - Self { local_waker: Some(local_waker), ..self } + Self { local_waker, ..self } } /// Builds the `Context`. - /// - /// # Panics - /// Panics if no `Waker` or `LocalWaker` is set. #[inline] #[unstable(feature = "local_waker", issue = "none")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn build(self) -> Context<'a> { let ContextBuilder { waker, local_waker } = self; - assert!( - waker.is_some() || local_waker.is_some(), - "at least one waker must be set with either the `local_waker` or `waker` methods on `ContextBuilder`." - ); - let local_waker = match local_waker { - Some(local_waker) => local_waker, - None => { - // SAFETY: - // It is safe to transmute a `&Waker` into a `&LocalWaker` since both are a transparent - // wrapper around a local waker. Also, the Option<&Waker> here cannot be None because - // of the previous assert. - unsafe { transmute(self.waker) } - } - }; Context { waker, local_waker, _marker: PhantomData, _marker2: PhantomData } } } @@ -549,8 +525,8 @@ impl fmt::Debug for Waker { /// Local wakers can be requested from a `Context` with the [`local_waker`] method. /// /// The typical life of a `LocalWaker` is that it is constructed by an executor, wrapped in a -/// [`Context`], then passed to [`Future::poll()`]. Then, if the future chooses to return -/// [`Poll::Pending`], it must also store the waker somehow and call [`Waker::wake()`] when +/// [`Context`] using [`ContextBuilder`], then passed to [`Future::poll()`]. Then, if the future chooses to return +/// [`Poll::Pending`], it must also store the waker somehow and call [`LocalWaker::wake()`] when /// the future should be polled again. /// /// Implements [`Clone`], but neither [`Send`] nor [`Sync`]; therefore, a local waker may @@ -564,7 +540,7 @@ impl fmt::Debug for Waker { /// unnecessarily if the two wakers [wake the same task](Self::will_wake). /// /// # Examples -/// +/// Usage of a local waker to implement a future /// ``` /// #![feature(local_waker)] /// use std::future::{Future, poll_fn}; @@ -582,12 +558,13 @@ impl fmt::Debug for Waker { /// return Poll::Ready(()) /// }) /// } +/// /// # #[allow(unused_must_use)] /// # async fn __() { /// yield_now().await; /// # } /// ``` -/// +/// /// [`Future::poll()`]: core::future::Future::poll /// [`Poll::Pending`]: core::task::Poll::Pending /// [`local_waker`]: core::task::Context::local_waker From 403718b19de6ad0979833fd2cf6074f2a1b50c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Wed, 6 Dec 2023 14:09:16 -0300 Subject: [PATCH 10/30] feat: add try_waker and From<&mut Context> for ContextBuilder to allow the extention of contexts by futures --- library/alloc/src/task.rs | 20 +++++---- library/core/src/task/wake.rs | 79 +++++++++++++++++++++++++++++------ 2 files changed, 78 insertions(+), 21 deletions(-) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 16ae5241da8a3..736a55700f63e 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -165,16 +165,15 @@ fn raw_waker(waker: Arc) -> RawWaker { /// # Examples /// -/// A -/// /// This is a simplified example of a `spawn` and a `block_on` function. The `spawn` function /// is used to push new tasks onto the run queue, while the block on function will remove them /// and poll them. When a task is woken, it will put itself back on the run queue to be polled by the executor. /// /// **Note:** A real world example would interlieve poll calls with calls to an io reactor to wait for events instead -/// of spinning on a loop. +/// of spinning on a loop. /// /// ```rust +/// #![feature(local_waker)] /// use std::task::{LocalWake, ContextBuilder, LocalWaker}; /// use std::future::Future; /// use std::pin::Pin; @@ -204,9 +203,9 @@ fn raw_waker(waker: Arc) -> RawWaker { /// where /// F: Future + 'static + Send + Sync /// { -/// let task = Rc::new(Box::pin(future)); +/// let task = RefCell::new(Box::pin(future)); /// RUN_QUEUE.with_borrow_mut(|queue| { -/// queue.push_back(task) +/// queue.push_back(Rc::new(Task(task))); /// }); /// } /// @@ -221,19 +220,22 @@ fn raw_waker(waker: Arc) -> RawWaker { /// return; /// }; /// // cast the Rc into a `LocalWaker` -/// let waker: LocalWaker = task.into(); +/// let waker: LocalWaker = task.clone().into(); /// // Build the context using `ContextBuilder` -/// let mut cx = ContextBuilder::new() -/// .local_waker(&waker) +/// let mut cx = ContextBuilder::from_local_waker(&waker) /// .build(); /// /// // Poll the task -/// task.0 +/// let _ = task.0 /// .borrow_mut() /// .as_mut() /// .poll(&mut cx); /// } /// } +/// +/// block_on(async { +/// println!("hello world"); +/// }); /// ``` /// #[unstable(feature = "local_waker", issue = "none")] diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index da36194d462d3..fa4590f18ec98 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -225,21 +225,32 @@ impl<'a> Context<'a> { /// # Panics /// This function will panic if no `Waker` was set on the context. This happens if /// the executor does not support working with thread safe wakers. An alternative - /// may be to call [`.local_waker()`](Context::local_waker) instead. + /// may be to call [`.local_waker()`](Context::local_waker) instead. For a fallible + /// version of this function see [`.try_waker()`](Context::try_waker). + #[inline] + #[must_use] #[stable(feature = "futures_api", since = "1.36.0")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[must_use] - #[inline] pub const fn waker(&self) -> &'a Waker { &self .waker .expect("no waker was set on this context, consider calling `local_waker` instead.") } /// Returns a reference to the [`LocalWaker`] for the current task. + #[inline] #[unstable(feature = "local_waker", issue = "none")] - pub fn local_waker(&self) -> &'a LocalWaker { + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn local_waker(&self) -> &'a LocalWaker { &self.local_waker } + /// Returns a `Some(&Waker)` if a waker was defined on the `Context`, + /// otherwise it returns `None`. + #[inline] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[unstable(feature = "local_waker", issue = "none")] + pub const fn try_waker(&self) -> Option<&'a Waker> { + self.waker + } } #[stable(feature = "futures_api", since = "1.36.0")] @@ -256,18 +267,19 @@ impl fmt::Debug for Context<'_> { /// ``` /// #![feature(local_waker)] /// #![feature(noop_waker)] -/// use std::task::{ContextBuilder, LocalWaker, Waker}; -/// +/// use std::task::{ContextBuilder, LocalWaker, Waker, Poll}; +/// use std::future::Future; +/// /// let local_waker = LocalWaker::noop(); /// let waker = Waker::noop(); /// -/// let context = ContextBuilder::from_local_waker(&local_waker) +/// let mut cx = ContextBuilder::from_local_waker(&local_waker) /// .waker(&waker) /// .build(); /// -/// let future = pin::pin!(async { 20 }); -/// let poll = future.poll(&mut context); -/// assert_eq!(poll, task::Poll::Ready(20)); +/// let mut future = std::pin::pin!(async { 20 }); +/// let poll = future.as_mut().poll(&mut cx); +/// assert_eq!(poll, Poll::Ready(20)); /// /// ``` #[unstable(feature = "local_waker", issue = "none")] @@ -323,6 +335,50 @@ impl<'a> ContextBuilder<'a> { } } +/// Construct a `ContextBuilder`` from a `Context`. This is useful for +/// overriding values from a context. +/// +/// # Examples +/// An example of a future that allows to set a Waker on Context if none was defined. +/// This can be used to await futures that require a `Waker` even if the runtime does not +/// support `Waker`. +/// ```rust +/// #![feature(noop_waker, local_waker)] +/// use std::task::{Waker, ContextBuilder}; +/// use std::future::{poll_fn, Future}; +/// use std::pin::pin; +/// +/// async fn with_waker(f: F, waker: &Waker) -> F::Output +/// where +/// F: Future +/// { +/// let mut f = pin!(f); +/// poll_fn(move |cx| { +/// let has_waker = cx.try_waker().is_some(); +/// if has_waker { +/// return f.as_mut().poll(cx); +/// } +/// +/// let mut cx = ContextBuilder::from(cx) +/// .waker(&waker) +/// .build(); +/// f.as_mut().poll(&mut cx) +/// }).await +/// } +/// +/// # async fn __() { +/// with_waker(async { /* ... */ }, &Waker::noop()).await; +/// # } +/// ``` +#[unstable(feature = "local_waker", issue = "none")] +impl<'a> From<&mut Context<'a>> for ContextBuilder<'a> { + #[inline] + fn from(value: &mut Context<'a>) -> Self { + let Context { waker, local_waker, .. } = *value; + ContextBuilder { waker, local_waker } + } +} + /// A `Waker` is a handle for waking up a task by notifying its executor that it /// is ready to be run. /// @@ -559,12 +615,11 @@ impl fmt::Debug for Waker { /// }) /// } /// -/// # #[allow(unused_must_use)] /// # async fn __() { /// yield_now().await; /// # } /// ``` -/// +/// /// [`Future::poll()`]: core::future::Future::poll /// [`Poll::Pending`]: core::task::Poll::Pending /// [`local_waker`]: core::task::Context::local_waker From 2012d4b70370d86c9ed0c60a2ad6bf03d9fc5157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Wed, 6 Dec 2023 15:19:24 -0300 Subject: [PATCH 11/30] fix: make LocalWake available in targets that don't support atomics by removing a #[cfg(target_has_atomic = ptr)] --- library/alloc/src/lib.rs | 2 +- library/alloc/src/task.rs | 18 +++++++++++------- library/core/src/task/wake.rs | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index c8b4cebdf8987..878aedce3a6a9 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -254,7 +254,7 @@ pub mod str; pub mod string; #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] pub mod sync; -#[cfg(all(not(no_global_oom_handling), not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +#[cfg(all(not(no_global_oom_handling), not(no_rc), not(no_sync)))] pub mod task; #[cfg(test)] mod tests; diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 736a55700f63e..9db4c9195052b 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -2,15 +2,17 @@ //! Types and Traits for working with asynchronous tasks. //! -//! **Note**: This module is only available on platforms that support atomic -//! loads and stores of pointers. This may be detected at compile time using +//! **Note**: Some of the types in this module are only available +//! on platforms that support atomic loads and stores of pointers. +//! This may be detected at compile time using //! `#[cfg(target_has_atomic = "ptr")]`. use core::mem::ManuallyDrop; -use core::task::{LocalWaker, RawWaker, RawWakerVTable, Waker}; - +use core::task::{LocalWaker, RawWaker, RawWakerVTable}; use crate::rc::Rc; -use crate::sync::Arc; + +#[cfg(target_has_atomic = "ptr")] +use core::{task::Waker, sync::Arc}; /// The implementation of waking a task on an executor. /// @@ -74,6 +76,7 @@ use crate::sync::Arc; /// println!("Hi from inside a future!"); /// }); /// ``` +#[cfg(target_has_atomic = "ptr")] #[stable(feature = "wake_trait", since = "1.51.0")] pub trait Wake { /// Wake this task. @@ -92,7 +95,7 @@ pub trait Wake { self.clone().wake(); } } - +#[cfg(target_has_atomic = "ptr")] #[stable(feature = "wake_trait", since = "1.51.0")] impl From> for Waker { /// Use a `Wake`-able type as a `Waker`. @@ -104,7 +107,7 @@ impl From> for Waker { unsafe { Waker::from_raw(raw_waker(waker)) } } } - +#[cfg(target_has_atomic = "ptr")] #[stable(feature = "wake_trait", since = "1.51.0")] impl From> for RawWaker { /// Use a `Wake`-able type as a `RawWaker`. @@ -120,6 +123,7 @@ impl From> for RawWaker { // the safety of `From> for Waker` does not depend on the correct // trait dispatch - instead both impls call this function directly and // explicitly. +#[cfg(target_has_atomic = "ptr")] #[inline(always)] fn raw_waker(waker: Arc) -> RawWaker { // Increment the reference count of the arc to clone it. diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index fa4590f18ec98..5696b63e5faf7 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -347,7 +347,7 @@ impl<'a> ContextBuilder<'a> { /// use std::task::{Waker, ContextBuilder}; /// use std::future::{poll_fn, Future}; /// use std::pin::pin; -/// +/// /// async fn with_waker(f: F, waker: &Waker) -> F::Output /// where /// F: Future @@ -365,7 +365,7 @@ impl<'a> ContextBuilder<'a> { /// f.as_mut().poll(&mut cx) /// }).await /// } -/// +/// /// # async fn __() { /// with_waker(async { /* ... */ }, &Waker::noop()).await; /// # } From 0cb7a0a90ef5862b87083546accb12467b279eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Thu, 14 Dec 2023 23:15:01 -0300 Subject: [PATCH 12/30] chore: add tracking issue number to local waker feature --- library/alloc/src/task.rs | 22 ++++++++++++---------- library/core/src/task/wake.rs | 28 ++++++++++++++-------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 9db4c9195052b..ce7aa3c44dc84 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -3,16 +3,18 @@ //! Types and Traits for working with asynchronous tasks. //! //! **Note**: Some of the types in this module are only available -//! on platforms that support atomic loads and stores of pointers. -//! This may be detected at compile time using +//! on platforms that support atomic loads and stores of pointers. +//! This may be detected at compile time using //! `#[cfg(target_has_atomic = "ptr")]`. +use crate::rc::Rc; use core::mem::ManuallyDrop; use core::task::{LocalWaker, RawWaker, RawWakerVTable}; -use crate::rc::Rc; #[cfg(target_has_atomic = "ptr")] -use core::{task::Waker, sync::Arc}; +use crate::sync::Arc; +#[cfg(target_has_atomic = "ptr")] +use core::task::Waker; /// The implementation of waking a task on an executor. /// @@ -174,7 +176,7 @@ fn raw_waker(waker: Arc) -> RawWaker { /// and poll them. When a task is woken, it will put itself back on the run queue to be polled by the executor. /// /// **Note:** A real world example would interlieve poll calls with calls to an io reactor to wait for events instead -/// of spinning on a loop. +/// of spinning on a loop. /// /// ```rust /// #![feature(local_waker)] @@ -242,10 +244,10 @@ fn raw_waker(waker: Arc) -> RawWaker { /// }); /// ``` /// -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] pub trait LocalWake { /// Wake this task. - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] fn wake(self: Rc); /// Wake this task without consuming the local waker. @@ -255,13 +257,13 @@ pub trait LocalWake { /// [`Rc`] and calls [`wake`] on the clone. /// /// [`wake`]: Rc::wake - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] fn wake_by_ref(self: &Rc) { self.clone().wake(); } } -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] impl From> for LocalWaker { /// Use a `Wake`-able type as a `LocalWaker`. /// @@ -273,7 +275,7 @@ impl From> for LocalWaker { } } #[allow(ineffective_unstable_trait_impl)] -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] impl From> for RawWaker { /// Use a `Wake`-able type as a `RawWaker`. /// diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 5696b63e5faf7..c02504b386e16 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -238,7 +238,7 @@ impl<'a> Context<'a> { } /// Returns a reference to the [`LocalWaker`] for the current task. #[inline] - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn local_waker(&self) -> &'a LocalWaker { &self.local_waker @@ -247,7 +247,7 @@ impl<'a> Context<'a> { /// otherwise it returns `None`. #[inline] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] pub const fn try_waker(&self) -> Option<&'a Waker> { self.waker } @@ -282,7 +282,7 @@ impl fmt::Debug for Context<'_> { /// assert_eq!(poll, Poll::Ready(20)); /// /// ``` -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] #[derive(Debug)] pub struct ContextBuilder<'a> { waker: Option<&'a Waker>, @@ -293,7 +293,7 @@ impl<'a> ContextBuilder<'a> { /// Create a ContextBuilder from a Waker. #[inline] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] pub const fn from_waker(waker: &'a Waker) -> Self { // SAFETY: LocalWaker is just Waker without thread safety let local_waker = unsafe { transmute(waker) }; @@ -303,7 +303,7 @@ impl<'a> ContextBuilder<'a> { /// Create a ContextBuilder from a LocalWaker. #[inline] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] pub const fn from_local_waker(local_waker: &'a LocalWaker) -> Self { Self { local_waker, waker: None } } @@ -312,14 +312,14 @@ impl<'a> ContextBuilder<'a> { #[inline] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] pub const fn waker(self, waker: &'a Waker) -> Self { Self { waker: Some(waker), ..self } } /// This method is used to set the value for the local waker on `Context`. #[inline] - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn local_waker(self, local_waker: &'a LocalWaker) -> Self { Self { local_waker, ..self } @@ -327,7 +327,7 @@ impl<'a> ContextBuilder<'a> { /// Builds the `Context`. #[inline] - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn build(self) -> Context<'a> { let ContextBuilder { waker, local_waker } = self; @@ -370,7 +370,7 @@ impl<'a> ContextBuilder<'a> { /// with_waker(async { /* ... */ }, &Waker::noop()).await; /// # } /// ``` -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] impl<'a> From<&mut Context<'a>> for ContextBuilder<'a> { #[inline] fn from(value: &mut Context<'a>) -> Self { @@ -623,19 +623,19 @@ impl fmt::Debug for Waker { /// [`Future::poll()`]: core::future::Future::poll /// [`Poll::Pending`]: core::task::Poll::Pending /// [`local_waker`]: core::task::Context::local_waker -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] #[repr(transparent)] pub struct LocalWaker { waker: RawWaker, } -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] impl Unpin for LocalWaker {} impl LocalWaker { /// Creates a new `LocalWaker` from [`RawWaker`]. /// - /// The behavior of the returned `Waker` is undefined if the contract defined + /// The behavior of the returned `LocalWaker` is undefined if the contract defined /// in [`RawWaker`]'s and [`RawWakerVTable`]'s documentation is not upheld. /// Therefore this method is unsafe. #[inline] @@ -651,7 +651,7 @@ impl LocalWaker { /// As long as the executor keeps running and the task is not finished, it is /// guaranteed that each invocation of [`wake()`](Self::wake) (or /// [`wake_by_ref()`](Self::wake_by_ref)) will be followed by at least one - /// [`poll()`] of the task to which this `Waker` belongs. This makes + /// [`poll()`] of the task to which this `LocalWaker` belongs. This makes /// it possible to temporarily yield to other tasks while running potentially /// unbounded processing loops. /// @@ -749,7 +749,7 @@ impl LocalWaker { unsafe { (self.waker.vtable.wake_by_ref)(self.waker.data) } } } -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] impl Clone for LocalWaker { #[inline] fn clone(&self) -> Self { From f82437396fe2d21ae73134a6faa6c58bd03d96e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Thu, 14 Dec 2023 23:15:27 -0300 Subject: [PATCH 13/30] feat: impl AsRef for Waker. --- library/core/src/task/wake.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index c02504b386e16..e90994df0c720 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -769,6 +769,14 @@ impl Clone for LocalWaker { } } +#[unstable(feature = "local_waker", issue = "118959")] +impl AsRef for Waker { + fn as_ref(&self) -> &LocalWaker { + // SAFETY: LocalWaker is just Waker without thread safety + unsafe { transmute(self) } + } +} + #[stable(feature = "futures_api", since = "1.36.0")] impl fmt::Debug for LocalWaker { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { From 093f80ba7edc53a7e53c949fb0d083f00088d45c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Thu, 14 Dec 2023 23:38:58 -0300 Subject: [PATCH 14/30] chore: fix ci failures --- library/alloc/src/task.rs | 2 +- library/core/src/task/wake.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index ce7aa3c44dc84..611468a9feaab 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -256,7 +256,7 @@ pub trait LocalWake { /// waker, it should override this method. By default, it clones the /// [`Rc`] and calls [`wake`] on the clone. /// - /// [`wake`]: Rc::wake + /// [`wake`]: LocalWaker::wake #[unstable(feature = "local_waker", issue = "118959")] fn wake_by_ref(self: &Rc) { self.clone().wake(); diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index e90994df0c720..54528ad83a04e 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -269,7 +269,7 @@ impl fmt::Debug for Context<'_> { /// #![feature(noop_waker)] /// use std::task::{ContextBuilder, LocalWaker, Waker, Poll}; /// use std::future::Future; -/// +/// /// let local_waker = LocalWaker::noop(); /// let waker = Waker::noop(); /// @@ -347,7 +347,7 @@ impl<'a> ContextBuilder<'a> { /// use std::task::{Waker, ContextBuilder}; /// use std::future::{poll_fn, Future}; /// use std::pin::pin; -/// +/// /// async fn with_waker(f: F, waker: &Waker) -> F::Output /// where /// F: Future @@ -365,7 +365,7 @@ impl<'a> ContextBuilder<'a> { /// f.as_mut().poll(&mut cx) /// }).await /// } -/// +/// /// # async fn __() { /// with_waker(async { /* ... */ }, &Waker::noop()).await; /// # } From ad28f755d82308b1e30f504d241b810f396233d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Fri, 15 Dec 2023 10:20:33 -0300 Subject: [PATCH 15/30] fix: change issue number of waker_getters from #87021 to #96992. --- library/core/src/task/wake.rs | 2 +- src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 54528ad83a04e..3fc97a199a6bf 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -713,7 +713,7 @@ impl LocalWaker { /// Get a reference to the underlying [`RawWaker`]. #[inline] #[must_use] - #[unstable(feature = "waker_getters", issue = "87021")] + #[unstable(feature = "waker_getters", issue = "96992")] pub fn as_raw(&self) -> &RawWaker { &self.waker } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index 1cb6ff8627a23..c3b806dec4dda 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -9630,9 +9630,9 @@ The tracking issue for this feature is: [#81944] label: "waker_getters", description: r##"# `waker_getters` -The tracking issue for this feature is: [#87021] +The tracking issue for this feature is: [#96992] -[#87021]: https://github.com/rust-lang/rust/issues/87021 +[#96992]: https://github.com/rust-lang/rust/issues/96992 ------------------------ "##, From 3e373f5ee7ab5cf6d9fe986f697fe27fe90c53ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Fri, 15 Dec 2023 10:30:43 -0300 Subject: [PATCH 16/30] chore: add and !Sync impls for LocalWaker as a stability guarantee. --- library/core/src/task/wake.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 3fc97a199a6bf..40f0b0c51446f 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -787,3 +787,8 @@ impl fmt::Debug for LocalWaker { .finish() } } + +#[unstable(feature = "local_waker", issue = "118959")] +impl !Send for LocalWaker {} +#[unstable(feature = "local_waker", issue = "118959")] +impl !Sync for LocalWaker {} From a8e71f225817d11acd9ba63721d539fb998ef319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Fri, 15 Dec 2023 15:10:15 -0300 Subject: [PATCH 17/30] doc: update thread safety explanation for RawWakerVTable and RawWaker. --- library/core/src/task/wake.rs | 62 ++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 40f0b0c51446f..3fa49d1ea0654 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -7,7 +7,7 @@ use crate::marker::PhantomData; use crate::ptr; /// A `RawWaker` allows the implementor of a task executor to create a [`Waker`] -/// which provides customized wakeup behavior. +/// or a [`LocalWaker`] which provides customized wakeup behavior. /// /// [vtable]: https://en.wikipedia.org/wiki/Virtual_method_table /// @@ -35,9 +35,18 @@ impl RawWaker { /// The value of this pointer will get passed to all functions that are part /// of the `vtable` as the first parameter. /// + /// It is important to consider that the `data` pointer must point to a + /// thread safe type such as an `[Arc]` + /// when used to construct a [`Waker`]. This restriction is lifted when + /// constructing a [`LocalWaker`], which allows using types that do not implement + /// [Send] + [Sync] like `[Rc]`. + /// /// The `vtable` customizes the behavior of a `Waker` which gets created /// from a `RawWaker`. For each operation on the `Waker`, the associated /// function in the `vtable` of the underlying `RawWaker` will be called. + /// + /// [`Arc`]: std::sync::Arc + /// [`Rc`]: std::rc::Rc #[inline] #[rustc_promotable] #[stable(feature = "futures_api", since = "1.36.0")] @@ -90,11 +99,19 @@ impl RawWaker { /// [`RawWaker`] implementation. Calling one of the contained functions using /// any other `data` pointer will cause undefined behavior. /// -/// These functions must all be thread-safe (even though [`RawWaker`] is -/// \![Send] + \![Sync]) -/// because [`Waker`] is [Send] + [Sync], and thus wakers may be moved to -/// arbitrary threads or invoked by `&` reference. For example, this means that if the -/// `clone` and `drop` functions manage a reference count, they must do so atomically. +/// # Thread safety +/// If the [`RawWaker`] will be used to construct a [`Waker`] then +/// these functions must all be thread-safe (even though [`RawWaker`] is +/// \![Send] + \![Sync]). This is because [`Waker`] is [Send] + [Sync], +/// and it may be moved to arbitrary threads or invoked by `&` reference. For example, +/// this means that if the `clone` and `drop` functions manage a reference count, +/// they must do so atomically. +/// +/// However, if the [`RawWaker`] will be used to construct a [`LocalWaker`] instead, then +/// these functions don't need to be thread safe. This means that \![Send] + \![Sync] +/// data can be stored in the data pointer, and reference counting does not need any atomic +/// synchronization. This is because [`LocalWaker`] is not thread safe itself, so it cannot +/// be sent across threads. #[stable(feature = "futures_api", since = "1.36.0")] #[derive(PartialEq, Copy, Clone, Debug)] pub struct RawWakerVTable { @@ -134,16 +151,22 @@ impl RawWakerVTable { /// Creates a new `RawWakerVTable` from the provided `clone`, `wake`, /// `wake_by_ref`, and `drop` functions. /// - /// These functions must all be thread-safe (even though [`RawWaker`] is - /// \![Send] + \![Sync]) - /// because [`Waker`] is [Send] + [Sync], and thus wakers may be moved to - /// arbitrary threads or invoked by `&` reference. For example, this means that if the - /// `clone` and `drop` functions manage a reference count, they must do so atomically. - /// + /// If the [`RawWaker`] will be used to construct a [`Waker`] then + /// these functions must all be thread-safe (even though [`RawWaker`] is + /// \![Send] + \![Sync]). This is because [`Waker`] is [Send] + [Sync], + /// and it may be moved to arbitrary threads or invoked by `&` reference. For example, + /// this means that if the `clone` and `drop` functions manage a reference count, + /// they must do so atomically. + /// + /// However, if the [`RawWaker`] will be used to construct a [`LocalWaker`] instead, then + /// these functions don't need to be thread safe. This means that \![Send] + \![Sync] + /// data can be stored in the data pointer, and reference counting does not need any atomic + /// synchronization. This is because [`LocalWaker`] is not thread safe itself, so it cannot + /// be sent across threads. /// # `clone` /// /// This function will be called when the [`RawWaker`] gets cloned, e.g. when - /// the [`Waker`] in which the [`RawWaker`] is stored gets cloned. + /// the [`Waker`]/[`LocalWaker`] in which the [`RawWaker`] is stored gets cloned. /// /// The implementation of this function must retain all resources that are /// required for this additional instance of a [`RawWaker`] and associated @@ -169,7 +192,7 @@ impl RawWakerVTable { /// /// # `drop` /// - /// This function gets called when a [`Waker`] gets dropped. + /// This function gets called when a [`Waker`]/[`LocalWaker`] gets dropped. /// /// The implementation of this function must make sure to release any /// resources that are associated with this instance of a [`RawWaker`] and @@ -335,13 +358,13 @@ impl<'a> ContextBuilder<'a> { } } -/// Construct a `ContextBuilder`` from a `Context`. This is useful for +/// Construct a [`ContextBuilder`] from a [`Context`]. This is useful for /// overriding values from a context. /// /// # Examples -/// An example of a future that allows to set a Waker on Context if none was defined. -/// This can be used to await futures that require a `Waker` even if the runtime does not -/// support `Waker`. +/// An example of a future that allows to set a [`Waker`] on Context if none was defined. +/// This can be used to await futures that require a [`Waker`] even if the runtime does not +/// support [`Waker`]. /// ```rust /// #![feature(noop_waker, local_waker)] /// use std::task::{Waker, ContextBuilder}; @@ -596,7 +619,7 @@ impl fmt::Debug for Waker { /// unnecessarily if the two wakers [wake the same task](Self::will_wake). /// /// # Examples -/// Usage of a local waker to implement a future +/// Usage of a local waker to implement a future analogous to `std::thread::yield_now()`. /// ``` /// #![feature(local_waker)] /// use std::future::{Future, poll_fn}; @@ -623,6 +646,7 @@ impl fmt::Debug for Waker { /// [`Future::poll()`]: core::future::Future::poll /// [`Poll::Pending`]: core::task::Poll::Pending /// [`local_waker`]: core::task::Context::local_waker + #[unstable(feature = "local_waker", issue = "118959")] #[repr(transparent)] pub struct LocalWaker { From c67a446e72764663892606b131ed35c6e32e7133 Mon Sep 17 00:00:00 2001 From: tvallotton <57121854+tvallotton@users.noreply.github.com> Date: Sat, 13 Jan 2024 10:44:01 -0300 Subject: [PATCH 18/30] fix: Apply suggestions from code review Co-authored-by: Mark Rousskov --- library/alloc/src/task.rs | 6 +++--- library/core/src/task/wake.rs | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 611468a9feaab..7dad2f3f82270 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -168,14 +168,14 @@ fn raw_waker(waker: Arc) -> RawWaker { /// to hold data that does not implement `Send` and `Sync`. Additionally, it saves calls /// to `Arc::clone`, which requires atomic synchronization. /// - +/// /// # Examples /// /// This is a simplified example of a `spawn` and a `block_on` function. The `spawn` function /// is used to push new tasks onto the run queue, while the block on function will remove them /// and poll them. When a task is woken, it will put itself back on the run queue to be polled by the executor. /// -/// **Note:** A real world example would interlieve poll calls with calls to an io reactor to wait for events instead +/// **Note:** A real world example would interleave poll calls with calls to an io reactor to wait for events instead /// of spinning on a loop. /// /// ```rust @@ -221,7 +221,7 @@ fn raw_waker(waker: Arc) -> RawWaker { /// { /// spawn(future); /// loop { -/// let Some(task) = RUN_QUEUE.with_borrow_mut(|queue|queue.pop_front()) else { +/// let Some(task) = RUN_QUEUE.with_borrow_mut(|queue| queue.pop_front()) else { /// // we exit, since there are no more tasks remaining on the queue /// return; /// }; diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 3fa49d1ea0654..da34e070e2922 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -332,7 +332,6 @@ impl<'a> ContextBuilder<'a> { } /// This field is used to set the value of the waker on `Context`. - #[inline] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] #[unstable(feature = "local_waker", issue = "118959")] @@ -598,6 +597,7 @@ impl fmt::Debug for Waker { } /// A `LocalWaker` is analogous to a [`Waker`], but it does not implement [`Send`] or [`Sync`]. +/// /// This handle encapsulates a [`RawWaker`] instance, which defines the /// executor-specific wakeup behavior. /// @@ -646,9 +646,8 @@ impl fmt::Debug for Waker { /// [`Future::poll()`]: core::future::Future::poll /// [`Poll::Pending`]: core::task::Poll::Pending /// [`local_waker`]: core::task::Context::local_waker - #[unstable(feature = "local_waker", issue = "118959")] -#[repr(transparent)] +#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/66401 pub struct LocalWaker { waker: RawWaker, } From eccb5e7c1bc3b36ed19bbca75ddf80470d3f0caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Sat, 13 Jan 2024 11:14:04 -0300 Subject: [PATCH 19/30] docs: remove recommendations to use LocalWaker in stable API documentation --- library/core/src/task/wake.rs | 103 +++++++++++++++------------------- 1 file changed, 46 insertions(+), 57 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index da34e070e2922..e64a25960e05e 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -39,7 +39,7 @@ impl RawWaker { /// thread safe type such as an `[Arc]` /// when used to construct a [`Waker`]. This restriction is lifted when /// constructing a [`LocalWaker`], which allows using types that do not implement - /// [Send] + [Sync] like `[Rc]`. + /// [Send] + [Sync] like `[Rc]`. /// /// The `vtable` customizes the behavior of a `Waker` which gets created /// from a `RawWaker`. For each operation on the `Waker`, the associated @@ -240,16 +240,6 @@ impl<'a> Context<'a> { } /// Returns a reference to the [`Waker`] for the current task. - /// - /// Note that if the waker does not need to be sent across threads, it - /// is preferable to call `local_waker`, which is more portable and - /// potentially more efficient. - /// - /// # Panics - /// This function will panic if no `Waker` was set on the context. This happens if - /// the executor does not support working with thread safe wakers. An alternative - /// may be to call [`.local_waker()`](Context::local_waker) instead. For a fallible - /// version of this function see [`.try_waker()`](Context::try_waker). #[inline] #[must_use] #[stable(feature = "futures_api", since = "1.36.0")] @@ -396,7 +386,7 @@ impl<'a> ContextBuilder<'a> { impl<'a> From<&mut Context<'a>> for ContextBuilder<'a> { #[inline] fn from(value: &mut Context<'a>) -> Self { - let Context { waker, local_waker, .. } = *value; + let Context { waker, local_waker, _marker, _marker2 } = *value; ContextBuilder { waker, local_waker } } } @@ -415,8 +405,7 @@ impl<'a> From<&mut Context<'a>> for ContextBuilder<'a> { /// Implements [`Clone`], [`Send`], and [`Sync`]; therefore, a waker may be invoked /// from any thread, including ones not in any way managed by the executor. For example, /// this might be done to wake a future when a blocking function call completes on another -/// thread. If the waker does not need to be moved across threads, it is better to use -/// [`LocalWaker`], which the executor may use to skip unnecessary memory synchronization. +/// thread. /// /// Note that it is preferable to use `waker.clone_from(&new_waker)` instead /// of `*waker = new_waker.clone()`, as the former will avoid cloning the waker @@ -656,19 +645,6 @@ pub struct LocalWaker { impl Unpin for LocalWaker {} impl LocalWaker { - /// Creates a new `LocalWaker` from [`RawWaker`]. - /// - /// The behavior of the returned `LocalWaker` is undefined if the contract defined - /// in [`RawWaker`]'s and [`RawWakerVTable`]'s documentation is not upheld. - /// Therefore this method is unsafe. - #[inline] - #[must_use] - #[stable(feature = "futures_api", since = "1.36.0")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - pub const unsafe fn from_raw(waker: RawWaker) -> LocalWaker { - Self { waker } - } - /// Wake up the task associated with this `LocalWaker`. /// /// As long as the executor keeps running and the task is not finished, it is @@ -703,6 +679,37 @@ impl LocalWaker { unsafe { (wake)(data) }; } + /// Wake up the task associated with this `LocalWaker` without consuming the `LocalWaker`. + /// + /// This is similar to [`wake()`](Self::wake), but may be slightly less efficient in + /// the case where an owned `Waker` is available. This method should be preferred to + /// calling `waker.clone().wake()`. + #[inline] + #[stable(feature = "futures_api", since = "1.36.0")] + pub fn wake_by_ref(&self) { + // The actual wakeup call is delegated through a virtual function call + // to the implementation which is defined by the executor. + + // SAFETY: see `wake` + unsafe { (self.waker.vtable.wake_by_ref)(self.waker.data) } + } + + /// Returns `true` if this `LocalWaker` and another `LocalWaker` would awake the same task. + /// + /// This function works on a best-effort basis, and may return false even + /// when the `Waker`s would awaken the same task. However, if this function + /// returns `true`, it is guaranteed that the `Waker`s will awaken the same task. + /// + /// This function is primarily used for optimization purposes — for example, + /// this type's [`clone_from`](Self::clone_from) implementation uses it to + /// avoid cloning the waker when they would wake the same task anyway. + #[inline] + #[must_use] + #[stable(feature = "futures_api", since = "1.36.0")] + pub fn will_wake(&self, other: &LocalWaker) -> bool { + self.waker == other.waker + } + /// Creates a new `LocalWaker` that does nothing when `wake` is called. /// /// This is mostly useful for writing tests that need a [`Context`] to poll @@ -733,43 +740,25 @@ impl LocalWaker { WAKER } - /// Get a reference to the underlying [`RawWaker`]. - #[inline] - #[must_use] - #[unstable(feature = "waker_getters", issue = "96992")] - pub fn as_raw(&self) -> &RawWaker { - &self.waker - } - - /// Returns `true` if this `LocalWaker` and another `LocalWaker` would awake the same task. - /// - /// This function works on a best-effort basis, and may return false even - /// when the `Waker`s would awaken the same task. However, if this function - /// returns `true`, it is guaranteed that the `Waker`s will awaken the same task. + /// Creates a new `LocalWaker` from [`RawWaker`]. /// - /// This function is primarily used for optimization purposes — for example, - /// this type's [`clone_from`](Self::clone_from) implementation uses it to - /// avoid cloning the waker when they would wake the same task anyway. + /// The behavior of the returned `LocalWaker` is undefined if the contract defined + /// in [`RawWaker`]'s and [`RawWakerVTable`]'s documentation is not upheld. + /// Therefore this method is unsafe. #[inline] #[must_use] #[stable(feature = "futures_api", since = "1.36.0")] - pub fn will_wake(&self, other: &LocalWaker) -> bool { - self.waker == other.waker + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const unsafe fn from_raw(waker: RawWaker) -> LocalWaker { + Self { waker } } - /// Wake up the task associated with this `LocalWaker` without consuming the `LocalWaker`. - /// - /// This is similar to [`wake()`](Self::wake), but may be slightly less efficient in - /// the case where an owned `Waker` is available. This method should be preferred to - /// calling `waker.clone().wake()`. + /// Get a reference to the underlying [`RawWaker`]. #[inline] - #[stable(feature = "futures_api", since = "1.36.0")] - pub fn wake_by_ref(&self) { - // The actual wakeup call is delegated through a virtual function call - // to the implementation which is defined by the executor. - - // SAFETY: see `wake` - unsafe { (self.waker.vtable.wake_by_ref)(self.waker.data) } + #[must_use] + #[unstable(feature = "waker_getters", issue = "96992")] + pub fn as_raw(&self) -> &RawWaker { + &self.waker } } #[unstable(feature = "local_waker", issue = "118959")] From 7c6a9cbef103c0a9511aedad691a040ceb003917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Sat, 13 Jan 2024 11:18:18 -0300 Subject: [PATCH 20/30] chore: make method order consistent with waker --- library/core/src/task/wake.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index e64a25960e05e..6a5b5a3fb544d 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -710,6 +710,19 @@ impl LocalWaker { self.waker == other.waker } + /// Creates a new `LocalWaker` from [`RawWaker`]. + /// + /// The behavior of the returned `LocalWaker` is undefined if the contract defined + /// in [`RawWaker`]'s and [`RawWakerVTable`]'s documentation is not upheld. + /// Therefore this method is unsafe. + #[inline] + #[must_use] + #[stable(feature = "futures_api", since = "1.36.0")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const unsafe fn from_raw(waker: RawWaker) -> LocalWaker { + Self { waker } + } + /// Creates a new `LocalWaker` that does nothing when `wake` is called. /// /// This is mostly useful for writing tests that need a [`Context`] to poll @@ -740,19 +753,6 @@ impl LocalWaker { WAKER } - /// Creates a new `LocalWaker` from [`RawWaker`]. - /// - /// The behavior of the returned `LocalWaker` is undefined if the contract defined - /// in [`RawWaker`]'s and [`RawWakerVTable`]'s documentation is not upheld. - /// Therefore this method is unsafe. - #[inline] - #[must_use] - #[stable(feature = "futures_api", since = "1.36.0")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - pub const unsafe fn from_raw(waker: RawWaker) -> LocalWaker { - Self { waker } - } - /// Get a reference to the underlying [`RawWaker`]. #[inline] #[must_use] From 038c6e046c450082ebd087336d9b11979e0dd4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Sat, 13 Jan 2024 14:57:13 -0300 Subject: [PATCH 21/30] refactor: make waker mandatory. This also removes * impl From<&Context> for ContextBuilder * Context::try_waker() The from implementation is removed because now that wakers are always supported, there are less incentives to override the current context. Before, the incentive was to add Waker support to a reactor that didn't have any. --- library/alloc/src/task.rs | 16 +++--- library/core/src/task/wake.rs | 99 +++++++---------------------------- 2 files changed, 28 insertions(+), 87 deletions(-) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 7dad2f3f82270..357f7e0650ae7 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -173,14 +173,16 @@ fn raw_waker(waker: Arc) -> RawWaker { /// /// This is a simplified example of a `spawn` and a `block_on` function. The `spawn` function /// is used to push new tasks onto the run queue, while the block on function will remove them -/// and poll them. When a task is woken, it will put itself back on the run queue to be polled by the executor. +/// and poll them. When a task is woken, it will put itself back on the run queue to be polled +/// by the executor. /// -/// **Note:** A real world example would interleave poll calls with calls to an io reactor to wait for events instead -/// of spinning on a loop. +/// **Note:** This example trades correctness for simplicity. A real world example would interleave +/// poll calls with calls to an io reactor to wait for events instead of spinning on a loop. /// /// ```rust /// #![feature(local_waker)] -/// use std::task::{LocalWake, ContextBuilder, LocalWaker}; +/// #![feature(noop_waker)] +/// use std::task::{LocalWake, ContextBuilder, LocalWaker, Waker}; /// use std::future::Future; /// use std::pin::Pin; /// use std::rc::Rc; @@ -225,10 +227,12 @@ fn raw_waker(waker: Arc) -> RawWaker { /// // we exit, since there are no more tasks remaining on the queue /// return; /// }; +/// let waker = Waker::noop(); /// // cast the Rc into a `LocalWaker` -/// let waker: LocalWaker = task.clone().into(); +/// let local_waker: LocalWaker = task.clone().into(); /// // Build the context using `ContextBuilder` -/// let mut cx = ContextBuilder::from_local_waker(&waker) +/// let mut cx = ContextBuilder::from_waker(&waker) +/// .local_waker(&local_waker) /// .build(); /// /// // Poll the task diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 6a5b5a3fb544d..a746fe58f47de 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -217,7 +217,7 @@ impl RawWakerVTable { #[stable(feature = "futures_api", since = "1.36.0")] #[lang = "Context"] pub struct Context<'a> { - waker: Option<&'a Waker>, + waker: &'a Waker, local_waker: &'a LocalWaker, // Ensure we future-proof against variance changes by forcing // the lifetime to be invariant (argument-position lifetimes @@ -245,9 +245,7 @@ impl<'a> Context<'a> { #[stable(feature = "futures_api", since = "1.36.0")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn waker(&self) -> &'a Waker { - &self - .waker - .expect("no waker was set on this context, consider calling `local_waker` instead.") + &self.waker } /// Returns a reference to the [`LocalWaker`] for the current task. #[inline] @@ -256,14 +254,6 @@ impl<'a> Context<'a> { pub const fn local_waker(&self) -> &'a LocalWaker { &self.local_waker } - /// Returns a `Some(&Waker)` if a waker was defined on the `Context`, - /// otherwise it returns `None`. - #[inline] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[unstable(feature = "local_waker", issue = "118959")] - pub const fn try_waker(&self) -> Option<&'a Waker> { - self.waker - } } #[stable(feature = "futures_api", since = "1.36.0")] @@ -286,8 +276,8 @@ impl fmt::Debug for Context<'_> { /// let local_waker = LocalWaker::noop(); /// let waker = Waker::noop(); /// -/// let mut cx = ContextBuilder::from_local_waker(&local_waker) -/// .waker(&waker) +/// let mut cx = ContextBuilder::from_waker(&waker) +/// .local_waker(&local_waker) /// .build(); /// /// let mut future = std::pin::pin!(async { 20 }); @@ -298,8 +288,16 @@ impl fmt::Debug for Context<'_> { #[unstable(feature = "local_waker", issue = "118959")] #[derive(Debug)] pub struct ContextBuilder<'a> { - waker: Option<&'a Waker>, + waker: &'a Waker, local_waker: &'a LocalWaker, + // Ensure we future-proof against variance changes by forcing + // the lifetime to be invariant (argument-position lifetimes + // are contravariant while return-position lifetimes are + // covariant). + _marker: PhantomData &'a ()>, + // Ensure `Context` is `!Send` and `!Sync` in order to allow + // for future `!Send` and / or `!Sync` fields. + _marker2: PhantomData<*mut ()>, } impl<'a> ContextBuilder<'a> { @@ -310,23 +308,7 @@ impl<'a> ContextBuilder<'a> { pub const fn from_waker(waker: &'a Waker) -> Self { // SAFETY: LocalWaker is just Waker without thread safety let local_waker = unsafe { transmute(waker) }; - Self { waker: Some(waker), local_waker } - } - - /// Create a ContextBuilder from a LocalWaker. - #[inline] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[unstable(feature = "local_waker", issue = "118959")] - pub const fn from_local_waker(local_waker: &'a LocalWaker) -> Self { - Self { local_waker, waker: None } - } - - /// This field is used to set the value of the waker on `Context`. - #[inline] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[unstable(feature = "local_waker", issue = "118959")] - pub const fn waker(self, waker: &'a Waker) -> Self { - Self { waker: Some(waker), ..self } + Self { waker: waker, local_waker, _marker: PhantomData, _marker2: PhantomData } } /// This method is used to set the value for the local waker on `Context`. @@ -342,52 +324,8 @@ impl<'a> ContextBuilder<'a> { #[unstable(feature = "local_waker", issue = "118959")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn build(self) -> Context<'a> { - let ContextBuilder { waker, local_waker } = self; - Context { waker, local_waker, _marker: PhantomData, _marker2: PhantomData } - } -} - -/// Construct a [`ContextBuilder`] from a [`Context`]. This is useful for -/// overriding values from a context. -/// -/// # Examples -/// An example of a future that allows to set a [`Waker`] on Context if none was defined. -/// This can be used to await futures that require a [`Waker`] even if the runtime does not -/// support [`Waker`]. -/// ```rust -/// #![feature(noop_waker, local_waker)] -/// use std::task::{Waker, ContextBuilder}; -/// use std::future::{poll_fn, Future}; -/// use std::pin::pin; -/// -/// async fn with_waker(f: F, waker: &Waker) -> F::Output -/// where -/// F: Future -/// { -/// let mut f = pin!(f); -/// poll_fn(move |cx| { -/// let has_waker = cx.try_waker().is_some(); -/// if has_waker { -/// return f.as_mut().poll(cx); -/// } -/// -/// let mut cx = ContextBuilder::from(cx) -/// .waker(&waker) -/// .build(); -/// f.as_mut().poll(&mut cx) -/// }).await -/// } -/// -/// # async fn __() { -/// with_waker(async { /* ... */ }, &Waker::noop()).await; -/// # } -/// ``` -#[unstable(feature = "local_waker", issue = "118959")] -impl<'a> From<&mut Context<'a>> for ContextBuilder<'a> { - #[inline] - fn from(value: &mut Context<'a>) -> Self { - let Context { waker, local_waker, _marker, _marker2 } = *value; - ContextBuilder { waker, local_waker } + let ContextBuilder { waker, local_waker, _marker, _marker2 } = self; + Context { waker, local_waker, _marker, _marker2 } } } @@ -600,8 +538,7 @@ impl fmt::Debug for Waker { /// Implements [`Clone`], but neither [`Send`] nor [`Sync`]; therefore, a local waker may /// not be moved to other threads. In general, when deciding to use wakers or local wakers, /// local wakers are preferable unless the waker needs to be sent across threads. This is because -/// wakers can incur in additional cost related to memory synchronization, and not all executors -/// may support wakers. +/// wakers can incur in additional cost related to memory synchronization. /// /// Note that it is preferable to use `local_waker.clone_from(&new_waker)` instead /// of `*local_waker = new_waker.clone()`, as the former will avoid cloning the waker @@ -738,7 +675,7 @@ impl LocalWaker { /// use std::future::Future; /// use std::task::{ContextBuilder, LocalWaker}; /// - /// let mut cx = task::ContextBuilder::new() + /// let mut cx = task::ContextBuilder::from_waker(Waker::noop()) /// .local_waker(LocalWaker::noop()) /// .build(); /// From 180c68bef5f42bd61f517e1a48db4152b6e1989e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Sat, 20 Jan 2024 10:25:00 -0300 Subject: [PATCH 22/30] doc: fix some doctests after rebase --- library/alloc/src/task.rs | 4 ++-- library/core/src/task/wake.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 357f7e0650ae7..87db8629ad09a 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -227,11 +227,11 @@ fn raw_waker(waker: Arc) -> RawWaker { /// // we exit, since there are no more tasks remaining on the queue /// return; /// }; -/// let waker = Waker::noop(); +/// /// // cast the Rc into a `LocalWaker` /// let local_waker: LocalWaker = task.clone().into(); /// // Build the context using `ContextBuilder` -/// let mut cx = ContextBuilder::from_waker(&waker) +/// let mut cx = ContextBuilder::from_waker(Waker::noop()) /// .local_waker(&local_waker) /// .build(); /// diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index a746fe58f47de..9ad71e394eacf 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -673,14 +673,14 @@ impl LocalWaker { /// #![feature(noop_waker)] /// /// use std::future::Future; - /// use std::task::{ContextBuilder, LocalWaker}; + /// use std::task::{ContextBuilder, LocalWaker, Waker, Poll}; /// - /// let mut cx = task::ContextBuilder::from_waker(Waker::noop()) + /// let mut cx = ContextBuilder::from_waker(Waker::noop()) /// .local_waker(LocalWaker::noop()) /// .build(); /// /// let mut future = Box::pin(async { 10 }); - /// assert_eq!(future.as_mut().poll(&mut cx), task::Poll::Ready(10)); + /// assert_eq!(future.as_mut().poll(&mut cx), Poll::Ready(10)); /// ``` #[inline] #[must_use] From cda358857214bb5a2b3df4f011102ffee3843a72 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 30 Sep 2023 08:15:34 +0200 Subject: [PATCH 23/30] make matching on NaN a hard error --- .../rustc_middle/src/mir/interpret/value.rs | 4 +- compiler/rustc_middle/src/ty/consts/int.rs | 71 ++++++++++------ compiler/rustc_mir_build/messages.ftl | 4 + compiler/rustc_mir_build/src/errors.rs | 9 ++ .../src/thir/pattern/const_to_pat.rs | 35 +++++--- .../issue-6804-nan-match.rs | 32 ++++++++ .../issue-6804-nan-match.stderr | 82 +++++++++++++++++++ .../issue-6804.rs | 21 ----- .../issue-6804.stderr | 25 ------ 9 files changed, 197 insertions(+), 86 deletions(-) create mode 100644 tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs create mode 100644 tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr delete mode 100644 tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs delete mode 100644 tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 5ecff04f3ae35..e937c17c8acec 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -418,8 +418,8 @@ impl<'tcx, Prov: Provenance> Scalar { #[inline] pub fn to_float(self) -> InterpResult<'tcx, F> { - // Going through `to_uint` to check size and truncation. - Ok(F::from_bits(self.to_uint(Size::from_bits(F::BITS))?)) + // Going through `to_bits` to check size and truncation. + Ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?)) } #[inline] diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index 310cf113b11b6..d810e358f8562 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -249,11 +249,6 @@ impl ScalarInt { } } - #[inline] - pub fn try_to_target_usize(&self, tcx: TyCtxt<'_>) -> Result { - Ok(self.to_bits(tcx.data_layout.pointer_size)? as u64) - } - /// Tries to convert the `ScalarInt` to an unsigned integer of the given size. /// Fails if the size of the `ScalarInt` is not equal to `size` and returns the /// `ScalarInt`s size in that case. @@ -262,24 +257,12 @@ impl ScalarInt { self.to_bits(size) } - // Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt` - // in not equal to `Size { raw: 1 }` or if the value is not 0 or 1 and returns the `size` - // value of the `ScalarInt` in that case. - #[inline] - pub fn try_to_bool(self) -> Result { - match self.try_to_u8()? { - 0 => Ok(false), - 1 => Ok(true), - _ => Err(self.size()), - } - } - // Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt` // in not equal to `Size { raw: 1 }` and returns the `size` value of the `ScalarInt` in // that case. #[inline] pub fn try_to_u8(self) -> Result { - self.to_bits(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap()) + self.try_to_uint(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap()) } /// Tries to convert the `ScalarInt` to `u16`. Fails if the size of the `ScalarInt` @@ -287,7 +270,7 @@ impl ScalarInt { /// that case. #[inline] pub fn try_to_u16(self) -> Result { - self.to_bits(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap()) + self.try_to_uint(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap()) } /// Tries to convert the `ScalarInt` to `u32`. Fails if the `size` of the `ScalarInt` @@ -295,7 +278,7 @@ impl ScalarInt { /// that case. #[inline] pub fn try_to_u32(self) -> Result { - self.to_bits(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap()) + self.try_to_uint(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap()) } /// Tries to convert the `ScalarInt` to `u64`. Fails if the `size` of the `ScalarInt` @@ -303,7 +286,7 @@ impl ScalarInt { /// that case. #[inline] pub fn try_to_u64(self) -> Result { - self.to_bits(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap()) + self.try_to_uint(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap()) } /// Tries to convert the `ScalarInt` to `u128`. Fails if the `size` of the `ScalarInt` @@ -311,7 +294,24 @@ impl ScalarInt { /// that case. #[inline] pub fn try_to_u128(self) -> Result { - self.to_bits(Size::from_bits(128)) + self.try_to_uint(Size::from_bits(128)) + } + + #[inline] + pub fn try_to_target_usize(&self, tcx: TyCtxt<'_>) -> Result { + self.try_to_uint(tcx.data_layout.pointer_size).map(|v| u64::try_from(v).unwrap()) + } + + // Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt` + // in not equal to `Size { raw: 1 }` or if the value is not 0 or 1 and returns the `size` + // value of the `ScalarInt` in that case. + #[inline] + pub fn try_to_bool(self) -> Result { + match self.try_to_u8()? { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(self.size()), + } } /// Tries to convert the `ScalarInt` to a signed integer of the given size. @@ -357,6 +357,27 @@ impl ScalarInt { pub fn try_to_i128(self) -> Result { self.try_to_int(Size::from_bits(128)) } + + #[inline] + pub fn try_to_target_isize(&self, tcx: TyCtxt<'_>) -> Result { + self.try_to_int(tcx.data_layout.pointer_size).map(|v| i64::try_from(v).unwrap()) + } + + #[inline] + pub fn try_to_float(self) -> Result { + // Going through `to_uint` to check size and truncation. + Ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?)) + } + + #[inline] + pub fn try_to_f32(self) -> Result { + self.try_to_float() + } + + #[inline] + pub fn try_to_f64(self) -> Result { + self.try_to_float() + } } macro_rules! from { @@ -399,11 +420,7 @@ impl TryFrom for bool { type Error = Size; #[inline] fn try_from(int: ScalarInt) -> Result { - int.to_bits(Size::from_bytes(1)).and_then(|u| match u { - 0 => Ok(false), - 1 => Ok(true), - _ => Err(Size::from_bytes(1)), - }) + int.try_to_bool() } } diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 9631b72f20c54..14d09535d7dba 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -232,6 +232,10 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa .note = mutating layout constrained fields cannot statically be checked for valid values .label = mutation of layout constrained field +mir_build_nan_pattern = cannot use NaN in patterns + .note = NaNs compare inequal to everything, even themselves, so this pattern would never match + .help = try using the `is_nan` method instead + mir_build_non_const_path = runtime values cannot be referenced in patterns mir_build_non_empty_never_pattern = diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index e1b998b24718c..eef0505198505 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -780,6 +780,15 @@ pub struct UnsizedPattern<'tcx> { pub non_sm_ty: Ty<'tcx>, } +#[derive(Diagnostic)] +#[diag(mir_build_nan_pattern)] +#[note] +#[help] +pub struct NaNPattern { + #[primary_span] + pub span: Span, +} + #[derive(LintDiagnostic)] #[diag(mir_build_float_pattern)] pub struct FloatPattern; diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 22305f03a769d..cb67bc6b0b3c0 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,3 +1,4 @@ +use rustc_apfloat::Float; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_index::Idx; @@ -16,7 +17,7 @@ use std::cell::Cell; use super::PatCtxt; use crate::errors::{ - FloatPattern, IndirectStructuralMatch, InvalidPattern, NonPartialEqMatch, + FloatPattern, IndirectStructuralMatch, InvalidPattern, NaNPattern, NonPartialEqMatch, NontrivialStructuralMatch, PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern, }; @@ -317,16 +318,6 @@ impl<'tcx> ConstToPat<'tcx> { let param_env = self.param_env; let kind = match ty.kind() { - ty::Float(_) => { - self.saw_const_match_lint.set(true); - tcx.emit_node_span_lint( - lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, - id, - span, - FloatPattern, - ); - return Err(FallbackToOpaqueConst); - } // If the type is not structurally comparable, just emit the constant directly, // causing the pattern match code to treat it opaquely. // FIXME: This code doesn't emit errors itself, the caller emits the errors. @@ -486,6 +477,28 @@ impl<'tcx> ConstToPat<'tcx> { } } }, + ty::Float(flt) => { + let v = cv.unwrap_leaf(); + let is_nan = match flt { + ty::FloatTy::F32 => v.try_to_f32().unwrap().is_nan(), + ty::FloatTy::F64 => v.try_to_f64().unwrap().is_nan(), + }; + if is_nan { + // NaNs are not ever equal to anything so they make no sense as patterns. + // Also see . + let e = tcx.dcx().emit_err(NaNPattern { span }); + self.saw_const_match_error.set(Some(e)); + } else { + self.saw_const_match_lint.set(true); + tcx.emit_node_span_lint( + lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, + id, + span, + FloatPattern, + ); + } + return Err(FallbackToOpaqueConst); + } ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => { // The raw pointers we see here have been "vetted" by valtree construction to be // just integers, so we simply allow them. diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs new file mode 100644 index 0000000000000..c5bca8c970dae --- /dev/null +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs @@ -0,0 +1,32 @@ +// Matching against NaN should result in an error +#![feature(exclusive_range_pattern)] +#![allow(unused)] +#![allow(illegal_floating_point_literal_pattern)] + +const NAN: f64 = f64::NAN; + +fn main() { + let x = NAN; + match x { + NAN => {}, //~ ERROR cannot use NaN in patterns + _ => {}, + }; + + match [x, 1.0] { + [NAN, _] => {}, //~ ERROR cannot use NaN in patterns + _ => {}, + }; + + // Also cover range patterns + match x { + NAN..=1.0 => {}, //~ ERROR cannot use NaN in patterns + //~^ ERROR lower range bound must be less than or equal to upper + -1.0..=NAN => {}, //~ ERROR cannot use NaN in patterns + //~^ ERROR lower range bound must be less than or equal to upper + NAN.. => {}, //~ ERROR cannot use NaN in patterns + //~^ ERROR lower range bound must be less than or equal to upper + ..NAN => {}, //~ ERROR cannot use NaN in patterns + //~^ ERROR lower range bound must be less than upper + _ => {}, + }; +} diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr new file mode 100644 index 0000000000000..deda07150c52d --- /dev/null +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr @@ -0,0 +1,82 @@ +error: cannot use NaN in patterns + --> $DIR/issue-6804-nan-match.rs:11:9 + | +LL | NAN => {}, + | ^^^ + | + = note: NaNs compare inequal to everything, even themselves, so this pattern would never match + = help: try using the `is_nan` method instead + +error: cannot use NaN in patterns + --> $DIR/issue-6804-nan-match.rs:16:10 + | +LL | [NAN, _] => {}, + | ^^^ + | + = note: NaNs compare inequal to everything, even themselves, so this pattern would never match + = help: try using the `is_nan` method instead + +error: cannot use NaN in patterns + --> $DIR/issue-6804-nan-match.rs:22:9 + | +LL | NAN..=1.0 => {}, + | ^^^ + | + = note: NaNs compare inequal to everything, even themselves, so this pattern would never match + = help: try using the `is_nan` method instead + +error[E0030]: lower range bound must be less than or equal to upper + --> $DIR/issue-6804-nan-match.rs:22:9 + | +LL | NAN..=1.0 => {}, + | ^^^^^^^^^ lower bound larger than upper bound + +error: cannot use NaN in patterns + --> $DIR/issue-6804-nan-match.rs:24:16 + | +LL | -1.0..=NAN => {}, + | ^^^ + | + = note: NaNs compare inequal to everything, even themselves, so this pattern would never match + = help: try using the `is_nan` method instead + +error[E0030]: lower range bound must be less than or equal to upper + --> $DIR/issue-6804-nan-match.rs:24:9 + | +LL | -1.0..=NAN => {}, + | ^^^^^^^^^^ lower bound larger than upper bound + +error: cannot use NaN in patterns + --> $DIR/issue-6804-nan-match.rs:26:9 + | +LL | NAN.. => {}, + | ^^^ + | + = note: NaNs compare inequal to everything, even themselves, so this pattern would never match + = help: try using the `is_nan` method instead + +error[E0030]: lower range bound must be less than or equal to upper + --> $DIR/issue-6804-nan-match.rs:26:9 + | +LL | NAN.. => {}, + | ^^^^^ lower bound larger than upper bound + +error: cannot use NaN in patterns + --> $DIR/issue-6804-nan-match.rs:28:11 + | +LL | ..NAN => {}, + | ^^^ + | + = note: NaNs compare inequal to everything, even themselves, so this pattern would never match + = help: try using the `is_nan` method instead + +error[E0579]: lower range bound must be less than upper + --> $DIR/issue-6804-nan-match.rs:28:9 + | +LL | ..NAN => {}, + | ^^^^^ + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0030, E0579. +For more information about an error, try `rustc --explain E0030`. diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs deleted file mode 100644 index 0260caa82cb1d..0000000000000 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Matching against NaN should result in a warning - -#![allow(unused)] -#![deny(illegal_floating_point_literal_pattern)] - -const NAN: f64 = f64::NAN; - -fn main() { - let x = NAN; - match x { - NAN => {}, //~ ERROR floating-point types cannot be used - //~| WARN this was previously accepted by the compiler but is being phased out - _ => {}, - }; - - match [x, 1.0] { - [NAN, _] => {}, //~ ERROR floating-point types cannot be used - //~| WARN this was previously accepted by the compiler but is being phased out - _ => {}, - }; -} diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr deleted file mode 100644 index f37255d0828cd..0000000000000 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error: floating-point types cannot be used in patterns - --> $DIR/issue-6804.rs:11:9 - | -LL | NAN => {}, - | ^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 -note: the lint level is defined here - --> $DIR/issue-6804.rs:4:9 - | -LL | #![deny(illegal_floating_point_literal_pattern)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: floating-point types cannot be used in patterns - --> $DIR/issue-6804.rs:17:10 - | -LL | [NAN, _] => {}, - | ^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 - -error: aborting due to 2 previous errors - From 1254ee48c458a5fe8fc1847539169df013098e05 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 6 Jan 2024 14:39:51 +0100 Subject: [PATCH 24/30] remove illegal_floating_point_literal_pattern lint --- compiler/rustc_lint/src/lib.rs | 4 + compiler/rustc_lint_defs/src/builtin.rs | 50 ------- compiler/rustc_mir_build/messages.ftl | 2 - compiler/rustc_mir_build/src/errors.rs | 4 - .../src/thir/pattern/const_to_pat.rs | 12 +- .../tests/ui/expect_tool_lint_rfc_2383.rs | 16 +-- .../tests/ui/expect_tool_lint_rfc_2383.stderr | 16 +-- .../lints/expect-tool-lint-rfc-2383.rs | 7 +- .../lints/expect-tool-lint-rfc-2383.stderr | 6 +- .../array-slice-vec/vec-matching-autoslice.rs | 1 - tests/ui/binding/match-range.rs | 1 - .../half-open-range-pats-exhaustive-fail.rs | 1 - ...alf-open-range-pats-exhaustive-fail.stderr | 136 +++++++++--------- .../half-open-range-pats-semantics.rs | 1 - .../half-open-range-pats-thir-lower-empty.rs | 1 - ...lf-open-range-pats-thir-lower-empty.stderr | 26 ++-- tests/ui/lint/issue-86600-lint-twice.rs | 15 -- tests/ui/lint/issue-86600-lint-twice.stderr | 12 -- .../expect_tool_lint_rfc_2383.rs | 16 +-- .../expect_tool_lint_rfc_2383.stderr | 8 +- tests/ui/match/issue-41255.rs | 50 ------- tests/ui/match/issue-41255.stderr | 115 --------------- tests/ui/parser/issues/issue-7222.rs | 1 - tests/ui/pattern/usefulness/floats.rs | 1 - tests/ui/pattern/usefulness/floats.stderr | 18 +-- .../usefulness/non-exhaustive-match.rs | 2 - .../usefulness/non-exhaustive-match.stderr | 20 +-- .../issue-6804-nan-match.rs | 1 - .../issue-6804-nan-match.stderr | 20 +-- tests/ui/union/union-pat-refutability.rs | 1 - 30 files changed, 143 insertions(+), 421 deletions(-) delete mode 100644 tests/ui/lint/issue-86600-lint-twice.rs delete mode 100644 tests/ui/lint/issue-86600-lint-twice.stderr delete mode 100644 tests/ui/match/issue-41255.rs delete mode 100644 tests/ui/match/issue-41255.stderr diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 1d9ce10bcaf38..0a15671e68692 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -516,6 +516,10 @@ fn register_builtins(store: &mut LintStore) { "converted into hard error, see PR #118649 \ for more information", ); + store.register_removed( + "illegal_floating_point_literal_pattern", + "no longer a warning, float patterns behave the same as `==`", + ); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index e6d837ecd92bd..07c0e75a71c96 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -45,7 +45,6 @@ declare_lint_pass! { FUZZY_PROVENANCE_CASTS, HIDDEN_GLOB_REEXPORTS, ILL_FORMED_ATTRIBUTE_INPUT, - ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, INCOMPLETE_INCLUDE, INDIRECT_STRUCTURAL_MATCH, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, @@ -1873,55 +1872,6 @@ declare_lint! { }; } -declare_lint! { - /// The `illegal_floating_point_literal_pattern` lint detects - /// floating-point literals used in patterns. - /// - /// ### Example - /// - /// ```rust - /// let x = 42.0; - /// - /// match x { - /// 5.0 => {} - /// _ => {} - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Previous versions of the compiler accepted floating-point literals in - /// patterns, but it was later determined this was a mistake. The - /// semantics of comparing floating-point values may not be clear in a - /// pattern when contrasted with "structural equality". Typically you can - /// work around this by using a [match guard], such as: - /// - /// ```rust - /// # let x = 42.0; - /// - /// match x { - /// y if y == 5.0 => {} - /// _ => {} - /// } - /// ``` - /// - /// This is a [future-incompatible] lint to transition this to a hard - /// error in the future. See [issue #41620] for more details. - /// - /// [issue #41620]: https://github.com/rust-lang/rust/issues/41620 - /// [match guard]: https://doc.rust-lang.org/reference/expressions/match-expr.html#match-guards - /// [future-incompatible]: ../index.md#future-incompatible-lints - pub ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, - Warn, - "floating-point literals cannot be used in patterns", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, - reference: "issue #41620 ", - }; -} - declare_lint! { /// The `unstable_name_collisions` lint detects that you have used a name /// that the standard library plans to add in the future. diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 14d09535d7dba..8a6ccdb857829 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -107,8 +107,6 @@ mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior .label = use of extern static -mir_build_float_pattern = floating-point types cannot be used in patterns - mir_build_indirect_structural_match = to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq)]` diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index eef0505198505..641822ae202aa 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -789,10 +789,6 @@ pub struct NaNPattern { pub span: Span, } -#[derive(LintDiagnostic)] -#[diag(mir_build_float_pattern)] -pub struct FloatPattern; - #[derive(LintDiagnostic)] #[diag(mir_build_pointer_pattern)] pub struct PointerPattern; diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index cb67bc6b0b3c0..9d3a9bf6745e7 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -17,7 +17,7 @@ use std::cell::Cell; use super::PatCtxt; use crate::errors::{ - FloatPattern, IndirectStructuralMatch, InvalidPattern, NaNPattern, NonPartialEqMatch, + IndirectStructuralMatch, InvalidPattern, NaNPattern, NonPartialEqMatch, NontrivialStructuralMatch, PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern, }; @@ -488,16 +488,10 @@ impl<'tcx> ConstToPat<'tcx> { // Also see . let e = tcx.dcx().emit_err(NaNPattern { span }); self.saw_const_match_error.set(Some(e)); + return Err(FallbackToOpaqueConst); } else { - self.saw_const_match_lint.set(true); - tcx.emit_node_span_lint( - lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, - id, - span, - FloatPattern, - ); + PatKind::Constant { value: mir::Const::Ty(ty::Const::new_value(tcx, cv, ty)) } } - return Err(FallbackToOpaqueConst); } ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => { // The raw pointers we see here have been "vetted" by valtree construction to be diff --git a/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs index 3811421dc7158..72097bfabd726 100644 --- a/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs +++ b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.rs @@ -20,12 +20,8 @@ mod rustc_ok { pub fn rustc_lints() { let x = 42.0; - #[expect(illegal_floating_point_literal_pattern)] - match x { - 5.0 => {} - 6.0 => {} - _ => {} - } + #[expect(invalid_nan_comparisons)] + let _b = x == f32::NAN; } } @@ -38,13 +34,9 @@ mod rustc_warn { pub fn rustc_lints() { let x = 42; - #[expect(illegal_floating_point_literal_pattern)] + #[expect(invalid_nan_comparisons)] //~^ ERROR: this lint expectation is unfulfilled - match x { - 5 => {} - 6 => {} - _ => {} - } + let _b = x == 5; } } diff --git a/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.stderr b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.stderr index 3f8d0b7243620..2a418d8456635 100644 --- a/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.stderr +++ b/src/tools/clippy/tests/ui/expect_tool_lint_rfc_2383.stderr @@ -1,5 +1,5 @@ error: this lint expectation is unfulfilled - --> $DIR/expect_tool_lint_rfc_2383.rs:35:14 + --> $DIR/expect_tool_lint_rfc_2383.rs:31:14 | LL | #[expect(dead_code)] | ^^^^^^^^^ @@ -8,31 +8,31 @@ LL | #[expect(dead_code)] = help: to override `-D warnings` add `#[allow(unfulfilled_lint_expectations)]` error: this lint expectation is unfulfilled - --> $DIR/expect_tool_lint_rfc_2383.rs:41:18 + --> $DIR/expect_tool_lint_rfc_2383.rs:37:18 | -LL | #[expect(illegal_floating_point_literal_pattern)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[expect(invalid_nan_comparisons)] + | ^^^^^^^^^^^^^^^^^^^^^^^ error: this lint expectation is unfulfilled - --> $DIR/expect_tool_lint_rfc_2383.rs:116:14 + --> $DIR/expect_tool_lint_rfc_2383.rs:108:14 | LL | #[expect(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^ error: this lint expectation is unfulfilled - --> $DIR/expect_tool_lint_rfc_2383.rs:124:14 + --> $DIR/expect_tool_lint_rfc_2383.rs:116:14 | LL | #[expect(clippy::bytes_nth)] | ^^^^^^^^^^^^^^^^^ error: this lint expectation is unfulfilled - --> $DIR/expect_tool_lint_rfc_2383.rs:130:14 + --> $DIR/expect_tool_lint_rfc_2383.rs:122:14 | LL | #[expect(clippy::if_same_then_else)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this lint expectation is unfulfilled - --> $DIR/expect_tool_lint_rfc_2383.rs:136:14 + --> $DIR/expect_tool_lint_rfc_2383.rs:128:14 | LL | #[expect(clippy::overly_complex_bool_expr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/rustdoc-ui/lints/expect-tool-lint-rfc-2383.rs b/tests/rustdoc-ui/lints/expect-tool-lint-rfc-2383.rs index 0901ac3640fdd..ffd694e987908 100644 --- a/tests/rustdoc-ui/lints/expect-tool-lint-rfc-2383.rs +++ b/tests/rustdoc-ui/lints/expect-tool-lint-rfc-2383.rs @@ -24,10 +24,9 @@ mod rustc_ok { pub fn rustc_lints() { let x = 42.0; - #[expect(illegal_floating_point_literal_pattern)] + #[expect(invalid_nan_comparisons)] match x { - 5.0 => {} - 6.0 => {} + f32::NAN => {} _ => {} } } @@ -40,7 +39,7 @@ mod rustc_warn { pub fn rustc_lints() { let x = 42; - #[expect(illegal_floating_point_literal_pattern)] + #[expect(invalid_nan_comparisons)] match x { 5 => {} 6 => {} diff --git a/tests/rustdoc-ui/lints/expect-tool-lint-rfc-2383.stderr b/tests/rustdoc-ui/lints/expect-tool-lint-rfc-2383.stderr index efc5f349f4f44..5ae3c039d275a 100644 --- a/tests/rustdoc-ui/lints/expect-tool-lint-rfc-2383.stderr +++ b/tests/rustdoc-ui/lints/expect-tool-lint-rfc-2383.stderr @@ -7,19 +7,19 @@ LL | #![expect(rustdoc::missing_crate_level_docs)] = note: `#[warn(unfulfilled_lint_expectations)]` on by default warning: this lint expectation is unfulfilled - --> $DIR/expect-tool-lint-rfc-2383.rs:71:14 + --> $DIR/expect-tool-lint-rfc-2383.rs:70:14 | LL | #[expect(rustdoc::broken_intra_doc_links)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: this lint expectation is unfulfilled - --> $DIR/expect-tool-lint-rfc-2383.rs:76:14 + --> $DIR/expect-tool-lint-rfc-2383.rs:75:14 | LL | #[expect(rustdoc::invalid_html_tags)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: this lint expectation is unfulfilled - --> $DIR/expect-tool-lint-rfc-2383.rs:81:14 + --> $DIR/expect-tool-lint-rfc-2383.rs:80:14 | LL | #[expect(rustdoc::bare_urls)] | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/array-slice-vec/vec-matching-autoslice.rs b/tests/ui/array-slice-vec/vec-matching-autoslice.rs index 8179edf420cb0..f839cd62b1a5d 100644 --- a/tests/ui/array-slice-vec/vec-matching-autoslice.rs +++ b/tests/ui/array-slice-vec/vec-matching-autoslice.rs @@ -1,5 +1,4 @@ // run-pass -#![allow(illegal_floating_point_literal_pattern)] // FIXME #41620 pub fn main() { let x = [1, 2, 3]; diff --git a/tests/ui/binding/match-range.rs b/tests/ui/binding/match-range.rs index 1dca84dfd45ff..cb7b93e7cc65f 100644 --- a/tests/ui/binding/match-range.rs +++ b/tests/ui/binding/match-range.rs @@ -1,5 +1,4 @@ // run-pass -#![allow(illegal_floating_point_literal_pattern)] // FIXME #41620 #![feature(exclusive_range_pattern)] pub fn main() { diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.rs b/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.rs index a2a4c62fa0295..33b99259dfec9 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.rs +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.rs @@ -1,7 +1,6 @@ // Test various non-exhaustive matches for `X..`, `..=X` and `..X` ranges. #![feature(exclusive_range_pattern)] -#![allow(illegal_floating_point_literal_pattern)] fn main() {} diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.stderr b/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.stderr index 6b20a820b7302..1e68235303b2e 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.stderr +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-exhaustive-fail.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:15:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:14:8 | LL | m!(0f32, f32::NEG_INFINITY..); | ^^^^ pattern `_` not covered @@ -11,7 +11,7 @@ LL | match $s { $($t)+ => {}, _ => todo!() } | ++++++++++++++ error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:16:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:15:8 | LL | m!(0f32, ..f32::INFINITY); | ^^^^ pattern `_` not covered @@ -23,7 +23,7 @@ LL | match $s { $($t)+ => {}, _ => todo!() } | ++++++++++++++ error[E0004]: non-exhaustive patterns: `'\u{10ffff}'` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:25:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:24:8 | LL | m!('a', ..core::char::MAX); | ^^^ pattern `'\u{10ffff}'` not covered @@ -35,7 +35,7 @@ LL | match $s { $($t)+ => {}, '\u{10ffff}' => todo!() } | +++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `'\u{10fffe}'..='\u{10ffff}'` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:26:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:25:8 | LL | m!('a', ..ALMOST_MAX); | ^^^ pattern `'\u{10fffe}'..='\u{10ffff}'` not covered @@ -47,7 +47,7 @@ LL | match $s { $($t)+ => {}, '\u{10fffe}'..='\u{10ffff}' => todo!() } | ++++++++++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `'\0'` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:27:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:26:8 | LL | m!('a', ALMOST_MIN..); | ^^^ pattern `'\0'` not covered @@ -59,7 +59,7 @@ LL | match $s { $($t)+ => {}, '\0' => todo!() } | +++++++++++++++++ error[E0004]: non-exhaustive patterns: `'\u{10ffff}'` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:28:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:27:8 | LL | m!('a', ..=ALMOST_MAX); | ^^^ pattern `'\u{10ffff}'` not covered @@ -71,7 +71,7 @@ LL | match $s { $($t)+ => {}, '\u{10ffff}' => todo!() } | +++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `'b'` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:29:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:28:8 | LL | m!('a', ..=VAL | VAL_2..); | ^^^ pattern `'b'` not covered @@ -83,7 +83,7 @@ LL | match $s { $($t)+ => {}, 'b' => todo!() } | ++++++++++++++++ error[E0004]: non-exhaustive patterns: `'b'` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:30:8 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:29:8 | LL | m!('a', ..VAL_1 | VAL_2..); | ^^^ pattern `'b'` not covered @@ -95,7 +95,7 @@ LL | match $s { $($t)+ => {}, 'b' => todo!() } | ++++++++++++++++ error[E0004]: non-exhaustive patterns: `u8::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:40:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:39:12 | LL | m!(0, ..u8::MAX); | ^ pattern `u8::MAX` not covered @@ -107,7 +107,7 @@ LL | match $s { $($t)+ => {}, u8::MAX => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `254_u8..=u8::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:41:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:40:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `254_u8..=u8::MAX` not covered @@ -119,7 +119,7 @@ LL | match $s { $($t)+ => {}, 254_u8..=u8::MAX => todo!() } | +++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `0_u8` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:42:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:41:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `0_u8` not covered @@ -131,7 +131,7 @@ LL | match $s { $($t)+ => {}, 0_u8 => todo!() } | +++++++++++++++++ error[E0004]: non-exhaustive patterns: `u8::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:43:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:42:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `u8::MAX` not covered @@ -143,7 +143,7 @@ LL | match $s { $($t)+ => {}, u8::MAX => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u8` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:44:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:43:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_u8` not covered @@ -155,7 +155,7 @@ LL | match $s { $($t)+ => {}, 43_u8 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u8` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:45:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:44:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_u8` not covered @@ -167,7 +167,7 @@ LL | match $s { $($t)+ => {}, 43_u8 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u16::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:53:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:52:12 | LL | m!(0, ..u16::MAX); | ^ pattern `u16::MAX` not covered @@ -179,7 +179,7 @@ LL | match $s { $($t)+ => {}, u16::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `65534_u16..=u16::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:54:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:53:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `65534_u16..=u16::MAX` not covered @@ -191,7 +191,7 @@ LL | match $s { $($t)+ => {}, 65534_u16..=u16::MAX => todo!() } | +++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `0_u16` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:55:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:54:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `0_u16` not covered @@ -203,7 +203,7 @@ LL | match $s { $($t)+ => {}, 0_u16 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u16::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:56:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:55:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `u16::MAX` not covered @@ -215,7 +215,7 @@ LL | match $s { $($t)+ => {}, u16::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u16` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:57:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:56:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_u16` not covered @@ -227,7 +227,7 @@ LL | match $s { $($t)+ => {}, 43_u16 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u16` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:58:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:57:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_u16` not covered @@ -239,7 +239,7 @@ LL | match $s { $($t)+ => {}, 43_u16 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u32::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:66:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:65:12 | LL | m!(0, ..u32::MAX); | ^ pattern `u32::MAX` not covered @@ -251,7 +251,7 @@ LL | match $s { $($t)+ => {}, u32::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `4294967294_u32..=u32::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:67:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:66:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `4294967294_u32..=u32::MAX` not covered @@ -263,7 +263,7 @@ LL | match $s { $($t)+ => {}, 4294967294_u32..=u32::MAX => todo!() } | ++++++++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `0_u32` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:68:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:67:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `0_u32` not covered @@ -275,7 +275,7 @@ LL | match $s { $($t)+ => {}, 0_u32 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u32::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:69:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:68:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `u32::MAX` not covered @@ -287,7 +287,7 @@ LL | match $s { $($t)+ => {}, u32::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u32` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:70:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:69:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_u32` not covered @@ -299,7 +299,7 @@ LL | match $s { $($t)+ => {}, 43_u32 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u32` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:71:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:70:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_u32` not covered @@ -311,7 +311,7 @@ LL | match $s { $($t)+ => {}, 43_u32 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u64::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:79:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:78:12 | LL | m!(0, ..u64::MAX); | ^ pattern `u64::MAX` not covered @@ -323,7 +323,7 @@ LL | match $s { $($t)+ => {}, u64::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `18446744073709551614_u64..=u64::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:80:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:79:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `18446744073709551614_u64..=u64::MAX` not covered @@ -335,7 +335,7 @@ LL | match $s { $($t)+ => {}, 18446744073709551614_u64..=u64::MAX => tod | ++++++++++++++++++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `0_u64` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:81:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:80:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `0_u64` not covered @@ -347,7 +347,7 @@ LL | match $s { $($t)+ => {}, 0_u64 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u64::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:82:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:81:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `u64::MAX` not covered @@ -359,7 +359,7 @@ LL | match $s { $($t)+ => {}, u64::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u64` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:83:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:82:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_u64` not covered @@ -371,7 +371,7 @@ LL | match $s { $($t)+ => {}, 43_u64 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u64` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:84:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:83:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_u64` not covered @@ -383,7 +383,7 @@ LL | match $s { $($t)+ => {}, 43_u64 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u128::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:92:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:91:12 | LL | m!(0, ..u128::MAX); | ^ pattern `u128::MAX` not covered @@ -395,7 +395,7 @@ LL | match $s { $($t)+ => {}, u128::MAX => todo!() } | ++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `340282366920938463463374607431768211454_u128..=u128::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:93:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:92:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `340282366920938463463374607431768211454_u128..=u128::MAX` not covered @@ -407,7 +407,7 @@ LL | match $s { $($t)+ => {}, 340282366920938463463374607431768211454_u1 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `0_u128` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:94:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:93:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `0_u128` not covered @@ -419,7 +419,7 @@ LL | match $s { $($t)+ => {}, 0_u128 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `u128::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:95:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:94:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `u128::MAX` not covered @@ -431,7 +431,7 @@ LL | match $s { $($t)+ => {}, u128::MAX => todo!() } | ++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u128` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:96:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:95:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_u128` not covered @@ -443,7 +443,7 @@ LL | match $s { $($t)+ => {}, 43_u128 => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_u128` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:97:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:96:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_u128` not covered @@ -455,7 +455,7 @@ LL | match $s { $($t)+ => {}, 43_u128 => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i8::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:108:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:107:12 | LL | m!(0, ..i8::MAX); | ^ pattern `i8::MAX` not covered @@ -467,7 +467,7 @@ LL | match $s { $($t)+ => {}, i8::MAX => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `126_i8..=i8::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:109:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:108:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `126_i8..=i8::MAX` not covered @@ -479,7 +479,7 @@ LL | match $s { $($t)+ => {}, 126_i8..=i8::MAX => todo!() } | +++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i8::MIN` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:110:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:109:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `i8::MIN` not covered @@ -491,7 +491,7 @@ LL | match $s { $($t)+ => {}, i8::MIN => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i8::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:111:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:110:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `i8::MAX` not covered @@ -503,7 +503,7 @@ LL | match $s { $($t)+ => {}, i8::MAX => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i8` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:112:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:111:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_i8` not covered @@ -515,7 +515,7 @@ LL | match $s { $($t)+ => {}, 43_i8 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i8` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:113:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:112:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_i8` not covered @@ -527,7 +527,7 @@ LL | match $s { $($t)+ => {}, 43_i8 => todo!() } | ++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i16::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:121:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:120:12 | LL | m!(0, ..i16::MAX); | ^ pattern `i16::MAX` not covered @@ -539,7 +539,7 @@ LL | match $s { $($t)+ => {}, i16::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `32766_i16..=i16::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:122:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:121:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `32766_i16..=i16::MAX` not covered @@ -551,7 +551,7 @@ LL | match $s { $($t)+ => {}, 32766_i16..=i16::MAX => todo!() } | +++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i16::MIN` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:123:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:122:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `i16::MIN` not covered @@ -563,7 +563,7 @@ LL | match $s { $($t)+ => {}, i16::MIN => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i16::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:124:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:123:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `i16::MAX` not covered @@ -575,7 +575,7 @@ LL | match $s { $($t)+ => {}, i16::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i16` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:125:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:124:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_i16` not covered @@ -587,7 +587,7 @@ LL | match $s { $($t)+ => {}, 43_i16 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i16` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:126:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:125:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_i16` not covered @@ -599,7 +599,7 @@ LL | match $s { $($t)+ => {}, 43_i16 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i32::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:134:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:133:12 | LL | m!(0, ..i32::MAX); | ^ pattern `i32::MAX` not covered @@ -611,7 +611,7 @@ LL | match $s { $($t)+ => {}, i32::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `2147483646_i32..=i32::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:135:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:134:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `2147483646_i32..=i32::MAX` not covered @@ -623,7 +623,7 @@ LL | match $s { $($t)+ => {}, 2147483646_i32..=i32::MAX => todo!() } | ++++++++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i32::MIN` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:136:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:135:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `i32::MIN` not covered @@ -635,7 +635,7 @@ LL | match $s { $($t)+ => {}, i32::MIN => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i32::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:137:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:136:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `i32::MAX` not covered @@ -647,7 +647,7 @@ LL | match $s { $($t)+ => {}, i32::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i32` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:138:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:137:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_i32` not covered @@ -659,7 +659,7 @@ LL | match $s { $($t)+ => {}, 43_i32 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i32` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:139:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:138:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_i32` not covered @@ -671,7 +671,7 @@ LL | match $s { $($t)+ => {}, 43_i32 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i64::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:147:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:146:12 | LL | m!(0, ..i64::MAX); | ^ pattern `i64::MAX` not covered @@ -683,7 +683,7 @@ LL | match $s { $($t)+ => {}, i64::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `9223372036854775806_i64..=i64::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:148:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:147:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `9223372036854775806_i64..=i64::MAX` not covered @@ -695,7 +695,7 @@ LL | match $s { $($t)+ => {}, 9223372036854775806_i64..=i64::MAX => todo | +++++++++++++++++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i64::MIN` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:149:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:148:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `i64::MIN` not covered @@ -707,7 +707,7 @@ LL | match $s { $($t)+ => {}, i64::MIN => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i64::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:150:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:149:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `i64::MAX` not covered @@ -719,7 +719,7 @@ LL | match $s { $($t)+ => {}, i64::MAX => todo!() } | +++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i64` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:151:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:150:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_i64` not covered @@ -731,7 +731,7 @@ LL | match $s { $($t)+ => {}, 43_i64 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i64` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:152:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:151:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_i64` not covered @@ -743,7 +743,7 @@ LL | match $s { $($t)+ => {}, 43_i64 => todo!() } | +++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i128::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:160:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:159:12 | LL | m!(0, ..i128::MAX); | ^ pattern `i128::MAX` not covered @@ -755,7 +755,7 @@ LL | match $s { $($t)+ => {}, i128::MAX => todo!() } | ++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `170141183460469231731687303715884105726_i128..=i128::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:161:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:160:12 | LL | m!(0, ..ALMOST_MAX); | ^ pattern `170141183460469231731687303715884105726_i128..=i128::MAX` not covered @@ -767,7 +767,7 @@ LL | match $s { $($t)+ => {}, 170141183460469231731687303715884105726_i1 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i128::MIN` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:162:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:161:12 | LL | m!(0, ALMOST_MIN..); | ^ pattern `i128::MIN` not covered @@ -779,7 +779,7 @@ LL | match $s { $($t)+ => {}, i128::MIN => todo!() } | ++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `i128::MAX` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:163:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:162:12 | LL | m!(0, ..=ALMOST_MAX); | ^ pattern `i128::MAX` not covered @@ -791,7 +791,7 @@ LL | match $s { $($t)+ => {}, i128::MAX => todo!() } | ++++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i128` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:164:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:163:12 | LL | m!(0, ..=VAL | VAL_2..); | ^ pattern `43_i128` not covered @@ -803,7 +803,7 @@ LL | match $s { $($t)+ => {}, 43_i128 => todo!() } | ++++++++++++++++++++ error[E0004]: non-exhaustive patterns: `43_i128` not covered - --> $DIR/half-open-range-pats-exhaustive-fail.rs:165:12 + --> $DIR/half-open-range-pats-exhaustive-fail.rs:164:12 | LL | m!(0, ..VAL_1 | VAL_2..); | ^ pattern `43_i128` not covered diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs b/tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs index 6c6ba93196b64..d5af7bea54388 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs @@ -4,7 +4,6 @@ // via `.contains(...)` and make sure the dynamic semantics match. #![feature(exclusive_range_pattern)] -#![allow(illegal_floating_point_literal_pattern)] #![allow(unreachable_patterns)] macro_rules! yes { diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-thir-lower-empty.rs b/tests/ui/half-open-range-patterns/half-open-range-pats-thir-lower-empty.rs index 4b14a314e7aea..158da6509662f 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-thir-lower-empty.rs +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-thir-lower-empty.rs @@ -1,5 +1,4 @@ #![feature(exclusive_range_pattern)] -#![allow(illegal_floating_point_literal_pattern)] macro_rules! m { ($s:expr, $($t:tt)+) => { diff --git a/tests/ui/half-open-range-patterns/half-open-range-pats-thir-lower-empty.stderr b/tests/ui/half-open-range-patterns/half-open-range-pats-thir-lower-empty.stderr index e9702bb380fb3..169e776fc20fd 100644 --- a/tests/ui/half-open-range-patterns/half-open-range-pats-thir-lower-empty.stderr +++ b/tests/ui/half-open-range-patterns/half-open-range-pats-thir-lower-empty.stderr @@ -1,77 +1,77 @@ error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-thir-lower-empty.rs:11:11 + --> $DIR/half-open-range-pats-thir-lower-empty.rs:10:11 | LL | m!(0, ..u8::MIN); | ^^^^^^^^^ error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-thir-lower-empty.rs:13:11 + --> $DIR/half-open-range-pats-thir-lower-empty.rs:12:11 | LL | m!(0, ..u16::MIN); | ^^^^^^^^^^ error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-thir-lower-empty.rs:15:11 + --> $DIR/half-open-range-pats-thir-lower-empty.rs:14:11 | LL | m!(0, ..u32::MIN); | ^^^^^^^^^^ error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-thir-lower-empty.rs:17:11 + --> $DIR/half-open-range-pats-thir-lower-empty.rs:16:11 | LL | m!(0, ..u64::MIN); | ^^^^^^^^^^ error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-thir-lower-empty.rs:19:11 + --> $DIR/half-open-range-pats-thir-lower-empty.rs:18:11 | LL | m!(0, ..u128::MIN); | ^^^^^^^^^^^ error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-thir-lower-empty.rs:22:11 + --> $DIR/half-open-range-pats-thir-lower-empty.rs:21:11 | LL | m!(0, ..i8::MIN); | ^^^^^^^^^ error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-thir-lower-empty.rs:24:11 + --> $DIR/half-open-range-pats-thir-lower-empty.rs:23:11 | LL | m!(0, ..i16::MIN); | ^^^^^^^^^^ error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-thir-lower-empty.rs:26:11 + --> $DIR/half-open-range-pats-thir-lower-empty.rs:25:11 | LL | m!(0, ..i32::MIN); | ^^^^^^^^^^ error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-thir-lower-empty.rs:28:11 + --> $DIR/half-open-range-pats-thir-lower-empty.rs:27:11 | LL | m!(0, ..i64::MIN); | ^^^^^^^^^^ error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-thir-lower-empty.rs:30:11 + --> $DIR/half-open-range-pats-thir-lower-empty.rs:29:11 | LL | m!(0, ..i128::MIN); | ^^^^^^^^^^^ error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-thir-lower-empty.rs:33:14 + --> $DIR/half-open-range-pats-thir-lower-empty.rs:32:14 | LL | m!(0f32, ..f32::NEG_INFINITY); | ^^^^^^^^^^^^^^^^^^^ error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-thir-lower-empty.rs:35:14 + --> $DIR/half-open-range-pats-thir-lower-empty.rs:34:14 | LL | m!(0f64, ..f64::NEG_INFINITY); | ^^^^^^^^^^^^^^^^^^^ error[E0579]: lower range bound must be less than upper - --> $DIR/half-open-range-pats-thir-lower-empty.rs:38:13 + --> $DIR/half-open-range-pats-thir-lower-empty.rs:37:13 | LL | m!('a', ..'\u{0}'); | ^^^^^^^^^ diff --git a/tests/ui/lint/issue-86600-lint-twice.rs b/tests/ui/lint/issue-86600-lint-twice.rs deleted file mode 100644 index 0e8a837d987f6..0000000000000 --- a/tests/ui/lint/issue-86600-lint-twice.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Regression test for #86600, where an instance of the -// `illegal_floating_point_literal_pattern` lint was issued twice. - -// check-pass - -fn main() { - let x = 42.0; - - match x { - 5.0 => {} - //~^ WARNING: floating-point types cannot be used in patterns - //~| WARNING: this was previously accepted by the compiler - _ => {} - } -} diff --git a/tests/ui/lint/issue-86600-lint-twice.stderr b/tests/ui/lint/issue-86600-lint-twice.stderr deleted file mode 100644 index 5a65c61212858..0000000000000 --- a/tests/ui/lint/issue-86600-lint-twice.stderr +++ /dev/null @@ -1,12 +0,0 @@ -warning: floating-point types cannot be used in patterns - --> $DIR/issue-86600-lint-twice.rs:10:9 - | -LL | 5.0 => {} - | ^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 - = note: `#[warn(illegal_floating_point_literal_pattern)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/lint/rfc-2383-lint-reason/expect_tool_lint_rfc_2383.rs b/tests/ui/lint/rfc-2383-lint-reason/expect_tool_lint_rfc_2383.rs index f80fe88cbb97e..82ca49461ed23 100644 --- a/tests/ui/lint/rfc-2383-lint-reason/expect_tool_lint_rfc_2383.rs +++ b/tests/ui/lint/rfc-2383-lint-reason/expect_tool_lint_rfc_2383.rs @@ -22,12 +22,8 @@ mod rustc_ok { pub fn rustc_lints() { let x = 42.0; - #[expect(illegal_floating_point_literal_pattern)] - match x { - 5.0 => {} - 6.0 => {} - _ => {} - } + #[expect(invalid_nan_comparisons)] + let _b = x == f32::NAN; } } @@ -40,13 +36,9 @@ mod rustc_warn { pub fn rustc_lints() { let x = 42; - #[expect(illegal_floating_point_literal_pattern)] + #[expect(invalid_nan_comparisons)] //~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] - match x { - 5 => {} - 6 => {} - _ => {} - } + let _b = x == 5; } } diff --git a/tests/ui/lint/rfc-2383-lint-reason/expect_tool_lint_rfc_2383.stderr b/tests/ui/lint/rfc-2383-lint-reason/expect_tool_lint_rfc_2383.stderr index 6d49e7543dc59..efe1aa04e5e29 100644 --- a/tests/ui/lint/rfc-2383-lint-reason/expect_tool_lint_rfc_2383.stderr +++ b/tests/ui/lint/rfc-2383-lint-reason/expect_tool_lint_rfc_2383.stderr @@ -1,5 +1,5 @@ warning: this lint expectation is unfulfilled - --> $DIR/expect_tool_lint_rfc_2383.rs:37:14 + --> $DIR/expect_tool_lint_rfc_2383.rs:33:14 | LL | #[expect(dead_code)] | ^^^^^^^^^ @@ -7,10 +7,10 @@ LL | #[expect(dead_code)] = note: `#[warn(unfulfilled_lint_expectations)]` on by default warning: this lint expectation is unfulfilled - --> $DIR/expect_tool_lint_rfc_2383.rs:43:18 + --> $DIR/expect_tool_lint_rfc_2383.rs:39:18 | -LL | #[expect(illegal_floating_point_literal_pattern)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[expect(invalid_nan_comparisons)] + | ^^^^^^^^^^^^^^^^^^^^^^^ warning: 2 warnings emitted diff --git a/tests/ui/match/issue-41255.rs b/tests/ui/match/issue-41255.rs deleted file mode 100644 index d163801fd1902..0000000000000 --- a/tests/ui/match/issue-41255.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Matching against float literals should result in a linter error - -#![feature(exclusive_range_pattern)] -#![allow(unused)] -#![forbid(illegal_floating_point_literal_pattern)] - -fn main() { - let x = 42.0; - match x { - 5.0 => {}, //~ ERROR floating-point types cannot be used in patterns - //~| WARNING hard error - 5.0f32 => {}, //~ ERROR floating-point types cannot be used in patterns - //~| WARNING hard error - -5.0 => {}, //~ ERROR floating-point types cannot be used in patterns - //~| WARNING hard error - 1.0 .. 33.0 => {}, //~ ERROR floating-point types cannot be used in patterns - //~| WARNING hard error - //~| ERROR floating-point types cannot be used in patterns - //~| WARNING hard error - 39.0 ..= 70.0 => {}, //~ ERROR floating-point types cannot be used in patterns - //~| ERROR floating-point types cannot be used in patterns - //~| WARNING hard error - //~| WARNING hard error - - ..71.0 => {} - //~^ ERROR floating-point types cannot be used in patterns - //~| WARNING this was previously accepted by the compiler - ..=72.0 => {} - //~^ ERROR floating-point types cannot be used in patterns - //~| WARNING this was previously accepted by the compiler - 71.0.. => {} - //~^ ERROR floating-point types cannot be used in patterns - //~| WARNING this was previously accepted by the compiler - _ => {}, - }; - let y = 5.0; - // Same for tuples - match (x, 5) { - (3.14, 1) => {}, //~ ERROR floating-point types cannot be used - //~| WARNING hard error - _ => {}, - } - // Or structs - struct Foo { x: f32 }; - match (Foo { x }) { - Foo { x: 2.0 } => {}, //~ ERROR floating-point types cannot be used - //~| WARNING hard error - _ => {}, - } -} diff --git a/tests/ui/match/issue-41255.stderr b/tests/ui/match/issue-41255.stderr deleted file mode 100644 index 9bc49654e6d4b..0000000000000 --- a/tests/ui/match/issue-41255.stderr +++ /dev/null @@ -1,115 +0,0 @@ -error: floating-point types cannot be used in patterns - --> $DIR/issue-41255.rs:10:9 - | -LL | 5.0 => {}, - | ^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 -note: the lint level is defined here - --> $DIR/issue-41255.rs:5:11 - | -LL | #![forbid(illegal_floating_point_literal_pattern)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: floating-point types cannot be used in patterns - --> $DIR/issue-41255.rs:12:9 - | -LL | 5.0f32 => {}, - | ^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 - -error: floating-point types cannot be used in patterns - --> $DIR/issue-41255.rs:14:10 - | -LL | -5.0 => {}, - | ^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 - -error: floating-point types cannot be used in patterns - --> $DIR/issue-41255.rs:16:9 - | -LL | 1.0 .. 33.0 => {}, - | ^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 - -error: floating-point types cannot be used in patterns - --> $DIR/issue-41255.rs:16:16 - | -LL | 1.0 .. 33.0 => {}, - | ^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 - -error: floating-point types cannot be used in patterns - --> $DIR/issue-41255.rs:20:9 - | -LL | 39.0 ..= 70.0 => {}, - | ^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 - -error: floating-point types cannot be used in patterns - --> $DIR/issue-41255.rs:20:18 - | -LL | 39.0 ..= 70.0 => {}, - | ^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 - -error: floating-point types cannot be used in patterns - --> $DIR/issue-41255.rs:25:11 - | -LL | ..71.0 => {} - | ^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 - -error: floating-point types cannot be used in patterns - --> $DIR/issue-41255.rs:28:12 - | -LL | ..=72.0 => {} - | ^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 - -error: floating-point types cannot be used in patterns - --> $DIR/issue-41255.rs:31:9 - | -LL | 71.0.. => {} - | ^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 - -error: floating-point types cannot be used in patterns - --> $DIR/issue-41255.rs:39:10 - | -LL | (3.14, 1) => {}, - | ^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 - -error: floating-point types cannot be used in patterns - --> $DIR/issue-41255.rs:46:18 - | -LL | Foo { x: 2.0 } => {}, - | ^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #41620 - -error: aborting due to 12 previous errors - diff --git a/tests/ui/parser/issues/issue-7222.rs b/tests/ui/parser/issues/issue-7222.rs index 6490731662664..fb18f4cd62eca 100644 --- a/tests/ui/parser/issues/issue-7222.rs +++ b/tests/ui/parser/issues/issue-7222.rs @@ -1,6 +1,5 @@ // run-pass // pretty-expanded FIXME #23616 -#![allow(illegal_floating_point_literal_pattern)] // FIXME #41620 pub fn main() { const FOO: f64 = 10.0; diff --git a/tests/ui/pattern/usefulness/floats.rs b/tests/ui/pattern/usefulness/floats.rs index 2616dfadb85e8..63ce26adab2a4 100644 --- a/tests/ui/pattern/usefulness/floats.rs +++ b/tests/ui/pattern/usefulness/floats.rs @@ -1,5 +1,4 @@ #![feature(exclusive_range_pattern)] -#![allow(illegal_floating_point_literal_pattern)] #![deny(unreachable_patterns)] fn main() { diff --git a/tests/ui/pattern/usefulness/floats.stderr b/tests/ui/pattern/usefulness/floats.stderr index f50419118246b..d99f05f52842d 100644 --- a/tests/ui/pattern/usefulness/floats.stderr +++ b/tests/ui/pattern/usefulness/floats.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/floats.rs:11:11 + --> $DIR/floats.rs:10:11 | LL | match 0.0 { | ^^^ pattern `_` not covered @@ -12,49 +12,49 @@ LL + _ => todo!() | error: unreachable pattern - --> $DIR/floats.rs:19:9 + --> $DIR/floats.rs:18:9 | LL | 0.01f64 => {} | ^^^^^^^ | note: the lint level is defined here - --> $DIR/floats.rs:3:9 + --> $DIR/floats.rs:2:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/floats.rs:20:9 + --> $DIR/floats.rs:19:9 | LL | 0.02f64 => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/floats.rs:21:9 + --> $DIR/floats.rs:20:9 | LL | 6.5f64 => {} | ^^^^^^ error: unreachable pattern - --> $DIR/floats.rs:23:9 + --> $DIR/floats.rs:22:9 | LL | 1.0f64..=4.0f64 => {} | ^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/floats.rs:35:9 + --> $DIR/floats.rs:34:9 | LL | 0.01f32 => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/floats.rs:36:9 + --> $DIR/floats.rs:35:9 | LL | 0.02f32 => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/floats.rs:37:9 + --> $DIR/floats.rs:36:9 | LL | 6.5f32 => {} | ^^^^^^ diff --git a/tests/ui/pattern/usefulness/non-exhaustive-match.rs b/tests/ui/pattern/usefulness/non-exhaustive-match.rs index 1cb58b8cebef7..44acd397f8d58 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-match.rs +++ b/tests/ui/pattern/usefulness/non-exhaustive-match.rs @@ -1,5 +1,3 @@ -#![allow(illegal_floating_point_literal_pattern)] - enum T { A, B } fn main() { diff --git a/tests/ui/pattern/usefulness/non-exhaustive-match.stderr b/tests/ui/pattern/usefulness/non-exhaustive-match.stderr index 4bebd3cbbefd0..61ed0eb4fc4e2 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-match.stderr +++ b/tests/ui/pattern/usefulness/non-exhaustive-match.stderr @@ -1,11 +1,11 @@ error[E0004]: non-exhaustive patterns: `T::A` not covered - --> $DIR/non-exhaustive-match.rs:7:11 + --> $DIR/non-exhaustive-match.rs:5:11 | LL | match x { T::B => { } } | ^ pattern `T::A` not covered | note: `T` defined here - --> $DIR/non-exhaustive-match.rs:3:6 + --> $DIR/non-exhaustive-match.rs:1:6 | LL | enum T { A, B } | ^ - not covered @@ -16,7 +16,7 @@ LL | match x { T::B => { }, T::A => todo!() } | +++++++++++++++++ error[E0004]: non-exhaustive patterns: `false` not covered - --> $DIR/non-exhaustive-match.rs:8:11 + --> $DIR/non-exhaustive-match.rs:6:11 | LL | match true { | ^^^^ pattern `false` not covered @@ -29,7 +29,7 @@ LL + false => todo!() | error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/non-exhaustive-match.rs:11:11 + --> $DIR/non-exhaustive-match.rs:9:11 | LL | match Some(10) { | ^^^^^^^^ pattern `Some(_)` not covered @@ -47,7 +47,7 @@ LL + Some(_) => todo!() | error[E0004]: non-exhaustive patterns: `(_, _, i32::MIN..=3_i32)` and `(_, _, 5_i32..=i32::MAX)` not covered - --> $DIR/non-exhaustive-match.rs:14:11 + --> $DIR/non-exhaustive-match.rs:12:11 | LL | match (2, 3, 4) { | ^^^^^^^^^ patterns `(_, _, i32::MIN..=3_i32)` and `(_, _, 5_i32..=i32::MAX)` not covered @@ -60,7 +60,7 @@ LL + (_, _, i32::MIN..=3_i32) | (_, _, 5_i32..=i32::MAX) => todo!() | error[E0004]: non-exhaustive patterns: `(T::A, T::A)` and `(T::B, T::B)` not covered - --> $DIR/non-exhaustive-match.rs:18:11 + --> $DIR/non-exhaustive-match.rs:16:11 | LL | match (T::A, T::A) { | ^^^^^^^^^^^^ patterns `(T::A, T::A)` and `(T::B, T::B)` not covered @@ -73,13 +73,13 @@ LL + (T::A, T::A) | (T::B, T::B) => todo!() | error[E0004]: non-exhaustive patterns: `T::B` not covered - --> $DIR/non-exhaustive-match.rs:22:11 + --> $DIR/non-exhaustive-match.rs:20:11 | LL | match T::A { | ^^^^ pattern `T::B` not covered | note: `T` defined here - --> $DIR/non-exhaustive-match.rs:3:6 + --> $DIR/non-exhaustive-match.rs:1:6 | LL | enum T { A, B } | ^ - not covered @@ -91,7 +91,7 @@ LL + T::B => todo!() | error[E0004]: non-exhaustive patterns: `[]` not covered - --> $DIR/non-exhaustive-match.rs:33:11 + --> $DIR/non-exhaustive-match.rs:31:11 | LL | match *vec { | ^^^^ pattern `[]` not covered @@ -104,7 +104,7 @@ LL + [] => todo!() | error[E0004]: non-exhaustive patterns: `[_, _, _, _, ..]` not covered - --> $DIR/non-exhaustive-match.rs:46:11 + --> $DIR/non-exhaustive-match.rs:44:11 | LL | match *vec { | ^^^^ pattern `[_, _, _, _, ..]` not covered diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs index c5bca8c970dae..c8808c7eea9e2 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs @@ -1,7 +1,6 @@ // Matching against NaN should result in an error #![feature(exclusive_range_pattern)] #![allow(unused)] -#![allow(illegal_floating_point_literal_pattern)] const NAN: f64 = f64::NAN; diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr index deda07150c52d..bfcf919fbe8fc 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr @@ -1,5 +1,5 @@ error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:11:9 + --> $DIR/issue-6804-nan-match.rs:10:9 | LL | NAN => {}, | ^^^ @@ -8,7 +8,7 @@ LL | NAN => {}, = help: try using the `is_nan` method instead error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:16:10 + --> $DIR/issue-6804-nan-match.rs:15:10 | LL | [NAN, _] => {}, | ^^^ @@ -17,7 +17,7 @@ LL | [NAN, _] => {}, = help: try using the `is_nan` method instead error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:22:9 + --> $DIR/issue-6804-nan-match.rs:21:9 | LL | NAN..=1.0 => {}, | ^^^ @@ -26,13 +26,13 @@ LL | NAN..=1.0 => {}, = help: try using the `is_nan` method instead error[E0030]: lower range bound must be less than or equal to upper - --> $DIR/issue-6804-nan-match.rs:22:9 + --> $DIR/issue-6804-nan-match.rs:21:9 | LL | NAN..=1.0 => {}, | ^^^^^^^^^ lower bound larger than upper bound error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:24:16 + --> $DIR/issue-6804-nan-match.rs:23:16 | LL | -1.0..=NAN => {}, | ^^^ @@ -41,13 +41,13 @@ LL | -1.0..=NAN => {}, = help: try using the `is_nan` method instead error[E0030]: lower range bound must be less than or equal to upper - --> $DIR/issue-6804-nan-match.rs:24:9 + --> $DIR/issue-6804-nan-match.rs:23:9 | LL | -1.0..=NAN => {}, | ^^^^^^^^^^ lower bound larger than upper bound error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:26:9 + --> $DIR/issue-6804-nan-match.rs:25:9 | LL | NAN.. => {}, | ^^^ @@ -56,13 +56,13 @@ LL | NAN.. => {}, = help: try using the `is_nan` method instead error[E0030]: lower range bound must be less than or equal to upper - --> $DIR/issue-6804-nan-match.rs:26:9 + --> $DIR/issue-6804-nan-match.rs:25:9 | LL | NAN.. => {}, | ^^^^^ lower bound larger than upper bound error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:28:11 + --> $DIR/issue-6804-nan-match.rs:27:11 | LL | ..NAN => {}, | ^^^ @@ -71,7 +71,7 @@ LL | ..NAN => {}, = help: try using the `is_nan` method instead error[E0579]: lower range bound must be less than upper - --> $DIR/issue-6804-nan-match.rs:28:9 + --> $DIR/issue-6804-nan-match.rs:27:9 | LL | ..NAN => {}, | ^^^^^ diff --git a/tests/ui/union/union-pat-refutability.rs b/tests/ui/union/union-pat-refutability.rs index 17ac6c6dfa9bf..edcc1add38fde 100644 --- a/tests/ui/union/union-pat-refutability.rs +++ b/tests/ui/union/union-pat-refutability.rs @@ -1,7 +1,6 @@ // run-pass #![allow(dead_code)] -#![allow(illegal_floating_point_literal_pattern)] #[repr(u32)] enum Tag { From 9f14fc4af4554b5ae8b727ae68460777051f1f74 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 Sep 2023 23:02:42 +0200 Subject: [PATCH 25/30] add test checking behavior of matching on floats, and NaNs in consts --- tests/ui/match/match-float.rs | 11 +++++++ .../issue-6804-nan-match.rs | 10 ++++++ .../issue-6804-nan-match.stderr | 31 ++++++++++++------- 3 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 tests/ui/match/match-float.rs diff --git a/tests/ui/match/match-float.rs b/tests/ui/match/match-float.rs new file mode 100644 index 0000000000000..8da6a9ed2049c --- /dev/null +++ b/tests/ui/match/match-float.rs @@ -0,0 +1,11 @@ +// run-pass +// Makes sure we use `==` (not bitwise) semantics for float comparison. + +fn main() { + const F1: f32 = 0.0; + const F2: f32 = -0.0; + assert_eq!(F1, F2); + assert_ne!(F1.to_bits(), F2.to_bits()); + assert!(matches!(F1, F2)); + assert!(matches!(F2, F1)); +} diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs index c8808c7eea9e2..d43db576b3848 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.rs @@ -4,6 +4,11 @@ const NAN: f64 = f64::NAN; +#[derive(PartialEq, Eq)] +struct MyType(T); + +const C: MyType = MyType(f32::NAN); + fn main() { let x = NAN; match x { @@ -16,6 +21,11 @@ fn main() { _ => {}, }; + match MyType(1.0f32) { + C => {}, //~ ERROR cannot use NaN in patterns + _ => {}, + } + // Also cover range patterns match x { NAN..=1.0 => {}, //~ ERROR cannot use NaN in patterns diff --git a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr index bfcf919fbe8fc..167ada783c24c 100644 --- a/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr +++ b/tests/ui/rfcs/rfc-1445-restrict-constants-in-patterns/issue-6804-nan-match.stderr @@ -1,5 +1,5 @@ error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:10:9 + --> $DIR/issue-6804-nan-match.rs:15:9 | LL | NAN => {}, | ^^^ @@ -8,7 +8,7 @@ LL | NAN => {}, = help: try using the `is_nan` method instead error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:15:10 + --> $DIR/issue-6804-nan-match.rs:20:10 | LL | [NAN, _] => {}, | ^^^ @@ -17,7 +17,16 @@ LL | [NAN, _] => {}, = help: try using the `is_nan` method instead error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:21:9 + --> $DIR/issue-6804-nan-match.rs:25:9 + | +LL | C => {}, + | ^ + | + = note: NaNs compare inequal to everything, even themselves, so this pattern would never match + = help: try using the `is_nan` method instead + +error: cannot use NaN in patterns + --> $DIR/issue-6804-nan-match.rs:31:9 | LL | NAN..=1.0 => {}, | ^^^ @@ -26,13 +35,13 @@ LL | NAN..=1.0 => {}, = help: try using the `is_nan` method instead error[E0030]: lower range bound must be less than or equal to upper - --> $DIR/issue-6804-nan-match.rs:21:9 + --> $DIR/issue-6804-nan-match.rs:31:9 | LL | NAN..=1.0 => {}, | ^^^^^^^^^ lower bound larger than upper bound error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:23:16 + --> $DIR/issue-6804-nan-match.rs:33:16 | LL | -1.0..=NAN => {}, | ^^^ @@ -41,13 +50,13 @@ LL | -1.0..=NAN => {}, = help: try using the `is_nan` method instead error[E0030]: lower range bound must be less than or equal to upper - --> $DIR/issue-6804-nan-match.rs:23:9 + --> $DIR/issue-6804-nan-match.rs:33:9 | LL | -1.0..=NAN => {}, | ^^^^^^^^^^ lower bound larger than upper bound error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:25:9 + --> $DIR/issue-6804-nan-match.rs:35:9 | LL | NAN.. => {}, | ^^^ @@ -56,13 +65,13 @@ LL | NAN.. => {}, = help: try using the `is_nan` method instead error[E0030]: lower range bound must be less than or equal to upper - --> $DIR/issue-6804-nan-match.rs:25:9 + --> $DIR/issue-6804-nan-match.rs:35:9 | LL | NAN.. => {}, | ^^^^^ lower bound larger than upper bound error: cannot use NaN in patterns - --> $DIR/issue-6804-nan-match.rs:27:11 + --> $DIR/issue-6804-nan-match.rs:37:11 | LL | ..NAN => {}, | ^^^ @@ -71,12 +80,12 @@ LL | ..NAN => {}, = help: try using the `is_nan` method instead error[E0579]: lower range bound must be less than upper - --> $DIR/issue-6804-nan-match.rs:27:9 + --> $DIR/issue-6804-nan-match.rs:37:9 | LL | ..NAN => {}, | ^^^^^ -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors Some errors have detailed explanations: E0030, E0579. For more information about an error, try `rustc --explain E0030`. From 3f3a153056b0d60305f5b7d676ab2d720a91fef9 Mon Sep 17 00:00:00 2001 From: wackbyte Date: Fri, 26 Jan 2024 11:09:27 -0500 Subject: [PATCH 26/30] Use `` for array/slice equality `impl`s Makes the trait implementation documentation for arrays and slices appear more consistent. --- library/core/src/array/equality.rs | 64 ++++++++++----------- library/core/src/slice/cmp.rs | 8 +-- tests/ui/consts/too_generic_eval_ice.stderr | 12 ++-- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/library/core/src/array/equality.rs b/library/core/src/array/equality.rs index d749865f76f51..bdb6599abf549 100644 --- a/library/core/src/array/equality.rs +++ b/library/core/src/array/equality.rs @@ -2,36 +2,36 @@ use crate::cmp::BytewiseEq; use crate::convert::TryInto; #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[B; N]> for [A; N] +impl PartialEq<[U; N]> for [T; N] where - A: PartialEq, + T: PartialEq, { #[inline] - fn eq(&self, other: &[B; N]) -> bool { + fn eq(&self, other: &[U; N]) -> bool { SpecArrayEq::spec_eq(self, other) } #[inline] - fn ne(&self, other: &[B; N]) -> bool { + fn ne(&self, other: &[U; N]) -> bool { SpecArrayEq::spec_ne(self, other) } } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[B]> for [A; N] +impl PartialEq<[U]> for [T; N] where - A: PartialEq, + T: PartialEq, { #[inline] - fn eq(&self, other: &[B]) -> bool { - let b: Result<&[B; N], _> = other.try_into(); + fn eq(&self, other: &[U]) -> bool { + let b: Result<&[U; N], _> = other.try_into(); match b { Ok(b) => *self == *b, Err(_) => false, } } #[inline] - fn ne(&self, other: &[B]) -> bool { - let b: Result<&[B; N], _> = other.try_into(); + fn ne(&self, other: &[U]) -> bool { + let b: Result<&[U; N], _> = other.try_into(); match b { Ok(b) => *self != *b, Err(_) => true, @@ -40,21 +40,21 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[A; N]> for [B] +impl PartialEq<[U; N]> for [T] where - B: PartialEq, + T: PartialEq, { #[inline] - fn eq(&self, other: &[A; N]) -> bool { - let b: Result<&[B; N], _> = self.try_into(); + fn eq(&self, other: &[U; N]) -> bool { + let b: Result<&[T; N], _> = self.try_into(); match b { Ok(b) => *b == *other, Err(_) => false, } } #[inline] - fn ne(&self, other: &[A; N]) -> bool { - let b: Result<&[B; N], _> = self.try_into(); + fn ne(&self, other: &[U; N]) -> bool { + let b: Result<&[T; N], _> = self.try_into(); match b { Ok(b) => *b != *other, Err(_) => true, @@ -63,61 +63,61 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<&[B]> for [A; N] +impl PartialEq<&[U]> for [T; N] where - A: PartialEq, + T: PartialEq, { #[inline] - fn eq(&self, other: &&[B]) -> bool { + fn eq(&self, other: &&[U]) -> bool { *self == **other } #[inline] - fn ne(&self, other: &&[B]) -> bool { + fn ne(&self, other: &&[U]) -> bool { *self != **other } } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[A; N]> for &[B] +impl PartialEq<[U; N]> for &[T] where - B: PartialEq, + T: PartialEq, { #[inline] - fn eq(&self, other: &[A; N]) -> bool { + fn eq(&self, other: &[U; N]) -> bool { **self == *other } #[inline] - fn ne(&self, other: &[A; N]) -> bool { + fn ne(&self, other: &[U; N]) -> bool { **self != *other } } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<&mut [B]> for [A; N] +impl PartialEq<&mut [U]> for [T; N] where - A: PartialEq, + T: PartialEq, { #[inline] - fn eq(&self, other: &&mut [B]) -> bool { + fn eq(&self, other: &&mut [U]) -> bool { *self == **other } #[inline] - fn ne(&self, other: &&mut [B]) -> bool { + fn ne(&self, other: &&mut [U]) -> bool { *self != **other } } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[A; N]> for &mut [B] +impl PartialEq<[U; N]> for &mut [T] where - B: PartialEq, + T: PartialEq, { #[inline] - fn eq(&self, other: &[A; N]) -> bool { + fn eq(&self, other: &[U; N]) -> bool { **self == *other } #[inline] - fn ne(&self, other: &[A; N]) -> bool { + fn ne(&self, other: &[U; N]) -> bool { **self != *other } } diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 8a8d634c0072a..e8b0010ba1574 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -8,15 +8,15 @@ use super::from_raw_parts; use super::memchr; #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[B]> for [A] +impl PartialEq<[U]> for [T] where - A: PartialEq, + T: PartialEq, { - fn eq(&self, other: &[B]) -> bool { + fn eq(&self, other: &[U]) -> bool { SlicePartialEq::equal(self, other) } - fn ne(&self, other: &[B]) -> bool { + fn ne(&self, other: &[U]) -> bool { SlicePartialEq::not_equal(self, other) } } diff --git a/tests/ui/consts/too_generic_eval_ice.stderr b/tests/ui/consts/too_generic_eval_ice.stderr index 843d6d9e04ba7..4089c850c8021 100644 --- a/tests/ui/consts/too_generic_eval_ice.stderr +++ b/tests/ui/consts/too_generic_eval_ice.stderr @@ -22,13 +22,13 @@ LL | [5; Self::HOST_SIZE] == [6; 0] | = help: the trait `PartialEq<[{integer}; 0]>` is not implemented for `[{integer}; Self::HOST_SIZE]` = help: the following other types implement trait `PartialEq`: - <[A; N] as PartialEq<[B; N]>> - <[A; N] as PartialEq<[B]>> - <[A; N] as PartialEq<&[B]>> - <[A; N] as PartialEq<&mut [B]>> + <[T; N] as PartialEq<[U; N]>> + <[T; N] as PartialEq<[U]>> + <[T; N] as PartialEq<&[U]>> + <[T; N] as PartialEq<&mut [U]>> <[T] as PartialEq>> - <[A] as PartialEq<[B]>> - <[B] as PartialEq<[A; N]>> + <[T] as PartialEq<[U; N]>> + <[T] as PartialEq<[U]>> <&[T] as PartialEq>> and 3 others From 471af8c5a382229e57656798900406e1ec82c5c8 Mon Sep 17 00:00:00 2001 From: kxxt Date: Wed, 31 Jan 2024 09:02:18 +0800 Subject: [PATCH 27/30] riscv only supports split_debuginfo=off for now Disable packed/unpacked options for riscv linux/android. Other riscv targets already only have the off option. The packed/unpacked options might be supported in the future. See upstream issue for more details: https://github.com/llvm/llvm-project/issues/56642 Fixes #110224 --- .../src/spec/targets/riscv32gc_unknown_linux_gnu.rs | 5 ++++- .../src/spec/targets/riscv32gc_unknown_linux_musl.rs | 5 ++++- .../rustc_target/src/spec/targets/riscv64_linux_android.rs | 5 ++++- .../src/spec/targets/riscv64gc_unknown_linux_gnu.rs | 5 ++++- .../src/spec/targets/riscv64gc_unknown_linux_musl.rs | 5 ++++- 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs index 06e8f1837637f..0be32cbd77165 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_gnu.rs @@ -1,4 +1,6 @@ -use crate::spec::{base, CodeModel, Target, TargetOptions}; +use std::borrow::Cow; + +use crate::spec::{base, CodeModel, SplitDebuginfo, Target, TargetOptions}; pub fn target() -> Target { Target { @@ -12,6 +14,7 @@ pub fn target() -> Target { features: "+m,+a,+f,+d,+c".into(), llvm_abiname: "ilp32d".into(), max_atomic_width: Some(32), + supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), ..base::linux_gnu::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs index 722703d2384fe..cfa9990dac9bd 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32gc_unknown_linux_musl.rs @@ -1,4 +1,6 @@ -use crate::spec::{base, CodeModel, Target, TargetOptions}; +use std::borrow::Cow; + +use crate::spec::{base, CodeModel, SplitDebuginfo, Target, TargetOptions}; pub fn target() -> Target { Target { @@ -12,6 +14,7 @@ pub fn target() -> Target { features: "+m,+a,+f,+d,+c".into(), llvm_abiname: "ilp32d".into(), max_atomic_width: Some(32), + supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), ..base::linux_musl::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs b/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs index 40e447dbb8362..762197d7217c7 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64_linux_android.rs @@ -1,4 +1,6 @@ -use crate::spec::{base, CodeModel, SanitizerSet, Target, TargetOptions}; +use std::borrow::Cow; + +use crate::spec::{base, CodeModel, SanitizerSet, SplitDebuginfo, Target, TargetOptions}; pub fn target() -> Target { Target { @@ -13,6 +15,7 @@ pub fn target() -> Target { llvm_abiname: "lp64d".into(), supported_sanitizers: SanitizerSet::ADDRESS, max_atomic_width: Some(64), + supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), ..base::android::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs index c0969d4e11eb9..e71929a190479 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_gnu.rs @@ -1,4 +1,6 @@ -use crate::spec::{base, CodeModel, Target, TargetOptions}; +use std::borrow::Cow; + +use crate::spec::{base, CodeModel, SplitDebuginfo, Target, TargetOptions}; pub fn target() -> Target { Target { @@ -12,6 +14,7 @@ pub fn target() -> Target { features: "+m,+a,+f,+d,+c".into(), llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), + supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), ..base::linux_gnu::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs index 656e260d094d8..8ea28d6b162cc 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_linux_musl.rs @@ -1,4 +1,6 @@ -use crate::spec::{base, CodeModel, Target, TargetOptions}; +use std::borrow::Cow; + +use crate::spec::{base, CodeModel, SplitDebuginfo, Target, TargetOptions}; pub fn target() -> Target { Target { @@ -12,6 +14,7 @@ pub fn target() -> Target { features: "+m,+a,+f,+d,+c".into(), llvm_abiname: "lp64d".into(), max_atomic_width: Some(64), + supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), ..base::linux_musl::opts() }, } From 61d1ebe50be6343a3d5ee22db486163ff47e9ff1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 2 Feb 2024 20:12:37 +0100 Subject: [PATCH 28/30] fix typo Co-authored-by: teor --- library/core/src/cmp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 9c18d5573e627..fcb15b7ef65d8 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -87,7 +87,7 @@ use self::Ordering::*; /// *not* do `impl PartialEq for ForeignType`. /// /// This avoids the problem of transitive chains that criss-cross crate boundaries: for all local -/// types `T`, you may assue that no other crate will add `impl`s that allow comparing `T == U`. In +/// types `T`, you may assume that no other crate will add `impl`s that allow comparing `T == U`. In /// other words, if other crates add `impl`s that allow building longer transitive chains `U1 == ... /// == T == V1 == ...`, then all the types that appear to the right of `T` must be types that the /// crate defining `T` already knows about. This rules out transitive chains where downstream crates @@ -967,7 +967,7 @@ pub macro Ord($item:item) { /// *not* do `impl PartialOrd for ForeignType`. /// /// This avoids the problem of transitive chains that criss-cross crate boundaries: for all local -/// types `T`, you may assue that no other crate will add `impl`s that allow comparing `T < U`. In +/// types `T`, you may assume that no other crate will add `impl`s that allow comparing `T < U`. In /// other words, if other crates add `impl`s that allow building longer transitive chains `U1 < ... /// < T < V1 < ...`, then all the types that appear to the right of `T` must be types that the crate /// defining `T` already knows about. This rules out transitive chains where downstream crates can From 8266657338994c5dc26f571a3776c2afa6ca150d Mon Sep 17 00:00:00 2001 From: r0cky Date: Mon, 5 Feb 2024 08:07:55 +0800 Subject: [PATCH 29/30] Remove unused struct --- library/std/src/sys/pal/sgx/net.rs | 3 --- library/std/src/sys/pal/teeos/net.rs | 3 --- library/std/src/sys/pal/xous/net/mod.rs | 3 --- 3 files changed, 9 deletions(-) diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/pal/sgx/net.rs index 03620a08f2c03..c4d5da1627cb2 100644 --- a/library/std/src/sys/pal/sgx/net.rs +++ b/library/std/src/sys/pal/sgx/net.rs @@ -542,7 +542,4 @@ pub mod netc { pub sin6_flowinfo: u32, pub sin6_scope_id: u32, } - - #[derive(Copy, Clone)] - pub struct sockaddr {} } diff --git a/library/std/src/sys/pal/teeos/net.rs b/library/std/src/sys/pal/teeos/net.rs index 0df681dbfa552..fed95205027a7 100644 --- a/library/std/src/sys/pal/teeos/net.rs +++ b/library/std/src/sys/pal/teeos/net.rs @@ -364,9 +364,6 @@ pub mod netc { pub sin6_flowinfo: u32, pub sin6_scope_id: u32, } - - #[derive(Copy, Clone)] - pub struct sockaddr {} } pub type Socket = UdpSocket; diff --git a/library/std/src/sys/pal/xous/net/mod.rs b/library/std/src/sys/pal/xous/net/mod.rs index b5a3da136a6b0..dd8b765aa74ae 100644 --- a/library/std/src/sys/pal/xous/net/mod.rs +++ b/library/std/src/sys/pal/xous/net/mod.rs @@ -78,7 +78,4 @@ pub mod netc { pub sin6_flowinfo: u32, pub sin6_scope_id: u32, } - - #[derive(Copy, Clone)] - pub struct sockaddr {} } From 35dad14dfb63d77cf4a2077f1e8e9cff5a02a92b Mon Sep 17 00:00:00 2001 From: WANG Xuerui Date: Mon, 5 Feb 2024 13:18:32 +0800 Subject: [PATCH 30/30] target: default to the medium code model on LoongArch targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Rust LoongArch targets have been using the default LLVM code model so far, which is "small" in LLVM-speak and "normal" in LoongArch-speak. As described in the "Code Model" section of LoongArch ELF psABI spec v20231219 [1], one can only make function calls as far as ±128MiB with the "normal" code model; this is insufficient for very large software containing Rust components that needs to be linked into the big text section, such as Chromium. Because: * we do not want to ask users to recompile std if they are to build such software, * objects compiled with larger code models can be linked with those with smaller code models without problems, and * the "medium" code model is comparable to the "small"/"normal" one performance-wise (same data access pattern; each function call becomes 2-insn long and indirect, but this may be relaxed back into the direct 1-insn form in a future LLVM version), but is able to perform function calls within ±128GiB, it is better to just switch the targets to the "medium" code model, which is also "medium" in LLVM-speak. [1]: https://github.com/loongson/la-abi-specs/blob/v2.30/laelf.adoc#code-models --- .../src/spec/targets/loongarch64_unknown_linux_gnu.rs | 3 ++- .../rustc_target/src/spec/targets/loongarch64_unknown_none.rs | 2 +- .../src/spec/targets/loongarch64_unknown_none_softfloat.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs index 0f05e7c475a83..cb24e740c86f2 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs @@ -1,4 +1,4 @@ -use crate::spec::{base, Target, TargetOptions}; +use crate::spec::{base, CodeModel, Target, TargetOptions}; pub fn target() -> Target { Target { @@ -7,6 +7,7 @@ pub fn target() -> Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(), arch: "loongarch64".into(), options: TargetOptions { + code_model: Some(CodeModel::Medium), cpu: "generic".into(), features: "+f,+d".into(), llvm_abiname: "lp64d".into(), diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs index 3b1ea8e206f1c..f448017a2a58d 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs @@ -16,7 +16,7 @@ pub fn target() -> Target { max_atomic_width: Some(64), relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, - code_model: Some(CodeModel::Small), + code_model: Some(CodeModel::Medium), ..Default::default() }, } diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs index ab9300ef9c723..d636c9599a7be 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs @@ -17,7 +17,7 @@ pub fn target() -> Target { max_atomic_width: Some(64), relocation_model: RelocModel::Static, panic_strategy: PanicStrategy::Abort, - code_model: Some(CodeModel::Small), + code_model: Some(CodeModel::Medium), ..Default::default() }, }