LiquidHaskell: Plugin Overview

LiquidHaskell runs as a GHC plugin, meaning it will hook into GHC in order to perform its magic at specific stages during the compilation.

Therefore, I start my analysis at the plugin definition in the liquidhaskell-boot/src/Language/Haskell/Liquid/GHC/Plugin.hs file.

plugin :: GHC.Plugin
plugin = GHC.defaultPlugin {
    driverPlugin          = lhDynFlags
  , parsedResultAction    = parsePlugin
  , typeCheckResultAction = typecheckPlugin
  , pluginRecompile       = purePlugin
  }
  • Field 1: the line driverPlugin = lhDynFlags is simply setting up some configurations before the compilation process.
  • Field 2: parsedResultAction = parsePlugin, it is signaling the compiler which function will be in charge of extracting LH specs. Those specs are stored in an IORef, which is just an (unsafe) mutable variable in memory. This function is not parsing the content of the specs themselves, but only gathering the source code containing the specs.
  • Field 3: typeCheckResultAction = typecheckPlugin, here is where the interesting stuff happens. This is where LH hooks into the compiler to parse the content of the specs and check liquid types.
  • Field 4: pluginRecompile = purePlugin indicates that the operations performed by the plugin are pure, meaning, on same input produces same input, regardless of global state.

Closing in on the error

By inspecting the flow of the code (and a huge number of debugLogs) I was able to identify the piece of code in the Plugin module that is returning an error:

let localVars = Resolve.makeLocalVars preNormalizedCore
    eBareSpec = resolveLHNames moduleCfg thisModule localVars
                               (imp_mods $ tcg_imports tcg)
                               (tcg_rdr_env tcg) bareSpec0
                               dependencies

According to its documentation, this function is supposed to resolve the names used within LH specs. It makes sense that this is failing, as it sounds somewhat related to the problem I’m trying to solve.

This finding takes me to the LHNameResolution module, I will continue my exploration there.

Useful references