rust-lang / rust-lang/rust

Async Send inference fails with PhantomData<*mut ()> + unsafe impl Send when T is a trait object

Open
#154,397 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

needs-triage
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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.