Repository: rust-lang/futures-rs
Pull request #2834 (2024-03-03 11:41:37)

Revert "Temporarily disable ref_wake_same test"
This reverts commit 42c2ab0e731892e9ff6c6e7fc28ab11d6eaee804.

~~Potentially fixed by https://github.com/rust-lang/rust/pull/121622.~~ Fixed by #2865


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9e8c472d..744168c0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -49,8 +49,9 @@ jobs:
           - os: ubuntu-latest
             target: i686-unknown-linux-gnu
     runs-on: ${{ matrix.os }}
+    timeout-minutes: 60
     steps:
-      - uses: actions/checkout@v4
+      - uses: taiki-e/checkout-action@v1
       - name: Install Rust
         # --no-self-update is necessary because the windows environment cannot self-update rustup.exe.
         run: rustup update nightly --no-self-update && rustup default nightly
@@ -72,8 +73,9 @@ jobs:
           # When updating this, the reminder to update the minimum required version in README.md and Cargo.toml.
           - '1.36'
     runs-on: ubuntu-latest
+    timeout-minutes: 60
     steps:
-      - uses: actions/checkout@v4
+      - uses: taiki-e/checkout-action@v1
       - name: Install Rust
         run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
       # cargo does not support for --features/--no-default-features with workspace, so use cargo-hack instead.
@@ -104,8 +106,9 @@ jobs:
           # When updating this, the reminder to update the minimum required version in README.md and Cargo.toml.
           - '1.56'
     runs-on: ubuntu-latest
+    timeout-minutes: 60
     steps:
-      - uses: actions/checkout@v4
+      - uses: taiki-e/checkout-action@v1
       - name: Install Rust
         run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
       - name: Install cargo-hack
@@ -136,8 +139,9 @@ jobs:
           - beta
           - nightly
     runs-on: ubuntu-latest
+    timeout-minutes: 60
     steps:
-      - uses: actions/checkout@v4
+      - uses: taiki-e/checkout-action@v1
       - name: Install Rust
         run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
       - name: Install cargo-hack
@@ -148,8 +152,9 @@ jobs:
   minimal-versions:
     name: cargo minimal-versions build
     runs-on: ubuntu-latest
+    timeout-minutes: 60
     steps:
-      - uses: actions/checkout@v4
+      - uses: taiki-e/checkout-action@v1
       - name: Install Rust
         run: rustup update nightly && rustup default nightly
       - name: Install cargo-hack
@@ -169,8 +174,9 @@ jobs:
           - thumbv7m-none-eabi
           - thumbv6m-none-eabi
     runs-on: ubuntu-latest
+    timeout-minutes: 60
     steps:
-      - uses: actions/checkout@v4
+      - uses: taiki-e/checkout-action@v1
       - name: Install Rust
         run: rustup update nightly && rustup default nightly
       - run: rustup target add ${{ matrix.target }}
@@ -201,8 +207,9 @@ jobs:
   bench:
     name: cargo bench
     runs-on: ubuntu-latest
+    timeout-minutes: 60
     steps:
-      - uses: actions/checkout@v4
+      - uses: taiki-e/checkout-action@v1
       - name: Install Rust
         run: rustup update nightly && rustup default nightly
       - run: cargo bench --workspace
@@ -211,8 +218,9 @@ jobs:
   features:
     name: cargo hack check --feature-powerset
     runs-on: ubuntu-latest
+    timeout-minutes: 60
     steps:
-      - uses: actions/checkout@v4
+      - uses: taiki-e/checkout-action@v1
       - name: Install Rust
         run: rustup update nightly && rustup default nightly
       - name: Install cargo-hack
@@ -233,8 +241,9 @@ jobs:
   miri:
     name: cargo miri test
     runs-on: ubuntu-latest
+    timeout-minutes: 60
     steps:
-      - uses: actions/checkout@v4
+      - uses: taiki-e/checkout-action@v1
       - name: Install Rust
         run: rustup toolchain install nightly --component miri && rustup default nightly
       - run: cargo miri test --workspace --all-features
@@ -253,10 +262,13 @@ jobs:
           - memory
           - thread
     runs-on: ubuntu-latest
+    timeout-minutes: 60
     steps:
