Support for close combinator for indexed effects #2988
Merged
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 adds support for a close combinator for indexed effects.
Why
With some profiling of F* performance on the Pulse checker, we found that F* was creating huge VCs for relatively small
TAC
functions, resulting in high memory usage and typechecking time.Adding general support for close combinators for indexed effects, and defining one for
TAC
, significantly improves the memory consumption and time taken by F* on Pulse checker---upto ~9x memory and ~19x time in some cases.What does close mean in the context?
Consider typechecking a
match
expression in F*:where
x
occurs free ine
.To typecheck the branch, F* opens
e
withx
and computes a computation typeM a is
for it, whereis
are the effect indices. To compose the computation type with the rest of the function, F* has to ensure thatx
does not escape ... i.e. weakenM a is
so thatx
does not occur free in the weakened type.For the result type
a
, F* ensures this by just checking thatx
is not free ina
, if it is, F* gives an error.For the effect indices
is
, F* uses a closing mechanism.Closing in wp effects
For primitive wp effects, the effect definition defines a close combinator. For example, for the
PURE
effect, the combinator is defined as follows:When the branch has a primitive wp effect, F* uses such an associated close combinator to close the branch.
Closing in indexed effects
For indexed effects, so far we were following a different approach. Instead of the effect defining a close combinator, we used projector functions applied to the scrutinee to substitute the pattern bound variables.
In the
match
expression above, for example, in the indicesis
we substitutex
byC? e
.This design was chosen in part to ensure that effect writers provide as few combinators as possible to get the effect going.
The problem
The following code in F*
is simply a sugar for:
The Pulse checker code is heavily using dependent tuples and pattern matching like this.
When closing
x
,y
, andz
in this example, F* substitutesx
, for example, byMkTuple3?._1 r
. But this is not the whole story: it is actuallyMkTuple3?._1 #t1 #t2 #t3 r
, wheret1
,t2
, andt3
are implicits for the type parameters. For dependent tuples, these implicits are lambda terms (for t2 and t3).On inspecting the VCs, we found that the bulk of the VC was these projector expressions, with implicits repeated everywhere. This results in huge VCs, high memory consumption, and high cost of passes over the VC.
Support for close combinators for indexed effects
To get around this problem, the PR adds support for custom close combinators for indexed effects, and defines one for
TAC
:So now the
MkTuple3
example above would result in a VC likeforall (x:t1) (y:t2) (z:t3). MkTuple3 #t1 #t2 #t3 x y z == r ==> ...
, and thus no duplication in the rest of the VC.Impact on the Pulse checker
For 3 of the slowest files, memory consumption and time taken (in MB and seconds resp.):
The linear regression slope comparing times 0.27 and memory consumption 0.13.
(Using the runlim scripts and with admit smt queries.)
Mechanics of the close combinator
The wiki page https://github.com/FStarLang/FStar/wiki/Indexed-effects describes how close combinators can be defined.
Combinator shape
The shape of a close combinator looks like:
and the body of the combinator is of the form
repr a js
, wherejs
are the closed indices.Combinator soundness
F* checks the soundness of the combinator by checking that:
I.e.,
forall (x:b)
, the indicesjs
are a weakening ofis x
, where the weakening is defined by the effect subcomp combinator.Applying the combinator
This is a small tweak in existing code for typechecking
match
. When the branch effect is a primitive wp effect, or an indexed effect with custom close combinator, then we use the combinator to close, else fallback to the substitution-based closing.