Possibility Reasoning and Linearizability Hoare Logic
Prerequisites. Direct starred prerequisites: Chapter 87. No later core chapter depends on this route.
Two queue enqueues overlap. After 𝖾𝗇𝗊(1) and 𝖾𝗇𝗊(2), either abstract queue [1,2] or [2,1] respects the observed calls. A later dequeue returns 1. The first queue advances to [2]; the second has no transition returning 1. A proof that chose [2,1] before seeing the dequeue is stuck even though the implementation trace is linearizable. The proof state must retain both compatible abstract executions and prune one only when the return makes it impossible.
The operational state has no scheduler component
The LTS and module operations are those of definition 87.2, definition 87.3. This chapter freezes the exact logic-facing state rather than adding an external scheduler.
For underlay signature 𝐸 and overlay signature 𝐹, a thread state is 𝖨𝖽𝗅𝖾∣𝖢𝗈𝗇𝗍(𝑚,𝑝)∣𝖴𝖢𝖺𝗅𝗅(𝑚,𝑢,𝑘), where 𝑚:𝐹(𝐴), 𝑝:𝖯𝗋𝗈𝗀𝐸(𝐴), and the last form records a suspended underlay call 𝑢:𝐸(𝐵) with continuation 𝑘:𝐵→𝖯𝗋𝗈𝗀𝐸(𝐴). A thread map assigns one such state to every 𝑖:𝖭𝖺𝗆𝖾(𝑇). For 𝑉𝐸:𝖲𝗉𝖾𝖼𝑇(𝐸), an interaction state is a pair (𝑞,𝑠) of a thread map and a state of 𝑉𝐸.
Overlay calls and returns use CL-Overlay-call and CL-Overlay-ret. Underlay calls and returns use CL-Underlay-call and CL-Underlay-ret. A silent step changes 𝖢𝗈𝗇𝗍(𝑚,𝖳𝖺𝗎(𝑝)) to 𝖢𝗈𝗇𝗍(𝑚,𝑝). An interleaving step chooses a thread name 𝑖 and changes only its thread state, together with the underlay state when the chosen rule has an underlay event. The chosen name is part of the transition label; no scheduler object occurs in the state.
For a target specification 𝑉𝐹:𝖲𝗉𝖾𝖼𝑇(𝐹), a possibility is a triple 𝜌=⟨𝜌𝑆,𝜌𝐶,𝜌𝑅⟩. The component 𝜌𝑆 is a state of 𝑉𝐹. The components 𝜌𝐶 and 𝜌𝑅 are partial maps indexed by thread. For each thread, 𝜌𝐶 has one of the three forms 𝖢𝖺𝗅𝗅𝖨𝖽𝗅𝖾,𝖢𝖺𝗅𝗅𝖯𝗈𝗌𝗌(𝑚),𝖢𝖺𝗅𝗅𝖣𝗈𝗇𝖾(𝑚). For each thread, 𝜌𝑅 is 𝖱𝖾𝗍𝖨𝖽𝗅𝖾 or 𝖱𝖾𝗍𝖯𝗈𝗌𝗌(𝑚,𝑣). A possibility set is represented by its predicate 𝖯𝗈𝗌𝗌𝖲𝖾𝗍(𝑉𝐹):=𝖯𝗈𝗌𝗌(𝑉𝐹)→𝖯𝗋𝗈𝗉. Thus the representation admits the empty predicate. Nonemptiness is a proof obligation of commit and return updates, not an invariant of the data type.
The initial possibility has target state 𝑠𝑉𝐹 and idle call/return maps. An overlay invocation changes the selected thread’s call component from idle to possible without moving the target state. Linearization commits are the only rules that move the target state:
every 𝜎 satisfying 𝑄 is reachable by 𝖯𝗈𝗌𝗌𝖲𝗍𝖾𝗉𝗌 from some 𝜌 satisfying 𝑃.
The first clause prevents the empty predicate from satisfying an update merely by vacuity. The second is the artifact’s direction of lifted coverage: successors must have represented predecessors. A rule may prune an incompatible predecessor, as the next calculation requires; no converse coverage is built into 𝖠𝖽𝗏𝖺𝗇𝖼𝖾.
After overlapping invocations of 𝖾𝗇𝗊(1) and 𝖾𝗇𝗊(2), the possibility set may contain target states [1,2] and [2,1]. If the observed dequeue returns 1, a lifted return commit advances the set to the singleton state [2]. Starting from the forced singleton [2,1] admits no such update.
Proof of Proposition 88.4 — The ambiguous-queue pruning calculation
Proof. From [1,2], the queue rule for dequeue gives [1,2]𝖽𝖾𝗊/1[2]. From [2,1], the only dequeue transition returns 2 and reaches [1]. Therefore the successor predicate 𝑄(𝜎):=(𝜎𝑆=[2]) is inhabited and every one of its elements is reachable from the first predecessor. The incompatible predecessor is pruned. If the predecessor set is the singleton [2,1], no target step labelled 𝖽𝖾𝗊/1 exists, so the nonemptiness clause of definition 88.3 fails. ◻
★★☆ Let 𝑄 be the empty predicate. Show that the universal successor-coverage clause of an update holds vacuously while the nonemptiness clause fails. Explain why omitting nonemptiness would make every impossible concrete return appear provable.
Assertions track concrete and abstract states together
Fix an underlay specification 𝑉𝐸, a target specification 𝑉𝐹, and an implementation 𝑀:𝖨𝗆𝗉𝗅(𝐸,𝐹). Let 𝑠,𝑡 range over interaction states and 𝑃,𝑄 over possibility predicates.
A precondition is a predicate P(𝑠,𝑃). A relational assertion is a predicate R(𝑠,𝑃,𝑡,𝑄). Relational composition is (R;G)(𝑠,𝑃,𝑢,𝑊):=∃𝑡,𝑄.R(𝑠,𝑃,𝑡,𝑄)∧G(𝑡,𝑄,𝑢,𝑊). For a precondition P, the mixed composition used below is (P;R)(𝑡,𝑄):=∃𝑠,𝑃.P(𝑠,𝑃)∧R(𝑠,𝑃,𝑡,𝑄). The relation Q is stable under R when (Q;R)(𝑠,𝑃,𝑡,𝑄)⟹Q(𝑠,𝑃,𝑡,𝑄). A precondition is stable when taking one R step from a state that satisfies it yields another state that satisfies it. A postcondition is stable pointwise in its returned value. We abbreviate this relation-stability condition by 𝖲𝗍𝖺𝖻𝗅𝖾(R,Q).
For each thread 𝑖, the rely R𝑖 describes steps by other threads, and the guarantee G𝑖 describes steps by 𝑖. The key side condition is 𝑖≠𝑗⟹G𝑖⊆R𝑗. It turns a guarantee proof for the active thread into the rely step needed by every inactive thread.
For maps 𝑓,𝑔 with domain 𝖭𝖺𝗆𝖾(𝑇), put 𝖲𝖺𝗆𝖾𝖤𝗑𝖼𝖾𝗉𝗍𝑖(𝑓,𝑔):=∀𝑗:𝖭𝖺𝗆𝖾(𝑇).𝑗≠𝑖⟹𝑔(𝑗)=𝑓(𝑗). The relation 𝖴𝗇𝖽𝖾𝗋𝖲𝗍𝖾𝗉(𝑢,𝜔,𝑢′) is exactly the local relation given by CL-Underlay-call, CL-Underlay-ret, and the silent clause following those rules; 𝜔 is respectively a call, a return, or 𝖭𝗈𝗇𝖾. Let 𝑠=(𝑞,𝑥) and 𝑡=(𝑞′,𝑥′) range over interaction states, and let 𝑋,𝑌:𝖯𝗈𝗌𝗌𝖲𝖾𝗍(𝑉𝐹).
The judgment 𝖢𝗈𝗆𝗆𝗂𝗍𝑖(G,P,𝑒,Q) for 𝑒:𝖤𝗏𝖾𝗇𝗍(𝐸) is the following quantified formula: ∀𝑠=(𝑞,𝑥),𝑋,𝑡=(𝑞′,𝑥′).P(𝑠,𝑋)∧𝖲𝖺𝗆𝖾𝖤𝗑𝖼𝖾𝗉𝗍𝑖(𝑞,𝑞′)∧𝖴𝗇𝖽𝖾𝗋𝖲𝗍𝖾𝗉(𝑞(𝑖),𝖲𝗈𝗆𝖾(𝑒),𝑞′(𝑖))∧𝑥𝖲𝗍𝖾𝗉𝑉𝐸(𝑖:𝑒)𝑥′⟹∃𝑌.𝖠𝖽𝗏𝖺𝗇𝖼𝖾(𝑋,𝑌)∧Q(𝑠,𝑋,𝑡,𝑌)∧G(𝑠,𝑋,𝑡,𝑌).
The judgment 𝖲𝗂𝗅𝖾𝗇𝗍𝑖(G,P,Q) is exact when ∀𝑞,𝑞′,𝑥,𝑋.P((𝑞,𝑥),𝑋)∧𝖲𝖺𝗆𝖾𝖤𝗑𝖼𝖾𝗉𝗍𝑖(𝑞,𝑞′)∧𝖴𝗇𝖽𝖾𝗋𝖲𝗍𝖾𝗉(𝑞(𝑖),𝖭𝗈𝗇𝖾,𝑞′(𝑖))⟹Q((𝑞,𝑥),𝑋,(𝑞′,𝑥),𝑋)∧G((𝑞,𝑥),𝑋,(𝑞′,𝑥),𝑋). Thus a silent step changes one thread state but keeps both the underlay state and the possibility predicate fixed.
Define 𝖱𝖾𝗌𝖾𝗍𝑖(𝑞,𝑥):=(𝑞[𝑖↦𝖨𝖽𝗅𝖾],𝑥). For 𝑚:𝐹(𝐴) and 𝑣:𝐴, the consumption relation is 𝖢𝗈𝗇𝗌𝗎𝗆𝖾𝑖,𝑚,𝑣(𝜌,𝜏):=𝜏𝐶(𝑖)=𝖢𝖺𝗅𝗅𝖨𝖽𝗅𝖾∧𝜌𝐶(𝑖)=𝖢𝖺𝗅𝗅𝖣𝗈𝗇𝖾(𝑚)∧𝜏𝑅(𝑖)=𝖱𝖾𝗍𝖨𝖽𝗅𝖾∧𝜌𝑅(𝑖)=𝖱𝖾𝗍𝖯𝗈𝗌𝗌(𝑚,𝑣)∧𝖲𝖺𝗆𝖾𝖤𝗑𝖼𝖾𝗉𝗍𝑖(𝜌𝐶,𝜏𝐶)∧𝖲𝖺𝗆𝖾𝖤𝗑𝖼𝖾𝗉𝗍𝑖(𝜌𝑅,𝜏𝑅)∧𝜏𝑆=𝜌𝑆, and 𝖢𝗈𝗇𝗌𝗎𝗆𝖾𝖲𝖾𝗍𝑖,𝑚,𝑣(𝑌)(𝜏):=∃𝜎.𝑌(𝜎)∧𝖢𝗈𝗇𝗌𝗎𝗆𝖾𝑖,𝑚,𝑣(𝜎,𝜏). The judgment 𝖱𝖾𝗍𝗎𝗋𝗇𝖲𝗍𝖾𝗉𝑖(G,P,𝑚,𝑣,C) is the formula ∀𝑠=(𝑞,𝑥),𝑋.P(𝑠,𝑋)∧𝑞(𝑖)=𝖢𝗈𝗇𝗍(𝑚,𝖱𝖾𝗍𝗎𝗋𝗇(𝑣))⟹∃𝑌.𝖠𝖽𝗏𝖺𝗇𝖼𝖾(𝑋,𝑌)∧(∀𝜎.𝑌(𝜎)⟹𝜎𝑅(𝑖)=𝖱𝖾𝗍𝖯𝗈𝗌𝗌(𝑚,𝑣)∧𝜎𝐶(𝑖)=𝖢𝖺𝗅𝗅𝖣𝗈𝗇𝖾(𝑚))∧C(𝑠,𝑋,𝖱𝖾𝗌𝖾𝗍𝑖(𝑠),𝖢𝗈𝗇𝗌𝗎𝗆𝖾𝖲𝖾𝗍𝑖,𝑚,𝑣(𝑌))∧G(𝑠,𝑋,𝖱𝖾𝗌𝖾𝗍𝑖(𝑠),𝖢𝗈𝗇𝗌𝗎𝗆𝖾𝖲𝖾𝗍𝑖,𝑚,𝑣(𝑌)). This display includes every premise of the artifact definition: the thread-return observation, successor inhabitation and coverage inside 𝖠𝖽𝗏𝖺𝗇𝖼𝖾, the two pending-status facts, the reset, and preservation of all unselected possibility components.
Fix 𝑚:𝐸(𝐴). The program judgment R,G,𝑖⊢{P}𝑝{Q} is the greatest relation closed by the following three rules. Equivalently, the source declares this judgment coinductively. The visible rule records separate commit obligations for the call and every possible return.
The rule order follows program observations: return, silent, and visible. At a neutral program no rule applies, because none of the three outer forms is known. For example, if 𝗌𝗉𝗂𝗇=𝖳𝖺𝗎(𝗌𝗉𝗂𝗇) and there is a relation S with 𝖲𝗍𝖺𝖻𝗅𝖾(R,S), 𝖲𝗂𝗅𝖾𝗇𝗍𝑖(G,P,S), and P;S=P, then guarded corecursion repeatedly uses LHL-Tau to derive R,G,𝑖⊢{P}𝗌𝗉𝗂𝗇{Q}. An inductive reading would reject this silent divergence and would therefore be a different logic.
Proof. The concrete step satisfies G𝑗 by the guarantee proof for thread 𝑗. The inclusion gives the corresponding R𝑖 step. Stability instantiated with the initial state and possibility predicate gives the final instance of P. ◻
★★☆ Let P assert that a shared lock owner is either none or thread 𝑖, and let R𝑖 forbid other threads from changing ownership from 𝑖. Prove stability of P. Then delete the rely restriction and give one step that invalidates the assertion.
★★☆ Let 𝑚:𝐹(𝐴), 𝑣:𝐴, and let 𝑌:𝖯𝗈𝗌𝗌𝖲𝖾𝗍(𝑉𝐹) be inhabited. Assume every 𝜎 satisfying 𝑌 has 𝜎𝐶(𝑖)=𝖢𝖺𝗅𝗅𝖣𝗈𝗇𝖾(𝑚) and 𝜎𝑅(𝑖)=𝖱𝖾𝗍𝖯𝗈𝗌𝗌(𝑚,𝑣). Prove that 𝖢𝗈𝗇𝗌𝗎𝗆𝖾𝖲𝖾𝗍𝑖,𝑚,𝑣(𝑌) is inhabited and that each consumed 𝜏 has idle call and return entries at 𝑖, preserves the target state, and agrees with its witnessing 𝜎 at every 𝑗≠𝑖. State the witness 𝜏 as explicit map updates. This is the reset-map sub-obligation of 𝖺𝗅𝗅_𝗋𝖾𝗍𝗎𝗋𝗇; no unspecified precondition, guarantee, or continuation relation is to be assumed.
The exchanger needs only a singleton possibility predicate. When the second offer is matched, the paired set transition records both possible returns in one target step; subsequent return steps consume them. Thus the exchanger demonstrates a non-atomic specification, not the necessity of multiple possibilities. The ambiguous queue of proposition 88.4 is the witness that forces a non-singleton set.
Let 𝑉𝐸 be atomic: every invocation transition is immediately followed by its same-thread response. Let 𝖱𝖺𝖼𝗒(𝑉𝐸) add an undefined-behavior state reached by concurrent invocations. If an implementation 𝑀𝐸 accesses 𝖱𝖺𝖼𝗒(𝑉𝐸) only between a successful acquire and its matching release, and a lock implementation is linearizable with respect to the atomic lock specification, then 𝖫𝗂𝗇((𝑉𝖫𝗈𝖼𝗄⊗𝖱𝖺𝖼𝗒(𝑉𝐸))▹𝑀𝐸,𝑉𝐸).
Proof. Use the invariant that at most one thread is between its acquire and release. If no thread owns the lock, no operation of 𝑀𝐸 can take a racy-object transition. If thread 𝑖 owns it, every 𝑗≠𝑖 is blocked before its first racy transition. Therefore the undefined-behavior state is unreachable. Each critical section contains one invocation/response pair of the atomic 𝑉𝐸 and supplies its linearization bracket. The program rules verify the acquire, body, and release sequence under this invariant. Horizontal composition combines lock and racy-object proofs, and vertical composition from theorem 87.8 hides the lock implementation. The remaining visible trace is admitted by 𝑉𝐸. ◻
Without mutual exclusion, two threads may enter the racy state, so the unreachable-state step of the proof fails. Without atomic adjacency in 𝑉𝐸, one critical section need not determine one abstract transition; the theorem then needs an interval or another non-atomic specification.
The one-shot write-snapshot proof uses an interval-sequential target in which one operation may span several classes. Its proof is separate from theorem 88.9; neither theorem is obtained by renaming the other’s target specification.
Proof. Induct on a finite execution of 𝑉𝐸▹𝑀, maintaining an inhabited possibility predicate and one invariant for each thread state.
Idle case. For an idle thread 𝑖, the precondition P𝑖(𝑚) holds for every operation that may next be invoked. The initial case uses field four of 𝖵𝖾𝗋𝗂𝖿𝗒𝖨𝗆𝗉𝗅; other threads preserve it by lemma 88.7.
Continuation case. For 𝖢𝗈𝗇𝗍(𝑚,𝑝), the corresponding program judgment relates the remaining program 𝑝 to its postcondition. A return uses 𝖺𝗅𝗅_𝗋𝖾𝗍𝗎𝗋𝗇; a silent program step uses LHL-Tau; and a visible operation uses the two commit premises of LHL-Vis. Each commit produces an inhabited successor predicate.
Underlay-call case. For 𝖴𝖢𝖺𝗅𝗅(𝑚,𝑢,𝑘), the call commit has been performed and the return commit is pending. An underlay return supplies the return-commit premise, advances the possibilities, and restores a continuation state. Other-thread steps are handled by the rely inclusion.
At the end of a completed execution every thread is idle. The 𝖺𝗅𝗅_𝗋𝖾𝗍𝗎𝗋𝗇 fields ensure that each retained possibility agrees with the concrete returns. Choose one possibility from the inhabited final predicate. Its target trace is the witness that the concrete trace lies in 𝖪(𝑉𝐹), which is 𝖫𝗂𝗇 by definition 87.6. ◻
If 𝖫𝗂𝗇(𝑉𝐸▹𝑀,𝑉𝐹), then there exist five families R,G,P,Q,C for which 𝖵𝖾𝗋𝗂𝖿𝗒𝖨𝗆𝗉𝗅(𝑉𝐸,𝑉𝐹,R,G,P,𝑀,Q,C). This is semantic, or relative, completeness. It is not an algorithm for inferring the five families.
Proof of Theorem 88.11 — Artifact-faithful semantic completeness
Proof. For an interaction state 𝑠 and possibility predicate 𝑃, define I(𝑠,𝑃) by three clauses:
𝑠 is reached by a concrete execution prefix 𝑝;
every 𝜌 satisfying 𝑃 is reached by possibility updates whose overlay projection is the overlay projection of 𝑝;
every target execution compatible with that overlay projection has a prefix represented by some 𝜌 satisfying 𝑃.
The third clause is the saturation that prevents a future-compatible possibility from being discarded.
Take every rely and guarantee to be the relation I(𝑠,𝑃)⟹I(𝑡,𝑄) at the appropriate thread step. Take preconditions, postconditions, and continuation conditions to be the instances of I before invocation, after the program return, and after consuming the overlay return. Reflexivity, transitivity, stability, and cross-thread inclusions follow by concatenating execution prefixes.
At a commit, advance to the predicate containing every compatible target successor. Linearizability supplies at least one such successor; clauses two and three prove successor coverage and retain every future-compatible continuation required by the saturation invariant of definition 88.3. At the end of a function, prune to possibilities that record its actual return, then consume that record. This constructs 𝖺𝗅𝗅_𝗋𝖾𝗍𝗎𝗋𝗇 and the family C. The three program forms derive LHL-Return, LHL-Tau, and LHL-Vis, respectively. These fields assemble the required 𝖵𝖾𝗋𝗂𝖿𝗒𝖨𝗆𝗉𝗅 witness. ◻
The proof constructs assertions from the entire semantic trace relation. Consequently it establishes existence, not usable proof search. Liveness, weak memory, crash behavior, and theorem transfer to Iris, ReLoC, TaDA, or RGSep are outside this system card.
The artifact’s source tree defines the verified exchanger, elimination-array, and stack modules; this chapter does not reproduce their implementation bodies. Combining those source modules uses theorem 88.10 once per module and theorem 87.8 at each vertical or horizontal link. No implementation body is inlined into its client proof.
Suggested first pass.
None of these problems is a prerequisite. Begin with exercise 88.1 and exercise 88.3; for the practical sequence, complete stages 1–3 before the singleton and stability checks.
★★★ Reconstruct the 𝖴𝖢𝖺𝗅𝗅 case of theorem 88.10. State the pending call and return components, apply the return commit, and show that every unselected thread component is unchanged. Finish by identifying the continuation-state invariant obtained.
★★☆ Delete clause three of I in the proof of theorem 88.11. Use the queue trace to show that the remaining invariant permits retaining only [2,1], after which the return-1 update has no successor. This is a counterexample to that weakened invariant, not to the theorem.
★★★Practical project.lhl-possibility-explorer Implement a self-contained Kappa explorer using the queue, possibility, and update definitions supplied in this chapter. Do not consume the checker output of chapter 87. Stage 1 prints {[1,2],[2,1]} after the two enqueues; stage 2 prunes it to {[2]} after deq=1; stage 3 forces singleton [2,1] and prints no-compatible-possibility; stage 4 checks that the exchanger fixture remains singleton; stage 5 checks successor nonemptiness and one stability fixture. Maintain the invariant that every retained possibility has the concrete overlay projection and at least one compatible target continuation. A mutation that retains the incompatible [2,1] state after deq=1 must fail the acceptance test.
Sources. The operational records, possibility representation, program rules, 𝖵𝖾𝗋𝗂𝖿𝗒𝖨𝗆𝗉𝗅 fields, and soundness/completeness boundaries are frozen to Hatti, Oliveira Vale, Wang, Feng, and Shao’s ECOOP 2026 paper, technical report, and artifact commit 6238a59af9515220d41869abf104e14d1dd26dbd. The artifact defines 𝖯𝗈𝗌𝗌𝖲𝖾𝗍 as a predicate, includes C and 𝖺𝗅𝗅_𝗋𝖾𝗍𝗎𝗋𝗇, and proves existence of all five families. Native Rocq replay checks that artifact; it is not evidence that the Kappa explorer proves the logic sound. The paper-level theorem statements are in [HOVW^+26].