Opened 5 years ago

Last modified 19 months ago

#5463 new bug

SPECIALISE pragmas generated from Template Haskell are ignored

Reported by: NickSmallbone Owned by:
Priority: normal Milestone:
Component: Template Haskell Version: 7.2.1
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: Runtime performance bug Test Case:
Blocked By: Blocking:
Related Tickets: #10047 Differential Rev(s):
Wiki Page:



I have the following program which contains a SPECIALISE pragma:

module Test(threeList) where

{-# NOINLINE three #-}
three :: Monad m => m Int
three = return 3
{-# SPECIALISE three :: [Int] #-}

threeList :: [Int]
threeList = three

The specialisation works and -ddump-simpl gives me the following:

a :: Int
[GblId, Caf=NoCafRefs, Str=DmdType m]
a = I# 3

threeList [InlPrag=NOINLINE] :: [Int]
[GblId, Caf=NoCafRefs, Str=DmdType]
threeList = : @ Int a ([] @ Int)

Suppose now I alter my program so that it uses Template Haskell to generate the SPECIALISE pragma:

{-# LANGUAGE TemplateHaskell #-}
module TestTH(threeList) where

import TH

{-# NOINLINE three #-}
three :: Monad m => m Int
three = return 3
$(specialise 'three)

threeList :: [Int]
threeList = three

{-# LANGUAGE TemplateHaskell #-}
module TH where

import Language.Haskell.TH

specialise :: Name -> DecsQ
specialise x = do
  listInt <- [t| [Int] |]
  return [ PragmaD (SpecialiseP x listInt Nothing) ]

The specialisation should work just as before. However, if I compile with -ddump-splices -ddump-simpl, I see that the correct pragma was spliced in but no specialisation happened and GHC generated icky code:

TestTH.hs:1:1: Splicing declarations
    specialise 'three
    {-# SPECIALIZE three :: [Int] #-}

==================== Tidy Core ====================
lvl :: Int
[GblId, Caf=NoCafRefs, Str=DmdType m]
lvl = I# 3

three [InlPrag=NOINLINE] :: forall (m :: * -> *). Monad m => m Int
[GblId, Arity=1, Caf=NoCafRefs, Str=DmdType U(AASA)]
three =
  \ (@ m::* -> *) ($dMonad :: Monad m) ->
    return @ m $dMonad @ Int lvl

threeList :: [Int]
 Unf=Unf{Src=<vanilla>, TopLvl=True, Arity=0, Value=False,
         ConLike=False, Cheap=False, Expandable=True,
         Guidance=IF_ARGS [] 2 0}]
threeList = three @ [] $fMonad[]

This happens on at least GHC 7.0.4 and 7.2.1.

Change History (8)

comment:1 Changed 5 years ago by simonpj

What is happening is this.

  • GHC compiles all decls down to, but not including the first splice
  • Then it runs the splice
  • Then it combines the result of the splice with all decls following, down to the next splice, and compiles them
  • And so on

In your case, then, the SPECIALISE pragma is getting compiled after the decl to which it refers has been compiled. This isn't expected, and should probably generate an error message, but doesn't. But it turns into a no-op as you found.

A workaround would be to generate the pragma before the defn to which it applies.

$(specialise (mkName "three"))
{-# NOINLINE three #-}
three :: Monad m => m Int
three = return 3

But since that defn would not be in scope when the splice is run, you have to use mkName which is messy.

Alternatively, maybe you can pass the decl to the function thus:

    [d| three :: Monad m => m Int
        three = return 3

In truth, what you originally wrote should really work. But there are so many other things to do. By all means say how important or otherwise this is, and enourage others to do the same.


comment:2 Changed 5 years ago by NickSmallbone

Aha, that explains it! I imagined that GHC, on encountering a splice, would typecheck everything up to that splice but would do all the code generation and so on after expanding the splices.

In reality my specialise function looks at the type of the function it's specialising, so I can't use your workaround. I would like to be able to write

three :: Monad m => m Int
$(specialise 'three)
three = return 3

but GHC of course complains that there is no definition for three, because the splice separates the definition from the type signature.

Anyway, I can easily live without this. It means that I lose some performance in a program I've written, but in my case the generated code is very respectable even without any SPECIALISE pragmas.

comment:3 Changed 5 years ago by igloo

Milestone: 7.6.1

comment:4 Changed 4 years ago by igloo


comment:5 Changed 3 years ago by thoughtpolice


Moving to 7.10.1.

comment:6 Changed 2 years ago by thoughtpolice


Moving to 7.12.1 milestone; if you feel this is an error and should be addressed sooner, please move it back to the 7.10.1 milestone.

comment:7 Changed 19 months ago by thomie

Milestone: 7.12.1
Type of failure: None/UnknownRuntime performance bug

comment:8 Changed 19 months ago by goldfire

Ticket #10047 discusses the annoyance of GHC's current behavior around top-level splices. One possible conclusion to that ticket suggests that a top-level quasiquote would indeed be spliced in before type-checking any declarations. This means both that the SPECIALISE pragma would work but also that the name of the function to specialise would be out of scope. At least under this scenario, the location of the splice wouldn't matter.

I still don't know what to suggest to really fix this, however.

Note: See TracTickets for help on using tickets.