Skip to content

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

Description

@sandersaares

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 (01f6ddf 2026-02-11)
  • Nightly: rustc 1.96.0-nightly (fd0c901 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageThis issue may need triage. Remove it if it has been sufficiently triaged.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions