Use `CoreM` just for core plugins; `CoreM` stays maximal
Problem
To solve #17957, we refactor GHC again and again, component by component to follow the so-called "Law of Demeter".
The problem is, while regular GHC components do a clear operation with clear requirements, plugins are black boxes supplied by the user of GHC. We don't know what they are doing (and couldn't, because yet-to-be-written plugins form an open world), and thus we cannot pass them just what is needed because that vary notion is ill-defined.
If anything, we should do the opposite for plugins: not knowing what they need we could error on passing them as much state as possible, giving them utmost flexibility.¹
Solution
Given these issues, the prudent option seems to be to not break existing plugins, passing them the same kitchen sink of state as before. Plugins use CoreM
today, and so they continue to use CoreM
tomorrow, and CoreM
itself remains unchanged.
What about #17957? If instead of envisioning the entire Core optimizer as a single component, we instead look at each pass as a component, we get ourselves unstuck.
- The passes that are part of GHC are regular code rather than opaque plugin parameters, and so we can do our normal "Law of Demeter" work. They would not longer use
CoreM
. -
GHC.Core.Opt.Pipeline
contains some core pass logic that should probably be moved to another module, but the main "coordinating/planning" code part of it, which would continue to dispatch to core plugins too, can move underGHC.Driver
, where manipulating the "whole state" is not verboten. -
CoreM
should therefore live inGHC.Plugins
and not in the Core optimizer namespace.
This issue tracks the first untangling step, which is making CoreM
just be used by the core plugins. The remaining work can then proceed per pass in parallel.
Example
The specialization pass in GHC.Core.Opt.Specialise
currently uses two environments:
-
SpecEnv
:data SpecEnv = SE { se_dflags :: DynFlags , ... } ``
- the one stored in
CoreM
:data CoreReader = CoreReader { cr_hsc_env :: HscEnv, ... }
After the refactoring the pass will run in the IO
monad and the parts of the CoreReader
type relevant to this pass will be contained in SpecEnv
.
Because of this, after the refactoring the only remaining users of this monad are Core2core plugins.
1: Full disclosure, in the way long term, @Ericson2314 thinks this is why plugins at all are not the right solution and a modular GHC can offer another way, but we're not trying to push that agenda here nor any time soon.