From d560d3a480edb3d3f39b7d4a6343c6765b153baa Mon Sep 17 00:00:00 2001 From: Sander Saares Date: Thu, 26 Mar 2026 09:13:54 +0800 Subject: [PATCH] Add test for async Send inference with PhantomData<*mut ()> + unsafe impl Send + dyn Trait Add a regression test covering the case where a type uses PhantomData<*mut ()> to opt out of Sync, restores Send via an unsafe impl, and is then captured across an .await point with a trait object type parameter (Box). The compiler currently erases lifetimes in MIR coroutine witnesses, losing the 'static bound needed to apply the unsafe impl Send. The test verifies this is fixed by -Zhigher-ranked-assumptions and documents the PhantomData> workaround as a comparison. See the PR description for links to the relevant issues. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...ranked-auto-trait-18.no_assumptions.stderr | 13 +++ .../higher-ranked-auto-trait-18.rs | 79 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 tests/ui/async-await/higher-ranked-auto-trait-18.no_assumptions.stderr create mode 100644 tests/ui/async-await/higher-ranked-auto-trait-18.rs diff --git a/tests/ui/async-await/higher-ranked-auto-trait-18.no_assumptions.stderr b/tests/ui/async-await/higher-ranked-auto-trait-18.no_assumptions.stderr new file mode 100644 index 0000000000000..5a4c99acd0d43 --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-18.no_assumptions.stderr @@ -0,0 +1,13 @@ +error: higher-ranked lifetime error + --> $DIR/higher-ranked-auto-trait-18.rs:69:5 + | +LL | / require_send(async { +LL | | let r = Receiver::> { _value: None, _not_sync: PhantomData }; +LL | | let _ = r.await; +LL | | }); + | |______^ + | + = note: could not prove `{async block@$DIR/higher-ranked-auto-trait-18.rs:69:18: 69:23}: Send` + +error: aborting due to 1 previous error + diff --git a/tests/ui/async-await/higher-ranked-auto-trait-18.rs b/tests/ui/async-await/higher-ranked-auto-trait-18.rs new file mode 100644 index 0000000000000..3839fd86cfeba --- /dev/null +++ b/tests/ui/async-await/higher-ranked-auto-trait-18.rs @@ -0,0 +1,79 @@ +// Repro for a bug where `PhantomData<*mut ()>` + `unsafe impl Send` fails to prove +// `Send` for an async block when the type parameter is a trait object (`dyn Trait`). +// +// The static assertion `assert_send::>>()` succeeds, +// but the same type captured across an `.await` in an async block does not. +// This is because MIR erases the `'static` lifetime from `dyn MyTrait + 'static`, +// and the auto-trait analysis cannot recover the `T: 'static` bound without +// higher-ranked assumptions. +// +// Using `PhantomData>` instead of `PhantomData<*mut ()>` avoids the +// issue because `Cell<()>` is natively `Send`, so no `unsafe impl` (with its +// `T: 'static` bound) is needed. +// +// See . +//@ edition: 2021 +//@ revisions: assumptions no_assumptions +//@[assumptions] compile-flags: -Zhigher-ranked-assumptions +//@[assumptions] check-pass +//@[no_assumptions] known-bug: #110338 + +use std::cell::Cell; +use std::future::Future; +use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; + +// --- PhantomData<*mut ()> version: needs `unsafe impl Send` --- + +struct Receiver { + _value: Option, + _not_sync: PhantomData<*mut ()>, +} + +unsafe impl Send for Receiver {} + +impl Future for Receiver { + type Output = T; + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Pending + } +} + +// --- PhantomData> version: auto-derived Send works --- + +struct ReceiverCell { + _value: Option, + _not_sync: PhantomData>, +} + +impl Future for ReceiverCell { + type Output = T; + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Pending + } +} + +trait MyTrait: Send {} + +fn require_send(_f: F) {} + +fn main() { + // PhantomData<*mut ()> + concrete type: always works. + require_send(async { + let r = Receiver:: { _value: None, _not_sync: PhantomData }; + let _ = r.await; + }); + + // PhantomData<*mut ()> + dyn Trait: fails without higher-ranked assumptions. + require_send(async { + let r = Receiver::> { _value: None, _not_sync: PhantomData }; + let _ = r.await; + }); + + // PhantomData> + dyn Trait: always works (auto-derived Send). + require_send(async { + let r = ReceiverCell::> { _value: None, _not_sync: PhantomData }; + let _ = r.await; + }); +}