Skip to content

Silent failure with `reifyInstances` in a TH block if class not in scope

Summary

This is a bit tricky. The code in question uses the discover-instances library.

Consider the following code:

module Class where

    import DiscoverInstances

    import Language.Haskell.TH

    class C a where
        c :: proxy a -> Int

    blah :: [SomeDict C] -> IO ()
    blah dicts = putStrLn "hello world"

In "real code", the blah function will do something real and useful with the [SomeDict C] parameter. But in this example, we're just using it to drive type inference.

module Foo where

    import Class

    data Foo = Foo
    
    instance Class Foo where c _ = 1

{-# LANGUAGE TemplateHaskell #-}
module Lib where

    import Foo
    import Class (blah)
    import DiscoverInstances
    import Language.Haskell.TH

    $(do
        runIO $ blah $$discoverInstances
        pure []  
     )

When compiling these modules, GHC dies without any warning at all. I've tested this on GHC 8.10 and GHC 9.0, but constraints fails to build on GHC 9.2 so I don't have an easy reproduction on that.

This is the output when the offending code is run in TemplateHaskell:

λ ~/Projects/disc-inst-bug/ cabal build
Build profile: -w ghc-9.0.1 -O1
In order, the following will be built (use -v for more details):
 - disc-inst-bug-0.1.0.0 (lib) (first run)
 - disc-inst-bug-0.1.0.0 (exe:disc-inst-bug-exe) (first run)
Preprocessing library for disc-inst-bug-0.1.0.0..
Building library for disc-inst-bug-0.1.0.0..
[3 of 4] Compiling Lib              ( src/Lib.hs, /home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build/Lib.o, /home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build/Lib.dyn_o )
cabal: Failed to build disc-inst-bug-0.1.0.0 (which is required by
exe:disc-inst-bug-exe from disc-inst-bug-0.1.0.0).

Compiling with --verbose doesn't provide more useful information.

λ ~/Projects/disc-inst-bug/ master* cabal build --verbose
this build was affected by the following (project) config files:
Build profile: -w ghc-9.0.1 -O1
In order, the following will be built:
 - disc-inst-bug-0.1.0.0 (lib) (first run)
 - disc-inst-bug-0.1.0.0 (exe:disc-inst-bug-exe) (first run)
creating /home/matt/Projects/disc-inst-bug/dist-newstyle/build
creating /home/matt/Projects/disc-inst-bug/dist-newstyle/tmp
creating
/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0
creating
/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/cache
Using self-exec internal setup method with build-type Simple and args:
["act-as-setup","--build-type=Simple","--","build","--verbose=2","--builddir=/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0"]
/home/matt/.ghcup/bin/cabal-3.0.0.0 act-as-setup --build-type=Simple -- build
--verbose=2
--builddir=/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0
Component build order: library
/home/matt/.ghcup/bin/ghc-pkg init /home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/package.conf.inplace
creating
/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build
creating
/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build/autogen
creating
/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build/autogen
Preprocessing library for disc-inst-bug-0.1.0.0..
Building library for disc-inst-bug-0.1.0.0..
creating
/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build
/home/matt/.ghcup/bin/ghc --make -fbuilding-cabal-package -O -static -dynamic-too -dynosuf dyn_o -dynhisuf dyn_hi -outputdir /home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build -odir /home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build -hidir /home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build -stubdir /home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build -i -i/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build -isrc -i/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build/autogen -i/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build/global-autogen -I/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build/autogen -I/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build/global-autogen -I/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build -optP-include -optP/home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build/autogen/cabal_macros.h -this-unit-id disc-inst-bug-0.1.0.0-inplace -hide-all-packages -Wmissing-home-modules -no-user-package-db -package-db /home/matt/.cabal/store/ghc-9.0.1/package.db -package-db /home/matt/Projects/disc-inst-bug/dist-newstyle/packagedb/ghc-9.0.1 -package-db /home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/package.conf.inplace -package-id base-4.15.0.0 -package-id discover-instances-0.1.0.0-4126b33401c84f8b3e0134b159298025056eca1769e16abd71aa53048325d16d -package-id template-haskell-2.17.0.0 -XHaskell2010 Class Instance Lib Paths_disc_inst_bug -hide-all-packages
[3 of 4] Compiling Lib              ( src/Lib.hs, /home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build/Lib.o, /home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build/Lib.dyn_o )
CallStack (from HasCallStack):
  die', called at ./Distribution/Client/ProjectOrchestration.hs:1035:55 in main:Distribution.Client.ProjectOrchestration
cabal: Failed to build disc-inst-bug-0.1.0.0-inplace.
Failed to build disc-inst-bug-0.1.0.0 because it depends on
disc-inst-bug-0.1.0.0 which itself failed to build.

If you write blah $$discoverInstances outside of a TemplateHaskell splice, then you get an error message.

{-# LANGUAGE TemplateHaskell #-}
module Lib where

    import Foo
    import Class (blah)
    import DiscoverInstances
    import Language.Haskell.TH

    someFunc :: IO ()
    someFunc = 
       blah $$discoverInstances

This fails with:

[3 of 4] Compiling Lib              ( src/Lib.hs, /home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build/Lib.o, /home/matt/Projects/disc-inst-bug/dist-newstyle/build/x86_64-linux/ghc-9.0.1/disc-inst-bug-0.1.0.0/build/Lib.dyn_o )

src/Lib.hs:12:22: error:
    • Not in scope: type constructor or class ‘C’
      Perhaps you want to add ‘C’ to the import list in the import of
      ‘Class’ (src/Lib.hs:7:1-29).
    • In the argument of reifyInstances: C a
      In the Template Haskell splice $$discoverInstances
      In the first argument of ‘printInstances’, namely
        ‘$$discoverInstances’
   |
12 |     printInstances $$discoverInstances
   |                      ^^^^^^^^^^^^^^^^^
cabal: Failed to build disc-inst-bug-0.1.0.0 (which is required by
exe:disc-inst-bug-exe from disc-inst-bug-0.1.0.0).

So, what appears to be happening here:

  1. I have discoverInstances :: forall (c :: _ -> Constraint). Q (TExp [SomeDict c])
  2. I have blah :: [SomeDict C] -> IO ()
  3. So GHC can infer that blah $$discoverInstances is supposed to select c ~ C, and then $$(discoverInstances :: Q (TExp [SomeDict C])).
  4. But then GHC complains that C is not in scope at the call to reifyInstances
  5. And somehow this error is swallowed by TemplateHaskell.

Steps to reproduce

This repository contains everything you need to reproduce the issue.

Expected behavior

I expect the error message to be present even when the code snippet is used in Template Haskell.

Environment

  • GHC version used: 8.10, 9.0

Optional:

  • Operating System: Ubuntu
  • System Architecture: x86
Edited by parsonsmatt
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information