Plugin installed hooks are largely ignored
Summary
If a compiler plugin installs hooks, for example on compiler phases or the logger action, they do not get called as expected. This bug was introduced in GHC v9.2 in interactive mode but also occurs for make mode in other versions (not sure when the bug was introduced in make mode but it is present as early as 8.10.7).
Steps to reproduce
Write a plugin that modifies the Logger:
plugin :: Plugin
plugin = defaultPlugin
{ driverPlugin = \_ hscEnv -> do
pure hscEnv
{ hsc_logger =
pushLogHook logHook $ hsc_logger hscEnv
}
}
logHook :: LogAction -> LogAction
logHook action dynFlags warnReason sev srcSpan msgDoc =
trace "Log hook called" $! action dynFlags warnReason sev srcSpan msgDoc
When compiling with this plugin, the log hook is invoked for warning messages but not error messages.
For the interactive mode case, I have determined that this occurs because when the plugins are first initialized (in setInteractiveDynFlags
), the loaded plugins are placed in the interactive context but do not end up in the HscEnv
. This results in the plugins not being present at key points surrounding the loading of modules. The fix for this seems simple enough:
setInteractiveDynFlags
in compiler/GHC.hs
:
-- Update both plugins cache and DynFlags in the interactive context.
return $ plugin_env -- use plugin_env here instead of hsc_env0
{ hsc_IC = ic0
{ ic_plugins = hsc_plugins plugin_env
, ic_dflags = hsc_dflags plugin_env
}
}
GHC 9.0.x does not discard the loaded plugins in this way and the bug seems to have been a side effect of moving the loaded plugins from DynFlags
to HscEnv
.
Similarly for phase hooks, the hook is not called at all in GHCi (and only for certain phases in make mode). I suspect the cause is the same as in the logger hook example.
I have not yet investigated why this happens for compilation in make mode (my current focus is plugins intended to be used with GHCi) but I suspect it's because plugins are simply not being initialized early enough.
Expected behavior
The log hook should be invoked for all messages. Phase hooks should be called for all phases.
Environment
- GHC version used: 9.2.x, head