Skip to content

Cycle detected in async fn but not with -> impl Future #119727

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Ekleog opened this issue Jan 8, 2024 · 3 comments
Closed

Cycle detected in async fn but not with -> impl Future #119727

Ekleog opened this issue Jan 8, 2024 · 3 comments
Labels
A-async-await Area: Async & Await AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. F-return_position_impl_trait_in_trait `#![feature(return_position_impl_trait_in_trait)]` I-cycle Issue: A query cycle occurred while none was expected T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-async Working group: Async & await

Comments

@Ekleog
Copy link

Ekleog commented Jan 8, 2024

Here is the reproducer:

use std::future::Future;
use futures::FutureExt;

struct Object(Box<Object>);

trait Trait {
    fn iter(&self) -> impl Sized + Send + Future<Output = ()>;
}

impl Trait for Object {
    // Compiles
    #[cfg(any())]
    fn iter(&self) -> impl Sized + Send + Future<Output = ()> {
        async move {
            self.0.iter().boxed().await;
        }
    }
    
    // Does not compile
    #[cfg(all())]
    async fn iter(&self) {
        self.0.iter().boxed().await;
    }
}

(playground link)

Interestingly, if you swap the any/all, then the version with -> impl Sized + Send + Future<Output = ()> does compile properly.

So something in async fn is not just desugared as -> impl Sized + Send + Future, but I'm not sure why that is?

Possibly related to:

@Ekleog Ekleog added the C-bug Category: This is a bug. label Jan 8, 2024
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Jan 8, 2024
@fmease fmease added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-async-await Area: Async & Await F-return_position_impl_trait_in_trait `#![feature(return_position_impl_trait_in_trait)]` I-cycle Issue: A query cycle occurred while none was expected and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Jan 22, 2024
@traviscross
Copy link
Contributor

traviscross commented Feb 26, 2024

@rustbot labels +AsyncAwait-Triaged +WG-async -C-bug

We reviewed this today in WG-async triage.

It seemed clear to us that this should necessarily result in a cycle error under the current rules. Doing otherwise in this case could be difficult. We didn't feel this was a bug.

@rustbot rustbot added AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. WG-async Working group: Async & await and removed C-bug Category: This is a bug. labels Feb 26, 2024
@Ekleog
Copy link
Author

Ekleog commented Mar 18, 2024

Hmm just for my education, would it be possible for you to explain why the version with -> impl Future { async move { ... } } is expected to have a different behavior from the version with async fn? I'd love to understand which rules are different between the two versions, to avoid hitting this issue in the future.

(For some context, the non-reduced original issue was a struct that had basically a Vec<Self> member, and recursively called the functions by turning the futures into Box<dyn Future> to avoid the infinite type issue — and this did work with -> impl Future { async move { ... } } but not with async fn; so the obvious infinite recursion at runtime from the reproducer wasn't present then)

@traviscross
Copy link
Contributor

traviscross commented Jul 13, 2024

It's not about the difference between async fn and -> impl Future, it's about the fact that in the -> impl Future case you explicitly added a Send bound. If we remove that from the -> impl Future case then it fails similarly:

use core::future::Future;

struct Object(Box<Object>);

trait Trait {
    fn iter(&self) -> impl Future<Output = ()> + Send;
}

impl Trait for Object {
    fn iter(&self) -> impl Future<Output = ()> {
        //~^ ERROR cycle detected
        async move {
            Box::new(self.0.iter()).await;
        }
    }
}

That's how the async fn version is desugared.

@traviscross traviscross closed this as not planned Won't fix, can't repro, duplicate, stale Jul 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-async-await Area: Async & Await AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. F-return_position_impl_trait_in_trait `#![feature(return_position_impl_trait_in_trait)]` I-cycle Issue: A query cycle occurred while none was expected T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-async Working group: Async & await
Projects
None yet
Development

No branches or pull requests

4 participants