Opened 3 years ago

Closed 3 years ago

#13642 closed bug (fixed)

GHCi 8.2 simply ignores TH splice using datatype with a forall'd kind signature

Reported by: RyanGlScott Owned by:
Priority: normal Milestone: 8.2.1
Component: Template Haskell Version: 8.2.1-rc1
Keywords: Cc: goldfire
Operating System: Unknown/Multiple Architecture: Unknown/Multiple
Type of failure: None/Unknown Test Case: th/T13642.hs
Blocked By: Blocking:
Related Tickets: Differential Rev(s):
Wiki Page:

Description

This is a pretty bizarre behavior I've noticed recently that only happens in GHC 8.2 or later. If you try to use a Template Haskell splice with a very particular feature (a datatype whose kind uses forall) in GHCi, then GHCi will flat-out ignore it!

$ /opt/ghc/8.2.1/bin/ghci
GHCi, version 8.2.0.20170427: http://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from /home/rgscott/.ghci
λ> :set -XGADTs -XTypeInType -XTemplateHaskell -XRankNTypes 
λ> import Language.Haskell.TH (stringE, pprint)
λ> import Data.Kind (Type)
λ> $([d| data Foo :: forall a. a -> Type where MkFoo :: Foo Int |] >>= stringE . pprint)
λ> print (5 + length $([d| data Foo :: forall a. a -> Type where MkFoo :: Foo Int |] >>= stringE . pprint))
λ> 5
5
λ> it
5
λ> $([d| data Foo :: forall a. a -> Type where MkFoo :: Foo Int |] >>= stringE . pprint)
λ> it
5

Notice how none of my attempts to use the splice seemed to register with GHCi.

This isn't really a regression per se, since GHC 8.0.1 nor 8.0.2 even allowed you to get that far:

$ /opt/ghc/8.0.2/bin/ghci
GHCi, version 8.0.2: http://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from /home/rgscott/.ghci
λ> :set -XGADTs -XTypeInType -XTemplateHaskell -XRankNTypes 
λ> import Language.Haskell.TH (stringE, pprint)
λ> import Data.Kind (Type)
λ> $([d| data Foo :: forall a. a -> Type where MkFoo :: Foo Int |] >>= stringE . pprint)

<interactive>:4:3: error:
    Exotic form of kind not (yet) handled by Template Haskell
      forall a. a -> Type

Change History (7)

comment:1 Changed 3 years ago by RyanGlScott

Actually, it extends even beyond GHCi! If you put this into a module:

{-# LANGUAGE GADTs, TypeInType, TemplateHaskell, RankNTypes #-}
module Bug where

import Data.Kind (Type)
import Language.Haskell.TH (stringE, pprint)

main :: IO ()
main = putStrLn $([d| data Foo :: forall a. a -> Type where MkFoo :: Foo Int |] >>= string
E . pprint)

Then some interesting things happen if you try to compile this. If you try to load it into GHCi, you get this:

$ /opt/ghc/8.2.1/bin/ghci Bug.hs
GHCi, version 8.2.0.20170427: http://www.haskell.org/ghc/  :? for help
Loaded GHCi configuration from /home/rgscott/.ghci
[1 of 1] Compiling Bug              ( Bug.hs, interpreted )
Failed, modules loaded: none.

Apparently the module fails to compile, despite the fact that no errors were emitted during compilation. Something similar happens if you directly invoke ghc on it:

$ /opt/ghc/8.2.1/bin/ghc Bug.hs
[1 of 1] Compiling Bug              ( Bug.hs, Bug.o )
$ echo $?
1

Again, no errors are emitted, but compilation definitely fails, since no .hi or .o files are emitted, and you get an error return code of 1.

comment:2 Changed 3 years ago by RyanGlScott

Huh, the culprit is commit ae6e63aa858d663952b67cc9969fd14782d307bb (Fix #12709 by not building bad applications).

comment:3 Changed 3 years ago by RyanGlScott

Cc: goldfire added

After some digging, the function responsible for suppressing the error message appears to be dsWhenNoErrs:

dsWhenNoErrs :: DsM a -> (a -> CoreExpr) -> DsM CoreExpr
dsWhenNoErrs thing_inside mk_expr
  = do { (result, no_errs) <- askNoErrsDs thing_inside
       ; return $ if no_errs
                  then mk_expr result
                  else unitExpr }

For whatever reason, askNoErrsDs does not appear to be propagating the error message correctly.

There's also similar issues with splicing in any other features which Template Haskell doesn't support. For example, you could just as well use [d| id x = x {-# SCC id #-} |] in the example above.

goldfire, do you have an idea of what's going on here?

comment:4 Changed 3 years ago by simonpj

I'm on this.

comment:5 Changed 3 years ago by Simon Peyton Jones <simonpj@…>

In e770197/ghc:

Deal with exceptions in dsWhenNoErrs

Gracious me.  Ever since this patch

  commit 374457809de343f409fbeea0a885877947a133a2
  Author: Jan Stolarek <jan.stolarek@p.lodz.pl>
  Date:   Fri Jul 11 13:54:45 2014 +0200

      Injective type families

TcRnMonad.askNoErrs has been wrong. It looked like this

   askNoErrs :: TcRn a -> TcRn (a, Bool)
   askNoErrs m
    = do { errs_var <- newTcRef emptyMessages
         ; res  <- setErrsVar errs_var m
         ; (warns, errs) <- readTcRef errs_var
         ; addMessages (warns, errs)
         ; return (res, isEmptyBag errs) }

The trouble comes if 'm' throws an exception in the TcRn monad.
Then 'errs_var is never read, so any errors are simply lost.

This mistake was then propgated into DsMonad.dsWhenNoErrs, where
it gave rise to Trac #13642.

Thank to Ryan for narrowing it down so sharply.

I did some refactoring, as usual.

comment:6 Changed 3 years ago by simonpj

Status: newmerge
Test Case: th/T13642.hs

Good stuff. Fixed.

comment:7 Changed 3 years ago by bgamari

Milestone: 8.2.1
Resolution: fixed
Status: mergeclosed
Note: See TracTickets for help on using tickets.