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

GAT type Assoc<T: ?Sized> implicitly requires Self to be 'static #131008

Open
Veetaha opened this issue Sep 29, 2024 · 4 comments
Open

GAT type Assoc<T: ?Sized> implicitly requires Self to be 'static #131008

Veetaha opened this issue Sep 29, 2024 · 4 comments
Labels
A-borrow-checker Area: The borrow checker A-diagnostics Area: Messages for errors, warnings, and lints A-lifetimes Area: Lifetimes / regions A-trait-objects Area: trait objects, vtable layout D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@Veetaha
Copy link
Contributor

Veetaha commented Sep 29, 2024

I tried this code:

trait Layout {}
trait Desc {
    type Children<A: ?Sized>;
    fn stage(_children: &Self::Children<dyn Layout>);
}

fn stage<D: Desc>(children: D::Children<dyn Layout>) {
    D::stage(&children);
}

This doesn't compile with two errors, and I can't explain why:

error[E0310]: the parameter type `D` may not live long enough
  --> crates/sandbox/src/main.rs:68:5
   |
68 |     D::stage(&children);
   |     ^^^^^^^^^^^^^^^^^^^
   |     |
   |     the parameter type `D` must be valid for the static lifetime...
   |     ...so that the type `D` will meet its required lifetime bounds
   |
help: consider adding an explicit lifetime bound
   |
67 | fn stage<D: Desc + 'static>(children: D::Children<dyn Layout>) {
   |                  +++++++++

error[E0597]: `children` does not live long enough
  --> crates/sandbox/src/main.rs:68:14
   |
67 | fn stage<D: Desc>(children: D::Children<dyn Layout>) {
   |                   -------- binding `children` declared here
68 |     D::stage(&children);
   |     ---------^^^^^^^^^-
   |     |        |
   |     |        borrowed value does not live long enough
   |     argument requires that `children` is borrowed for `'static`
69 | }
   | - `children` dropped here while still borrowed

Why is the D required to be 'static here?
Why does the &children need to have a 'static lifetime here as well?


Here are variations that do compile, but I also can't explain why they compile:

Adding `dyn Layout + 'static` in the trait definition

trait Layout {}
trait Desc {
    type Children<A: ?Sized>;
    fn stage(_children: &Self::Children<dyn Layout + 'static>);
}

fn stage<D: Desc>(children: D::Children<dyn Layout>) {
    D::stage(&children);
}

Adding `dyn Layout + '_` in the trait definition

trait Layout {}
trait Desc {
    type Children<A: ?Sized>;
    fn stage(_children: &Self::Children<dyn Layout + '_>);
}

fn stage<D: Desc>(children: D::Children<dyn Layout>) {
    D::stage(&children);
}

This variation doesn't compile, but it removes the `D` must be valid for the static lifetime error and I also don't understand why that is:

trait Layout {}
trait Desc {
    type Children<A: ?Sized>: 'static;
    fn stage(_children: &Self::Children<dyn Layout>);
}

fn stage<D: Desc>(children: D::Children<dyn Layout>) {
    D::stage(&children);
}

There is definitely something implicit going on here which I don't know. Some helpful people suggested this may be related to #87479, but I don't see how.

@nikomatsakis do you have an idea if this is related? Is this some compiler bug or smth not documented?

Meta

rustc --version --verbose:

rustc 1.81.0 (eeb90cda1 2024-09-04)
binary: rustc
commit-hash: eeb90cda1969383f56a2637cbd3037bdf598841c
commit-date: 2024-09-04
host: x86_64-unknown-linux-gnu
release: 1.81.0
LLVM version: 18.1.7
@Veetaha Veetaha added the C-bug Category: This is a bug. label Sep 29, 2024
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Sep 29, 2024
@ismailarilik
Copy link
Contributor

@lukas-code
Copy link
Member

lukas-code commented Sep 29, 2024

This is not a bug, but the error message here is terrible.

Due to how trait object lifetime elision works,

  • &Self::Children<dyn Layout> actually means &'a Self::Children<dyn Layout + 'a> for one specific lifetime 'a
  • &Self::Children<dyn Layout + '_> actually means &'a Self::Children<dyn Layout + 'b> for two distinct lifetimes 'a and 'b
  • D::Children<dyn Layout> actually means D::Children<dyn Layout + 'static>

So with all inferred lifetimes annotated explicitly, your program becomes this:

trait Layout {}
trait Desc {
    type Children<A: ?Sized>;
    fn stage<'a>(_children: &'a Self::Children<dyn Layout + 'a>);
}

fn stage<D: Desc>(children: D::Children<dyn Layout + 'static>) {
    // here we unify the argument `&'b D::Children<dyn Layout + 'static>`
    // with the function parameter `&'a D::Children<dyn Layout + 'a>`
    // and end up with `'b == 'a == 'static`.
    D::stage(&/*'b */ children);
}

So the borrowchecker thinks that the borrow must be valid for 'static -- this causes the (correct) errors that children and D do not live long enough.

(In this example it is also not possible to shorten the trait object lifetime from D::Children<dyn Layout + 'static> to D::Children<dyn Layout + 'b>, because GAT arguments are invariant.)

The diagnostic here should probably mention default trait object lifetimes and offer the two workarounds that you already managed to come up with.

@rustbot label -C-bug +A-diagnostics +A-borrow-checker +A-trait-objects +D-terse -needs-triage

@rustbot rustbot added A-borrow-checker Area: The borrow checker A-diagnostics Area: Messages for errors, warnings, and lints A-trait-objects Area: trait objects, vtable layout D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. and removed C-bug Category: This is a bug. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Sep 29, 2024
@Veetaha
Copy link
Contributor Author

Veetaha commented Sep 29, 2024

So the borrowchecker thinks that the borrow must be valid for 'static -- this causes the (correct) errors that children and D do not live long enough.

@lukas-code, thank you for the explanation! Although I don't understand why D is required to be 'static? There is no trait Desc: 'static requirement in the trait, and the fact that the compiler unifies lifetimes to &'static D::Children<dyn Layout + 'static> doesn't explain why D has to be static to me. Sure, the D::Children<...> needs to be 'static (because it's under a 'static reference), but why D itself? Maybe it's documented somewhere and I can't find it?

@lukas-code
Copy link
Member

lukas-code commented Sep 29, 2024

Sure, the D::Children<...> needs to be 'static (because it's under a 'static reference)

Yeah and D::Children<dyn Layout + 'static> could be equal to D, for example when a dependant of your crate defines:

struct Wrapper<T>(T);
impl<T> Desc for Wrapper<T> {
    type Children<A: ?Sized> = Wrapper<T>;
}

In that case when the dependant calls your function stage, you would create a &'static D, so D has to outlive 'static.

@fmease fmease added A-lifetimes Area: Lifetimes / regions T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-borrow-checker Area: The borrow checker A-diagnostics Area: Messages for errors, warnings, and lints A-lifetimes Area: Lifetimes / regions A-trait-objects Area: trait objects, vtable layout D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. 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

5 participants