Specialization failure for newtype containing class constraints in RHS.
Summary
For this sort of program:
newtype Foo a = Foo { unFoo :: (Num a, Integral a) => a -> Int }
foo :: Foo a
foo = Foo (\x -> fromIntegral x)
{-# SPECIALIZE foo :: Foo Int #-}
g x = (unFoo foo) x :: Int
GHC fails it's users in multiple ways:
- It warns about a specialization pragma for a non-overloaded function.
- It still generates a specialization rule for
foo
. - The generated specializations still uses overloaded functions.
Steps to reproduce
Compile the above program with -O
Expected behavior
We get a warning, that I wouldn't expect to get:
test.hs:27:1: warning: [GHC-35827]
SPECIALISE pragma for non-overloaded function `foo'
|
27 | {-# SPECIALIZE foo :: Foo Int #-}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This presumable happens because the warning doesn't look through the newtype and therefore the dictionary argument is hidden from whatever check we do.
After getting the warning I was expecting the specialization to just fail. However we do generate a rule:
------ Local rules for imported ids --------
"USPEC foo @Int"
forall.
foo @Int
= M.foo1
`cast` (Sym (M.N:Foo[0] <Int>_N)
:: ((Num Int, Integral Int) => Int -> Int) ~R# Foo Int)
It's a pleasant surprise. But I would have expected GHC to not generate any specialization rule given the warning it emits.
Last but not least, the specialization we generate is fairly useless:
-- RHS size: {terms: 11, types: 7, coercions: 0, joins: 0/0}
M.foo1 :: (Num Int, Integral Int) => Int -> Int
[GblId,
Arity=3,
Str=<A><1P(A,A,A,A,A,A,A,A,1C(1,L))><L>,
Cpr=1,
Unf=Unf{Src=StableSystem, TopLvl=True,
Value=True, ConLike=True, WorkFree=True, Expandable=True,
Guidance=ALWAYS_IF(arity=3,unsat_ok=True,boring_ok=False)
Tmpl= \ _ [Occ=Dead]
($dIntegral_aFq [Occ=Once1] :: Integral Int)
(x_aEi [Occ=Once1] :: Int) ->
case GHC.Num.Integer.integerToInt#
(toInteger @Int $dIntegral_aFq x_aEi)
of ds_aXI [Occ=Once1]
{ __DEFAULT ->
GHC.Types.I# ds_aXI
}}]
M.foo1
= \ _ [Occ=Dead] ($dIntegral_aFq :: Integral Int) (x_aEi :: Int) ->
case GHC.Num.Integer.integerToInt#
(toInteger @Int $dIntegral_aFq x_aEi)
of ds_aXI
{ __DEFAULT ->
GHC.Types.I# ds_aXI
}
Note that it still takes two dictionary arguments, and calls their class methods. Instead of using the presumably known Integral Int
dictionary which would allow it to optimize well.
Environment
- GHC version used: A recentish version of the master branch as well as 9.4.7