-      - uses: actions/checkout@v4
+      - uses: taiki-e/checkout-action@v1
       - name: Install Rust
         run: rustup toolchain install nightly --component rust-src && rustup default nightly
+      # https://github.com/google/sanitizers/issues/1716 / https://github.com/actions/runner-images/issues/9491
+      - run: sudo sysctl vm.mmap_rnd_bits=28
       - run: cargo -Z build-std test --workspace --all-features --target x86_64-unknown-linux-gnu --lib --tests
         env:
           # TODO: Once `cfg(sanitize = "..")` is stable, replace
@@ -267,8 +279,9 @@ jobs:
   clippy:
     name: cargo clippy
     runs-on: ubuntu-latest
+    timeout-minutes: 60
     steps:
-      - uses: actions/checkout@v4
+      - uses: taiki-e/checkout-action@v1
       - name: Install Rust
         run: rustup toolchain install nightly --component clippy && rustup default nightly
       - run: cargo clippy --workspace --all-features --all-targets
@@ -276,8 +289,9 @@ jobs:
   fmt:
     name: cargo fmt
     runs-on: ubuntu-latest
+    timeout-minutes: 60
     steps:
-      - uses: actions/checkout@v4
+      - uses: taiki-e/checkout-action@v1
       - name: Install Rust
         run: rustup update stable
       - run: cargo fmt --all -- --check
@@ -285,8 +299,9 @@ jobs:
   docs:
     name: cargo doc
     runs-on: ubuntu-latest
+    timeout-minutes: 60
     steps:
-      - uses: actions/checkout@v4
+      - uses: taiki-e/checkout-action@v1
       - name: Install Rust
         run: rustup update nightly && rustup default nightly
       - run: cargo doc --workspace --no-deps --all-features
diff --git a/Cargo.toml b/Cargo.toml
index 13e2ebcc..3314bc3a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -24,6 +24,7 @@ missing_debug_implementations = "warn"
 rust_2018_idioms = "warn"
 single_use_lifetimes = "warn"
 unreachable_pub = "warn"
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(futures_sanitizer)'] }
 # unsafe_op_in_unsafe_fn = "warn" # Set at crate-level instead since https://github.com/rust-lang/rust/pull/100081 is not available on MSRV
 [workspace.lints.clippy]
 incompatible_msrv = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/12273, https://github.com/rust-lang/rust-clippy/issues/12257
diff --git a/futures-channel/src/mpsc/mod.rs b/futures-channel/src/mpsc/mod.rs
index e810d188..aa3e4b2d 100644
--- a/futures-channel/src/mpsc/mod.rs
+++ b/futures-channel/src/mpsc/mod.rs
@@ -297,13 +297,13 @@ struct State {
 }
 
 // The `is_open` flag is stored in the left-most bit of `Inner::state`
-const OPEN_MASK: usize = usize::max_value() - (usize::max_value() >> 1);
+const OPEN_MASK: usize = usize::MAX - (usize::MAX >> 1);
 
 // When a new channel is created, it is created in the open state with no
 // pending messages.
 const INIT_STATE: usize = OPEN_MASK;
 
-// The maximum number of messages that a channel can track is `usize::max_value() >> 1`
+// The maximum number of messages that a channel can track is `usize::MAX >> 1`
 const MAX_CAPACITY: usize = !(OPEN_MASK);
 
 // The maximum requested buffer size must be less than the maximum capacity of
diff --git a/futures-core/src/stream.rs b/futures-core/src/stream.rs
index ad5350b7..19114e7d 100644
--- a/futures-core/src/stream.rs
+++ b/futures-core/src/stream.rs
@@ -38,15 +38,15 @@ pub trait Stream {
     /// stream state:
     ///
     /// - `Poll::Pending` means that this stream's next value is not ready
-    /// yet. Implementations will ensure that the current task will be notified
-    /// when the next value may be ready.
+    ///   yet. Implementations will ensure that the current task will be notified
+    ///   when the next value may be ready.
     ///
     /// - `Poll::Ready(Some(val))` means that the stream has successfully
-    /// produced a value, `val`, and may produce further values on subsequent
-    /// `poll_next` calls.
+    ///   produced a value, `val`, and may produce further values on subsequent
+    ///   `poll_next` calls.
     ///
     /// - `Poll::Ready(None)` means that the stream has terminated, and
-    /// `poll_next` should not be invoked again.
+    ///   `poll_next` should not be invoked again.
     ///
     /// # Panics
     ///
diff --git a/futures-executor/src/lib.rs b/futures-executor/src/lib.rs
index ec3a5a40..a0dc49d7 100644
--- a/futures-executor/src/lib.rs
+++ b/futures-executor/src/lib.rs
@@ -46,7 +46,6 @@
 ))]
 #![warn(missing_docs, unsafe_op_in_unsafe_fn)]
 #![cfg_attr(docsrs, feature(doc_cfg))]
