Allow generic associate types in trait paths
matthewjasper opened this issue · 13 comments
The following code should be accepted:
#![feature(generic_associated_types)]
trait X {
type Y<'a>;
}
fn f(x: Box<dyn X<Y<'a>=&'a ()>>) {}
// or perhaps:
// fn f(x: Box<dyn for<'a> X<Y<'a>=&'a ()>>) {}
fn g<T: X<Y<'a>=&'a ()>>() {}I'll take at least the ast part of it for now
Am I correct in that we don't accept any type bounds on the left hand side and suggest moving them into a where clause?
Do you mean is X<Y<T: SomeTrait>=()> accepted? I would guess not because the bounds on T are already determined by the trait.
We allow repeating bounds in impl blocks though, i.e. this is permitted currently
#![feature(generic_associated_types)]
#![allow(incomplete_features)]
trait X<'b> {
type Y<'a: 'b>;
}
struct _S {}
impl<'a> X<'a> for _S {
type Y<'b: 'a> = ();
}
Edit: thinking about it, we probably wouldn't be able to do it even if we want to because we can't parse where clauses anyway
Is this still being handled? I am willing to look into it myself, I am currently stuck on needing this feature (and admittedly have a lot of time on my hands with recent events).
@SuperTails You are welcome to take it, Im busy with chalk work atm
I found another case where this is needed: I have a trait definition which looks like this:
pub trait ViewFn<S> {
type Output<'ast>: Sized;
fn view<'ast>(&self, node: &'ast S) -> Self::Output<'ast>;
// ...
}If I want to have a trait bound on ViewFn with Output constrained to be a reference to a object which implements a specific type, I would require this, e.g. the following currently doesn't work:
/*[...]*/<T, O, F>/*[...]*/
where
O: // omitted [...]
F: ViewFn<T, for<'x> Output<'x> = &'x O>,Is this a duplicate? I copied this from the RFC. Playground.
trait StreamingIterator {
type Item<'a>;
fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
}
fn foo<T: for<'a> StreamingIterator<Item<'a> = &'a [i32]>>(iter: T) { unimplemented!() }@vandenheuvel I think that is the same issue, yes.
Some thoughts:
Trait<A<'a>: Clone>should be supported as well (#52662).- The syntax should be future compatible with type parameters in associated types:
trait Trait { type A<T>; } Trait<A<T> = u8>
- What is inside the angle brackets really, arguments or parameters -
A<???>?
(Arguments use already defined names and parameters introduce names.)
DoesTrait<A<u8> = u8>orTrait<A<[u8; 10]> = u8>make sense, or???must be a single type parameter?
The fn f(x: Box<dyn for<'a> X<Y<'a>=&'a ()>>) {} example in the top comment implies that ??? is an argument after all (use, not definition).
I assume it's equivalent to an (unimplemented) type equality predicate in a where clause (for<'a> X::Y<'a> == &'a).
Then ??? can indeed be an arbitrary type (or lifetime) like u8 or [u8; 10].
From that we see that the associated type constraint syntax is really ambiguous with generic type parameters, but also get a clue on how to approach it during parsing.
When parsing a generic argument (inside Trait<...>) we just parse a type and then look at the next token:
- If the next token is not
=, then it's a type argument. - If the next token is
=(Trait<TYPE = ...>), then- if the parsed
TYPEis a single-segment path, possibly with generic arguments (Trait<Ident<Args> = ...>), then we store it into AST as an associated type constraint, - otherwise we are reporting a syntactic error.
- if the parsed
for<...> will be required to introduce names, but our syntax already accepts for<...> in that position
for<'a> Trait<A<'a> = &'a u8>, so we don't have to introduce any new syntax for the name introducer like
Trait<for<'a> A<'a> = &'a u8>(The only question is whether these two forms can be considered equivalent, it seems like yes.)
personally I would prefer
Trait<for<'a> A<'a> = &'a u8>(about the alternative:) I just wonder what would happen if it may clash, like:
for<'a> X<'a>: Trait<A<'a> = &'a u8>vs.
for<'x> X<'x>: Trait<for<'a> A<'a> = &'a u8>I think we should allow both syntax '(es), which should be considered equivalent in simple cases, but may be able to express more complex relations cleanly.
The Trait<for<'a> A<'a> = &'a u8> form introduces a new syntax and requires a more complex disambiguation because types can start with for (Trait<for<'a> A<'a>> is a type argument, Trait<for<'a> A<'a> = &'a u8> is an assoc type constraint), so I'd prefer to avoid it for now.
It can be added later if it becomes necessary.
Edit: Seems it's just impossible to instantiate something with one of these bounds in general?
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=5a3435e0d8ac7db81cb8df292d055203
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=dd5ea1fed4bdf844703aa9c6c57a913d
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=f301a4610471105bcde823047cd522a2
It seems that gat equals gat equality doesn't work, i.e.:
<A, B>
A: Gat,
B: for<'x> Gat<Domain<'x> = A::Domain<'x>>,When you try to use something which uses the Domain for both A and B, you get an E0271, type mismatched an associated type of a trait.
However the following (and other infinitely many variations) do work:
<A, B, T>
A: for<'x> Gat<Domain<'x> = T>,
B: for<'x> Gat<Domain<'x> = T>,<A, B, T: ?Sized>
A: for<'x> Gat<Domain<'x> = &'x T>,
B: for<'x> Gat<Domain<'x> = &'x T>,I think the first bound should cover both the second and third cases, or am I missing something?