Skip to content

Draft: Split `Backend` into multiple sum types

John Ericson requested to merge wip/maybe-backend into master

(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, because checkCg allows any foreign declaration with NoBackend. 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 a ActualBackend -> Validity callback, demonstrating that NoBackend 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.

Edited by John Ericson

Merge request reports