-#![allow(clippy::thread_local_initializer_can_be_made_const)] // clippy bug: this lint doesn't consider MSRV
 
 #[cfg(feature = "std")]
 extern crate std;
diff --git a/futures-macro/src/join.rs b/futures-macro/src/join.rs
index 94e356f7..0e891b3a 100644
--- a/futures-macro/src/join.rs
+++ b/futures-macro/src/join.rs
@@ -1,4 +1,4 @@
-//! The futures-rs `join! macro implementation.
+//! The futures-rs `join!` macro implementation.
 
 use proc_macro::TokenStream;
 use proc_macro2::{Span, TokenStream as TokenStream2};
diff --git a/futures-macro/src/lib.rs b/futures-macro/src/lib.rs
index 6dd165d8..c1b94785 100644
--- a/futures-macro/src/lib.rs
+++ b/futures-macro/src/lib.rs
@@ -8,11 +8,6 @@
     )
 ))]
 
-// Since https://github.com/rust-lang/cargo/pull/7700 `proc_macro` is part of the prelude for
-// proc-macro crates, but to support older compilers we still need this explicit `extern crate`.
-#[allow(unused_extern_crates)]
-extern crate proc_macro;
-
 use proc_macro::TokenStream;
 
 mod executor;
diff --git a/futures-macro/src/select.rs b/futures-macro/src/select.rs
index 57bb74a8..7b45cfe4 100644
--- a/futures-macro/src/select.rs
+++ b/futures-macro/src/select.rs
@@ -1,4 +1,4 @@
-//! The futures-rs `select! macro implementation.
+//! The futures-rs `select!` macro implementation.
 
 use proc_macro::TokenStream;
 use proc_macro2::Span;
diff --git a/futures-task/src/waker.rs b/futures-task/src/waker.rs
index b1410a19..747457a5 100644
--- a/futures-task/src/waker.rs
+++ b/futures-task/src/waker.rs
@@ -36,6 +36,7 @@ unsafe fn increase_refcount<T: ArcWake + 'static>(data: *const ()) {
 }
 
 // used by `waker_ref`
+#[inline(always)]
 unsafe fn clone_arc_raw<T: ArcWake + 'static>(data: *const ()) -> RawWaker {
     unsafe { increase_refcount::<T>(data) }
     RawWaker::new(data, waker_vtable::<T>())
