Properly treat cross-stage type variables in typed quotes
The crux of the problem is that we didn't consider cross-stage persistence for type variables. With the advent of type applications this has become more pertinent but has been a problem for a long time in untyped splices.
The original program presented in the ticket was
foo :: forall a . Q (TExp Int)
foo = [|| get @a ||]
This should be rejected immediately as a
is bound at stage 0 and used at stage 1 which is a stage violation.
However, we can fix this by implicitly lifting type variables like we do for term variables.
To do this, we define a new built-in class called LiftT
with a single method which lifts a type to its representation.
class LiftT (t :: k) where
liftTyCl :: Q Type
Instances for this class are solved automatically by the compiler for all types.
Now if we use a cross-stage variable, like for terms, we have to add a class constraint which says we can represent it.
foo :: forall a . LiftT a => Q (TExp Int)
foo = [|| get @a ||]
This is then implicitly desugared into
foo :: forall a . Typeable a => Q (TExp Int)
foo = [|| get @$$(liftTyCl @a) ||]
where liftTyCl
turns a type into a Q Type
.
The knowledgeable reader will know that this problem has already been solved in the same manner for Cloud Haskell but obviously the code paths are completely different.