rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.81k stars 12.5k forks source link

'static lifetime ignored when applied to GAT #91395

Open tuxmark5 opened 2 years ago

tuxmark5 commented 2 years ago

I tried this code:

#![feature(generic_associated_types)]

trait Table {
    type Blocks<'a>: Iterator where Self: 'a;

    fn blocks(&self) -> Self::Blocks<'_>;
}

fn box_static_block<T: 'static>(block: T) {
    let _any: Box<dyn std::any::Any> = Box::new(block);
}

fn clone_static_table<'a, T>(table: &'a T) 
where 
    T: Table,
    <<T as Table>::Blocks<'a> as Iterator>::Item: 'static,
{
    for block in table.blocks() {
        box_static_block(block);
    }
}

I expected this code to compile, but it doesn't. rustc suggests this:

error[E0310]: the associated type `<<T as Table>::Blocks<'_> as Iterator>::Item` may not live long enough
  --> src/lib.rs:19:9
   |
19 |         box_static_block(block);
   |         ^^^^^^^^^^^^^^^^
   |
   = help: consider adding an explicit lifetime bound `<<T as Table>::Blocks<'_> as Iterator>::Item: 'static`...
   = note: ...so that the type `<<T as Table>::Blocks<'_> as Iterator>::Item` will meet its required lifetime bounds...
note: ...that is required by this bound
  --> src/lib.rs:9:24
   |
9  | fn box_static_block<T: 'static>(block: T) {
   |                        ^^^^^^^

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

But such bound already exists and does seemingly nothing.

Meta

rustc --version --verbose:

rustc 1.59.0-nightly (db9d361a4 2021-11-28)
binary: rustc
commit-hash: db9d361a4731ca0bb48533fab6297a8fea75696f
commit-date: 2021-11-28
host: x86_64-unknown-linux-gnu
release: 1.59.0-nightly
LLVM version: 13.0.0
jackh726 commented 2 years ago

Replacing the box_static_block call with box_static_block::<<<T as Table>::Blocks<'a> as Iterator>::Item>(block) makes this pass...looking into this now

jackh726 commented 2 years ago

Looking more into this, it looks somewhat related to the autoderef on table.blocks(): https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=186ffccf48c7b69388263f638a6a4a7b

Still looking

jackh726 commented 2 years ago

Ok, so I'll talk to Niko about this sometime soonish, but for now, here's a basic analysis of what's going on. I'll use the Foo trait from the playground above rather than Iterator, just for simplicity.

When we call table.blocks(), rustc essentially makes the return of this <T as Table>::Blocks<'_#1r> , where '_#1r is some lifetime. Then, we call blocks.item() to get a block of type <<T as Table>::Blocks<'_#1r> as Foo>::Item. To pass this to box_static_block, we need that to outlive static. Well, we know that <<T as Table>::Blocks<'a> as Foo>::Item: 'static, not the lifetime 'a, but that doesn't tell us anything about the lifetime '_#1r. Ultimately, we do know a bit about how 'a and '_#1r are related, because of the method calls, but because the Item type is through a projection, we can't know that <<T as Table>::Blocks<'_#1r> as Foo>::Item: 'static holds if <<T as Table>::Blocks<'a> as Foo>::Item: 'static holds. Well, maybe we should be able to - that's not clear yet.

Interestingly, I expected changing the bound to for<'x> <<T as Table>::Blocks<'x> as Foo>::Item: 'static to fix this, but that runs into a different error that I haven't looked into.