Skip to content

Source of lifetime coercion is not reported starting in 1.63 #99256

Description

@mqudsi

When the lifetime of a value is coerced to a longer one due to the use of an associated function or trait member and the previous lifetime used by other call sites is no longer sufficient, rustc no longer includes any mention of the coercion site (and includes a red herring that forces one to go chasing after the wrong goose). This makes it much, much harder to figure out why code is not working as all the locations rustc mentions are perfectly fine and have nothing to do with the actual cause of the error.

Code

use std::path::{Path, PathBuf};

enum PolyStr<'a> {
    Str(&'a str),
}

impl<'a> From<&'static str> for PolyStr<'static> {
    fn from(s: &'static str) -> Self {
        PolyStr::Str(s)
    }
}

#[derive(Default)]
struct Ffcommand<'a> {
    inputs: Vec<&'a Path>,
    output: Option<PolyStr<'a>>,
}

impl<'a> Ffcommand<'a> {
    pub fn run(&self) -> bool {
        true
    }

    pub fn input(mut self, p: &'a Path) -> Self {
        self.inputs.push(p);
        self
    }

    pub fn output<P>(mut self, p: P) -> Self
    where
        P: Into<PolyStr<'a>> + 'a
    {
        self.output = Some(p.into());
        self
    }
}

#[derive(Default)]
struct Ffinfo<'a> {
    cmd: Ffcommand<'a>,
    path: PathBuf,
}

impl Ffinfo<'_> {
    fn foo(&self) -> bool {
        let cmd = Ffcommand::default()
            .input(&self.path)
            // uncomment the next line to see the error
            // .output("pipe:1")
            ;
            
        cmd.run()
    }
}

fn main() {
    let mut info = Ffinfo::default();
    info.path = PathBuf::from("./hello.world");
    info.foo();
}

When the commented-out line is added back in, rustc will emit the following error:

error[[E0521]](https://doc.rust-lang.org/beta/error-index.html#E0521): borrowed data escapes outside of associated function
  --> src/main.rs:46:19
   |
45 |       fn foo(&self) -> bool {
   |              -----
   |              |
   |              `self` is a reference that is only valid in the associated function body
   |              let's call the lifetime of this reference `'1`
46 |           let cmd = Ffcommand::default()
   |  ___________________^
47 | |             .input(&self.path)
   | |                              ^
   | |                              |
   | |______________________________`self` escapes the associated function body here
   |                                argument requires that `'1` must outlive `'static`

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

Try on playground.

The error message points to self and .input() as the locations pertaining to this error, when in fact the error stems entirely from the usage of .output(), which coerces the lifetime to 'static due to the trait impl, at the same time as .input(), which doesn't last as long.

rustc stable (1.62) emits the following error message instead, which is infinitely more helpful:

error[[E0759]](https://doc.rust-lang.org/stable/error-index.html#E0759): `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
  --> src/main.rs:47:20
   |
45 |     fn foo(&self) -> bool {
   |            ----- this data with an anonymous lifetime `'_`...
46 |         let cmd = Ffcommand::default()
47 |             .input(&self.path)
   |                    ^^^^^^^^^^ ...is used here...
48 |             .output("pipe:1")
   |              ------ ...and is required to live as long as `'static` here

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

Notice that here the correct call sites (.input() and .output()) are mentioned. In particular, the fact that .output() is where 'static is required is clearly mentioned, making it easy to figure out what is going on.

Needless to say, this is a highly stripped-down example for illustration purposes. It took me perhaps half-an-hour to chase down the actual issue in a real codebase; IIRC the .output() equivalent was part of the original code and .input() was added some months later, and there were a lot of false trails to chase down before finding the .output() lifetime coercion.

Version it worked on

It most recently worked on: 1.62.0

Version with regression

rustc --version --verbose:

1.63.0-beta.5 2022-07-10 93ef0cd7fd86d3d05cee

Activity

  1. added
    C-bugCategory: This is a bug.
    regression-untriagedUntriaged performance or correctness regression.
    on Jul 14, 2022
  2. added
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    and removed
    regression-untriagedUntriaged performance or correctness regression.
    on Jul 14, 2022
  3. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Jul 21, 2022
  4. apiraino commented on Jul 27, 2022

    @apiraino
    Contributor

    bisection seems to lead to commit bb55bd4 #95565 cc @jackh726

    searched nightlies: from nightly-2022-05-01 to nightly-2022-07-27
    regressed nightly: nightly-2022-06-08
    searched commit range: 50b0025...5435ed6
    regressed commit: bb55bd4

    bisected with cargo-bisect-rustc v0.6.3

    Host triple: x86_64-unknown-linux-gnu
    Reproduce with:

    cargo bisect-rustc --start=2022-05-01 --script=./script.sh 
  5. apiraino commented on Jul 27, 2022

    @apiraino
    Contributor

    WG-prioritization assigning priority (Zulip discussion).

    @rustbot label -I-prioritize +P-high +A-diagnostics

  6. added
    A-diagnosticsArea: Messages for errors, warnings, and lints
    P-highHigh priority
    and removed
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Jul 27, 2022
  7. mqudsi commented on Jul 27, 2022

    @mqudsi
    ContributorAuthor

    @apiraino thanks for teaching me about cargo-bisect-rustc 👍

  8. estebank commented on Aug 4, 2022

    @estebank
    Contributor

    This is of course fallout from removing the migrate borrowck.

    CC @rust-lang/wg-diagnostics, I believe this should be high priority, but having looked at it in the past it will be non-trivial to get back to the same quality we had before :-/

  9. added this to the 1.63.0 milestone on Aug 9, 2022
  10. pnkfelix commented on Aug 11, 2022

    @pnkfelix
    Contributor

    @rustbot label: -regression-from-stable-to-beta +regression-from-stable-to-stable

  11. added
    regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.
    and removed on Aug 11, 2022
  12. pnkfelix commented on Dec 16, 2022

    @pnkfelix
    Contributor

    Visiting for P-high review

    @estebank do you have any ideas on what wg-diagnostics might attempt to do to try to recover the quality here?

  13. estebank commented on Jan 4, 2023

    @estebank
    Contributor

    We need to investigate what E0521 is doing that diverges significantly from E0759. I suspect this won't be trivial to fix :-/

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-lifetimesArea: Lifetimes / regionsC-bugCategory: This is a bug.P-highHigh priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.WG-diagnosticsWorking group: Diagnosticsregression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions