@@ -829,3 +829,83 @@ theorem foldM_eq_sum (xs : Array Nat) {m ps} [Monad m] [LawfulMonad m]
829829 mvcgen
830830 case inv1 => exact ⇓⟨cur, n⟩ => ⌜n = cur.prefix.sum⌝
831831 all_goals grind
832+
833+ /-! ### The partial correctness trick
834+
835+ Specifications are written in the object language as boolean monadic predicates. A triple
836+ hypothesis is consumed by observing its program via `Triple.observe`
837+ and stepping through it with `mvcgen`. -/
838+
839+ namespace PartialCorrectnessTrick
840+
841+ inductive RustError where
842+ | rustPanic
843+ | integerOverflow
844+
845+ abbrev RustM := Except RustError
846+
847+ /-- A boolean monadic predicate holds when it returns `true` without throwing. -/
848+ abbrev RustM.holds (p : RustM Bool) : Prop := ⦃⌜True⌝⦄ p ⦃⇓ r => ⌜r⌝⦄
849+
850+ /-- Running an `Except` program and discarding its result preserves any goal. -/
851+ theorem wp_const (p : RustM α) (C : Assertion (.except RustError .pure)) :
852+ wp⟦p⟧ (⇓ _ => C) ⊢ₛ C := by
853+ cases p with
854+ | ok a => exact SPred.entails.refl _
855+ | error e => exact SPred.false_elim
856+
857+ namespace Unfoldable
858+
859+ def panicAdd (x y : Nat) : RustM Nat := do
860+ if (x + y) ≤ 100 then pure (x + y) else Except.error .integerOverflow
861+
862+ def pre (x : Nat) : RustM Bool := do
863+ if x ≤ 10 then
864+ pure ((← panicAdd x 10 ) == 15 )
865+ else
866+ pure false
867+
868+ def f (x : Nat) : RustM Nat := do
869+ panicAdd x 5
870+
871+ def post (x : Nat) (res : Nat) : RustM Bool := do
872+ pure (res == 10 )
873+
874+ example (x : Nat) :
875+ (pre x).holds → ⦃⌜True⌝⦄ f x ⦃⇓ res => ⌜(post x res).holds⌝⦄ := by
876+ intro h
877+ apply Triple.observe (wp_const (pre x)) h
878+ mvcgen [pre, panicAdd] <;> try grind
879+ intro hx
880+ mvcgen [f, post, panicAdd] <;> try grind
881+ mvcgen [post] <;> grind
882+
883+ end Unfoldable
884+
885+ namespace Opaque
886+
887+ opaque g : Nat → RustM Nat
888+
889+ def pre (x : Nat) : RustM Bool := do pure (x == (← g 0 ))
890+
891+ def f (x : Nat) : RustM Nat := pure (x + 1 )
892+
893+ def post (x : Nat) (res : Nat) : RustM Bool := do pure (res - 1 == (← g 0 ))
894+
895+ /-- Partial-correctness self-specification: if `p` succeeds with `r`, then `p = .ok r`. -/
896+ theorem self_spec (p : RustM α) : ⦃⌜True⌝⦄ p ⦃⇓? r => ⌜p = .ok r⌝⦄ := by
897+ cases p with
898+ | ok a => exact Triple.pure a (by simp)
899+ | error e => apply Triple.of_entails_wp; intro _; exact True.intro
900+
901+ example (x : Nat) :
902+ (pre x).holds → ⦃⌜True⌝⦄ f x ⦃⇓ res => ⌜(post x res).holds⌝⦄ := by
903+ intro h
904+ apply Triple.observe (wp_const (pre x)) h
905+ have hg := self_spec (g 0 )
906+ mvcgen [pre, hg, f] <;> try grind
907+ simp
908+
909+ end Opaque
910+
911+ end PartialCorrectnessTrick
0 commit comments