goldfirere / th-desugar

Desugars Template Haskell abstract syntax to a simpler format without changing semantics

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Locally reified GADT record selectors have incorrect types

RyanGlScott opened this issue · comments

Run this program:

#!/usr/bin/env cabal
{- cabal:
build-depends: base, pretty-show, template-haskell, th-desugar >= 1.9
-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
module Main where

import Language.Haskell.TH
import Language.Haskell.TH.Desugar
import Text.Show.Pretty

main :: IO ()
main = putStrLn
 $([d| data T :: * -> * where
         MkT :: forall a. { unT :: a } -> T a
     |] >>= \ds -> withLocalDeclarations ds (dsReify (mkName "unT")) >>= stringE . ppShow)

And you'll get this:

$ cabal new-run Bug.hs
Resolving dependencies...
Build profile: -w ghc-8.6.3 -O1

<elided>

Just
  (DVarI
     unT
     (DAppT
        (DAppT DArrowT (DConT T_6989586621679053292))
        (DVarT a_6989586621679053295))
     Nothing)

After some cleanup, that's:

unT :: T -> a

That type is utterly bogus, as it should be:

unT :: forall a. T a -> a

Here's a similar example:

main :: IO ()
main = putStrLn
 $([d| data T b where
         MkT :: forall a. { unT :: a } -> T a
     |] >>= \ds -> withLocalDeclarations ds (dsReify (mkName "unT")) >>= stringE . ppShow)
$ cabal new-run Bug.hs
Resolving dependencies...
Build profile: -w ghc-8.6.3 -O1

<elided>

Just
  (DVarI
     unT
     (DForallT
        [ DPlainTV b_6989586621679053293 ]
        []
        (DAppT
           (DAppT
              DArrowT
              (DAppT
                 (DConT T_6989586621679053290) (DVarT b_6989586621679053293)))
           (DVarT a_6989586621679053294)))
     Nothing)
unT :: forall b. T b -> a

The reason this happens is because of this part of Language.Haskell.TH.Desugar.Reify:

reifyInDec n decs (DataD _ ty_name tvbs _mk cons _)
| Just info <- maybeReifyCon n decs ty_name (map tvbToType tvbs) cons
= Just info

We're grabbing the arguments to the data type constructor from the head of the GADT, but this is a dangerous thing to do, since there might not be enough arguments entirely (as in the data T :: * -> * case) or they may have different names (as in the data T b case).

The plot thickens. This bug also affects GADT constructors without record selectors, as in the following example:

{-# LANGUAGE TemplateHaskell #-}
module Main where

import Language.Haskell.TH
import Language.Haskell.TH.Desugar

main :: IO ()
main = putStrLn
 $([d| data T a where
         MkT :: Int -> T Int
     |] >>= \ds -> withLocalDeclarations ds (reifyWithLocals (mkName "MkT")) >>= stringE . pprint)
Constructor from T_0: MkT :: forall a_1 .
                             GHC.Types.Int -> T_0 GHC.Types.Int

That forall a_1 shouldn't be there.