rust-lang / rust

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

Bad Error Message: error[E0277]: the trait bound `T: Generator<ResumeTy>` is not satisfied #79648

Open keithmss opened 3 years ago

keithmss commented 3 years ago

error message

Hello! This is a bug report for a confusing/bad error message:

   Compiling bug v0.1.0 (E:\Users\keith\Documents\Projects\bug)
error[E0277]: the trait bound `T: Generator<ResumeTy>` is not satisfied

error[E0308]: mismatched types
  --> src\main.rs:76:25
   |
14 |             .filter_map(|r| async { r.ok() })
   |                                   ----------
   |                                   |
   |                                   the expected generator
   |                                   the found generator
...
76 |     let foobar_runner = tokio::spawn(async move {
   |                         ^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected opaque type `impl futures::Future`
              found opaque type `impl futures::Future`

error[E0308]: mismatched types
  --> src\main.rs:76:25
   |
15 |             .filter_map(|m| async move { Some(Foo::new(m)) })
   |                                        ---------------------
   |                                        |
   |                                        the expected generator
   |                                        the found generator
...
76 |     let foobar_runner = tokio::spawn(async move {
   |                         ^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected opaque type `impl futures::Future`
              found opaque type `impl futures::Future`

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `bug`

To learn more, run the command again with --verbose.

code

I apologize that the following probably isn't the easiest way to trigger it. I am relatively new to Rust and am just now working with Generics and Lifetimes now which is how I got here. The following is a very stripped down version of my code (it compiles without the commented line).

main.rs:

use core::future;
use futures::stream::{self, StreamExt};
use parking_lot::RwLock;
use std::borrow::Cow;
use std::sync::Arc;

struct Bar {}

impl Bar {
    async fn get_foo<'a>(&self) -> Vec<Foo<'a>> {
        let results: Vec<Result<String, ()>> =
            vec![Ok("shoutout to `impl Fox for Salix` for this vector".to_string()); 10];
        let foo: Vec<Foo<'a>> = stream::iter(results)
            .filter_map(|r| async { r.ok() })
            .filter_map(|m| async move { Some(Foo::new(m)) })
            .filter(|m| future::ready(test_foo(m.as_str()))) // Code compiles without this line
            .collect()
            .await;
        foo
    }

    async fn new() -> Arc<Self> {
        Arc::new(Self {})
    }
}

struct Foo<'a> {
    raw: Cow<'a, str>,
}

impl<'a> Foo<'a> {
    fn as_str(&self) -> &str {
        &self.raw
    }

    fn new<T>(raw: T) -> Foo<'a>
    where
        T: Into<Cow<'a, str>>,
    {
        Foo { raw: raw.into() }
    }
}

struct FooBar<'a> {
    bar: Arc<Bar>,
    foo: RwLock<Vec<Foo<'a>>>,
}

impl<'a> FooBar<'a> {
    async fn get_foo(&self) -> Vec<Foo<'a>> {
        let foo: Vec<Foo<'a>> = self.bar.get_foo().await;
        foo
    }

    async fn new(bar: Arc<Bar>) -> Arc<FooBar<'a>> {
        let foobar = FooBar {
            bar: bar.clone(),
            foo: RwLock::new(Vec::new()),
        };
        Arc::new(foobar)
    }

    async fn run(&self) {
        let new_foo: Vec<Foo<'a>> = self.get_foo().await;
        {
            let mut foo = self.foo.write();
            *foo = new_foo;
        }
    }
}

#[tokio::main]
async fn main() {
    let bar = Bar::new().await;
    let foobar = FooBar::new(bar).await;
    let foobar_runner = tokio::spawn(async move {
        foobar.run().await;
    })
    .await;
}

fn test_foo(foo: &str) -> bool {
    println!("{}", foo);
    true
}

Meta

rustc --version --verbose:

PS E:\Users\keith\Documents\Projects\bug> rustc --version --verbose
rustc 1.48.0 (7eac88abb 2020-11-16)
binary: rustc
commit-hash: 7eac88abb2e57e752f3302f02be5f3ce3d7adfb4
commit-date: 2020-11-16
host: x86_64-pc-windows-msvc
release: 1.48.0
LLVM version: 11.0

Cargo.toml:

[package]
name = "bug"
version = "0.1.0"
authors = ["keith miller"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
futures           = "0.3.8"
parking_lot       = "0.11.1"
tokio = { version = "0.2.22", features = ["macros", "rt-threaded", "time"] }

This is my first bug report for a project of this size. Please let me know if you need me to modify anything. Thank you!

SNCPlay42 commented 3 years ago

Reduced some (but it'd be nice to remove the futures dependency):

use futures::stream;
use futures::stream::StreamExt;
use std::future;
struct Bar {}
impl Bar {
    async fn get_foo<'a>(&self) -> Vec<Foo<'a>> {
        let results = vec![()];
        let foo: Vec<Foo<'a>> = stream::iter(results)
            .filter_map(|_| async move { Some(Foo::new()) })
            .filter(|m| future::ready(true))
            .collect()
            .await;
        foo
    }
}
struct Foo<'a> {
    _use_a: &'a (),
}
impl<'a> Foo<'a> {
    fn new() -> Foo<'a> {
        Foo { _use_a: &() }
    }
}
fn demand_is_send<T>(t: T)
where
    T: Send,
{
}
fn main() {
    let bar = Bar {};
    demand_is_send(async move { bar.get_foo().await })
}

