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

Tracking issue for RFC 2289, "Associated type bounds" #52662

Closed
9 tasks done
Centril opened this issue Jul 24, 2018 · 39 comments · Fixed by #122055
Closed
9 tasks done

Tracking issue for RFC 2289, "Associated type bounds" #52662

Centril opened this issue Jul 24, 2018 · 39 comments · Fixed by #122055
Labels
A-trait-system Area: Trait system B-RFC-approved Blocker: Approved by a merged RFC but not yet implemented. B-RFC-implemented Blocker: Approved by a merged RFC and implemented. B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC F-associated_type_bounds `#![feature(associated_type_bounds)]` S-tracking-impl-incomplete Status: The implementation is incomplete. S-types-tracked Status: Being actively tracked by the types team T-lang Relevant to the language team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@Centril
Copy link
Contributor

Centril commented Jul 24, 2018

This is a tracking issue for the RFC "Associated type bounds of form MyTrait<AssociatedType: Bounds>" (rust-lang/rfcs#2289). The feature gate is #![feature(associated_type_bounds)].

Steps:

Unresolved questions:

TODO:

@Centril Centril added A-trait-system Area: Trait system B-RFC-approved Blocker: Approved by a merged RFC but not yet implemented. T-lang Relevant to the language team, which will review and decide on the PR/issue. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC WG-traits Working group: Traits, https://internals.rust-lang.org/t/announcing-traits-working-group/6804 labels Jul 24, 2018
@kennytm
Copy link
Member

kennytm commented Jul 24, 2018

Does allowing this for dyn trait objects introduce any unforeseen issues?

😕 How does it work? For instance while dyn Iterator<Item = T> is object-safe, dyn Iterator<Item: Debug> should not since its .next() returns... impl Debug (but dynamic)?

trait Tr {
    type Assoc;
    fn input(&self, assoc: &Self::Assoc);
    fn output(&self) -> Self::Assoc;
}

let a: &Tr<Assoc: Debug> = ...;
let o = a.output();  // ???
a.input(&o);         // ???

let b: &Tr<Assoc: Debug> = ...;
b.input(&o);         // ??????????

@Centril
Copy link
Contributor Author

Centril commented Jul 24, 2018

@kennytm I think that while dyn Iterator<Item: Debug> make syntactic sense to parse (to keep the parser simpler), it doesn't make much sense semantically for the reasons you've noted. Nicely spotted. :)

@kennytm
Copy link
Member

kennytm commented Jul 24, 2018

So I suppose we could parse dyn Trait<Assoc: Bound> into AST, but will reject it during HIR lowering or typeck.

@Centril
Copy link
Contributor Author

Centril commented Jul 24, 2018

@kennytm sounds reasonable. 👍

@LukasKalbertodt
Copy link
Member

I haven't found any discussion about this: what about super trait bounds? In particular: whether other code can "rely" on it. Example (current Rust, Playground):

trait Color {
    type Channel;
}

trait CloneColor: Color
where
    Self::Channel: Clone,
{}

fn foo<T: CloneColor>() {
    let f = T::Channel::clone;
}

This errors. While foo can assume that T also implements Color (the direct super trait bound) and thus T::Channel works, foo cannot assume the where bound of the trait to be true, and thus ::clone doesn't work. The compiler says "the trait bound <T as Color>::Channel: std::clone::Clone is not satisfied". I can't quite remember what this is called, but I once heard a term that describes what things other code can rely on and what not (maybe something fancy like "projection"?).

My question is: how would the following code behave?

trait CloneColor: Color<Channel: Clone> {}

Would it behave as the first code example or would other code now be able to deduce T::Channel: Clone from T: CloneColor?

(I hope I'm not missing something obvious here)

@Centril
Copy link
Contributor Author

Centril commented Mar 17, 2019

This errors.

#44491 will likely change this :) (cc @scalexm)

My question is: how would the following code behave?

Same as:

trait CloneColor where Self: Color, <Self as Color>::Channel: Clone {}

Would it behave as the first code example or would other code now be able to deduce T::Channel: Clone from T: CloneColor?

Depends on whether you have implied bounds or not :)

@LukasKalbertodt
Copy link
Member

@Centril Thanks for the quick explanation! I didn't know implied bounds would change this too -- that's great!

@Nemo157
Copy link
Member

Nemo157 commented Mar 21, 2019

How does this interact with HRLB? I just discovered that trait aliases allow you to write the constraints required for working with async functions/closures, but could this be another way to do so?

You can currently write:

