Async Send inference fails with PhantomData<*mut ()> + unsafe impl Send when T is a trait object
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
I tried this code:
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
struct Receiver<T: Send + 'static> {
_value: Option<T>,
_not_sync: PhantomData<*mut ()>,
}
unsafe impl<T: Send + 'static> Send for Receiver<T> {}
impl<T: Send + 'static> Future for Receiver<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Pending
}
}
trait MyTrait: Send {}
fn require_send<F: Future + Send>(_f: F) {}
fn main() {
// Static assertion works:
fn assert_send<T: Send>() {}
assert_send::<Receiver<Box<dyn MyTrait>>>();
// Async block with concrete type works:
require_send(async {
let r = Receiver::<u32> { _value: None, _not_sync: PhantomData };
let _ = r.await;
});
// Async block with dyn Trait FAILS:
require_send(async {
let r = Receiver::<Box<dyn MyTrait>> { _value: None, _not_sync: PhantomData };
let _ = r.await;
});
}
I expected this to compile, because Receiver<Box<dyn MyTrait>>: Send is established by the unsafe impl (and the static assertion confirms it).
Instead, I got:
error: higher-ranked lifetime error
= note: could not prove `{async block@...}: Send`
Pattern
This is the common pattern of using PhantomData<*mut ()> to opt out of Sync while restoring Send via unsafe impl. It works for static assertions and for concrete type parameters, but fails when T is a trait object (Box<dyn Trait>) and the type is captured across an .await.
A workaround is to use PhantomData<Cell<()>> instead of PhantomData<*mut ()>, since Cell<()> is natively Send + !Sync and avoids the need for unsafe impl Send.
Versions
- Stable: rustc 1.93.1 (01f6ddf75 2026-02-11)
- Nightly: rustc 1.96.0-nightly (fd0c901b0 2026-03-18)
Relationship to #110338
This is an instance of #110338. The code compiles successfully with -Zhigher-ranked-assumptions on nightly.
@rustbot label +A-async-await +A-auto-traits +A-coroutines +fixed-by-higher-ranked-assumptions
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the minimal Rust reproduction in this report and compare its behavior with and without -Zhigher-ranked-assumptions. Read issue #110338 for the related higher-ranked assumptions context; done means the async block using Box satisfies Send without that nightly flag.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100