Skip to content

GHC no longer instantiates representation-polymorphic newtype constructors

Consider the following program:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE UnliftedNewtypes #-}

import GHC.Exts

newtype N r (a :: TYPE r) = MkN a

foo :: Int# -> N IntRep Int#
foo i = MkN i

On GHC 9.2, the output of -ddump-ds-preopt is:

foo = \ (i :: Int#) -> (\ (ds :: Int#) -> MkN @'IntRep @Int# ds) i

That is, we have instantiated the representation-polymorphic MkN :: forall r (a :: TYPE r) . a -> N r a, with MkN @IntRep @Int#.

However, on GHC HEAD, we get:

foo = \ (i :: Int#) -> (\ @(r :: RuntimeRep) @(a :: TYPE r) (ds :: a) -> MkN @r @a ds) i

here we have a representation-polymorphic binder ds :: a!

I think this issue was introduced in !5640 (closed). There used to be logic in tc_infer_id to instantiate a representation-polymorphic data constructor, but that logic seems to have vanished entirely, and the new tcInferDataCon function does not do any instantiation. It's left with the original argument types, and thus dsConLike gets passed argument types which don't have a fixed runtime representation.

I think we should have an invariant that the argument types stored in ConLikeTc have a syntactically fixed RuntimeRep, which is necessary as they are the types of variables bound in a lambda (created by dsConLike), and enforce it by instantiating the newtype constructor.

This issue is causing me trouble with the implementation of PHASE 2 of FixedRuntimeRep.

Edited by sheaf
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information