Skip to content

Allow generic associate types in trait paths #67510

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
Tracked by #44265
matthewjasper opened this issue Dec 22, 2019 · 13 comments · Fixed by #79554
Closed
Tracked by #44265

Allow generic associate types in trait paths #67510

matthewjasper opened this issue Dec 22, 2019 · 13 comments · Fixed by #79554
Labels
A-GATs Area: Generic associated types (GATs) A-parser Area: The parsing of Rust source code to an AST C-feature-accepted Category: A feature request that has been accepted pending implementation. F-generic_associated_types `#![feature(generic_associated_types)]` a.k.a. GATs requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@matthewjasper
Copy link
Contributor

matthewjasper commented Dec 22, 2019

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 ()>>() {}
@matthewjasper matthewjasper added the F-generic_associated_types `#![feature(generic_associated_types)]` a.k.a. GATs label Dec 22, 2019
@matthewjasper matthewjasper added the C-feature-accepted Category: A feature request that has been accepted pending implementation. label Dec 22, 2019
@Centril Centril added A-parser Area: The parsing of Rust source code to an AST T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. requires-nightly This issue requires a nightly compiler in some way. labels Dec 22, 2019
@basil-cow
Copy link
Contributor

I'll take at least the ast part of it for now

@basil-cow
Copy link
Contributor

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?

@matthewjasper
Copy link
Contributor Author

Do you mean is X<Y<T: SomeTrait>=()> accepted? I would guess not because the bounds on T are already determined by the trait.

@basil-cow
Copy link
Contributor

basil-cow commented Feb 11, 2020

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

@SuperTails
Copy link

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).

@basil-cow
Copy link
Contributor

@SuperTails You are welcome to take it, Im busy with chalk work atm

@fogti
Copy link
Contributor

fogti commented Jul 8, 2020

#44265 (comment)

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>,

@vandenheuvel
Copy link
Contributor

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!() }

@fogti
Copy link
Contributor

fogti commented Jul 22, 2020

@vandenheuvel I think that is the same issue, yes.

@petrochenkov
Copy link
Contributor

Some thoughts:

  • Trait<A<'a>: Clone> should be supported as well (Tracking issue for RFC 2289, "Associated type bounds" #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.)
    Does Trait<A<u8> = u8> or Trait<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 TYPE is 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.

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.)

@fogti
Copy link
Contributor

fogti commented Sep 25, 2020

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.

@petrochenkov
Copy link
Contributor

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.

m-ou-se added a commit to m-ou-se/rust that referenced this issue Feb 5, 2021
…-trait-paths, r=jackh726

Generic associated types in trait paths

This is the second part of rust-lang#78978

This should fix:

Fixes rust-lang#67510
Fixes rust-lang#68648
Fixes rust-lang#68649
Fixes rust-lang#68650
Fixes rust-lang#68652
Fixes rust-lang#74684
Fixes rust-lang#76535
Fixes rust-lang#79422
Fixes rust-lang#80433

and implement the remaining functionality needed for rust-lang#44265

r? `@matthewjasper`
@bors bors closed this as completed in deec6a9 Feb 5, 2021
@MingweiSamuel
Copy link
Contributor

MingweiSamuel commented Mar 3, 2021

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.

Playground Link

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?

@fmease fmease added the A-GATs Area: Generic associated types (GATs) label Nov 2, 2024
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
A-GATs Area: Generic associated types (GATs) A-parser Area: The parsing of Rust source code to an AST C-feature-accepted Category: A feature request that has been accepted pending implementation. F-generic_associated_types `#![feature(generic_associated_types)]` a.k.a. GATs requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
9 participants