Metatheory for Expression.freeVars (defined in Subst.lean): membership lemmas that unfold
freeVars's foldl (· ∪ ·) list cases to plain ∃ e ∈ es, …, plus the same freeVars
vocabulary lifted over ComputableGuardedPlusCal.
x does not occur free in e — the freshness side condition every binder-introducing
lemma needs.
Equations
- ComputableTLAPlus.Expression.FreshIn x e = (x ∉ e.freeVars)
Instances For
If x avoids every name e could possibly read, x isn't one of them. The bridge from a
scope-membership fact (WellFormedness.WellScoped's vocabulary) to an actual freshness fact
(ExprSemantics.evalSubst's vocabulary): a with's bound name is fresh against inScope by
construction, and a substituted-in expression's free variables are always a subset of inScope
(it can only mention already-declared names), so the two never collide.
Membership lemmas #
freeVars's list cases fold (· ∪ ·) over es.attach.map (…); these unfold that fold to a plain
∃ e ∈ es, …, so evalLocal/evalSubst's inductions can move a memory-agreement hypothesis from
a compound expression down to its components.
De Bruijn traversals #
liftBound/openVar/close/subst/instantiate are each mapVars f 0 for a specific per-node
f. freeVars_mapVars_subset bounds the free names of a mapVars result once — every other
free-variable fact about those operations is a corollary.
Every free name of e.mapVars f k is either a free name f keeps at some .var _ (.free _)
node of e, or a name in S — the extra set f may introduce at .bound nodes.
liftBound shifts .bound indices and never touches a .free name.
openVar turns the removed binder's index into the free name x, and moves no other name in
or out.
A name free in e.openVar x, other than x itself, was already free in e.
A name free after instantiating the outermost binders is free in the body or in one of the instantiated arguments.
subst of a name not free in the target is the identity.
subst distributes over instantiate when the substituend is locally closed and the body is
closed (Ξ.WellScoped gives operator bodies freeVars = ∅).
Lifted over ComputableGuardedPlusCal — flat by construction (module doc,
Core/GuardedPlusCal/Syntax.lean), so every Expr field is a direct component, not a nested
Block: no well-founded recursion is needed here, unlike Expression.freeVars above.
Every name a Ref's base variable and index expressions read. The base name itself is a
use, same as any other Expression.var occurrence — a Ref is how a statement spells "read/write
this variable", not a binder.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The base name is one of them — the {r.name} ∪ … above, named, so that a freshness hypothesis
stated over a whole Ref yields the inequality against r.name that memory lemmas need.
set's recipients are read in the enclosing scope; recipient then binds inside val, same
shape as Expression.fn/.map'.
Equations
Instances For
Every name a statement reads — every Expr/Ref field's free variables, unioned. A with's
own bound name is not included (it's introduced, not read); its domain/bound expression e is.
Equations
- (GuardedPlusCal.Statement.with name ann bound e).freeVars = ComputableTLAPlus.Expression.freeVars e
- (GuardedPlusCal.Statement.await e).freeVars = ComputableTLAPlus.Expression.freeVars e
- (GuardedPlusCal.Statement.receive c r coe).freeVars = c.freeVars ∪ r.freeVars
- GuardedPlusCal.Statement.skip.freeVars = ∅
- (GuardedPlusCal.Statement.print e).freeVars = ComputableTLAPlus.Expression.freeVars e
- (GuardedPlusCal.Statement.assert e).freeVars = ComputableTLAPlus.Expression.freeVars e
- (GuardedPlusCal.Statement.send c e).freeVars = c.freeVars ∪ ComputableTLAPlus.Expression.freeVars e
- (GuardedPlusCal.Statement.multicast c filter).freeVars = filter.freeVars
- (GuardedPlusCal.Statement.assign r e).freeVars = r.freeVars ∪ ComputableTLAPlus.Expression.freeVars e
- (GuardedPlusCal.Statement.goto label).freeVars = ∅
Instances For
Every name any statement in B reads.
Equations
- B.freeVars = List.foldl (fun (x1 x2 : Finset String) => x1 ∪ x2) ∅ (List.map GuardedPlusCal.Statement.freeVars B.begin) ∪ GuardedPlusCal.Statement.freeVars B.last
Instances For
x is read nowhere in Br — precondition guard list (if any) and action block alike. The
AtomicBranch-level freshness side condition the reorder_assign_guard pair needs: a preceding
action's assigned name must stay fresh in every later guard for the substitution 𝒞_reord
performs to be sound.
Equations
- GuardedPlusCal.AtomicBranch.FreshIn x Br = (x ∉ (match Br.precondition with | none => ∅ | some B => B.freeVars) ∪ Br.action.freeVars)