Flattening calls whose callee is spelled out #
Flatten.flattenProcedureCalls rewrites every call of a statement whose callee is written out (a
proc literal) into the callee's body, and proves the result denotationally equivalent to the
input (StmtWithHoles.EquivInLens) along a lens that renames the locals.
Flatten.flattenProcedure does the same to a procedure, with a proof that its
procedureDenotation is unchanged.
Flatten.unfoldProcedure and Flatten.inlineProcedure first evaluate a callee named through
modules, so that it can be flattened.
One call is flattened by renaming the locals: the caller's keep their names (Flatten.trafo,
renaming by Flatten.escape, the identity on every name not starting with @), the callee's
frame moves to names the caller cannot have (Flatten.emb, renaming n to @.n by
Flatten.tag), and the call becomes
reset_vars stringsStartingWith "@."; -- the callee's frame back to its initial values
«@.z₁», …, «@.zₙ» <- (args); -- the arguments, in the callee's parameter slots
‹callee body, renamed›
x <- ‹callee return value, renamed›;
The two renamings have disjoint images, which is what makes the callee's frame disjoint from the
caller's without looking at either. Readable names come last, in a pass of their own
(Flatten.renameNames): a permutation of names (Flatten.rename ren, «@.w» ↦ w0), which is
sound for any choice of names.
What a reset puts back is given by a set of names, which every later round and the cleanup
renaming map along with the rest. The cleaning keeps such a set in a normal form,
{"z", "w0"} ∪ stringsStartingWith "@@." \ {"@@.z", "@@.w"} say (section Reset regions, and
Flatten.cleanNameSet?, the only meta code that knows that form).
Names #
In a flattening round the caller's names go through escape, the callee's through tag; the two
have disjoint images (escape_ne_tag), and escape is the identity on every name that does not
start with @. The readable names come from a separate permutation, swaps ren.
The caller's names: @… ↦ @@…, every other name unchanged.
Instances For
The callee's names: n ↦ @.n.
Equations
- GaudisCrypt.Flatten.tag n = String.ofList ('@' :: '.' :: n.toList)
Instances For
The cleanup renaming: for each (a, f), the names a and f swap places.
Equations
- GaudisCrypt.Flatten.swaps [] = 1
- GaudisCrypt.Flatten.swaps ((a, f) :: ren) = Equiv.swap a f * GaudisCrypt.Flatten.swaps ren
Instances For
The name n after the cleanup renaming ren.
Equations
- GaudisCrypt.Flatten.renameName ren n = (GaudisCrypt.Flatten.swaps ren) n
Instances For
Renaming the locals of a program state #
Writing through one embed is invisible through another whose image is disjoint.
Reset regions #
The names a flattened call resets start out as all of the callee's (Set.univ, seen through
emb), and every later round and the cleanup renaming map them along with the rest. The
cleaning keeps such a set in the normal form nf p add rem, that is
{add…} ∪ stringsStartingWith p \ {rem…}: the names that start with p, except those in
rem, and those in add. The three name maps keep that form: tag and escape move the
prefix (@., @@., …), and each swap of the cleanup renaming moves at most one name out of the
set and one into it.
This section and cleanNameSet? (which computes the normal form, with these lemmas as proofs)
are all there is to the form; another one replaces both.
The names in l. Never printed: the cleaning writes it as a set literal (ofList_cons,
ofList_singleton).
Instances For
The normal form of a reset region: the names that start with p and are not in rem, and
those in add. Never printed: the cleaning writes it out without its empty parts
(nf_eq_pretty and the rest).
Equations
Instances For
nf p add rem written out, given the set literals A, R of add, rem.
The image of a normal form under an injective name map that maps the names with prefix p
onto those with prefix p'.
The steps of cleanNameSet?: each takes the region in normal form and a check that the
meta code proves by evaluation (rfl).
The slot of a variable whose name is given as another term for the same string.
A lens on the locals, as a lens on the program state that keeps the globals.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Cleaning: a local variable seen through a renaming of the locals is the renamed variable.
(The content type is spelled T rather than (VariableName.mk n T …).type: simp reduces the
latter in the term before it looks for lemmas, so a lemma indexed by it would never be found.)
A renaming of the locals leaves the entry state of a procedure alone when it keeps the names
of the parameters: the parameters are where they were, and every other slot is init, whatever
it is called.
Cleaning: a reset seen through a renaming of the locals resets the renamed names.
Resetting every variable.
The caller's side of a flattening round: its locals, renamed by escape (which keeps every
name that does not start with @).
Equations
Instances For
The cleanup renaming ren, on the locals: a permutation of the names, hence an
isomorphism.
Equations
Instances For
Writing the callee's frame is invisible to the caller, except for the globals.
The reset a flattened call starts with: every variable of the callee, seen through emb,
which is the callee's frame.
Cleaning: a caller's local variable, seen through trafo, is the escaped variable.
Cleaning: a callee's local variable, seen through emb, is the tagged variable.
Cleaning: a local variable, seen through the cleanup renaming, is the renamed variable.
The composite lenses stepAllCalls and withRenaming build (rename ren or trafo in front
of the earlier rounds), reassociated so the variable lemmas above meet one renaming at a time.
Stated per head rather than as Lens.chain_assoc, so that cleaning reassociates the inliner's own
lenses only.
A read at a state seen through one of the renamings, as a read through the composite: the
plain-get counterparts of eval_chain_lens, for conditions whose reads were already turned
into gets (hoareInline).
emb_chain_varLens, with the new name given: what the meta code instantiates, proving the
name by evaluation.
A lens composed with a pair is the pair of the composites — stated with the composites
given up to an equation (and any Disjoint instance for them), so that the cleaning pass can
rewrite the components and re-synthesize the instance (Flatten.reduceChainPair).
Re-targeting a statement along a lens #
Re-target a statement along a lens between program states. The call' case does not
recurse into the callee: its body and return value run in the callee's own frame, which the lens
does not touch.
Equations
- One or more equations did not get rendered due to their size.
- GaudisCrypt.StmtWithHoles.applyLens l (GaudiProg[ skip; ]) = GaudiProg[ skip; ]
- GaudisCrypt.StmtWithHoles.applyLens l (GaudiProg[ x_1 <$ §e; ]) = GaudiProg[ (l.chainSetter x_1) <$ §(l.chainGetter e); ]
- GaudisCrypt.StmtWithHoles.applyLens l (GaudisCrypt.StmtWithHoles.call' x_1 ns hl hn b r p) = GaudisCrypt.StmtWithHoles.call' (l.chainSetter x_1) ns hl hn b r (l.chainGetter p)
- GaudisCrypt.StmtWithHoles.applyLens l (GaudiProg[ x_1 <- holecall n (§p); ]) = GaudiProg[ (l.chainSetter x_1) <- holecall n (§(l.chainGetter p)); ]
- GaudisCrypt.StmtWithHoles.applyLens l (s₁.seq s₂) = (GaudisCrypt.StmtWithHoles.applyLens l s₁).seq (GaudisCrypt.StmtWithHoles.applyLens l s₂)
- GaudisCrypt.StmtWithHoles.applyLens l (GaudisCrypt.StmtWithHoles.while c b) = GaudisCrypt.StmtWithHoles.while (l.chainGetter c) (GaudisCrypt.StmtWithHoles.applyLens l b)
Instances For
applyLens commutes with hole instantiation: it never touches a hole's index, and the
hole ↦ call step only re-tags the same setter and getter.
zoom against the primitives #
(The equivalence StmtWithHoles.EquivInLens it is used for is in Logic/StmtEquiv.lean.)
A state-blind draw is unaffected by zooming.
zoom is ω-continuous in its program argument — what the loop case needs.
zoom commutes with while_loop (Kleene/ωSup argument): running the whole loop in the
sublens and writing back once is the same as writing back after every iteration.
The transport theorem #
applyLens l s runs s in the sublens l — which is exactly zoom l. The only assumption is
that l leaves the globals alone, which is what a call needs (the callee runs on the globals,
and must see the same ones before and after re-targeting).
This holds for an arbitrary statement s, which is why the flattener may wrap whole
subprograms in applyLens without looking at them.
Zooming is always a correct replacement: projecting the result of the zoomed program back gives the original program's result.
applyLens is always the right replacement: whatever the statement, re-targeting it
along a globals-preserving lens is denotationally equivalent in that lens.
Placing a hole-free statement into a hole context #
A flattened callee body is hole-free, but the statement it is spliced into need not be.
Re-type a hole-free statement in any hole context. (hole cannot occur: HoleIndex .empty
is uninhabited.)
Equations
- GaudisCrypt.StmtWithHoles.weaken (GaudiProg[ skip; ]) = GaudiProg[ skip; ]
- GaudisCrypt.StmtWithHoles.weaken (GaudiProg[ x_1 <$ §e; ]) = GaudiProg[ x_1 <$ §e; ]
- GaudisCrypt.StmtWithHoles.weaken (GaudisCrypt.StmtWithHoles.call' x_1 ns hl hn b r p) = GaudisCrypt.StmtWithHoles.call' x_1 ns hl hn b r p
- GaudisCrypt.StmtWithHoles.weaken (s₁.seq s₂) = (GaudisCrypt.StmtWithHoles.weaken s₁).seq (GaudisCrypt.StmtWithHoles.weaken s₂)
- GaudisCrypt.StmtWithHoles.weaken (GaudisCrypt.StmtWithHoles.ifThenElse c t e) = GaudisCrypt.StmtWithHoles.ifThenElse c (GaudisCrypt.StmtWithHoles.weaken t) (GaudisCrypt.StmtWithHoles.weaken e)
- GaudisCrypt.StmtWithHoles.weaken (GaudisCrypt.StmtWithHoles.while c b) = GaudisCrypt.StmtWithHoles.while c (GaudisCrypt.StmtWithHoles.weaken b)
Instances For
Weakening is undone by instantiation: a hole-free statement has nothing to fill in.
Flattening one call #
The one step with real content. Everything else above is congruence and transport.
A call statement, run at a state: run the callee's body on fresh locals holding the
arguments, then store its return value.
The expansion of a call, run at a state: after the prelude f, the callee's body runs in
emb, and its return value is stored through the caller's setter.
Flattening a call is sound. A call is denotationally equivalent, along trafo, to
- a prelude
fthat puts the callee's entry locals into the callee's frame of the wide state (hpre_get) without disturbing anything visible throughtrafo(hpre_vis); - the callee's body, re-targeted with
applyLens emb; - a store of the callee's return value through the caller's setter.
The two lenses must preserve the globals (htg, heg), and writing the callee's frame must be
invisible through trafo except for the globals (hdisj).
The statement-level form, for any prelude pre that is the deterministic state map f.
P writes an argument tuple into the parameter slots names of the callee's frame emb:
the wide-state form of VariableAssignment.setParams. The meta code builds P as the tuple
of the parameters' renamed variables, which is how it prints.
Equations
- One or more equations did not get rendered due to their size.
Instances For
No parameters: nothing to write.
One parameter: its variable, seen through emb.
Two or more parameters: the first one's variable, paired with the rest.
Flattening a call, as the meta code instantiates it: the call is equivalent, along
trafo, to resetting the callee's frame, writing the arguments with P, the callee's body in
emb, and the store of its return value. Every side condition of the general
StmtWithHoles.equivInLens_flattenCall is discharged here, once.
P's content type is spelled typeListToTuple sig.params, not sig.ParamType: the two are
equal by definition, but only the former reduces at the transparency simp matches at, and the
cleaning lemmas have to see the parameters' types inside P when a later round renames them.
The procedure level #
What turns the statement-level result into a fact about procedureDenotation, and therefore
into something usable where the procedure is called.
l leaves the entry state of a procedure with parameters names : tys alone: the arguments
in their slots, init everywhere else.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Two renamings that keep the globals compose to one that keeps them.
The procedure-level statement: flattening the body of a procedure does not change what it computes, given that the renaming leaves the entry state and the globals alone.
The lemmas cleanStmt rewrites with #
The closed set. Flatten.cleanLemmas lists exactly these names and nothing else; the cleaning
pass runs simp only with that list, the name evaluation of Flatten.reduceNames and
VariableName.reduceEncode, no default simp set and zeta := false, so statement lets survive.
Three groups: through the statement wrappers (applyLens, weaken), into l-values, and into
expressions. Everything that does not match keeps its wrapper — that costs prettiness, never
correctness.
Through the statement wrappers #
Two nested re-targetings collapse into one — what a second flattening round leaves behind.
Weakening and re-targeting commute, so the two wrappers can be pushed inwards together.
Into l-values #
A variable used as an l-value travels along the lens.
A global l-value is unaffected by a renaming of the locals.
A discarded result stays discarded, so a void call does not acquire an l-value.
Into expressions #
A GaudiExpr getter is Getter.mk (fun st => body) with every variable read spelled
eval x at the current state ⟨st⟩. Composing it with a lens beta-reduces to the same body at
l.get st, and these lemmas move the lens from the state to the variable — after which the
variable lemmas rename it, and the read prints as §x again.
The three renamings, unfolded where the lemmas above need mapLocal #
trafo, emb and rename ren are definitions (so that they print by name), and simp does not
see the mapLocal inside; each gets the global-variable lemmas restated.
Resets #
A renaming around a reset resets the renamed region; cleanNameSet? then puts the region back in
normal form.
The meta code #
The passes, of which only the first can fail:
flattenCall n— flatten call siten, leaving deliberately ugly terms behind: everything off the path to the call is wrapped inapplyLens trafowithout being looked at;cleanStmt— push those wrappers inwards with the closed lemma set ofClean;flattenSeq— re-associateseqto the right and dropskipand_ <- e;flattenCallCleaned n— the three in a row, proofs composed. The callee's variables now have made-up names («@.w»);renameNames— the cleanup: give the made-up names readable ones (w, orw0on a clash), by one renaming of the whole statement;flattenProcedureCalls— repeatflattenCallCleaneduntil no call can be flattened, thenrenameNames.
flattenProcedure does it to a procedure, and Inlining (unfoldProcedure, inlineProcedure)
evaluates a callee that is named through modules first.
Lean.Expr, under a name that GaudisCrypt.Expr (the program-expression abbreviation) does
not shadow.
Equations
Instances For
Evaluating renamed names #
escape n, tag n or renameName ren n, with ren and n literals, evaluated.
Equations
- One or more equations did not get rendered due to their size.
Instances For
varLens ⟨escape "x", T, …⟩ (or tag, renameName ren) ↦ varLens ⟨"y", T, …⟩, the name
evaluated and the key with it, by varLens_rename.
A proof, not a definitional step: the two are equal by definition too, but checking that means
evaluating VariableName.encode of a computed string, which decodes it character by character
in the unifier (tens of seconds for a short name). With the proof, the name is compared once,
and the key is the literal's.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Reset regions in normal form #
Everything the meta code knows about the normal form of a reset region (section Reset regions
above) is in this section; cleanNameSet? is its entry point.
nf p add rem, as a term.
Equations
- GaudisCrypt.Flatten.nfExpr p add rem = Lean.mkAppN (Lean.mkConst `GaudisCrypt.Flatten.nf) #[Lean.mkStrLit p, Lean.toExpr add, Lean.toExpr rem]
Instances For
A reset region as nfToPretty writes it (or Set.univ), read back as nf p add rem, with
a proof of S = nf p add rem.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The image of nf p add rem under the swap of a and f, in normal form: a name leaving
the set goes out of add and, if it has the prefix, into rem; a name entering it goes out of
rem if it has the prefix, and into add otherwise. New names go to the front, so that after a
whole renaming (whose last pair is swapped first) add and rem are in the order of the
renaming.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Apply the lemma lem (partially applied) to a proof, by evaluation, of its next hypothesis,
a Bool check … = true.
Equations
- One or more equations did not get rendered due to their size.
Instances For
renameName ren '' S in normal form, given S in normal form: the swaps of ren
one at a time, the last pair first (image_renameName_nil/_cons).
The set cleaning: ι '' S, for S in normal form (or Set.univ) and ι one of tag,
escape, renameName ren (with ren a literal), in normal form again, written out
(nfToPretty). none when the term is not of that shape.
Instances For
See cleanNameSet?.
Equations
- One or more equations did not get rendered due to their size.
Instances For
l.chain (Lens.pair x y) ↦ Lens.pair x' y', where x', y' are l.chain x, l.chain y
cleaned (renamed, typically) and the Disjoint instance is synthesized for them anew. Not a
plain lemma: the instance argument of Lens.pair depends on the components, so simp cannot
rewrite them in place.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Statements #
Expose the statement hiding behind projections and definitions (someProc.body), without
zeta-reducing the lets of the statement — those are the user's own binders.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The elements of a literal List expression. Reducible transparency only: a callee named by
a definition stays stuck here, which is what makes "literal callees only" bite.
Is this application a call site — one of the two call forms, or a hole?
A hole is a call whose callee is not known yet, so it is one of the things a user counts when numbering the call sites of a statement, even though neither flattening nor inlining can do anything with it. Numbering it along with the rest is what keeps the call-site numbers of a statement independent of which sites happen to be flattenable.
Equations
- One or more equations did not get rendered due to their size.
Instances For
How many call sites the statement contains, in the pre-order they are numbered by. A callee's own body is not searched: its calls belong to a later round.
The pieces of a call: its signature, result l-value, the callee's parameter names (with their two proofs), body and return value, and the argument expression.
- sig : Expr
The callee's signature.
- lvalue : Expr
Where the result is stored.
- names : Expr
The callee's parameter names.
- hlen : Expr
names.length = sig.params.length. - hnodup : Expr
names.Nodup. - body : Expr
The callee's body.
- retVal : Expr
The callee's return value.
- args : Expr
The argument expression.
Instances For
Read a call's pieces off the application. Both call forms are accepted; a call needs a
callee that is a procedure written out.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Annotate whatever goes wrong inside k with the step that was being built.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Names in use #
The cleanup renaming gives the made-up names the names of the frame that are free. Which names are taken is read off the term, the literal variable names it mentions; a frame a statement reads opaquely (through a getter that is not a variable) is not seen, but the renaming is sound regardless, only the printed names could then be confusing.
Every literal variable name of the frame e runs in (as VariableName.mk "x" … or
localVarLens "x" …), and every let binder name, in first-occurrence order. The callees of
calls run in frames of their own and are skipped.
A name the flattening rounds made up: @.w for a callee's w, @@.w once a later round
has escaped it, and so on. (Or a name a user wrote with a leading @, which the cleanup renames
just as well.)
Equations
- GaudisCrypt.Flatten.isMadeUp n = n.startsWith "@"
Instances For
The readable name a made-up name stands for: without its leading @s and .s.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Flattening one call #
The parameters' new variables as one l-value — Setter.throwaway for none, the variable for
one, the Lens.pair tuple for more — with the proof that it writes the callee's parameter slots
(IsParamWriter).
names.length = tys.length, for literal lists of the same length.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Parameter z : T after renaming, (varLens ⟨@.z, T⟩).intoLocal, together with the
instance, key and key proof of the callee's own ⟨z, T⟩ and the proof that emb takes the one
to the other.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Build the expansion and its correctness proof for one call site.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Rebuild the statement along the path to call idx, planning the call where it is found —
inside whatever let binders enclose it, since the call may mention them. Everything off the
path is re-targeted wholesale with applyLens, without being inspected.
The statement's hole context, read off its type.
Equations
- One or more equations did not get rendered due to their size.
Instances For
a.EquivInLens b l.
Equations
Instances For
Flatten call site n (pre-order), leaving the result deliberately uncleaned.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Cleaning #
simp only with cleanLemmas, the name evaluation of cleanSimprocs and nothing else: no
default simp set, and zeta := false / zetaDelta := false, so the user's lets survive. The
only declaration unfolded is Lens.chainGetter, which has to go away for the eval lemmas to
see the state they are composed with.
The closed lemma list of the cleaning pass. Nothing outside this list ever rewrites the statement.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The lemmas of the cleaning pass that have to apply before Lens.chainGetter is unfolded.
Equations
- GaudisCrypt.Flatten.cleanPreLemmas = [`GaudisCrypt.Lens.chainGetter_toGetter]
Instances For
The definitions the cleaning pass unfolds: Lens.chainGetter, and typeListToTuple, which
the content type of the parameter writer is spelled with (Flatten.equivInLens_call) — a
variable renamed in a later round would otherwise take that spelling for its type.
Equations
- GaudisCrypt.Flatten.cleanUnfold = [`GaudisCrypt.Lens.chainGetter, `GaudisCrypt.typeListToTuple]
Instances For
The simprocs of the cleaning pass: evaluating the new names, renaming inside a tuple, and the reset regions.
Equations
Instances For
The simp context and simprocs of the cleaning pass, and nothing else.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Push applyLens / weaken / chain* inwards as far as the terms allow, and evaluate the new
variable names. Never fails; whatever does not match keeps its wrapper, which costs prettiness,
never correctness.
The lets around the statement (a procedure's holes, the user's binders) are kept, and the
statement under them is cleaned: simp with zeta := false would not go under them.
Composition and the fixed point #
(flattenSeq, the sequence pass, is in Logic/TacticMisc.lean.)
Clean step.stmt, and carry step.proof (about input) along: the cleaned statement, and
the proof of input.EquivInLens ‹cleaned› step.trafo.
Equations
- One or more equations did not get rendered due to their size.
Instances For
flattenCall, then cleanStmt, then flattenSeq, with the proofs composed. The only step
that can fail is the first.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Run step on the first call site it succeeds on, or report why there is none.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Run step on call sites until it succeeds on none, composing the proofs and the lenses.
Throws if it succeeds on nothing at all, or after fuel rounds.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The cleanup renaming #
Give the made-up names of stmt (isMadeUp) readable ones: baseName, or with 0, 1,
… appended on a clash with a name the statement uses or one in avoid. One renaming of the whole
statement, applyLens (rename ren), cleaned; sound for any choice of names, since rename ren is
a permutation of the names. none if there is nothing to rename.
Equations
- One or more equations did not get rendered due to their size.
Instances For
applyLens l under the lets a statement starts with (a procedure's holes, the user's
binders), with its proof equivInLens_applyLens: cleaning does not go under an applyLens
into a let.
step, followed by the cleanup renaming (renameNames), proofs and lenses composed.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Flatten calls until none is left, then give the made-up names readable ones (avoiding
avoid too). Throws if there was nothing to flatten at all; each round removes one call
node, so the loop terminates.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The procedure wrapper #
Run a pass on the body, give the made-up names readable ones (never the parameters' names), then
re-target return_val along the composite lens, and prove with procedureDenotation_congr that
the procedure computes the same: every renaming involved keeps the parameters' names, so the
procedure's entry state is unchanged.
∀ n ∈ names, f n = n, by decide.
Equations
- One or more equations did not get rendered due to their size.
Instances For
FixesEntry names tys hlen l for a composite of trafo and rename ren: each keeps the
parameters' names (checked by decide).
Run a pass on the body of a hole-free procedure and put the procedure back together: the
return_val re-targeted along the pass's lens and cleaned, and procedureDenotation_congr for
the proof. Whatever else the pass reports (α) is passed on.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Flatten the calls of a hole-free procedure. Returns the new procedure, a proof that it has
the same procedureDenotation, and the number of calls flattened.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Inlining: unfolding a module expression to a procedure #
A call site may name its callee through modules: call (Module.Proc.procedure (T.f (Module.app M A))) …. Flattening cannot handle that — it wants the callee spelled out at the call site — so
this section provides the step before it: evaluate such a term to the procedure it denotes.
What it evaluates, and how far #
The term is built from Module.app, the constants the module and moduletype commands emit,
Module.pair/Module.fst/Module.snd, Module.proc and Module.Proc.procedure. Two shapes
matter (ModuleSyntax.lean documents what each command declares):
T.f stufffor amoduletypeaccessorT.f— the argument is evaluated only as far as the accessor needs: to a record, i.e. aT.mkor aModule.pair, and no further. What comes out is the field, which is then evaluated in turn;M.f stufffor amodule-declared procedureM.f, which for a module with no module parameters is a straight substitution of the definition.
The steps are the generated lemmas, each applied at the head of the term and nowhere else:
M.apply_simp (applying a module to its parameters, giving the record of its procedures),
T.f.mk_simp (reading a field off that record), Module.fst_pair/snd_pair (the same for the
anonymous record a module without a module type builds), M.f.apply_simp (applying a procedure
of a module to the parameters it uses, giving Module.proc (M.f.procedure.instantiate ‹callees›))
and finally M.f.procedure.apply_simp (the body as written, with the hole calls turned back into
calls of those callees). Nothing is rewritten inside the body: the callees it calls are left
exactly as the module wrote them.
Everything is head-position, so there is no simp set here and no risk of a lemma firing somewhere
unintended; what the pass cannot step is an error, not a silent stop. In particular, a module
that is not concrete — T.f (Module.app M A) for a M that is a variable or an axiom — has no
body to find, and unfoldProcedure fails.
And back into a statement #
inlineProcedureRaw n rewrites call site n of a statement with unfoldProcedure, so that its
callee becomes literal; inlineProcedure n does that and then flattens the site with
flattenCallCleaned n — which is what "inlining" means here. Call sites are numbered by
countCallSites, which counts call, call' and hole nodes: all three are calls as far as
someone reading the statement is concerned, and numbering them together makes the numbers
independent of which sites happen to be flattenable. A hole has no callee, so both functions
fail on one.
One rewrite with a named equation, at the head of e and nowhere else: instantiate the
lemma's binders with metavariables, unify its left-hand side with e, and hand back the
instantiated right-hand side together with the instantiated lemma as its proof.
none if the lemma does not exist (the callers name lemmas that a module/moduletype
declaration may have emitted), if it does not match, or if matching leaves anything open —
whatever comes back is a closed term.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Replace argument i of an application.
Equations
- GaudisCrypt.Flatten.setArg e i x = Lean.mkAppN (Lean.Expr.getAppFn e) ((Lean.Expr.getAppArgs e).set! i x)
Instances For
congrArg at argument i of an application: from h : ‹arg i› = x to e = e[arg i := x].
Equations
- One or more equations did not get rendered due to their size.
Instances For
Lift a rewrite of argument i to the whole application.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Peel the let telescope off a procedure and look at what is underneath.
Is this a procedure written out — a ProcedureWithHoles.mk, possibly under the let
telescope that binds its holes?
Equations
- GaudisCrypt.Flatten.isProcLiteral e = do let __do_lift ← GaudisCrypt.Flatten.procCore e pure (Lean.Expr.isAppOfArity __do_lift `GaudisCrypt.ProcedureWithHoles.mk 7)
Instances For
‹literal tuple›.lookup ‹literal index›, computed. This is what the three
HoleSigs.Instantiation.lookup_* lemmas say, and each of them is rfl, so the result is
definitionally the term it replaces — no proof is needed for the step, only a re-typing of the
proof that mentions it.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Compute away every lookup of a literal instantiation.
Equations
Instances For
Reduce a projection function applied to a record — the shape a mk_simp leaves behind
(T.Structure.f {f := …, …}). Anything else is returned unchanged.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Spell a procedure out: M.f.procedure.instantiate ‹callees› becomes the body as it was
written — with each hole call turned back into a call of the callee it was made from — by the
M.f.procedure.apply_simp lemma the module command emits; a procedure named by a constant is
unfolded, its definition being the body. Fails when no body can be reached.
The function argument of a Module.app — the last argument is what it is applied to.
Equations
- GaudisCrypt.Flatten.moduleAppFn? e = if (Lean.Expr.isAppOf e `GaudisCrypt.Module.app && decide (Lean.Expr.getAppNumArgs e ≥ 2)) = true then some (Lean.Expr.getAppNumArgs e - 2) else none
Instances For
The head of a Module.app spine: Module.app (… (Module.app h x₁) …) xₖ ↦ h.
The lemma that reads a projection off a Module.pair — the anonymous record a module
without a module type builds.
Equations
- GaudisCrypt.Flatten.pairLemma? `GaudisCrypt.Module.fst = some `GaudisCrypt.Module.fst_pair
- GaudisCrypt.Flatten.pairLemma? `GaudisCrypt.Module.snd = some `GaudisCrypt.Module.snd_pair
- GaudisCrypt.Flatten.pairLemma? `GaudisCrypt.Module.fst' = some `GaudisCrypt.Module.fst_pair'
- GaudisCrypt.Flatten.pairLemma? `GaudisCrypt.Module.snd' = some `GaudisCrypt.Module.snd_pair'
- GaudisCrypt.Flatten.pairLemma? x✝ = none
Instances For
Is this constant an accessor of a moduletype declaration? It is exactly when the command
emitted the mk_simp lemma that reads the field back off a built module.
Equations
- GaudisCrypt.Flatten.isAccessor c = do let __do_lift ← Lean.getEnv pure (__do_lift.contains (c ++ `mk_simp))
Instances For
One step of head evaluation of a module expression: applying a module or one of its procedures
to its parameters, reading a field off a record, or — when none of those applies yet — the same
one step in the position that has to be evaluated first (the function of a Module.app, the
module a projection is taken of).
Evaluate a module expression until its head is Module.proc, then spell that procedure out.
Fails as soon as no step applies — which is exactly the case where the term has no concrete
procedure body.
Unfold a term built from modules that evaluates to a procedure, and hand back the result with a proof that it equals the term.
Accepts both levels: a module term of type Module.Proc sig, whose result is
Module.proc ‹procedure›, and a procedure term — Module.Proc.procedure ‹module›, the shape a
call site carries — whose result is the procedure itself. Fails if no concrete body can be
found.
This is the pass itself; unfoldProcedure wraps it.
Equations
- One or more equations did not get rendered due to their size.
Instances For
unfoldProcedureCore, with the proof re-typed to say what it proves.
A step that unfolds a definition carries no proof of its own — none is rfl — so what the
composed proof states can end at a term that is only definitionally the result. Both are
correct; this is the one a caller (and a #check) would rather read.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Rewrite call site idx of a statement by unfolding its callee, and prove that the result
equals the statement. The traversal is the one that numbers the call sites, with the single
difference that it stops at a StmtWithHoles.call (openStmt): whnf would step through it to
the call' it is defined as, which spreads the callee over several arguments instead of one.
Unfold the callee of call site n, leaving the call itself alone. The proof is an
equation: the statement is rewritten, not re-targeted.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Inline call site n, leaving the callee's variables with made-up names: unfold its callee
(inlineProcedureRaw), then flatten the call (flattenCallCleaned). The two proofs compose: the
first is an equation, which transports the second's EquivInLens back to the statement it
started from.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Inline call site n (inlineCall), then give the made-up names readable ones (avoiding
avoid too).
Equations
- GaudisCrypt.Flatten.inlineProcedure n stmt avoid = do let __do_lift ← GaudisCrypt.Flatten.inlineCall n stmt GaudisCrypt.Flatten.withRenaming avoid __do_lift
Instances For
inlineProcedure at the procedure level: inline call site n of the body of a
hole-free procedure that is spelled out, and hand back the new procedure together with a proof
that it has the same procedureDenotation.
proc (…) { xxx; call bla; yyy } becomes proc (…) { xxx; ‹the body of bla›; yyy }, with the
callee's variables added to the procedure's own (renamed where they collide) and its result
stored where the call stored it.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Hoare triples #
A triple can be proved about the flattened statement instead, along
hoareStmt_of_equivInLens (Logic/StmtEquiv.lean).
fun τ => C (l.get τ), cleaned like a statement, with the equation.
Equations
- One or more equations did not get rendered due to their size.
Instances For
On a goal hoareStmt A s B: inline call site n of s (inlineProcedure) and continue with
the triple about the result, its conditions read through the renaming and cleaned. The names the
conditions use count as taken.
Equations
- One or more equations did not get rendered due to their size.
Instances For
hoare_inline n: on a goal hoareStmt A s B, inline call site n of s (numbered in
pre-order, holes included; the callee spelled out, named by a definition or through modules), and
continue with the triple about the result. The callee's own calls stay calls.
Equations
- One or more equations did not get rendered due to their size.