Type-class specialiser fails to specialise a pretty simple program
Consider this code:
wombat :: Show b => Int -> b -> String
wombat a b | a>0 = wombat (a-1) b
| otherwise = show a ++ wombat a b
class C a where
meth :: Show b => a -> b -> String
dummy :: a -> () -- Force a datatype dictionary representation
instance C Int where
meth = wombat
dummy _ = ()
f :: (C a, Show b) => a -> b -> String
{-# INLINABLE[0] f #-}
f a b = meth a b ++ "!"
main = putStrLn (f (42::Int) (True::Bool))
The call in main
generates a specialised version of f
; the
specialised code contains a call (meth $dCInt). We must do the method
selection so that we can in turn specialise meth
, and thence wombat
.
This commit did the job:
commit 4d2ee313f23a4454d12c9f94ff132f078dd64d31
Author: Sebastian Graf <sebastian.graf@kit.edu>
Date: Thu Apr 7 17:21:08 2022 +0200
Specialising through specialised method calls (#19644)
by adding rewriteClassOps
, which rewrites (meth $dCInt)
to wombat
.
But ALAS it all falls apart if we have (a) two calls to meth
and
(b) the dictionary is let-bound, not an argument. Here:
class C a => D a where
op :: a -> a
instance D Int where
op x = x
f :: (D a, Show b) => a -> b -> String
{-# INLINABLE[0] f #-}
f a b = meth a b ++ "!" ++ meth a b
Now f
turns into:
f @a @b (dd :: D a) (ds :: Show b) a b
= let dc :: D a = %p1 dd -- Superclass selection
in meth @a dc ....
meth @a dc ....
Now, for rewriteClassOpts
to fire on (meth dc)
, when dd
is instantiated
to $dDInt :: D Int
we must:
-
Fire the superclass selector
%p1 dd
(this is just another use ofrewriteClassOps
) to simplify it to$fCInt
. -
Make
dc
's new binding to$fCInt
visible to(meth dc)
.
Currently we don't succeed in specialising meth
or wombat
. Boo!