Skip to content

GHC HEAD-only panic (emitPrimOp: handled above in cgOpApp)

I noticed this issue originally as deriving-compat-0.5.7's test suite fails to compile on HEAD. Here is a minimized version of the test suite that compiles successfully with optimizations on GHC 8.8.1 and earlier:

{-# LANGUAGE MagicHash #-}
{-# LANGUAGE TypeFamilies #-}
module Bug where

import Control.Monad
import GHC.Arr (Ix(..))
import GHC.Base (getTag)
import GHC.Exts

data family D
data instance D = MkD
  deriving (Eq, Ord, Show)

instance Ix D where
  range (a, b) =
    let a# = getTag a
        b# = getTag b
    in map (\(I# i#) -> tagToEnum# i# :: D)
           (enumFromTo (I# a#) (I# b#))
  unsafeIndex (a, _) c =
    let a# = getTag a
        c# = getTag c
        d# = c# -# a#
    in I# d#
  inRange (a, b) c =
    let a# = getTag a
        b# = getTag b
        c# = getTag c
    in tagToEnum# (c# >=# a#) && tagToEnum# (c# <=# b#)

shouldBe :: (Eq a, Show a) => a -> a -> IO ()
shouldBe x y =
  unless (x == y) $ fail $ show x ++ " is not equal to " ++ show y

ixLaws :: (Ix a, Show a) => a -> a -> a -> IO ()
ixLaws l u i = do
    inRange (l,u) i                 `shouldBe` elem i (range (l,u))
    range (l,u) !! index (l,u) i    `shouldBe` i
    map (index (l,u)) (range (l,u)) `shouldBe` [0..rangeSize (l,u)-1]
    rangeSize (l,u)                 `shouldBe` length (range (l,u))

dIsLawfulIx :: IO ()
dIsLawfulIx = ixLaws MkD MkD MkD

However, it panics on GHC HEAD:

$ ~/Software/ghc5/inplace/bin/ghc-stage2 Bug.hs -O -fforce-recomp
[1 of 1] Compiling Bug              ( Bug.hs, Bug.o )
ghc-stage2: panic! (the 'impossible' happened)
  (GHC version 8.9.0.20191023:
        emitPrimOp: handled above in cgOpApp

Some notes:

  • The use of optimization -O is critical to triggering the bug.

  • The panic goes away if D is an ordinary data type instead of a data family.

  • The code in the hand-written Ix instance is very similar to the code you would get for a derived Ix instance, but with one key difference: the hand-written code uses getTag instead of a function that does something like this:

    dTag :: D -> Int#
    dTag MkD = 0#

    If you replace all uses of getTag with dTag, then the panic goes away. As a consequence, you won't trigger the panic if you derive the Ix instance.

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