Skip to content
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

False-positive "temporary dropped while borrowed" involving return-position impl Trait #98997

Closed
abonander opened this issue Jul 7, 2022 · 2 comments
Labels
A-async-await Area: Async & Await A-impl-trait Area: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@abonander
Copy link
Contributor

abonander commented Jul 7, 2022

I'm getting a "temporary value dropped while borrowed" error from code that I otherwise would expect to compile:

use std::fmt::Display;
use std::future::Future;

fn takes_display(d: impl Display) -> impl Future<Output = ()> + 'static {
    let s = format!("{}", d);

    async move {
        ()
    }
}

async fn calls_takes_display() {
    let bar = 0i32;
    let fut = takes_display(format_args!("{bar}"));
    // fut.await;
}
error[E0716]: temporary value dropped while borrowed
  --> src/lib.rs:14:29
   |
14 |     let fut = takes_display(format_args!("{bar}"));
   |                             ^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
   |                             |
   |                             creates a temporary which is freed while still in use
15 |     // fut.await;
16 | }
   | - borrow might be used here, when `fut` is dropped and runs the destructor for type `impl Future<Output = ()>`
   |
   = note: consider using a `let` binding to create a longer lived value
   = note: this error originates in the macro `format_args` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0716`.
error: could not compile `playground` due to previous error

This is a revised version of my original attempt, which fails due to #64477 (comment) (Playground link). After reading through the discussions, I understand why that version doesn't work.

However, I expected the revised version above to work because takes_display() should be eagerly evaluated, and thus not hold any temporaries longer than necessary. It may be an overly eager application of the deferred drop rules as .await is not even involved here, the future returned from takes_display() is explicitly not lifetime-bound to the input, and the return value is bound in a let statement and is not in return position.

Meta

rustc --version --verbose:

rustc 1.62.0 (a8314ef7d 2022-06-27)
@abonander abonander added the C-bug Category: This is a bug. label Jul 7, 2022
@compiler-errors
Copy link
Member

compiler-errors commented Jul 9, 2022

So I only have a cursory understanding of how this works, but I think the problem has to do with the way we calculate "type outlives lifetime" relationships for Return position impl Trait (RPIT).

Specifically, this is an interaction between a few things:

  1. Argument position impl Trait is treated like a type generic argument, so we can think of fn takes_display more like: fn takes_display<D: Display>(d: D).
  2. RPITs "capture" the generics of the function signature they're associated with, even if they're not actually returned.
  3. An opaque type cannot outlive any of those generics (I think, need to read up on RFC 1214)...

I think this is demonstrated by the fact that you can pass a dyn Display without an issue here. Still kinda sucks, but afaict that "an opaque type cannot outlive any of its generics" rule is there to make the type system sound... but maybe it's possible to patch this hole, seems very strange that a impl Trait + 'static shouldn't be 'static 🤷

@fmease fmease added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. A-impl-trait Area: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch. A-async-await Area: Async & Await and removed needs-triage-legacy labels Jan 26, 2024
@fmease
Copy link
Member

fmease commented Jan 26, 2024

Compiles successfully on nightly (the new capture rules in Rust 2024 shouldn't have a negative influence on this either, right?). Closing as fixed.

@fmease fmease closed this as completed Jan 26, 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 A-impl-trait Area: `impl Trait`. Universally / existentially quantified anonymous types with static dispatch. C-bug Category: This is a bug. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

4 participants