Can anyone explain why this segment doesn't work and overflows the evaluator?
struct S;
trait T {
type Assoc;
}
impl T for S {
type Assoc = u32;
}
impl T for <S as T>::Assoc {
type Assoc = ();
}
Is there any way to work around this in the context of a macro generating that last impl block that has access to the name S, but not the literal name of the associated type?
The error message why this happens is somehow a lot clearer when you don't use fully qualified syntax for <S as T>::Assoc[1]:
struct Struct;
trait Trait {
type Assoc;
}
impl Trait for Struct {
type Assoc = u32;
}
impl Trait for Struct::Assoc {
type Assoc = ();
}
prints the following error message on the playground:
error[E0391]: cycle detected when finding trait impls of `Trait`
--> src/lib.rs:3:1
|
3 | trait Trait {
| ^^^^^^^^^^^
|
note: ...which requires computing type of `<impl at src/lib.rs:11:1: 11:29>`...
--> src/lib.rs:11:1
|
11 | impl Trait for Struct::Assoc {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which again requires finding trait impls of `Trait`, completing the cycle
note: cycle used when checking that `Trait` is well-formed
--> src/lib.rs:3:1
|
3 | trait Trait {
| ^^^^^^^^^^^
= 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
For more information about this error, try `rustc --explain E0391`.
I don't think you can use macros to generate the last impl block if all you know is S's name or implementation.
The best workaround I could come up with is to somewhat reverse the implementation requirements by adding a bound on Assoc that requires the Assoc to implement Trait<Assoc=()>. It satisfies the requirement that <Struct as Trait>::Assoc: Trait<Assoc=()>. But it is not as ergonomic as your suggested solution as you need to add the implementations for all the types where Trait<Assoc=()> yourself:
struct Struct;
trait Trait {
type Assoc: Trait<Assoc=()>;
}
impl Trait for Struct {
type Assoc = u32;
}
impl Trait for u32 {
type Assoc = ();
}
impl Trait for () {
type Assoc = ();
}
Thanks, that error is clearer, but I don't understand why it has to continue looking given that Struct has an explicit implementation. Is this purely a compiler limitation or is there a conceptual reason this shouldn't work?
The problem is technical rather than conceptual. Chalk, which as I understand it is supposed to be the new trait solver in rustc eventually, is able to compute your query:
?- program
Enter a program; press Ctrl-D when finished
| struct Struct {}
| trait Trait { type Assoc; }
| impl Trait for Struct { type Assoc = u32; }
| impl Trait for <Struct as Trait>::Assoc { type Assoc = (); }
|
?- u32: Trait
Unique
Chalk has been superseded by "next-solver" (I'm unaware of another name anyhow). It doesn't handle this yet, though it doesn't overflow either. I'm not sure if it's intended to eventually accept this or not.
So it turned out that I'd over-simplified my problem to end up with the test case above, and the situation I actually have works out fine because the trait I want to implement in the last block isn't the same as the one providing the associated trait.