Draft: Split `Backend` into multiple sum types
(What's the additive version of "factor"? :))
Preliminary step towards #20927 (closed) and #21034
I have wanted to do this for a while, and @nrnrnr's work convinced me I should hurry up :) and do so.
I think enumerating "No backend" with actual backends is not good. We are very far from having any nice notion of an "identity backend", and absent that I cannot think of any other justification for the status quo.
NoBackend
is kept as a pattern synonym so the code doesn't regress in
readability when there are no type annotation nearby.
Note this is a bare minimum refactor; I didn't make much of an attempt to "prove" this was the right direction. But a few low-hanging fruits nevertheless did arise:
-
platformDefaultBackend
is guaranteed to return an actual backend. -
In
GHC.Tc.Gen.Foreign
,checkCOrAsmOrLlvmOrInterp
is dead code, becausecheckCg
allows any foreign declaration withNoBackend
. This makes sense to me: without a choice of the next pipeline stage committed to committed to, who's to say some constructor is outside its domain?checkCg
now takes aActualBackend -> Validity
callback, demonstrating thatNoBackend
is handled separately.
This is enough to make me feel good I am not barking down the wrong tree.
@nrnrnr's !7442 (merged) will end up touching many/most of the same lines that this touches, but I think that is OK.
I am all for, downstream of DynFlags
, trying to get rid of anything
looking at the Backend
datatype, because we should support arbitrary
backends where are free to very those knobs however they like, fully
independently.
However, I don't thinking folding in NoBackend
to our more "semantic"
representation of a backend (the new record) will make sense.
Conversely, I think the more we try to better structure what a backend
is/does, the more NoBackend
would stick out like a sore thumb, ruining
our abstractions.
If we do this before !7442
, we have the opportunity to use Maybe SemanticBackend
downstream to side-step those issues, but even that I
think will be somewhat temporary. As we continue to purge DynFlags
from the code base, I think we will increasingly separate code that
needs an actual backend from code that is agnostic. And the code that
agnostic I don't think should get a Maybe SemanticBackend
, but rather
expose the knobs it would infer from the backend directly.
Why? This is the same argument as checkCg
: if you haven't chosen a
backend yet, who is to say some choices are invalid? Not the
non-existent backend! Conversely if we a backend requires a certain
choice made "upstream" in order for it to work, that that code should go
with the backend, not the upstream component. This preserves the
separation of concerns, and allows arbitrary backends to have arbitrary
policies.