Guarded2Network compiles a receive away entirely, replacing it with a real second kind of
thread (NetworkPlusCal.Thread.rx) that loops draining a fresh process-local inbox sequence
variable, and rewrites every later await/with guard that referenced the received value to
read inbox instead — the guard's truth no longer depends on an abstract "did a message
arrive" primitive, only on ordinary sequence operations (Head/Tail/Len) over inbox.
- Monad-polymorphic:
{m} [Monad m] [MonadFresh m] [MonadDiagnostic Empty G2NError m], the sameDiagT-based calling convention every other diagnostics-producing pass uses.Emptyfor the warning channel: this pass reports no warnings. - Every fresh name — the process-local
inboxvariable and each.rxthread's own block label — comes fromfreshName(Common/Fresh.lean), giving the same$-based hygiene as every other pass's fresh binder: a name a real user could have written can never collide with one of these.inboxis fresh once per process, shared by every thread of that process; each new.rxthread gets its own fresh label. That label is what the semantics schedules the receiving loop by, and what its own terminalgototargets; it must therefore stay distinct from everyAtomicBlock.labelin the process, whichfreshNameguarantees. - A
receive's storedCoercion(Core/TypedTLAPlus/Coercion.lean) is discharged viaCoercion.applyComputabledirectly against the builtHead(inbox)expression, not left unapplied. - Only
G2NError.internalInvariantViolatedguards areceive's channel resolution — and only "channel not found" is actually reachable:GuardedPlusCal.Declarations.channels/.fifosalready store a channel's checked element type directly (Elaborator/ PlusCal.lean'scheckChannelDeclunwrapsChannel(τ)/dom → Channel(τ)down toτbefore it reachesDeclarations), so there's no wrappedTypleft to mismatch on by the time this pass runs. - Guard-expression substitution reuses
ComputableTLAPlus.Expression.substRef(Core/ComputableTLAPlus/Subst.lean, already written forComputable2Guarded/FlatReord.lean's own𝒞_reordcase): barersubstitutes the name directly; a compoundrsubstitutes the whole base variable with a one-entryEXCEPT. Fold direction matters: each new(Ref, Expr)pair is appended to the end ofnewInstrsas its receive is processed, and a later guard's substitution is afoldrover that list — a later-appended "advanceinboxpast what this receive consumed" pair applies before an earlier-appended one (sincefoldrprocesses right-to-left), which is what makes a second receive's freshly-substitutedHead(inbox)get caught and advanced toHead(Tail(inbox))by the first receive's still-pending advance. Switching tofoldlsilently breaks this. - Positions are carried across, not dropped: every
NetworkPlusCal.Statementthis pass builds is registered (@@) at theGuardedPlusCal.Statementit replaces, and the consumption assignments areceivecompiles into take thatreceive's own span. An unregistered node is oneposOfanswers for with an unrelated node's span.
receive(c, x[0]);
await x[0] + y = 0;
receive(c, y);
await x[0] + y = 0;
compiles to (guards, symbolically, before any assignment runs):
await Len(inbox) > 0;
await [x EXCEPT ![0] = Head(inbox)][0] + y = 0;
await Len(inbox) > 1;
await [x EXCEPT ![0] = Head(inbox)][0] + Head(Tail(inbox)) = 0;
and the branch's action block gains, as its own new prefix (ordinary sequential assignments — no
substitution needed here, since each one's Head(inbox) is read after the previous one's own
inbox := Tail(inbox) has already run):
x[0] := Head(inbox); inbox := Tail(inbox); y := Head(inbox); inbox := Tail(inbox);
One process's channel/fifo element-type table — a channel name resolves to its already-checked
element type directly (see the module doc above for why no wrapped Channel(_)/dom → Channel(_) shape ever needs matching here), built by merging global and process-local
channels/fifos declarations. Looked up by a receive's channel Ref's base name, ignoring any
index arguments — a channel's element type doesn't depend on which array slot is referenced.
Equations
Instances For
The three sequence expressions this pass builds over inbox. Public, and namespaced rather
than file-private, because the refinement proof has to state what they mean — Guarded2Network/ Lemmas/Seq.lean's SeqBuiltins gives each one its evaluation law, and a law about a private
definition cannot be written at all.
Head(e).
Equations
- Guarded2Network.head τ e = (ComputableTLAPlus.Expression.var (SurfaceTLAPlus.Typ.operator [SurfaceTLAPlus.Typ.seq τ] τ) (TypedTLAPlus.Origin.module "Sequences" "Head")).opCall [e]
Instances For
Tail(e).
Equations
- One or more equations did not get rendered due to their size.
Instances For
Len(e) > n, n a literal Nat.
Equations
- One or more equations did not get rendered due to their size.
Instances For
One assignment's effect substituted into a guard-class statement — .with's bound expression or
.await's condition, the only expression field either constructor carries. Delegates to
ComputableTLAPlus.Expression.substRef, so a bare and a compound Ref behave here exactly as they
do everywhere else substitution is applied.
Public, namespaced and @[expose] for the same reasons convertActionStmt below is: the refinement
proof's reorder lemmas (Guarded2Network/Lemmas/Reorder.lean) are stated against the pass's own
substitution rather than against a re-derivation of it, and their proofs reduce through this body.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Every receive processed so far (processPrecondition's newInstrs) substituted into a later
guard — see the module doc's foldr/fold-direction explanation. The per-entry span plays no part in
substitution and is dropped here.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Every action-class constructor GuardedPlusCal.Statement/NetworkPlusCal.Statement share
verbatim (all but receive, already compiled away above, and with, guard-class only) — Ref/
Multicast are the same types under both pinnings (Core/NetworkPlusCal/Syntax.lean reuses
GuardedPlusCal.Ref/.Multicast directly), so this is a plain re-tagging, not a
translation. Public and namespaced, not file-private: the refinement proof states its semantic
equations (Guarded2Network/Lemmas/Statement.lean), and a lemma about a private definition cannot
be written — and @[expose], so that the equations it satisfies are provable by rfl in another
module rather than only inside this one.
Equations
- One or more equations did not get rendered due to their size.
Instances For
One process's channel table (already merged with that process's own local channels/fifos
by Process.toNetwork) and its single shared inbox name (fresh once per process, shared by
every thread of the process) drive the whole compilation: every AtomicBranch's precondition is
walked (processPrecondition), its action block gets the resulting consumption assignments
prepended, and a new .rx thread is added the first time this call encounters a not-yet-seen
channel.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.