trait Foo<'a> = FnOnce<(&'a u8,)> where <Self as FnOnce<(&'a u8,)>>::Output: Future<Output = u8> + 'a;

fn foo<F: for<'a> Foo<'a>>(f: F)

and presumably once this is implemented it could be:

trait Foo<'a> = FnOnce<(&'a u8,), Output: Future<Output = u8> + 'a>;

fn foo<F: for<'a> Foo<'a>>(f: F)

but can you go all the way to:

fn foo<F: for<'a> FnOnce<(&'a u8,), Output: Future<Output = u8> + 'a>>(f: F)

@Centril
Copy link
Contributor Author

Centril commented Mar 21, 2019

@Nemo157 When you write:

where
    Foo: for<'a> Bar<'a, Assoc: Bound>

it should desugar into:

where
    Foo: for<'a> Bar<'a>,
    for<'a> <Foo as Bar<'a>>::Assoc: Bound,

However, rustc is generally unhappy with for<'a> <Foo as Bar<'a>>::Assoc: Bound today as I found out when writing tests for #57428. Chalk will hopefully makes things better in the future and throw out some of the bugs.

@Centril
Copy link
Contributor Author

Centril commented Aug 8, 2019

A fix to #61752 landed in #61919.

@Centril
Copy link
Contributor Author

Centril commented Aug 10, 2019

#63350 by @iluuu1994 & reviewed by @Centril fixed #61738. The standard library now uses ATBs in some places. This is further enhanced in #63584.

Centril added a commit to Centril/rust that referenced this issue Aug 15, 2019

Verified

This commit was signed with the committer’s verified signature. The key has expired.
jhpratt Jacob Pratt
… r=alexreg

libcore: more cleanups using `#![feature(associated_type_bounds)]`

Turns out this was indeed a bootstrapping issue from a test with `./x.py check` locally after rust-lang#63534 merged.

Closes rust-lang#63393

r? @alexreg
cc @iluuu1994
cc rust-lang#52662
Centril added a commit to Centril/rust that referenced this issue Aug 15, 2019

Verified

This commit was signed with the committer’s verified signature. The key has expired.
jhpratt Jacob Pratt
… r=alexreg

libcore: more cleanups using `#![feature(associated_type_bounds)]`

Turns out this was indeed a bootstrapping issue from a test with `./x.py check` locally after rust-lang#63534 merged.

Closes rust-lang#63393

r? @alexreg
cc @iluuu1994
cc rust-lang#52662
Centril added a commit to Centril/rust that referenced this issue Aug 15, 2019

Verified

This commit was signed with the committer’s verified signature. The key has expired.
jhpratt Jacob Pratt
… r=alexreg

libcore: more cleanups using `#![feature(associated_type_bounds)]`

Turns out this was indeed a bootstrapping issue from a test with `./x.py check` locally after rust-lang#63534 merged.

Closes rust-lang#63393

r? @alexreg
cc @iluuu1994
cc rust-lang#52662
Centril added a commit to Centril/rust that referenced this issue Aug 15, 2019

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
… r=alexreg

libcore: more cleanups using `#![feature(associated_type_bounds)]`

Turns out this was indeed a bootstrapping issue from a test with `./x.py check` locally after rust-lang#63534 merged.

Closes rust-lang#63393

r? @alexreg
cc @iluuu1994
cc rust-lang#52662
@joshtriplett

This comment was marked as resolved.

