Skip to content

re-enable the defun RULE from a SPECIALISE instance pragma

As of commit 51d89a55c3, SPECIALISE instance pragmas do not result in a RULE for the dictionary function.

For example, consider this Eq instance for List.

module M where

data List a = Nil | Cons a (List a)

instance (Eq a) => Eq (List a) where
    {-# SPECIALISE instance Eq (List Char) #-}
    Nil     == Nil     = True
    (Cons x xs) == (Cons y ys) = x == y && xs == ys
    _xs    == _ys    = False

With ghc-7.4.2, we get:

==================== Tidy Core rules ====================
"SPEC $c/=" [ALWAYS]
    forall ($dEq :: GHC.Classes.Eq GHC.Types.Char).
      M.$fEqList_$c/=1 @ GHC.Types.Char $dEq
      = M.$fEqList_$c/=
"SPEC $c==" [ALWAYS]
    forall ($dEq :: GHC.Classes.Eq GHC.Types.Char).
      M.$fEqList_$c==1 @ GHC.Types.Char $dEq
      = M.$fEqList_$c==
"SPEC M.$fEqList" [ALWAYS]
    forall ($dEq :: GHC.Classes.Eq GHC.Types.Char).
      M.$fEqList @ GHC.Types.Char $dEq
      = M.$fEqList_$fEqList

Note the last rule: it specializes the normal defun M.$fEqList when applied at type Char.

With anything after 7.4.2 -- eg if you download the binary sources for 7.4.2 and make the change at line 779 from that commit --- you instead get this:

==================== Tidy Core rules ====================
"SPEC $c/=" [ALWAYS]
    forall ($dEq :: GHC.Classes.Eq GHC.Types.Char).
      M.$fEqList_$c/=1 @ GHC.Types.Char $dEq
      = M.$fEqList_$c/=
"SPEC $c==" [ALWAYS]
    forall ($dEq :: GHC.Classes.Eq GHC.Types.Char).
      M.$fEqList_$c==1 @ GHC.Types.Char $dEq
      = M.$fEqList_$c==

(Actually, after some patch the /= RULE disappears too, but I don't know which/why.)

If the dictionary is used at the relevant type in the same module, the specializer will automatically create the omitted rule. That will not, however, currently happen across module boundaries.

In my contrived example, omitting this defun specialization increases runtime by a factor of 2 at -O1.

{-# LANGUAGE ExistentialQuantification #-}

module Main where

import M

data Box = forall a. Eq a => Box a a

box = Box (go 10000000) (go 10000000) where
  go :: Int -> List Char
  go 0 = Nil
  go n = Cons 'c' $ go (n - 1)
{-# NOINLINE box #-}

main = print $ case box of
  Box l r -> l == r

(-O2 squashes the runtime difference; I haven't investigated in detail.)

Trac metadata
Trac field Value
Version 7.6.2
Type Bug
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Compiler
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information