Denotation of open cfgs in presence of function calls #166
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR describes the ongoing work to denote open control flow graphs, such as in the
asm
language used in the tutorial, in presence of functional calls and return statements. The motivation for this PR comes from Vellvm.Currently, we only extend
asm
with return statements, aBret
case in theBranch
data-type, so that we do not have to bother with call-stacks. We quickly describe why this extension is already problematic.A natural denotation for
Bret r
is to return the value stored in the registerr
. However, this impacts the type of the denotation of a block: fromitree E label
, we now may return a value, i.e. we now work with anitree E (label + value)
.This simple modification however completely jeopardize the denotation of open programs: by relying on a
loop
operator over thektree
category, we rely on the uniformity of the entry and return types. Intuitively, the issue is that returning acts as an exception, so that the loop combinator do not know what to do when fed such a value.The natural answer is naturally "since it acts as an effect, it should be an event"! However, this cannot work either. Indeed, if the event is simply parametized by the register and returning a value, then our type has not changes. To avoid carrying a value, it would have to be parameterized by the continuation, i.e. the continuation at call-site, which would require some higher-order trickery that
itrees
do not support.We therefore are exploring a more blasphemous path: stranding away from the beautiful land of the freer monad and denote (before interpretation!) our programs directly into
eitherT value (itree E)
. By doing so, the exception behavior is internalize by theeitherT
monad transformers, and we recover the right type for our loop combinator.As far as denotation is concerned, it is fairly straightforward: it only requires a slight generalization of the finite type manipulated, as well as of course lifting the Kleisli structure via the
EitherT
transformer (seeBasics/MonadEither.v
).Things get a bit more troublesome from there. We now need a new interpreter, and as far as I can see it cannot be simply expressed in terms of the current
interp
. The fileInterEither
therefore define two such, where the hesitation comes from whether the handlers themselves should live in theeither
monad.This path will likely lead to the duplication of all the code related to interpreters. Alternatively, we could try defining a type class of "Interpretable" monads in order to factor things out.
If anyone sees a simpler way to tackle this issue, suggestions are welcome :)