diff --git a/futures-util/src/abortable.rs b/futures-util/src/abortable.rs
index 9dbcfc2b..e1e79e37 100644
--- a/futures-util/src/abortable.rs
+++ b/futures-util/src/abortable.rs
@@ -64,7 +64,7 @@ impl<T> Abortable<T> {
     /// This means that it will return `true` even if:
     /// * `abort` was called after the task had completed.
     /// * `abort` was called while the task was being polled - the task may still be running and
-    /// will not be stopped until `poll` returns.
+    ///   will not be stopped until `poll` returns.
     pub fn is_aborted(&self) -> bool {
         self.inner.aborted.load(Ordering::Relaxed)
     }
@@ -200,7 +200,7 @@ impl AbortHandle {
     /// even if:
     /// * `abort` was called after the task had completed.
     /// * `abort` was called while the task was being polled - the task may still be running and
-    /// will not be stopped until `poll` returns.
+    ///   will not be stopped until `poll` returns.
     ///
     /// This operation has a Relaxed ordering.
     pub fn is_aborted(&self) -> bool {
diff --git a/futures-util/src/async_await/select_mod.rs b/futures-util/src/async_await/select_mod.rs
index 84c6ca5a..638a92eb 100644
--- a/futures-util/src/async_await/select_mod.rs
+++ b/futures-util/src/async_await/select_mod.rs
@@ -3,6 +3,7 @@
 macro_rules! document_select_macro {
     // This branch is required for `futures 0.3.1`, from before select_biased was introduced
     ($select:item) => {
+        #[allow(clippy::too_long_first_doc_paragraph)]
         /// Polls multiple futures and streams simultaneously, executing the branch
         /// for the future that finishes first. If multiple futures are ready,
         /// one will be pseudo-randomly selected at runtime. Futures directly
@@ -153,6 +154,7 @@ macro_rules! document_select_macro {
     ($select:item $select_biased:item) => {
         document_select_macro!($select);
 
+        #[allow(clippy::too_long_first_doc_paragraph)]
         /// Polls multiple futures and streams simultaneously, executing the branch
         /// for the future that finishes first. Unlike [`select!`], if multiple futures are ready,
         /// one will be selected in order of declaration. Futures directly
diff --git a/futures-util/src/async_await/stream_select_mod.rs b/futures-util/src/async_await/stream_select_mod.rs
index 541b4cd9..039e65a6 100644
--- a/futures-util/src/async_await/stream_select_mod.rs
+++ b/futures-util/src/async_await/stream_select_mod.rs
@@ -3,6 +3,7 @@
 #[doc(hidden)]
 pub use futures_macro::stream_select_internal;
 
+#[allow(clippy::too_long_first_doc_paragraph)]
 /// Combines several streams, all producing the same `Item` type, into one stream.
 /// This is similar to `select_all` but does not require the streams to all be the same type.
 /// It also keeps the streams inline, and does not require `Box<dyn Stream>`s to be allocated.
diff --git a/futures-util/src/compat/compat03as01.rs b/futures-util/src/compat/compat03as01.rs
index 2f9d65e7..3c5bb77b 100644
--- a/futures-util/src/compat/compat03as01.rs
+++ b/futures-util/src/compat/compat03as01.rs
@@ -15,6 +15,7 @@ use futures_sink::Sink as Sink03;
 use std::marker::PhantomData;
 use std::{mem, pin::Pin, sync::Arc, task::Context};
 
+#[allow(clippy::too_long_first_doc_paragraph)] // clippy bug, see https://github.com/rust-lang/rust-clippy/issues/13315
 /// Converts a futures 0.3 [`TryFuture`](futures_core::future::TryFuture) or
 /// [`TryStream`](futures_core::stream::TryStream) into a futures 0.1
 /// [`Future`](futures_01::future::Future) or
diff --git a/futures-util/src/future/always_ready.rs b/futures-util/src/future/always_ready.rs
new file mode 100644
index 00000000..28625e72
--- /dev/null
+++ b/futures-util/src/future/always_ready.rs
@@ -0,0 +1,62 @@
+use super::assert_future;
+use core::pin::Pin;
+use futures_core::future::{FusedFuture, Future};
+use futures_core::task::{Context, Poll};
+
+/// Future for the [`always_ready`](always_ready()) function.
+#[must_use = "futures do nothing unless you `.await` or poll them"]
+pub struct AlwaysReady<T, F: Fn() -> T>(F);
+
+impl<T, F: Fn() -> T> core::fmt::Debug for AlwaysReady<T, F> {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        f.debug_tuple("AlwaysReady").finish()
+    }
+}
+
+impl<T, F: Fn() -> T + Clone> Clone for AlwaysReady<T, F> {
+    fn clone(&self) -> Self {
+        Self(self.0.clone())
+    }
+}
+
+impl<T, F: Fn() -> T + Copy> Copy for AlwaysReady<T, F> {}
+
+impl<T, F: Fn() -> T> Unpin for AlwaysReady<T, F> {}
+
+impl<T, F: Fn() -> T> FusedFuture for AlwaysReady<T, F> {
+    fn is_terminated(&self) -> bool {
+        false
+    }
+}
+
+impl<T, F: Fn() -> T> Future for AlwaysReady<T, F> {
+    type Output = T;
+
+    #[inline]
+    fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<T> {
+        Poll::Ready(self.0())
+    }
+}
+
+/// Creates a future that is always immediately ready with a value.
+///
+/// This is particularly useful in avoiding a heap allocation when an API needs [`Box<dyn Future<Output = T>>`],
+/// as [`AlwaysReady`] does not have to store a boolean for `is_finished`.
+///
+/// # Examples
+///
+/// ```
+/// # futures::executor::block_on(async {
+/// use std::mem::size_of_val;
+///
+/// use futures::future;
+///
+/// let a = future::always_ready(|| 1);
+/// assert_eq!(size_of_val(&a), 0);
+/// assert_eq!(a.await, 1);
+/// assert_eq!(a.await, 1);
+/// # });
+/// ```
+pub fn always_ready<T, F: Fn() -> T>(prod: F) -> AlwaysReady<T, F> {
+    assert_future::<T, _>(AlwaysReady(prod))
+}
diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs
index a95a4feb..b4d9bff8 100644
--- a/futures-util/src/future/future/shared.rs
+++ b/futures-util/src/future/future/shared.rs
@@ -82,7 +82,7 @@ const POLLING: usize = 1;
 const COMPLETE: usize = 2;
 const POISONED: usize = 3;
 
-const NULL_WAKER_KEY: usize = usize::max_value();
+const NULL_WAKER_KEY: usize = usize::MAX;
 
 impl<Fut: Future> Shared<Fut> {
     pub(super) fn new(future: Fut) -> Self {
diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs
index 21b1a4eb..1280ce98 100644
--- a/futures-util/src/future/mod.rs
+++ b/futures-util/src/future/mod.rs
@@ -74,6 +74,9 @@ pub use self::poll_immediate::{poll_immediate, PollImmediate};
 mod ready;
 pub use self::ready::{err, ok, ready, Ready};
 
+mod always_ready;
+pub use self::always_ready::{always_ready, AlwaysReady};
+
 mod join;
 pub use self::join::{join, Join};
 
diff --git a/futures-util/src/io/buf_reader.rs b/futures-util/src/io/buf_reader.rs
index c211118a..30611bd8 100644
--- a/futures-util/src/io/buf_reader.rs
+++ b/futures-util/src/io/buf_reader.rs
@@ -52,7 +52,9 @@ impl<R: AsyncRead> BufReader<R> {
         let buffer = vec![0; capacity];
         Self { inner, buffer: buffer.into_boxed_slice(), pos: 0, cap: 0 }
     }
+}
 
+impl<R> BufReader<R> {
     delegate_access_inner!(inner, R, ());
 
     /// Returns a reference to the internally buffered data.
@@ -211,7 +213,7 @@ impl<R: AsyncRead + AsyncSeek> AsyncSeek for BufReader<R> {
             // it should be safe to assume that remainder fits within an i64 as the alternative
             // means we managed to allocate 8 exbibytes and that's absurd.
             // But it's not out of the realm of possibility for some weird underlying reader to
-            // support seeking by i64::min_value() so we need to handle underflow when subtracting
+            // support seeking by i64::MIN so we need to handle underflow when subtracting
             // remainder.
             if let Some(offset) = n.checked_sub(remainder) {
                 result =
diff --git a/futures-util/src/io/buf_writer.rs b/futures-util/src/io/buf_writer.rs
index e0037262..ddcf5e1b 100644
--- a/futures-util/src/io/buf_writer.rs
+++ b/futures-util/src/io/buf_writer.rs
@@ -79,6 +79,26 @@ impl<W: AsyncWrite> BufWriter<W> {
         Poll::Ready(ret)
     }
 
+    /// Write directly using `inner`, bypassing buffering
+    pub(super) fn inner_poll_write(
+        self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        buf: &[u8],
+    ) -> Poll<io::Result<usize>> {
+        self.project().inner.poll_write(cx, buf)
+    }
+
+    /// Write directly using `inner`, bypassing buffering
+    pub(super) fn inner_poll_write_vectored(
+        self: Pin<&mut Self>,
+        cx: &mut Context<'_>,
+        bufs: &[IoSlice<'_>],
+    ) -> Poll<io::Result<usize>> {
+        self.project().inner.poll_write_vectored(cx, bufs)
+    }
+}
+
+impl<W> BufWriter<W> {
     delegate_access_inner!(inner, W, ());
 
     /// Returns a reference to the internally buffered data.
@@ -131,24 +151,6 @@ impl<W: AsyncWrite> BufWriter<W> {
             this.buf.set_len(old_len + buf_len);
         }
     }
-
-    /// Write directly using `inner`, bypassing buffering
-    pub(super) fn inner_poll_write(
-        self: Pin<&mut Self>,
-        cx: &mut Context<'_>,
-        buf: &[u8],
-    ) -> Poll<io::Result<usize>> {
-        self.project().inner.poll_write(cx, buf)
-    }
-
-    /// Write directly using `inner`, bypassing buffering
-    pub(super) fn inner_poll_write_vectored(
-        self: Pin<&mut Self>,
-        cx: &mut Context<'_>,
-        bufs: &[IoSlice<'_>],
-    ) -> Poll<io::Result<usize>> {
-        self.project().inner.poll_write_vectored(cx, bufs)
-    }
 }
 
 impl<W: AsyncWrite> AsyncWrite for BufWriter<W> {
diff --git a/futures-util/src/lib.rs b/futures-util/src/lib.rs
index abe35e68..c5af6960 100644
--- a/futures-util/src/lib.rs
+++ b/futures-util/src/lib.rs
@@ -10,7 +10,6 @@
     )
 ))]
 #![warn(missing_docs, unsafe_op_in_unsafe_fn)]
-#![cfg_attr(feature = "write-all-vectored", feature(io_slice_advance))]
 #![cfg_attr(docsrs, feature(doc_cfg))]
 #![allow(clippy::needless_borrow)] // https://github.com/rust-lang/futures-rs/pull/2558#issuecomment-1030745203
 #![allow(clippy::arc_with_non_send_sync)] // false positive https://github.com/rust-lang/rust-clippy/issues/11076
diff --git a/futures-util/src/lock/bilock.rs b/futures-util/src/lock/bilock.rs
index a89678e0..5970ca2b 100644
--- a/futures-util/src/lock/bilock.rs
+++ b/futures-util/src/lock/bilock.rs
@@ -102,7 +102,7 @@ impl<T> BiLock<T> {
                 // so we need to update that task.
                 _ => unsafe {
                     let mut prev = Box::from_raw(n);
-                    *prev = cx.waker().clone();
+                    (*prev).clone_from(cx.waker());
                     waker = Some(prev);
                 },
             }
diff --git a/futures-util/src/stream/iter.rs b/futures-util/src/stream/iter.rs
index 20471c2e..48b6519a 100644
--- a/futures-util/src/stream/iter.rs
+++ b/futures-util/src/stream/iter.rs
@@ -10,6 +10,23 @@ pub struct Iter<I> {
     iter: I,
 }
 
+impl<I> Iter<I> {
+    /// Acquires a reference to the underlying iterator that this stream is pulling from.
+    pub fn get_ref(&self) -> &I {
+        &self.iter
+    }
+
+    /// Acquires a mutable reference to the underlying iterator that this stream is pulling from.
+    pub fn get_mut(&mut self) -> &mut I {
+        &mut self.iter
+    }
+
+    /// Consumes this stream, returning the underlying iterator.
+    pub fn into_inner(self) -> I {
+        self.iter
+    }
+}
+
 impl<I> Unpin for Iter<I> {}
 
 /// Converts an `Iterator` into a `Stream` which is always ready
diff --git a/futures-util/src/stream/repeat.rs b/futures-util/src/stream/repeat.rs
index 3f9aa87d..e09cfa2e 100644
--- a/futures-util/src/stream/repeat.rs
+++ b/futures-util/src/stream/repeat.rs
@@ -44,7 +44,7 @@ where
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
-        (usize::max_value(), None)
+        (usize::MAX, None)
     }
 }
 
diff --git a/futures-util/src/stream/repeat_with.rs b/futures-util/src/stream/repeat_with.rs
index f5a81b4e..a4825107 100644
--- a/futures-util/src/stream/repeat_with.rs
+++ b/futures-util/src/stream/repeat_with.rs
@@ -24,7 +24,7 @@ impl<A, F: FnMut() -> A> Stream for RepeatWith<F> {
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
-        (usize::max_value(), None)
+        (usize::MAX, None)
     }
 }
 
diff --git a/futures-util/src/stream/select_with_strategy.rs b/futures-util/src/stream/select_with_strategy.rs
index 3be933e9..4375f3e2 100644
--- a/futures-util/src/stream/select_with_strategy.rs
+++ b/futures-util/src/stream/select_with_strategy.rs
@@ -75,6 +75,7 @@ pin_project! {
     }
 }
 
+#[allow(clippy::too_long_first_doc_paragraph)]
 /// This function will attempt to pull items from both streams. You provide a
 /// closure to tell [`SelectWithStrategy`] which stream to poll. The closure can
 /// store state on `SelectWithStrategy` to which it will receive a `&mut` on every
diff --git a/futures-util/src/stream/stream/cycle.rs b/futures-util/src/stream/stream/cycle.rs
index 1365557b..9e0f50e9 100644
--- a/futures-util/src/stream/stream/cycle.rs
+++ b/futures-util/src/stream/stream/cycle.rs
@@ -1,5 +1,4 @@
 use core::pin::Pin;
-use core::usize;
 use futures_core::ready;
 use futures_core::stream::{FusedStream, Stream};
 use futures_core::task::{Context, Poll};
@@ -48,7 +47,7 @@ where
         match self.orig.size_hint() {
             size @ (0, Some(0)) => size,
             (0, _) => (0, None),
-            _ => (usize::max_value(), None),
+            _ => (usize::MAX, None),
         }
     }
 }
diff --git a/futures-util/src/stream/stream/flatten_unordered.rs b/futures-util/src/stream/stream/flatten_unordered.rs
index 37811a10..c79edbd2 100644
--- a/futures-util/src/stream/stream/flatten_unordered.rs
+++ b/futures-util/src/stream/stream/flatten_unordered.rs
@@ -119,7 +119,7 @@ impl SharedPollState {
     /// - `!WAKING` as
     ///   * Wakers called during the `POLLING` phase won't propagate their calls
     ///   * `POLLING` phase can't start if some of the wakers are active
-    ///   So no wrapped waker can touch the inner waker's cell, it's safe to poll again.
+    ///     So no wrapped waker can touch the inner waker's cell, it's safe to poll again.
     fn stop_polling(&self, to_poll: u8, will_be_woken: bool) -> u8 {
         self.state
             .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |mut value| {
diff --git a/futures-util/src/stream/stream/mod.rs b/futures-util/src/stream/stream/mod.rs
index 2a505415..ee30f8da 100644
--- a/futures-util/src/stream/stream/mod.rs
+++ b/futures-util/src/stream/stream/mod.rs
@@ -336,7 +336,7 @@ pub trait StreamExt: Stream {
     /// # Overflow Behavior
     ///
     /// The method does no guarding against overflows, so enumerating more than
-    /// [`prim@usize::max_value()`] elements either produces the wrong result or panics. If
+    /// [`usize::MAX`] elements either produces the wrong result or panics. If
     /// debug assertions are enabled, a panic is guaranteed.
     ///
     /// # Panics
diff --git a/futures/Cargo.toml b/futures/Cargo.toml
index 71482ba3..84edf900 100644
--- a/futures/Cargo.toml
+++ b/futures/Cargo.toml
@@ -28,7 +28,6 @@ futures-executor = { path = "../futures-executor", features = ["thread-pool"] }
 futures-test = { path = "../futures-test" }
 assert_matches = "1.3.0"
 pin-project = "1.0.11"
-pin-utils = "0.1.0"
 static_assertions = "1"
 tokio = "0.1.11"
 
diff --git a/futures/src/lib.rs b/futures/src/lib.rs
index 71e71c23..839a0a1f 100644
--- a/futures/src/lib.rs
+++ b/futures/src/lib.rs
@@ -145,6 +145,7 @@ pub use futures_util::lock;
 #[doc(inline)]
 pub use futures_util::io;
 
+#[allow(clippy::mixed_attributes_style)] // https://github.com/rust-lang/rust-clippy/issues/12435
 #[cfg(feature = "executor")]
 #[cfg_attr(docsrs, doc(cfg(feature = "executor")))]
 pub mod executor {
@@ -197,6 +198,7 @@ pub mod executor {
     pub use futures_executor::{ThreadPool, ThreadPoolBuilder};
 }
 
+#[allow(clippy::mixed_attributes_style)] // https://github.com/rust-lang/rust-clippy/issues/12435
 #[cfg(feature = "compat")]
 #[cfg_attr(docsrs, doc(cfg(feature = "compat")))]
 pub mod compat {
diff --git a/triagebot.toml b/triagebot.toml
index fa0824ac..e4e7a2d7 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -1,1 +1,2 @@
 [assign]
+warn_non_default_branch = true


Test patch:
diff --git a/futures-test/Cargo.toml b/futures-test/Cargo.toml
index 54a33d4a..3d4c6cd8 100644
--- a/futures-test/Cargo.toml
+++ b/futures-test/Cargo.toml
@@ -18,7 +18,6 @@ futures-util = { version = "=0.4.0-alpha.0", path = "../futures-util", default-f
 futures-executor = { version = "=0.4.0-alpha.0", path = "../futures-executor", default-features = false }
 futures-sink = { version = "=0.4.0-alpha.0", path = "../futures-sink", default-features = false }
 futures-macro = { version = "=0.4.0-alpha.0", path = "../futures-macro", default-features = false }
-pin-utils = { version = "0.1.0", default-features = false }
 pin-project = "1.0.11"
 
 [dev-dependencies]
diff --git a/futures/tests/future_join.rs b/futures/tests/future_join.rs
index f5df9d77..0556a6e6 100644
--- a/futures/tests/future_join.rs
+++ b/futures/tests/future_join.rs
@@ -1,5 +1,5 @@
 use futures::executor::block_on;
-use futures::future::Future;
+use futures::future::{self, Future};
 use std::task::Poll;
 
 /// This tests verifies (through miri) that self-referencing
@@ -21,7 +21,7 @@ async fn trouble() {
 
 fn yield_now() -> impl Future<Output = ()> {
     let mut yielded = false;
-    std::future::poll_fn(move |cx| {
+    future::poll_fn(move |cx| {
         if core::mem::replace(&mut yielded, true) {
             Poll::Ready(())
         } else {
diff --git a/futures/tests/future_try_join_all.rs b/futures/tests/future_try_join_all.rs
index 9a824872..892e7759 100644
--- a/futures/tests/future_try_join_all.rs
+++ b/futures/tests/future_try_join_all.rs
@@ -1,8 +1,7 @@
 use futures::executor::block_on;
+use futures::future::{err, ok, try_join_all, Future, TryJoinAll};
 use futures::pin_mut;
-use futures_util::future::{err, ok, try_join_all, TryJoinAll};
 use std::fmt::Debug;
-use std::future::Future;
 
 #[track_caller]
 fn assert_done<T>(actual_fut: impl Future<Output = T>, expected: T)
diff --git a/futures/tests/no-std/build.rs b/futures/tests/no-std/build.rs
index a96a6827..ba79f203 100644
--- a/futures/tests/no-std/build.rs
+++ b/futures/tests/no-std/build.rs
@@ -1,6 +1,7 @@
 use std::{env, process::Command};
 
 fn main() {
+    println!("cargo:rustc-check-cfg=cfg(nightly)");
     if is_nightly() {
         println!("cargo:rustc-cfg=nightly");
     }
diff --git a/futures/tests/task_arc_wake.rs b/futures/tests/task_arc_wake.rs
index 36c7c67b..aedc15bc 100644
--- a/futures/tests/task_arc_wake.rs
+++ b/futures/tests/task_arc_wake.rs
@@ -44,18 +44,17 @@ fn create_from_arc() {
     assert_eq!(1, Arc::strong_count(&some_w));
 }
 
-// TODO: rustc regression: https://github.com/rust-lang/rust/issues/121600
-// #[test]
-// fn ref_wake_same() {
-//     let some_w = Arc::new(CountingWaker::new());
-//
-//     let w1: Waker = task::waker(some_w.clone());
-//     let w2 = task::waker_ref(&some_w);
-//     let w3 = w2.clone();
-//
-//     assert!(w1.will_wake(&w2));
-//     assert!(w2.will_wake(&w3));
-// }
+#[test]
+fn ref_wake_same() {
+    let some_w = Arc::new(CountingWaker::new());
+
+    let w1: Waker = task::waker(some_w.clone());
+    let w2 = task::waker_ref(&some_w);
+    let w3 = w2.clone();
+
+    assert!(w1.will_wake(&w2));
+    assert!(w2.will_wake(&w3));
+}
 
 #[test]
 fn proper_refcount_on_wake_panic() {