Skip to content

A nice pattern synonym record field became naughty in GHC 9.6

Consider the following:

pattern N1 :: forall a. () => forall. () => a -> Any
pattern N1 { fld1 } <- ( unsafeCoerce -> fld1 )
  where N1 = unsafeCoerce

pattern N2 :: forall. () => forall a. () => a -> Any
pattern N2 { fld2 } <- ( unsafeCoerce -> fld2 )
  where N2 = unsafeCoerce

test1, test2 :: forall a. Any -> a
test1 = fld1
test2 = fld2

We should accept test1, and reject test2 because we have an existential variable escaping its scope (fld2 is a "naughty record selector").

This is indeed what happens on GHC 9.4. However, on GHC 9.6, test1 is rejected as it considers fld1 to also be a naughty record selector.

This regression showed up when compiling the cleff package, which contains the following:

pattern Any :: forall a. a -> Any                                               
pattern Any {fromAny} <- (unsafeCoerce -> fromAny)                              
  where Any = unsafeCoerce                                                      
src/Cleff/Internal/Monad.hs:46:26: error: [GHC-55876]
    • Cannot use record selector ‘fromAny’ as a function due to escaped type variables
    • In the first argument of ‘($)’, namely ‘fromAny’
      In the expression:
        fromAny $ Vec.lookup (unHandlerPtr (Rec.index @e re)) mem
      In an equation for ‘readEnv’:
          readEnv (Env _ re mem)
            = fromAny $ Vec.lookup (unHandlerPtr (Rec.index @e re)) mem
    Suggested fix: Use pattern-matching syntax instead
   |
46 | readEnv (Env _ re mem) = fromAny $ Vec.lookup (unHandlerPtr (Rec.index @e re)) mem
   |
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information