On custom error handlers for the GHC API

Intro

It’s hard to get into writing code that uses GHC API. It’s huge there are so many options around and not a lot of introduction-level tutorials.

In this series of blog posts I’ll elaborate on some of the peculiar, interesting problems I’ve encountered during my experience writing code that uses GHC API and also provide various tips I find useful.

I have built for myself a small layer of helper functions that helped me with using GHC API for the interactive-diagrams project. The source can be found on GitHub and I plan on refactoring the code and releasing it separately.

Error handling

Today I would like to talk about setting your own error handlers for GHC API. By default you can expect GHC to spew all the errors onto your screen, but for my purposes I wanted to log them.

Naturally at first I tried the following:

I am in need of setting up custom exception handlers when using GHC API to compile modules. Right now I have the following piece of code:

-- Main.hs:
import GHC
import GHC.Paths
import MonadUtils
import Exception
import Panic
import Unsafe.Coerce
import System.IO.Unsafe

-- I thought this code would handle the exception
handleException :: (ExceptionMonad m, MonadIO m)
                   => m a -> m (Either String a)
handleException m =
  ghandle (\(ex :: SomeException) -> return (Left (show ex))) $
  handleGhcException (\ge -> return (Left (showGhcException ge ""))) $
  flip gfinally (liftIO restoreHandlers) $
  m >>= return . Right

-- Initializations, needed if you want to compile code on the fly
initGhc :: Ghc ()
initGhc = do
  dfs <- getSessionDynFlags
  setSessionDynFlags $ dfs { hscTarget = HscInterpreted
                           , ghcLink = LinkInMemory }
  return ()

-- main entry point
main = test >>= print

test :: IO (Either String Int)
test = handleException $ runGhc (Just libdir) $ do
  initGhc
  setTargets =<< sequence [ guessTarget "file1.hs" Nothing ]
  graph <- depanal [] False
  loaded <- load LoadAllTargets
  -- when (failed loaded) $ throw LoadingException
  setContext (map (IIModule . moduleName . ms_mod) graph)
  let expr = "run"
  res <- unsafePerformIO . unsafeCoerce <$> compileExpr expr
  return res

-- file1.hs:
module Main where

main = return ()

run :: IO Int
run = do
  n <- x
  return (n+1)

The problem is when I run the ‘test’ function above I receive the following output:

h> test

test/file1.hs:4:10: Not in scope: `x'

Left "Cannot add module Main to context: not a home module"
it :: Either String Int

What the ..? My exception handler did catch the error, but:

  1. A strange one
  2. The error I intended to catch got

Is there a way to fix this?

Solution

I even asked this problem on the Haskell-Cafe mailing list, but the folks there don’t seem to be very keen on GHC/GHC API (which is understandable) and I haven’t got any answers.

But thanks to my mentor Luite Stegeman we’ve found the solution.

Errors are handled using the LogAction specified in the DynFlags for your GHC session. So to fix this you need to change ‘log_action’ parameter in dynFlags. For example, you can do this:

initGhc = do
  ..
  ref <- liftIO $ newIORef ""
  dfs <- getSessionDynFlags
  setSessionDynFlags $ dfs { hscTarget  = HscInterpreted
                           , ghcLink    = LinkInMemory
                           , log_action = logHandler ref -- ^ this
                           }

-- LogAction == DynFlags -> Severity -> SrcSpan -> PprStyle -> MsgDoc -> IO ()
logHandler :: IORef String -> LogAction
logHandler ref dflags severity srcSpan style msg =
  case severity of
     SevError ->  modifyIORef' ref (++ printDoc)
     SevFatal ->  modifyIORef' ref (++ printDoc)
     _        ->  return () -- ignore the rest
  where cntx = initSDocContext dflags style
        locMsg = mkLocMessage severity srcSpan msg
        printDoc = show (runSDoc locMsg cntx)

Outro

That’s the first tip and the first post in the series, stay tuned for more updates.

About these ads

One thought on “On custom error handlers for the GHC API

  1. Pingback: GHC API: Interpreted, compiled and package modules | (parentheses)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s