Note [Representation-polymorphic TyCons]

GHC/Core/TyCon.hs:954 compiler 1 ticket

To check for representation-polymorphism directly in the typechecker,
e.g. when using GHC.Tc.Utils.TcMType.checkTypeHasFixedRuntimeRep,
we need to compute whether a type has a syntactically fixed RuntimeRep,
as per Note [Fixed RuntimeRep] in GHC.Tc.Utils.Concrete.

It's useful to have a quick way to check whether a saturated application
of a type constructor has a fixed RuntimeRep. That is, we want
to know, given a TyCon 'T' of arity 'n', does

  T a_1 ... a_n

always have a fixed RuntimeRep? That is, is it always the case
that this application has a kind of the form

  T a_1 ... a_n :: TYPE rep

in which 'rep' is a concrete 'RuntimeRep'?
('Concrete' in the sense of Note [The Concrete mechanism] in GHC.Tc.Utils.Concrete:
it contains no type-family applications or type variables.)

To answer this question, we have 'tcHasFixedRuntimeRep'.
If 'tcHasFixedRuntimeRep' returns 'True', it means we're sure that
every saturated application of `T` has a fixed RuntimeRep.
However, if it returns 'False', we don't know: perhaps some application might not
have a fixed RuntimeRep.

Examples:

  - For type families, we won't know in general whether an application
    will have a fixed RuntimeRep:

      type F :: k -> k
      type family F a where {..}

    `tcHasFixedRuntimeRep F = False'

  - For newtypes, we're usually OK:

      newtype N a b c = MkN Int

    No matter what arguments we apply `N` to, we always get something of
    kind `Type`, which has a fixed RuntimeRep.
    Thus `tcHasFixedRuntimeRep N = True`.

    However, with `-XUnliftedNewtypes`, we can have representation-polymorphic
    newtypes:

      type UN :: TYPE rep -> TYPE rep
      newtype UN a = MkUN a

    `tcHasFixedRuntimeRep UN = False`

    For example, `UN @Int8Rep Int8#` is represented by an 8-bit value,
    while `UN @LiftedRep Int` is represented by a heap pointer.

    To distinguish whether we are dealing with a representation-polymorphic newtype,
    we keep track of which situation we are in using the 'nt_fixed_rep'
    field of the 'NewTyCon' constructor of 'AlgTyConRhs', and read this field
    to compute 'tcHasFixedRuntimeRep'.

  - A similar story can be told for datatypes: we're usually OK,
    except with `-XUnliftedDatatypes` which allows for levity polymorphism,
    e.g.:

      type UC :: TYPE (BoxedRep l) -> TYPE (BoxedRep l)
      type UC a = MkUC a

    `tcHasFixedRuntimeRep UC = False`

    Here, we keep track of whether we are dealing with a levity-polymorphic
    unlifted datatype using the 'data_fixed_lev' field of the 'DataTyCon'
    constructor of 'AlgTyConRhs'.

    N.B.: technically, the representation of a datatype is fixed,
    as it is always a pointer. However, we currently require that we
    know the specific `RuntimeRep`: knowing that it's `BoxedRep l`
    for a type-variable `l` isn't enough. See #15532.

References 2

Referenced by 1