I have a pretty minimal test case that causes rustc to spin (probably) forever computing a query about generic type substitution. We're talking rustc stable 1.64, beta, nightly, the lot.
struct Node<'a, G: NodeGenerics<'a>> {
kind: Kind<'a, G>,
}
enum Kind<'a, G: NodeGenerics<'a>> {
Empty,
Var(Var<'a, G::R>),
}
trait NodeGenerics<'a> {
type R: 'a;
}
struct RGen<T>;
impl<'a, T: 'a> NodeGenerics<'a> for RGen<T> {
type R = T;
}
struct Var<'a, R: 'a> {
node: Box<Node<'a, RGen<R>>>,
_phantom: std::marker::PhantomData<R>,
}
You can see for yourself that the playground times out: Rust Playground. When I sample the rustc process, the spin is around this query (mangled, sorry), which appears many times in the sample at many depths:
_RNvXs4_NtNtCsiVaogSVR4MG_12rustc_middle2ty4listINtB5_4ListNtNtB7_5subst10GenericArgENtNtCs669WPFKHeg5_4core3cmp3Ord3cmpB9_ (in librustc_driver-2bbd7f122e6c8090.dylib) + 68 [0x10e219090]
There are three changes you can make, any one of which gets it to compile fine.
- Remove the "carrier trait" NodeGenerics, replace it with
T: 'a
and useT
instead ofG::R
. You would think that adding a carrier trait with the same bounds would not really change anything. But it does. (In my code, NodeGenerics has a bunch of different types attached to it, which are used in many different arms ofKind
. It makes everything a lot neater.) - Changing
RGen<T>
to justRGen
and its implementation ofNodeGenerics
to have a constanttype R = ()
. - Don't use lifetimes at all, put
'static
bounds in everywhere instead, i.e. make NodeGenerics taketype R: 'static
.
Why is this spinning? Is there something I'm missing about lifetime variance that is deeply upsetting to Rustc? Is the solution to whack a well-placed PhantomData<&'a T>
in there? Or is this a rustc bug?