Opened 3 years ago

Last modified 20 months ago

#5463 new bug

SPECIALISE pragmas generated from Template Haskell are ignored

Reported by: NickSmallbone Owned by:
Priority: normal Milestone: 7.6.2
Component: Template Haskell Version: 7.2.1
Keywords: Cc:
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Difficulty:
Test Case: Blocked By:
Blocking: Related Tickets:

Description

Hi,

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
  ======>
    TestTH.hs:9:3-19
    {-# 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]
[GblId,
 Str=DmdType,
 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 (4)

comment:1 Changed 3 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:

$(specialise
    [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.

Simon

comment:2 Changed 3 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 2 years ago by igloo

  • Milestone set to 7.6.1

comment:4 Changed 20 months ago by igloo

  • Milestone changed from 7.6.1 to 7.6.2
Note: See TracTickets for help on using tickets.