Repository navigation
Trait inheritance gives unexpected compile error when inherited traits use associated types defined in trait #35237
Description
If one defines a trait such that inherited traits involve one of the trait's associated types, the compiler throws a "cyclic reference" error when one does not need to be thrown. Take the following code:
trait Foo: AsRef<Self::Bar> {
type Bar;
}which throws this error:
error: unsupported cyclic reference between types/traits detected [--explain E0391]
--> <anon>:1:18
1 |> trait Foo: AsRef<Self::Bar> {
|> ^^^^^^^^^
note: the cycle begins when computing the supertraits of `Foo`...
note: ...which then again requires computing the supertraits of `Foo`, completing the cycle.
error: aborting due to previous error
This error, while it accurately describes what is happening inside of the compiler, does not actually provide any way to fix the error. Even worse, the situation where the error arises isn't consistent with how associated types work in other areas of the trait syntax.
Making the above code work is fairly simple, although it does unnecessarily clutter the trait definition. The following compiles:
trait Foo: AsRef<<Self as Foo>::Bar> {
type Bar;
}However, this is not an immediately obvious fix due to how referencing associated types work in other areas of the trait syntax. In all other areas, the as syntax is unnecessary if the associated type is a member of the trait being implemented, which is true of the Bar type, only requiring the usage of as if the associated type is part of a supertrait. For example, take the following trait Baz with the associated type A:
trait Baz {
type A;
fn do_something(&self) -> Self::A;
}This compiles, as expected. The compiler correctly infers that, because of the lack of as syntax, the programmer is referring to a type belonging to the Baz trait. The programmer would also expect using Self::A to work when defining trait inheritance, although this is currently not the case. However, this should be the case - the compiler should, before throwing the "cyclic reference" error, scan the current trait for the given associated type and only throw the error if a type is not detected, as it does in other areas of trait definitions. The error should also outline the solution to the problem, suggesting that the programmer use the as syntax to specify the supertrait that the associated type is coming from.
Meta
Rust version:
rustc 1.10.0 (cfcb716cf 2016-07-03)
binary: rustc
commit-hash: cfcb716cf0961a7e3a4eceac828d94805cf8140b
commit-date: 2016-07-03
host: x86_64-unknown-linux-gnu
release: 1.10.0
Activity
clarfonthey commentedon Jun 17, 2017
Bumping this to say that I just ran into this problem too. I think that we should at least improve the error to recommend the change because that's not clear at all from the error.
vitalyd commentedon Jul 12, 2017
Another example of the same confusion: https://users.rust-lang.org/t/using-phantomdata-to-deal-with-unused-type-parameter/11799
ensko commentedon Jun 23, 2018
I just ran into this as well. I guess the problem with
Self::Baris that the compiler thinks about whatSelfcould be in a concrete instance, instead of just looking at the trait being defined?I think this warrants at least a changed error message, if not some cleaner way to "quote" the use of
Selfin this situation.oli-obk commentedon Jul 3, 2020
cc @rust-lang/wg-diagnostics not sure on the false positive rate on this, but maybe we could just preemptively lint on
Self::Foowithin supertraits before we even get to HIR.Alternatively we can try hooking into the cycle error. It already knows it's in the supertrait computation. If y'all agree on doing stuff in cycle errors is the right place to do it, I can extend the query system to allow us to add more notes to the cycle error on a per-query basis.
estebank commentedon Jul 3, 2020
It feels to me that it should be accepted code, but I think that decision falls under @rust-lang/lang's purview. If they decide against it, then either proposed solution seems fine, with the former looking easiest to implement. Even if we decide to fix this so the code compiles it'd be nice to have a better message with a suggestion in the meantime.
joshtriplett commentedon Jul 4, 2020
If we can straightforwardly accept this, I think we should.
estebank commentedon Jul 5, 2020
I think I just found out a trivial way of making this work for associated types on the base trait. What do we want to do for super traits, like the following case?
I can easily make the first one work, but for the later 1) I don't know how easy it will be to make it work, but probably doable, 2) don't know if we want to make it work when there's a single unambiguous assoc type that can be used from a super trait 3) I would imagine if more than one assoc type that could match we would want to error like we do for assoc fns:
oli-obk commentedon Jul 6, 2020
I think it's fine if it just works for the
Footrait declaration but not for theBartrait declaration. Currently if you use<Self as Bar>in theBartrait declarationthen we get
and keeping this behaviour in a first PR seems totally reasonable to me
estebank commentedon Jul 7, 2020
I got the behavior I was aiming at last night and just published a cleaned up PR with it.
It works in exactly the way you would expect. The following builds:
and the following fails because of ambiguity, providing a good suggestion:
The code has a special case when it recognizes it is converting an hir path written like
Self::Fooand uses an evaluation mode that then skips all super traits that referenceSelf. When doing so, we won't expand those, making the cycle error go away. The only thing that wouldn't work is trying to referenceSelf::Associn a super-trait that hasAssoc, that will continue (correctly) emitting a cycle error (although its output could be made a bit nicer).@joshtriplett I don't know if that PR qualifies as straight-forward :)
13 remaining items
tshepang commentedon Dec 3, 2020
should this be re-opened, given that the fix got a revert
jackh726 commentedon Mar 7, 2021
Should have been closed by #80732