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 · 8 comments

Comments

Projects
None yet
7 participants
@soltanmm
Copy link

soltanmm commented Dec 19, 2015

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

This comment has been minimized.

Copy link
Author

soltanmm commented Dec 30, 2015

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

This comment has been minimized.

Copy link
Member

eddyb commented Jan 1, 2016

@arielb1

This comment has been minimized.

Copy link
Contributor

arielb1 commented Jan 2, 2016

Known issue.

@soltanmm

This comment has been minimized.

Copy link
Author

soltanmm commented Jan 16, 2016

@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

This comment has been minimized.

Copy link
Contributor

nikomatsakis commented Jan 16, 2016

@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

This comment has been minimized.

Copy link
Author

soltanmm commented Jan 18, 2016

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

This comment has been minimized.

Copy link
Member

Mark-Simulacrum commented May 6, 2017

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

@dylanede

This comment has been minimized.

Copy link
Contributor

dylanede commented Apr 20, 2018

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 label Apr 20, 2018

alecmocatta added a commit to alecmocatta/office-hours that referenced this issue Sep 13, 2018

Combining HRTBs with associated types
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!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.