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

regression 1.51: lifetime may not live long enough with &impl Error #81460

Closed
camelid opened this issue Jan 28, 2021 · 10 comments
Closed

regression 1.51: lifetime may not live long enough with &impl Error #81460

camelid opened this issue Jan 28, 2021 · 10 comments
Labels
A-lifetimes Area: lifetime related P-high High priority regression-from-stable-to-nightly Performance or correctness regression from stable to nightly. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Milestone

Comments

@camelid
Copy link
Member

camelid commented Jan 28, 2021

This compiles on stable and beta, but not nightly (1.51):

use std::error::Error;
use std::iter;

fn walk_source_chain(error: &impl Error) {
    for e in iter::successors(error.source(), |e| e.source()) {
        println!("{}", e);
    }
}

Error with nightly-2021-01-27:

error: lifetime may not live long enough
 --> src/lib.rs:5:51
  |
5 |     for e in iter::successors(error.source(), |e| e.source()) {
  |                                                -- ^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
  |                                                ||
  |                                                |return type of closure is Option<&'2 dyn std::error::Error>
  |                                                has type `&'1 &dyn std::error::Error`

error: aborting due to previous error

Changing the closure parameter pattern to match &e fixes the borrow checker error in nightly.

Originally posted by @mzabaluev in #80949 (comment)

@camelid camelid added the regression-from-stable-to-nightly Performance or correctness regression from stable to nightly. label Jan 28, 2021
@rustbot rustbot added the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Jan 28, 2021
@camelid camelid added E-needs-bisection Call for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustc A-lifetimes Area: lifetime related T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jan 28, 2021
@camelid
Copy link
Member Author

camelid commented Jan 28, 2021

Regressed yesterday (!):

searched nightlies: from nightly-2020-12-01 to nightly-2021-01-28
regressed nightly: nightly-2021-01-26
searched commits: from 1d0d76f to f4eb5d9
regressed commit: d3163e9

bisected with cargo-bisect-rustc v0.6.0

Host triple: x86_64-apple-darwin
Reproduce with:

cargo bisect-rustc --regress=error --start=2020-12-01 --preserve 

I suspect #75180. cc @KodrAus @m-ou-se

@rustbot label: -E-needs-bisection +T-libs

@rustbot rustbot added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. and removed E-needs-bisection Call for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustc labels Jan 28, 2021
@camelid camelid changed the title regression 1.51: lifetime may not live long enough regression 1.51: lifetime may not live long enough with &impl Error Jan 28, 2021
@camelid
Copy link
Member Author

camelid commented Jan 28, 2021

From that PR:

What breaks?

A crater run revealed a few crates broke with something like the following:

// where e: &'short &'long dyn Error
err.source()

previously we'd auto-deref that &'short &'long dyn Error to return a Option<&'long dyn Error> from source, but now will call directly on &'short impl Error, so will return a Option<&'short dyn Error>. The fix is to manually deref:

// where e: &'short &'long dyn Error
(*err).source()

In the recent Libs meeting we considered this acceptable breakage.

Given that someone ran into this so quickly (the day after it was merged), should that decision be reconsidered?

@apiraino apiraino added P-high High priority and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels Jan 28, 2021
@apiraino
Copy link
Contributor

Assigning P-high as discussed as part of the Prioritization Working Group procedure and removing I-prioritize.

@mzabaluev
Copy link
Contributor

In the recent Libs meeting we considered this acceptable breakage.

I find it startling that breaking existing code that isn't wrong could be considered acceptable.

@Mark-Simulacrum Mark-Simulacrum added this to the 1.51.0 milestone Jan 28, 2021
@camelid
Copy link
Member Author

camelid commented Jan 28, 2021

Nominating for T-libs discussion and removing T-compiler label (I don't think this issue has a compiler component).

@rustbot label: +I-nominated -T-compiler

@rustbot rustbot added I-nominated and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jan 28, 2021
@sfackler
Copy link
Member

I find it startling that breaking existing code that isn't wrong could be considered acceptable.

If we never considered a change that could break not-wrong existing code acceptable, we would, for example, never be able to add a new method to a trait, or add an implementation of a trait to an existing type.

@yaahc
Copy link
Member

yaahc commented Feb 3, 2021

related: eyre-rs/stable-eyre#4

@m-ou-se
Copy link
Member

m-ou-se commented Feb 3, 2021

We discussed this in the library team meeting again just now, and agreed that this breakage still seems acceptable. Closing this issue for now.

If this turns out to cause any significant breakage in the ecosystem, feel free to re-open.

@m-ou-se m-ou-se closed this as completed Feb 3, 2021
@KodrAus
Copy link
Contributor

KodrAus commented Feb 3, 2021

It's unfortunate that we no longer get that auto-deref for free that lops off the 'short borrow. As noted in #80949 this can be fixed by using a reference binding:

fn walk_source_chain(error: &impl Error) {
    for e in iter::successors(error.source(), |&e| e.source()) {
        println!("{}", e);
    }
}

Or alternatively by accepting a &(dyn Error + 'static) when you can instead, since it's still a more useful bound:

fn walk_source_chain(error: &(dyn Error + 'static)) {
    for e in error.chain() {
        println!("{}", e);
    }
}

To make impl Error more useful, we could look at adding a chain method to the Error trait itself:

trait ChainExt: Error {
    fn chain(&self) -> Chain
    where
        Self: Sized + 'static,
    {
        <dyn Error + 'static>::chain(self)
    }
}

impl<E: Error> ChainExt for E {}

fn _assert_object_safe(_: &dyn ChainExt) {
    
}

fn walk_source_chain(error: &(impl Error + 'static)) {
    for e in error.chain() {
        println!("{}", e);
    }
}

which wouldn't clash with this:

fn walk_source_chain(error: &(dyn Error + 'static)) {
    for e in error.chain() {
        println!("{}", e);
    }
}

but would with this:

fn dyn_walk_source_chain<'a>(error: &'a &'a (dyn Error + 'static)) {
    for e in error.chain() { // <-- would now fail to assert that `&'a (dyn Error + 'static)` is `'static`
        println!("{}", e);
    }
}

That's basically the same cause as above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lifetimes Area: lifetime related P-high High priority regression-from-stable-to-nightly Performance or correctness regression from stable to nightly. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

9 participants