rust-lang / rust

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

GAT combined with HRTB doesn't work and has confusing diagnostics #96465

Open udoprog opened 2 years ago

udoprog commented 2 years ago

I tried this code:

#![feature(generic_associated_types)]

use std::marker;

trait Trait {
    type Gat<'this>
    where
        Self: 'this;

    fn function<F>(self, _: F)
    where
        F: FnOnce(Self::Gat<'_>);
}

struct Struct<T>(marker::PhantomData<T>);

impl<T> Trait for Struct<T> {
    type Gat<'this> = &'this mut T where Self: 'this;

    fn function<F>(self, _: F)
    where
        F: FnOnce(Self::Gat<'_>),
    {
    }
}

Playground

I expected to see this happen: Since the GAT has the Self: 'this bound I'd expect T as being part of Struct<T> which is Self to outlive 'this and compile.

Instead, this happens and we get a (confusing) lifetime error which proposes to add a bound for a type 'a which isn't specified in the code:

error[[E0311]](https://doc.rust-lang.org/nightly/error-index.html#E0311): the parameter type `T` may not live long enough
  [--> src/lib.rs:20:8
](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=fbd726c1f1bd90e514358a91016581ec#)   |
17 | impl<T> Trait for Struct<T> {
   |      - help: consider adding an explicit lifetime bound...: `T: 'a`
...
20 |     fn function<F>(self, _: F)
   |        ^^^^^^^^ ...so that the type `Struct<T>` will meet its required lifetime bounds

I'm assuming this is a name for the HRTB '_ but I'm not sure. However, this bound should not be necessary since T is part of the impl associated with Struct<T>. This HRTB working w/o complaints making me unsure what 'a is referring to:

use std::marker;

struct Struct<T>(marker::PhantomData<T>);

impl<T> Struct<T> {
    fn function<F>(self, _: F) where F: FnOnce(&T) {}
}

Related issues

At first seemed to be related to #95331 but I was unable to reproduce without GATs. I.e. this compiles:

trait FooBad: for<'a> TheItem<'a>  {
    fn items<F>(walker: F) where F: FnOnce(<Self as TheItem<'_>>::Item);
}

trait TheItem<'a> {
    type Item;
}

Playground

Meta

rustc --version --verbose:

rustc 1.62.0-nightly (de1026a67 2022-04-23)
binary: rustc
commit-hash: de1026a67b0a3f5d6b61a1f77093af97d4799376
commit-date: 2022-04-23
host: x86_64-pc-windows-msvc
release: 1.62.0-nightly
LLVM version: 14.0.1
aliemjay commented 2 years ago

Here is a reproduction without GAT:

trait FooBad: for<'a> TheItem<'a> {
    fn items<F>(walker: F)
    where
        F: FnOnce(<Self as TheItem<'_>>::Item);
}

trait TheItem<'a> {
    type Item;
}

struct Struct<T>(T);

impl<'a, T> TheItem<'a> for Struct<T>
where 
    Self: 'a,
{
    type Item = &'a mut T;
}

impl<T> FooBad for Struct<T> {
}

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=0b67f00ab31d29fea792890947646164

aliemjay commented 2 years ago

A workaround from #96831 :

impl<T> Trait for Struct<T> {
    type Gat<'this> = &'this mut T where Self: 'this;

    fn function<F>(self, _: F)
    where
-        F: FnOnce(Self::Gat<'_>),
+        F: FnOnce(&mut T),
    {
    }
}

@rustbot label F-generic_associated_types

udoprog commented 2 years ago

@aliemjay that is... actually a workable solution (all though patch looks backwards ;)! Thanks!

udoprog commented 2 years ago

So sadly while the proposed solution gets past the initial issue, there are more similar downstream issues that happen like this one:

#![feature(generic_associated_types)]

use std::marker;

trait Trait {
    type Gat<'this>
    where
        Self: 'this;

    fn function<F>(self, _: F)
    where
        F: FnOnce(Self::Gat<'_>);

    fn function2(self);
}

struct Struct<T>(marker::PhantomData<T>);

impl<T> Trait for Struct<T> {
    type Gat<'this> = &'this mut T where Self: 'this;

    fn function<F>(self, callback: F)
    where
        F: FnOnce(&mut T),
    {
    }

    fn function2(self) {
        self.function(|value| {})
    }
}

Playground

error[[E0311]](https://doc.rust-lang.org/nightly/error-index.html#E0311): the parameter type `T` may not live long enough
  [--> src/lib.rs:29:14
](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=f4b62bb6c9773f5807cd8fe25ae8266a#)   |
29 |         self.function(|value| {})
   |              ^^^^^^^^ ...so that the type `Struct<T>` will meet its required lifetime bounds
   |
help: consider adding an explicit lifetime bound...
   |
19 | impl<T: 'a> Trait for Struct<T> {
   |       ++++