/-- Both threads work for a while, then both write `[9]`. -/deflate:Prog:=.par(.seq(.wr101)(.wr91))(.seq(.wr201)(.wr92))/-- Everything starts at `0`. -/defσ0:State:=fun_=>.int0-- Phase 1 walks the tree and collects every fork whose children fail Bernstein. It is linear in-- the program, and it *is* the obligation: `raceSites_nil_iff` says an empty list is race freedom.#evalraceSiteslate.toParMσ0[{ left := { reads := [], writes := [10, 9] }, right := { reads := [], writes := [20, 9] }, conflicts := [9] }]-- so the program is rejected, by evaluation rather than by searchexample:wellFormedBlate.toParMσ0=false:=⊢ wellFormedBlate.toParMσ0=falseAll goals completed! 🐙-- Phase 2 is a bounded search, aimed at the cells phase 1 named. It returns a `Schedule`, and-- `follow_sound` turns any schedule that replays into a `Reaches` proof — so a wrong guess-- cannot produce a wrong claim, it just fails to find anything.#check@follow_sound@follow_sound : ∀{τ:Type}(s:Schedule)(cc':Confτ),followcs=somec'→Reachescc'-- the counterexample, replayed against the real `step`#evalIO.println(explainProglateσ0)step:thread action state
0:T0 write [10] ← 1 10↦1, 9↦0, 20↦0
1:T1 write [20] ← 1 10↦1, 9↦0, 20↦1
2:T1 write [9] ← 2 pending
2:T0 write [9] ← 1 pending
Parallel aborts at Step 2. Reason: T0, T1 write to: {9}.
Parallel: 9↦1
Sequential: 9↦2