@rustbot label A-async-await A-diagnostics

SNCPlay42 commented 3 years ago

The T: Generator<ResumeTy> error is gone on nightly (bisected to #73905) but the confusing type mismatch error remains

tmandry commented 3 years ago

Further minimized for the remaining error.. playground

use futures::stream;
use futures::stream::StreamExt;
use std::future::{self, Future};

fn get_foo<'a>() -> impl Future<Output = Vec<()>> + 'a {
    async {
        let results = vec![()];
        let foo: Vec<()> = stream::iter(results)
            .filter_map(|_| async move { Some(()) })
            .filter(|m| future::ready(true))
            .collect()
            .await;
        foo
    }
}
fn demand_is_send<T>(t: T)
    where T: Send
{}
fn main() {
    demand_is_send(get_foo())
}

Haven't tried removing the futures dep yet.

frxstrem commented 3 years ago

I think I came across the same issue, but with a slightly different snippet of code:

use futures::future::{join, ready, Future};
use futures::stream::{self, StreamExt};

fn f() -> impl Future<Output = ()> + Send {
    async {
        let a = &();
        join(
            stream::empty().for_each(|_: ()| async { drop(a) }),
            ready(()),
        )
        .await;
    }
}
error[E0308]: mismatched types
  --> src/lib.rs:4:11
   |
4  | fn f() -> impl Future<Output = ()> + Send {
   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
...
8  |             stream::empty().for_each(|_: ()| async { drop(a) }),
   |                                                    -----------
   |                                                    |
   |                                                    the expected generator
   |                                                    the found generator
   |
   = note: expected opaque type `impl futures::Future`
              found opaque type `impl futures::Future`

Playground

When compiling on stable, I'm also getting the error about T: Generator<ResumeTy>.

Curiously, the usage of join (or the join! macro) seems necessary to trigger the error message in this case, as just awaiting the future directly compiles just fine.

SNCPlay42 commented 3 years ago

One-crate repro: (playground)

use std::future::Future;
use std::marker::PhantomData;

trait Stream {
    type Item;
}

struct Filter<St: Stream> {
    pending_item: St::Item,
}

fn filter<St: Stream>(_: St) -> Filter<St> {
    unimplemented!();
}

struct FilterMap<Fut, F> {
    f: F,
    pending: PhantomData<Fut>,
}

impl<Fut, F> Stream for FilterMap<Fut, F>
where
    F: FnMut() -> Fut,
    Fut: Future,
{
    type Item = ();
}

pub fn get_foo() -> impl Future + Send {
    async {
        let _y = &();
        let _x = filter(FilterMap {
            f: || async move { *_y },
            pending: PhantomData,
        });
        async {}.await;
    }
}
SNCPlay42 commented 3 years ago

This and #71723 look like the same thing.

peku33 commented 3 years ago

Is there any progress or workaround for this?

estebank commented 3 years ago

CC https://github.com/rust-lang/rust/issues/82921

estebank commented 2 years ago

Triage: current output (the bound being pointed at is new):

error[E0308]: mismatched types
  --> src/main.rs:20:5
   |
9  |             .filter_map(|_| async move { Some(()) })
   |                                        ------------
   |                                        |
   |                                        the expected `async` block
   |                                        the found `async` block
...
20 |     demand_is_send(get_foo())
   |     ^^^^^^^^^^^^^^ lifetime mismatch
   |
   = note: expected opaque type `impl futures::Future`
              found opaque type `impl futures::Future`
note: the lifetime requirement is introduced here
  --> src/main.rs:17:14
   |
17 |     where T: Send
   |              ^^^^
estebank commented 2 years ago

Triage: Current output (fallout from NLL stabilization):

error: higher-ranked lifetime error
  --> src/main.rs:20:5
   |
20 |     demand_is_send(get_foo())
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: could not prove for<'r, 's> impl futures::Future<Output = Vec<()>>: std::marker::Send
estebank commented 11 months ago

Current output:

error[E0308]: mismatched types
  --> src/main.rs:20:5
   |
9  |             .filter_map(|_| async move { Some(()) })
   |                             -----------------------
   |                             |
   |                             the expected `async` block
   |                             the found `async` block
...
20 |     demand_is_send(get_foo())
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected `async` block `{async block@src/main.rs:9:29: 9:52}`
              found `async` block `{async block@src/main.rs:9:29: 9:52}`
note: the lifetime requirement is introduced here
  --> src/main.rs:17:14
   |
17 |     where T: Send
   |              ^^^^