You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Inconsistent behavior when using associated type bounds in super trait with constraints #153002
error[E0391]: cycle detected when computing the implied predicates of `B`
--> src/lib.rs:5:19
|
5 | pub trait B: A<T: B> {}
| ^
|
= note: ...which immediately requires computing the implied predicates of `B` again
note: cycle used when computing normalized predicates of `B`
--> src/lib.rs:5:1
|
5 | pub trait B: A<T: B> {}
| ^^^^^^^^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
As per @Centril comment here: #65913 (comment)
if I try the equivalent version, it also fails with the same error:
pubtraitBwhereSelf:A<T:B>{}
but If I tried the further equivalent version, it works:
pubtraitBwhereSelf:A,
<SelfasA>::T:B{}
I expected to see the same error as the first two variants.
The two syntaxes seem to have different behavior elsewhere, too. For example:
traitBound{}traitSuper{typeAssoc;}traitSub1:Super<Assoc:Bound>{}traitSub2:SuperwhereSelf::Assoc:Bound{}// The compilesfnworks<T:Sub1<Assoc = i32>>(){}// This errorsfnfails<T:Sub2<Assoc = i32>>(){}
error[E0277]: the trait bound `i32: Bound` is not satisfied
--> src/lib.rs:13:13
|
13 | fn fails<T: Sub2<Assoc = i32>>() {}
| ^^^^^^^^^^^^^^^^^ the trait `Bound` is not implemented for `i32`
|
help: this trait has no implementations, consider adding one
--> src/lib.rs:1:1
|
1 | trait Bound {}
| ^^^^^^^^^^^
note: required by a bound in `Sub2`
--> src/lib.rs:8:38
|
8 | trait Sub2: Super where Self::Assoc: Bound {}
| ^^^^^ required by this bound in `Sub2`
For more information about this error, try `rustc --explain E0277`.
traitBound{}traitSuper{typeAssoc;}traitSub1:Super<Assoc:Bound>{}traitSub2:SuperwhereSelf::Assoc:Bound{}// This compilesfnworks<T:Sub1>(){}// This errorsfnfails<T:Sub2>(){}
error[E0277]: the trait bound `<T as Super>::Assoc: Bound` is not satisfied
--> src/lib.rs:13:13
|
13 | fn fails<T: Sub2>() {}
| ^^^^ the trait `Bound` is not implemented for `<T as Super>::Assoc`
|
note: required by a bound in `Sub2`
--> src/lib.rs:8:38
|
8 | trait Sub2: Super where Self::Assoc: Bound {}
| ^^^^^ required by this bound in `Sub2`
help: consider further restricting the associated type
|
13 | fn fails<T: Sub2>() where <T as Super>::Assoc: Bound {}
| ++++++++++++++++++++++++++++++++
For more information about this error, try `rustc --explain E0277`.
error[E0277]: the trait bound `<impl Sub as Super>::Assoc: Bound` is not satisfied
--> src/lib.rs:25:12
|
25 | make().method();
| ^^^^^^ the trait `Bound` is not implemented for `<impl Sub as Super>::Assoc`
|
help: the trait `Bound` is implemented for `i32`
--> src/lib.rs:10:1
|
10 | impl Bound for i32 {}
| ^^^^^^^^^^^^^^^^^^
note: required by a bound in `Sub::method`
--> src/lib.rs:5:37
|
5 | trait Sub: Super where Self::Assoc: Bound {
| ^^^^^ required by this bound in `Sub::method`
6 | fn method(&self) {}
| ------ required by a bound in this associated function
For more information about this error, try `rustc --explain E0277`.
As there is inconsistent behavior, this is a good time to converge to a behavior which would be better for generic programming. We need to take best of both behaviors.
Currently, inline constraints automatically infer the constraint for associated type.
pubtraitBase{typeT}pubtraitRefined:Base<T:Ord>{}fnf<C:Refined>(c:C){// automatically derives C::T is Ord}
However, with where version I explicitly need to mention it:
pubtraitRefined2:BasewhereSelf::T:Ord{}fng<C:Refined2>(c:C)whereC::T:Ord// need to put it explicitly to compile for no reason.{}
There is no reason, compiler can't infer the constraint automatically.
On the other hand, where version can handle the recursive constraints but inline constraints can't:
pubtraitRefined3:BasewhereSelf::T:Refined3{}pubtraitRefined4:Base<T:Refined4>// doesn't compile :( with error saying cycle in constraints detected{}
Handling these cases would be a big win for generic programming :)
It is generally a non-breaking change to go from 2 to 1 or from 2 to 3. But it is a breaking change to go from 1 to 2 or 3, and it is a breaking change to go from 3 to 1 or 2.
So if you make Sub2 act like Sub1, you will be removing the ability of trait authors to loosen their bounds without breaking downstream. Or in other words, trait authors should have the ability to opt-in (or opt-out) of implied bounds.
AFAIK the only way an author can choose between having bounds be implied or not today is to arrange to have the choice between using an associated type bound or using a bound on an associated type projection. It would be nice to broaden the ability to choose and to make it more explicit. But the choice shouldn't be taken away.
Activity
theemathas commentedon Feb 23, 2026
The two syntaxes seem to have different behavior elsewhere, too. For example:
theemathas commentedon Feb 23, 2026
Another weirdness:
theemathas commentedon Feb 23, 2026
Possibly related: #56003
theemathas commentedon Feb 23, 2026
Another weirdness:
RishabhRD commentedon Feb 23, 2026
As there is inconsistent behavior, this is a good time to converge to a behavior which would be better for generic programming. We need to take best of both behaviors.
Currently, inline constraints automatically infer the constraint for associated type.
However, with
whereversion I explicitly need to mention it:There is no reason, compiler can't infer the constraint automatically.
On the other hand,
whereversion can handle the recursive constraints but inline constraints can't:Handling these cases would be a big win for generic programming :)
QuineDot commentedon Feb 23, 2026
This case is about supertrait elaboration / implied bounds, and these two cases should not have the same behavior (without opting in to it).
Consider three scenarios:
Sub1)Sub2)It is generally a non-breaking change to go from 2 to 1 or from 2 to 3. But it is a breaking change to go from 1 to 2 or 3, and it is a breaking change to go from 3 to 1 or 2.
So if you make
Sub2act likeSub1, you will be removing the ability of trait authors to loosen their bounds without breaking downstream. Or in other words, trait authors should have the ability to opt-in (or opt-out) of implied bounds.Associated type bounds were intentionally made implied; see the links in this stabilization PR.
AFAIK the only way an author can choose between having bounds be implied or not today is to arrange to have the choice between using an associated type bound or using a bound on an associated type projection. It would be nice to broaden the ability to choose and to make it more explicit. But the choice shouldn't be taken away.