yvt added a commit to r3-os/r3 that referenced this issue Jun 11, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
This feature is tracked by [rust-lang/rust#52662][1].

Fixes `[ref:trait_constraints_on_associated_types_do_not_propagate]`.
That is, when you have `C: ~const CfgTimer`, you don't need `C::System:
KernelTimer` anymore.

[1]: rust-lang/rust#52662
@jackh726 jackh726 added T-types Relevant to the types team, which will review and decide on the PR/issue. S-types-tracked Status: Being actively tracked by the types team and removed WG-traits Working group: Traits, https://internals.rust-lang.org/t/announcing-traits-working-group/6804 labels Jun 24, 2022
@jackh726 jackh726 assigned oli-obk and unassigned alexreg Jun 24, 2022
@mimoo

This comment was marked as off-topic.

@clarfonthey
Copy link
Contributor

I assume that fully implementing this feature isn't very high priority, but what do people think in terms of marking this flag as incomplete, given the comment I posted earlier this year?

I would offer to help with this but I'm not sure I'm aware of what's involved to do this. I'm also not sure where this is on the road map, but my assumption is it's lower than other planned features and no one's going to be working ensure this doesn't ambiguify (?) the existing trait behaviour for a while.

caio-oliv added a commit to caio-oliv/keyword-generic-syntax that referenced this issue Mar 3, 2023
Using a special keyword "effect", its possible to
indicate that a type, function or trait is
generic over a effect with the "rust way" of
expressing generics.

The effect keyword also has a role in the where
clause, capable of specifing the keywords beeing
generic (e.g. async, !async, ?async, const +
?async, etc) and the keyword bounds of traits in
generic arguments.

This design also takes into account possible
syntax changes in other proposals and ideas.

- [associated type bounds](rust-lang/rust#52662)
- [return type notation](https://smallcultfollowing.com/babysteps/blog/2023/02/13/return-type-notation-send-bounds-part-2/)
@joshtriplett

This comment was marked as off-topic.

@maia-s

This comment was marked as off-topic.

@gui1117

This comment was marked as off-topic.

@rust-lang rust-lang locked and limited conversation to collaborators Mar 3, 2024
@oli-obk
Copy link
Contributor

oli-obk commented Mar 3, 2024

Tracking issues are impractical for discussions. I'm locking this issue. Please open new issues for problems and discuss the feature on zulip instead

workingjubilee added a commit to workingjubilee/rustc that referenced this issue Mar 18, 2024
…li-obk

Stabilize associated type bounds (RFC 2289)

This PR stabilizes associated type bounds, which were laid out in [RFC 2289]. This gives us a shorthand to express nested type bounds that would otherwise need to be expressed with nested `impl Trait` or broken into several `where` clauses.

### What are we stabilizing?

We're stabilizing the associated item bounds syntax, which allows us to put bounds in associated type position within other bounds, i.e. `T: Trait<Assoc: Bounds...>`. See [RFC 2289] for motivation.

In all position, the associated type bound syntax expands into a set of two (or more) bounds, and never anything else (see "How does this differ[...]" section for more info).

Associated type bounds are stabilized in four positions:
* **`where` clauses (and APIT)** - This is equivalent to breaking up the bound into two (or more) `where` clauses. For example, `where T: Trait<Assoc: Bound>` is equivalent to `where T: Trait, <T as Trait>::Assoc: Bound`.
* **Supertraits** - Similar to above, `trait CopyIterator: Iterator<Item: Copy> {}`. This is almost equivalent to breaking up the bound into two (or more) `where` clauses; however, the bound on the associated item is implied whenever the trait is used. See rust-lang#112573/rust-lang#112629.
* **Associated type item bounds** - This allows constraining the *nested* rigid projections that are associated with a trait's associated types. e.g. `trait Trait { type Assoc: Trait2<Assoc2: Copy>; }`.
* **opaque item bounds (RPIT, TAIT)** - This allows constraining associated types that are associated with the opaque without having to *name* the opaque. For example, `impl Iterator<Item: Copy>` defines an iterator whose item is `Copy` without having to actually name that item bound.

The latter three are not expressible in surface Rust (though for associated type item bounds, this will change in rust-lang#120752, which I don't believe should block this PR), so this does represent a slight expansion of what can be expressed in trait bounds.

### How does this differ from the RFC?

Compared to the RFC, the current implementation *always* desugars associated type bounds to sets of `ty::Clause`s internally. Specifically, it does *not* introduce a position-dependent desugaring as laid out in [RFC 2289], and in particular:
* It does *not* desugar to anonymous associated items in associated type item bounds.
* It does *not* desugar to nested RPITs in RPIT bounds, nor nested TAITs in TAIT bounds.

This position-dependent desugaring laid out in the RFC existed simply to side-step limitations of the trait solver, which have mostly been fixed in rust-lang#120584. The desugaring laid out in the RFC also added unnecessary complication to the design of the feature, and introduces its own limitations to, for example:
* Conditionally lowering to nested `impl Trait` in certain positions such as RPIT and TAIT means that we inherit the limitations of RPIT/TAIT, namely lack of support for higher-ranked opaque inference. See this code example: rust-lang#120752 (comment).
* Introducing anonymous associated types makes traits no longer object safe, since anonymous associated types are not nameable, and all associated types must be named in `dyn` types.

This last point motivates why this PR is *not* stabilizing support for associated type bounds in `dyn` types, e.g, `dyn Assoc<Item: Bound>`. Why? Because `dyn` types need to have *concrete* types for all associated items, this would necessitate a distinct lowering for associated type bounds, which seems both complicated and unnecessary compared to just requiring the user to write `impl Trait` themselves. See rust-lang#120719.

### Implementation history:

Limited to the significant behavioral changes and fixes and relevant PRs, ping me if I left something out--
* rust-lang#57428
* rust-lang#108063
* rust-lang#110512
* rust-lang#112629
* rust-lang#120719
* rust-lang#120584

Closes rust-lang#52662

[RFC 2289]: https://rust-lang.github.io/rfcs/2289-associated-type-bounds.html
@bors bors closed this as completed in 21d94a3 Mar 19, 2024
GuillaumeGomez pushed a commit to GuillaumeGomez/rust that referenced this issue Jul 10, 2024
…-obk

Stabilize associated type bounds (RFC 2289)

This PR stabilizes associated type bounds, which were laid out in [RFC 2289]. This gives us a shorthand to express nested type bounds that would otherwise need to be expressed with nested `impl Trait` or broken into several `where` clauses.

### What are we stabilizing?

We're stabilizing the associated item bounds syntax, which allows us to put bounds in associated type position within other bounds, i.e. `T: Trait<Assoc: Bounds...>`. See [RFC 2289] for motivation.

In all position, the associated type bound syntax expands into a set of two (or more) bounds, and never anything else (see "How does this differ[...]" section for more info).

Associated type bounds are stabilized in four positions:
* **`where` clauses (and APIT)** - This is equivalent to breaking up the bound into two (or more) `where` clauses. For example, `where T: Trait<Assoc: Bound>` is equivalent to `where T: Trait, <T as Trait>::Assoc: Bound`.
* **Supertraits** - Similar to above, `trait CopyIterator: Iterator<Item: Copy> {}`. This is almost equivalent to breaking up the bound into two (or more) `where` clauses; however, the bound on the associated item is implied whenever the trait is used. See rust-lang#112573/rust-lang#112629.
* **Associated type item bounds** - This allows constraining the *nested* rigid projections that are associated with a trait's associated types. e.g. `trait Trait { type Assoc: Trait2<Assoc2: Copy>; }`.
* **opaque item bounds (RPIT, TAIT)** - This allows constraining associated types that are associated with the opaque without having to *name* the opaque. For example, `impl Iterator<Item: Copy>` defines an iterator whose item is `Copy` without having to actually name that item bound.

The latter three are not expressible in surface Rust (though for associated type item bounds, this will change in rust-lang#120752, which I don't believe should block this PR), so this does represent a slight expansion of what can be expressed in trait bounds.

### How does this differ from the RFC?

Compared to the RFC, the current implementation *always* desugars associated type bounds to sets of `ty::Clause`s internally. Specifically, it does *not* introduce a position-dependent desugaring as laid out in [RFC 2289], and in particular:
* It does *not* desugar to anonymous associated items in associated type item bounds.
* It does *not* desugar to nested RPITs in RPIT bounds, nor nested TAITs in TAIT bounds.

This position-dependent desugaring laid out in the RFC existed simply to side-step limitations of the trait solver, which have mostly been fixed in rust-lang#120584. The desugaring laid out in the RFC also added unnecessary complication to the design of the feature, and introduces its own limitations to, for example:
* Conditionally lowering to nested `impl Trait` in certain positions such as RPIT and TAIT means that we inherit the limitations of RPIT/TAIT, namely lack of support for higher-ranked opaque inference. See this code example: rust-lang#120752 (comment).
* Introducing anonymous associated types makes traits no longer object safe, since anonymous associated types are not nameable, and all associated types must be named in `dyn` types.

This last point motivates why this PR is *not* stabilizing support for associated type bounds in `dyn` types, e.g, `dyn Assoc<Item: Bound>`. Why? Because `dyn` types need to have *concrete* types for all associated items, this would necessitate a distinct lowering for associated type bounds, which seems both complicated and unnecessary compared to just requiring the user to write `impl Trait` themselves. See rust-lang#120719.

### Implementation history:

Limited to the significant behavioral changes and fixes and relevant PRs, ping me if I left something out--
* rust-lang#57428
* rust-lang#108063
* rust-lang#110512
* rust-lang#112629
* rust-lang#120719
* rust-lang#120584

Closes rust-lang#52662

[RFC 2289]: https://rust-lang.github.io/rfcs/2289-associated-type-bounds.html
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A-trait-system Area: Trait system B-RFC-approved Blocker: Approved by a merged RFC but not yet implemented. B-RFC-implemented Blocker: Approved by a merged RFC and implemented. B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC F-associated_type_bounds `#![feature(associated_type_bounds)]` S-tracking-impl-incomplete Status: The implementation is incomplete. S-types-tracked Status: Being actively tracked by the types team T-lang Relevant to the language team, which will review and decide on the PR/issue. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.