Skip to content

RPITIT with Send trait marker breaks borrow checker #111105

Open
@nandesu-utils

Description

I tried this code:

#![feature(return_position_impl_trait_in_trait)]
use std::future::Future;

pub trait Foo: Sync {
    fn run<'a, 'b: 'a, T: Sync>(&'a self, _: &'b T) -> impl Future<Output = ()> + 'a + Send; // doesn't compile
    // fn run<'a, 'b, T: Sync>(&'a self, _: &'b T) -> impl Future<Output = ()> + 'a + Send; // compiles
}

pub trait FooExt: Foo {
    fn run_via<'a, 'b: 'a, T: Sync>(&'a self, t: &'b T) -> impl Future<Output = ()> + 'a + Send {
        async move {
            // asks for an unspecified lifetime to outlive itself? weird diagnostics
            self.run(t).await;
        }
    }
}

I expected to see this happen: successful compilation

Instead, this happened: an outlives lifetime error with very bad diagnostics

error: lifetime bound not satisfied
  --> src/lib.rs:12:9
   |
12 | /         async move {
13 | |             // asks for an unspecified lifetime to outlive itself? weird diagnostics
14 | |             self.run(t).await;
15 | |         }
   | |_________^
   |
note: the lifetime defined here...
  --> src/lib.rs:14:18
   |
14 |             self.run(t).await;
   |                  ^^^
note: ...must outlive the lifetime defined here
  --> src/lib.rs:14:18
   |
14 |             self.run(t).await;
   |                  ^^^
   = note: this is a known limitation that will be removed in the future ([see issue #100013 <https://github.com/rust-lang/rust/issues/100013>](https://github.com/rust-lang/rust/issues/100013) for more information)

Then I tried this code:

pub trait Foo: Sync {
    fn run<'a, 'b: 'a, T: Sync>(&'a self, _: &'b T) -> impl Future<Output = ()> + 'a;
}

pub trait FooExt: Foo {
    fn run_via<'a, 'b: 'a, T: Sync>(&'a self, t: &'b T) -> impl Future<Output = ()> + 'a {
        async move {
            self.run(t).await; // compiles now!
        }
    }
}

And the code compiled! Seems like Send bound on the return impl trait type in run function breaks borrow checker in some way, or possibly breaks inner definition of the anonymous impl type since the following code with a trait object works.

#![feature(return_position_impl_trait_in_trait)]
use std::{pin::Pin, future::Future};

pub trait Foo: Sync {
    fn run<'a, 'b: 'a, T: Sync>(&'a self, _: &'b T) -> Pin<Box<dyn Future<Output = ()> + 'a + Send>>;
}

pub trait FooExt: Foo {
    // this function still has a RPITIT
    fn run_via<'a, 'b: 'a, T: Sync>(&'a self, t: &'b T) -> impl Future<Output = ()> + 'a + Send {
        async move {
            // works!
            self.run(t).await;
        }
    }
}

-Ztrait-solver=next and polonius

First code snippet leads to an ICE when using -Ztrait-solver=next, I am not sure whether it's worth filing an ICE report right now because this is a bug in the default solver. As for polonius, it doesn't change anything.

Meta

rustc --version --verbose:

rustc 1.71.0-nightly (dbba59457 2023-05-01)
binary: rustc
commit-hash: dbba594575ce75b1b57ccb1e4223b9909a28b1b8
commit-date: 2023-05-01
host: x86_64-unknown-linux-gnu
release: 1.71.0-nightly
LLVM version: 16.0.2

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions