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

Associated type projections don't play well with HRTBs and normalization #30472

Open
soltanmm opened this issue Dec 19, 2015 · 13 comments
Open
Labels
A-associated-items Area: Associated items such as associated types and consts. A-lazy-normalization Area: lazy normalization (tracking issue: #60471) A-lifetimes Area: lifetime related A-typesystem Area: The type system C-bug Category: This is a bug. E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@soltanmm
Copy link

trait MyFrom<T> {}
impl<T> MyFrom<T> for T {}

trait MyInto<T> {}
impl<T, U> MyInto<U> for T where U: MyFrom<T> {}


trait A<'self_> {
    type T;
}
trait B: for<'self_> A<'self_> {
    //type U: for<'self_> MyFrom<<Self as A<'self_>>::T>;  // <-- this won't compile
    type U: for<'self_> MyInto<<Self as A<'self_>>::T>;  // <-- but this will
}


struct M;
impl<'self_> A<'self_> for M {
    type T = usize;
}
impl B for M {
    type U = usize;
}


fn main() {}

playpen link

This could've been written with MyInto written as Into and similarly for MyFrom/From. It was written this way only to make explicit the necessity of one blanket impl for satisfying the other.

I'm guessing that extensional equality is not judged, even though it is possible to make such a judgment and such a judgment must be made to compile code that compiles today. <Self as A<'self_>>::T on line 12 ought to be equated to usize (which must be detected at some other point in time else line 13 wouldn't be able to compile).

EDIT: Playing around a bit, it looks more and more like it's all about the HKL bounds...

@soltanmm soltanmm changed the title Trait A having blanket impl dependence on trait B may be satisfied, but trait B may not A trait having blanket impl dependence on a 2nd trait may be satisfied, but the 2nd trait may not Dec 19, 2015
@soltanmm soltanmm changed the title A trait having blanket impl dependence on a 2nd trait may be satisfied, but the 2nd trait may not HKL bounds on associated types causes types that would otherwise be judged equivalent to not be Dec 29, 2015
@soltanmm
Copy link
Author

HRTBs (I've been using the wrong nomenclature the whole time!) make projections sad.

///////////////
// HRTB setup
//
trait A {
    type S;
}
trait B<'self_> {
    type T;
}
trait C: for<'self_> B<'self_> {
    type U: for<'self_> A<S=<Self as B<'self_>>::T>;  // requires normalizing through an HRTB
}

//////////////////////
// Problematic impls
//
impl A for usize {
    type S = usize;
}
impl<'self_> B<'self_> for usize {
    type T = usize;
}
impl C for usize {
    type U = usize;
}


fn main() {}
<anon>:23:1: 25:2 error: type mismatch resolving `for<'self_> <usize as A>::S == <usize as B<'self_>>::T`:
 expected usize,
    found associated type [E0271]
<anon>:23 impl C for usize {
<anon>:24     type U = usize;
<anon>:25 }
<anon>:23:1: 25:2 help: see the detailed explanation for E0271
<anon>:23:1: 25:2 note: required by `C`
error: aborting due to previous error
playpen: application terminated with error code 101

Very likely a dupe of #28994.

@soltanmm soltanmm changed the title HKL bounds on associated types causes types that would otherwise be judged equivalent to not be Associated type projections don't play well with HRTBs and normalization Dec 30, 2015
@eddyb eddyb added A-lifetimes Area: lifetime related A-associated-items Area: Associated items such as associated types and consts. A-typesystem Area: The type system labels Jan 1, 2016
@eddyb
Copy link
Member

eddyb commented Jan 1, 2016

cc @nikomatsakis

@arielb1
Copy link
Contributor

arielb1 commented Jan 2, 2016

Known issue.

@soltanmm
Copy link
Author

@arielb1 (or @nikomatsakis? Still a bit unclear on y'all's role separation)
What's the expected overall approach to fixing this issue? Even if you don't expect a newcomer to handle it, I'd like to know anyway. :-D

@nikomatsakis
Copy link
Contributor

@soltanmm actually, I'm not entirely sure. There are a serious of refactorings that I have in mind for the type system / trait resolver, and I've been figuring that after those are done, I would come back to revisit some of these issues, if they still occur, but it may be worth digging into this example (or #28994) in detail. Honestly it's not all fresh in my mind.

The refactorings I am thinking of are, first, lazy normalization (as described in this discuss thread), which may indeed help here, though I've not thought it through very deeply. Second, I'd like to generally rework how the trait checker is working to make it easier to extend the environment as you go -- the current setup, where the set of assumptions etc is semi-fixed when the inference context is created -- makes it hard to "descend through" a binder like for and then do trait resolutions and normalizations within that context.

@soltanmm
Copy link
Author

So, potentially w.r.t. lazy normalization, I've been playing with another bit of code (trying to shrink it a bit):

trait Hkt<'a> { type Output; }
trait A where for<'s> <Self::B as Hkt<'s>>::Output: Clone {
    type B: for<'s> Hkt<'s>;
}
trait C: A
    where for<'s> <Self::B as Hkt<'s>>::Output: Clone
{}

impl<'a> Hkt<'a> for usize { type Output = usize; }

struct S;
impl A for S {
    type B = usize;
}

From reading the debug logs, it seems that when checking the well-formed-ness of impl A for S, upon normalizing S::B to usize, and registering and selecting on the implied <usize as Hkt<'a>>::Output: Clone, the compiler has no idea that <usize as Hkt<'a>>::Output == usize and never normalizes to it either. Is this a situation in which the compiler might have assumed that everything was already normalized the best it could be and thus gave up?

An aside: does lazy normalization have something to do with this asterisk in project.rs?

@Mark-Simulacrum
Copy link
Member

Is this #30867? It looks similar, but uncertain.

@Mark-Simulacrum Mark-Simulacrum added the C-bug Category: This is a bug. label Jul 24, 2017
@dylanede
Copy link
Contributor

What's the state of this now? I fairly frequently run into problems with associated types involved with HRTBs not projecting/unifying.

@kennytm kennytm added the T-lang Relevant to the language team, which will review and decide on the PR/issue. label Apr 20, 2018
alecmocatta added a commit to alecmocatta/office-hours that referenced this issue Sep 13, 2018
I'm working on data-parallelism library, heavily inspired by Rayon but focused on describing certain computations on a dataset in an SQL-like manner, somewhat akin to Diesel.

One part of this combines HRTBs with associated types. I've bumped into a few issues that have made things a bit tricky ([#30472](rust-lang/rust#30472), [#30867](rust-lang/rust#30867), [#53943](rust-lang/rust#53943), and similar), the hardest to work around however has been that I don't believe it's currently possible to write this:
```rust
type Task = for<'a> <B as Abc<&'a A::Item>>::Task;
```
Currently I've resorted to a dummy trait, manually reimplemented on various structs without the reference, such that I can do:
```rust
type Task = <B as AbcDummy<A::Item>>::Task;
```
and (horribly) transmute between the two.

I'd be very interested to discuss

 1) issues I've bumped into combining HRTBs and associated types;
 2) how to get rid of this transmute and the dummy trait;
 3) any feedback on the library before I publish and promote it, given it's heavily inspired by your work on Rayon!

I can do any of the Monday or Friday times listed, though I have a preference for the 16 - 16.30 slots.

Again, much appreciation for you doing this, I think it's awesome!
@steveklabnik
Copy link
Member

Triage: last two comments asking for clarification, no replies.

Given @nikomatsakis's comments earlier in the thread, I imagine this issue is "post-chalk".

@nikomatsakis
Copy link
Contributor

This is one of the things that the traits working group is kind of actively working towards. It's blocked to some extent on the universes work, see e.g. #65232 which tries to unblock that effort, plus lazy norm. I think we'll be making active progress though over the next few months.

Centril added a commit to Centril/rust that referenced this issue Dec 21, 2019
…atsakis

Make GATs less ICE-prone.

After this PR simple lifetime-generic associated types can now be used in a compiling program. There are two big limitations:

* rust-lang#30472 has not been addressed in any way (see src/test/ui/generic-associated-types/iterable.rs)
* Using type- and const-generic associated types errors because bound types and constants aren't handled by trait solving.
    * The errors are technically non-fatal, but they happen in a [part of the compiler](https://github.com/rust-lang/rust/blob/4abb0ad2731e9ac6fd5d64d4cf15b7c82e4b5a81/src/librustc_typeck/lib.rs#L298) that fairly aggressively stops compiling on errors.

closes rust-lang#47206
closes rust-lang#49362
closes rust-lang#62521
closes rust-lang#63300
closes rust-lang#64755
closes rust-lang#67089
Centril added a commit to Centril/rust that referenced this issue Dec 21, 2019
…atsakis

Make GATs less ICE-prone.

After this PR simple lifetime-generic associated types can now be used in a compiling program. There are two big limitations:

* rust-lang#30472 has not been addressed in any way (see src/test/ui/generic-associated-types/iterable.rs)
* Using type- and const-generic associated types errors because bound types and constants aren't handled by trait solving.
    * The errors are technically non-fatal, but they happen in a [part of the compiler](https://github.com/rust-lang/rust/blob/4abb0ad2731e9ac6fd5d64d4cf15b7c82e4b5a81/src/librustc_typeck/lib.rs#L298) that fairly aggressively stops compiling on errors.

closes rust-lang#47206
closes rust-lang#49362
closes rust-lang#62521
closes rust-lang#63300
closes rust-lang#64755
closes rust-lang#67089
@runiq
Copy link

runiq commented Jan 9, 2020

Triage: According to #30472 (comment), this should get tagged A-lazy-normalization.

@matthewjasper matthewjasper added A-lazy-normalization Area: lazy normalization (tracking issue: #60471) E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot. labels Jan 9, 2020
@jonas-schievink jonas-schievink added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Feb 24, 2020
@demurgos
Copy link

demurgos commented Mar 19, 2020

I think this issue is related to an error I encountered when trying to use the Iterable trait example from the Generic Associated Types RFC (RFC 1598).

The trait is defined as follow:

trait Iterable {
    type Item<'a>;
    type Iter<'a>: Iterator<Item = Self::Item<'a>>;
    
    fn iter<'a>(&'a self) -> Self::Iter<'a>;
}

I implemented it for a vector of booleans:

struct BoolVec(Vec<bool>);

impl Iterable for BoolVec {
  type Item<'a> = &'a bool;
  type Iter<'a> = core::slice::Iter<'a, bool>;

  fn iter<'a>(&'a self) -> Self::Iter<'a> {
    self.0.iter()
  }
}

