rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.69k stars 12.75k forks source link

RPITIT with Send trait marker breaks borrow checker #111105

Open nandesu-utils opened 1 year ago

nandesu-utils commented 1 year ago

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

nandesu-utils commented 1 year ago

If we put explicit lifetimes, the diagnostic will become clearer:

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

With diagnostic:

error: lifetime bound not satisfied
  --> src/lib.rs:10:9
   |
10 | /         async move {
11 | |             self.run::<'a, 'b, T>(t).await;
12 | |         }
   | |_________^
   |
note: the lifetime `'a` defined here...
  --> src/lib.rs:9:16
   |
9  |     fn run_via<'a, 'b: 'a, T: Sync>(&'a self, t: &'b T) -> impl Future<Output = ()> + 'a + Send {
   |                ^^
note: ...must outlive the lifetime `'a` defined here
  --> src/lib.rs:9:16
   |
9  |     fn run_via<'a, 'b: 'a, T: Sync>(&'a self, t: &'b T) -> impl Future<Output = ()> + 'a + Send {
   |                ^^
   = 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)
Jules-Bertholet commented 1 year ago

@rustbot label +A-borrow-checker +F-return_position_impl_trait_in_trait

rynoV commented 5 months ago

If it helps in debugging, this might be another example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=bdfcc8a7ba16afef1ba7c86636a23fce

Deleting the Send bound on async_method makes it compile