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

Allow generic associate types in trait paths #67510

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in 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