Playground

I believe this should compile, but it errors.

See full error
error[E0271]: type mismatch resolving `for<'a> <<BoolVec as Iterable>::Iter<'a> as std::iter::Iterator>::Item == <BoolVec as Iterable>::Item<'a>`
  --> src/main.rs:14:3
   |
13 | impl Iterable for BoolVec {
   | ------------------------- in this `impl` item
14 |   type Item<'a> = &'a bool;
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&bool`, found associated type
   |
   = note:    expected reference `&bool`
           found associated type `<BoolVec as Iterable>::Item<'_>`
   = note: consider constraining the associated type `<BoolVec as Iterable>::Item<'_>` to `&bool`
   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html

error[E0271]: type mismatch resolving `for<'a> <<BoolVec as Iterable>::Iter<'a> as std::iter::Iterator>::Item == <BoolVec as Iterable>::Item<'a>`
  --> src/main.rs:17:28
   |
4  | trait Iterable {
   | -------------- required by `Iterable`
...
17 |   fn iter<'a>(&'a self) -> Self::Iter<'a> {
   |                            ^^^^^^^^^^^^^^ expected associated type, found `&bool`
   |
   = note: expected associated type `<BoolVec as Iterable>::Item<'_>`
                    found reference `&bool`
   = note: consider constraining the associated type `<BoolVec as Iterable>::Item<'_>` to `&bool` or calling a method that returns `<BoolVec as Iterable>::Item<'_>`
   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html

It does not recognize that &bool and <BoolVec as Iterable>::Item<'_> are the same.

@vandenheuvel
Copy link
Contributor

@demurgos You example compiles now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-associated-items Area: Associated items such as associated types and consts. A-lazy-normalization Area: lazy normalization (tracking issue: #60471) A-lifetimes Area: lifetime related A-typesystem Area: The type system C-bug Category: This is a bug. E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests