🔬 Tracking issue for generic associated types (GAT) #44265
Comments
Here is a kind of implementation plan I will endeavor to keep updated.
|
Let me start by writing about the AST in more detail. First let's discuss how it works today: A A Methods are an interesting case because they can already be made generic. Those generic parameters are declared in the field My take is that the best thing to do would be to "lift" Perhaps a decent first PR would be to just do that change, keeping all other existing functionality the same. That is, we would more |
@nikomatsakis Cool! Thank you so much! I started experimenting with this last night and I'm proud to say that I found the same places you pointed to in your comment about the AST. I didn't think to lift generics into Here's the change I would have made: pub enum TraitItemKind {
// Generics aren't supported here yet
Const(P<Ty>, Option<P<Expr>>),
// `Generics` is already a field in `MethodSig`
Method(MethodSig, Option<P<Block>>),
// Added `Generics` here:
Type(Generics, TyParamBounds, Option<P<Ty>>),
Macro(Mac),
} Edit: Answer by nikomatsakis on Gitter
|
All right! Next step is to extend the parser. Here are a few tips. Let's start with trait items. This routine parses trait items. We want to extend the case that handles associated types to also parse things like Currently it is using Once we've done that, we should be able to add some parsing tests. I would do that by making a directory like Something else -- we need to feature gate this work at this point, to avoid people using this stuff in stable builds. There are some instructions for adding a feature-gate here on forge (see the final section). We should add a |
Move Generics from MethodSig to TraitItem and ImplItem As part of `rust-impl-period/WG-compiler-traits`, we want to "lift" `Generics` from `MethodSig` into `TraitItem` and `ImplItem`. This is in preparation for adding associated type generics. (#44265 (comment)) Currently this change is only made in the AST. In the future, it may also impact the HIR. (Still discussing) To understand this PR, it's probably best to start from the changes to `ast.rs` and then work your way to the other files to understand the far reaching effects of this change. r? @nikomatsakis
Move Generics from MethodSig to TraitItem and ImplItem As part of `rust-impl-period/WG-compiler-traits`, we want to "lift" `Generics` from `MethodSig` into `TraitItem` and `ImplItem`. This is in preparation for adding associated type generics. (#44265 (comment)) Currently this change is only made in the AST. In the future, it may also impact the HIR. (Still discussing) To understand this PR, it's probably best to start from the changes to `ast.rs` and then work your way to the other files to understand the far reaching effects of this change. r? @nikomatsakis
Move Generics from MethodSig to TraitItem and ImplItem As part of `rust-impl-period/WG-compiler-traits`, we want to "lift" `Generics` from `MethodSig` into `TraitItem` and `ImplItem`. This is in preparation for adding associated type generics. (#44265 (comment)) Currently this change is only made in the AST. In the future, it may also impact the HIR. (Still discussing) To understand this PR, it's probably best to start from the changes to `ast.rs` and then work your way to the other files to understand the far reaching effects of this change. r? @nikomatsakis
To setup name resolution, I think all we have to do is to get the proper "ribs" in place (the name resolution stuff organizes the sets of names that are in scope into ribs; each rib represents one binding level). e.g. for an impl: impl<A,B> Foo<B> for Vec<A> {
fn bar<T,U>(x: ...) {
for y in ... {
}
}
} we would have the following ribs:
In general, modeling things on how methods work is not a bad idea. We might also do a bit of "future proofing" here, I suppose. Here is the code that brings a method's type parameters into scope (this is for a method defined in a trait): rust/src/librustc_resolve/lib.rs Lines 1890 to 1892 in a35a3ab Whereas for a rust/src/librustc_resolve/lib.rs Lines 1897 to 1901 in a35a3ab Now that generics are in place on every trait/impl item, I think we probably want to remove the handling for Other points of interest: You get the idea. @petrochenkov -- sound about right? |
Everything looks correct. |
Generic Associated Types Parsing & Name Resolution Hi! This PR adds parsing for generic associated types!🎉 🎉 🎉 Tracking Issue: #44265 ## Notes For Reviewers * [x] I still need to add the stdout and stderr files to my ui tests. It takes me a *long* time to compile the compiler locally, so I'm going to add this as soon as possible in the next day or so. * [ ] My current ui tests aren't very good or very thorough. I'm reusing the `parse_generics` and `parse_where_clause` methods from elsewhere in the parser, so my changes work without being particularly complex. I'm not sure if I should duplicate all of the generics test cases for generic associated types. It might actually be appropriate to duplicate everything here, since we don't want to rely on an implementation detail in case it changes in the future. If you think so too, I'll adapt all of the generics test cases into the generic associated types test cases. * [ ] There is still more work required to make the run-pass tests pass here. In particular, we need to make the following errors disappear: ``` error[E0110]: lifetime parameters are not allowed on this type --> ./src/test/run-pass/rfc1598-generic-associated-types/streaming_iterator.rs:23:41 | 23 | bar: <T as StreamingIterator>::Item<'static>, | ^^^^^^^ lifetime parameter not allowed on this type ``` ``` error[E0261]: use of undeclared lifetime name `'a` --> ./src/test/run-pass/rfc1598-generic-associated-types/iterable.rs:15:47 | 15 | type Iter<'a>: Iterator<Item = Self::Item<'a>>; | ^^ undeclared lifetime ``` There is a FIXME comment in streaming_iterator. If you uncomment that line, you get the following: ``` error: expected one of `!`, `+`, `,`, `::`, or `>`, found `=` --> ./src/test/run-pass/rfc1598-generic-associated-types/streaming_iterator.rs:29:45 | 29 | fn foo<T: for<'a> StreamingIterator<Item<'a>=&'a [i32]>>(iter: T) { /* ... */ } | ^ expected one of `!`, `+`, `,`, `::`, or `>` here ``` r? @nikomatsakis
This can be simplified even more when rust-lang/rust#63063 and (maybe) rust-lang/rust#44265 are stable.
I don't like to write please hurry up already posts, but this feature has been stalled for nearly three years already, and I believe ranks as one of the most-desired new features. Where are we with this? The most recent status summary here is by @LukasKalbertodt from June 2018. Is it waiting on "chalkification"? Naive observation: nearly all of my desired uses for GATs require only one lifetime parameter. Would a cut-down lifetime-only version of GATs be simpler to deliver? |
#44265 (comment) is a (somewhat terse) update. |
Would this RFC make |
@ibraheemdev The RFC states
|
@ibraheemdev: my feeling is that the most idiomatic way to make monads and functors possible would be to introduce generic associated traits, but generic associated types is certainly a prerequisite. |
Is there a path to |
@ibraheemdev Hypothetically yes, it would be possible to implement HKTs directly and then implement a |
Maybe I'm wrong, but I think GAT allow something like trait MonadFamily {
type Monad<T>;
fn pure<T>(inner: T) -> Self::Monad<T>;
fn bind<T, U, F: FnOnce(T) -> U>(this: Self::Monad<T>, f: F) -> Self::Monad<U>;
} |
Yup, see Method for Emulating Higher-Kinded Types in Rust. It even works on stable now. |
This can be simplified even more when rust-lang/rust#63063 and (maybe) rust-lang/rust#44265 are stable.
Is there any route to updating the current |
@alercah I don’t think that would be possible because introducing a lifetime arg for an associated type would be a breaking change. |
Is it? |
I assume you mean a design similar to the streaming-iterator crate? With the current let first = it.next();
let second = it.next();
println!("{:?} {:?}", first, second); This isn't possible with a streaming iterator, because the memory used by the first element might be reused for the second. So I think it would be a breaking change. |
Ah, right. Thanks. |
@lambda-fairy I don't think that's necessarily a problem. If we were to change In order to change the current
With that, I guess it's rather unlikely the existing |
I don't think we can find a reasonable solution on the side that uses the trait. Currently, |
We would suggest something along the lines of default lifetimes.
and then collect would require |
If what @SoniEx2 suggested would work that'd be pretty cool. If not perhaps something like this could work: pub trait StreamingIterator {
type Item<'a>;
fn next(&mut self) -> Option<Self::Item<'_>>;
}
impl<T> StreamingIterator for T where T: Iterator {
type Item<'a> = <T as Iterator>::Item;
fn next(&mut self) -> Option<Self::Item<'_>> {
Iterator::next(self)
}
// possibly move/copy the combinators here?
}
pub trait IntoStreamingIterator {
type IntoStreamingIter: StreamingIterator;
// type Item either can't be here or IDK how
// not too important I think
fn into_streaming_iter(self) -> Self::IntoStreamingIter;
}
impl<T> IntoStreamingIterator for T where T: IntoIterator {
type IntoStreamingIter = <T as IntoIterator>::IntoIter;
fn into_streaming_iter(self) -> Self::IntoStreamingIter;
} Finally modify |
I believe that
And thus different and incompatible. However, this tracking issue isn’t the right place for this pontification. I suggest you move it to IRLO |
This is a tracking issue for generic associated types (rust-lang/rfcs#1598)
TODO:
Blocking bugs
#30472No longer blockingThe text was updated successfully, but these errors were encountered: