fix(phl): check the bound of the upper-bound while rule on all memories - #1130
Open
Yiping106283 wants to merge 1 commit into
Open
fix(phl): check the bound of the upper-bound while rule on all memories#1130Yiping106283 wants to merge 1 commit into
while rule on all memories#1130Yiping106283 wants to merge 1 commit into
Conversation
…ries
Summary: the pHL `while` tactic (invariant only, `<=`) accepted
`phoare[M.p : true ==> false] <= (-1%r)` for `while (true) {}` (upstream
EasyCrypt#1102), and likewise with the invariant `false` and a diverging prefix.
Root cause (src/phl/ecPhlWhile.ml, `t_bdhoare_while_rev_r`): the rule is a
fixpoint induction whose base case (the loop diverges: probability 0) and
exit case need `0%r <= bd`, and whose exit case needs
`inv /\ !e /\ post => bd = 1%r`. The exit condition was emitted inside the
post-condition of the hoare judgment on the prefix, which only has to hold
on its terminating runs, and `0%r <= bd` was not required at all, so a
diverging loop -- or a diverging prefix with the invariant `false` -- proved
any bound.
Fix: the prefix goal becomes `hoare[s : pre ==> inv]`; the exit condition
`forall &hr, inv /\ !e /\ post => bd = 1%r` and the non-negativity
`forall &hr, pre \/ inv => 0%r <= bd` are emitted by the rule as separate
goals quantified over all memories (always, in this order, after the body
and prefix goals). The `while` tactic (`process_while`) tries `t_trivial` on
the two of them so trivial cases stay effort-free. examples/PRG.ec closes
the two relocated goals.
Test: tests/phoare-while-neg-bound.ec (invariants `true` and `false`). The
remaining goal `0%r <= -1%r` is introduced with `move=> &hr _` (which fails
when the goal is not emitted) and asserted unprovable with `fail (by smt())`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1102.
Summary
The pHL
whiletactic (invariant only,<=) acceptedphoare[M.p : true ==> false] <= (-1%r)forwhile (true) {}, and likewise with the invariantfalseand a diverging prefix (the variant noted by @oskgo in #1119). Following #1095, the conditions on the bound are now emitted by the rule as separate goals quantified over all memories, and thewhiletactic triest_trivialon them. The semantics of phoare judgements are unchanged.A distinct bug of the same rule, the bound being written by the statements preceding the loop, is not fixed here (its goals hold for that example); see #1124 and the companion PR #1131.
Root cause (
src/phl/ecPhlWhile.ml,t_bdhoare_while_rev_r)The rule is a fixpoint induction whose base case (the loop diverges: probability 0) and exit case need
0%r <= bd, and whose exit case needsinv /\ !e /\ post => bd = 1%r. The exit condition was emitted inside the post-condition of the hoare judgment on the prefix, which only has to hold on its terminating runs, and0%r <= bdwas not required at all, so a diverging loop -- or a diverging prefix with the invariantfalse-- proved any bound.Fix
The prefix goal becomes
hoare[s : pre ==> inv]; the exit conditionforall &hr, inv /\ !e /\ post => bd = 1%rand the non-negativityforall &hr, pre \/ inv => 0%r <= bdare emitted by the rule as separate goals quantified over all memories (always, in this order, after the body and prefix goals).pre \/ invis exactly what the derivation consumes:prefor the mass lost to a diverging prefix,invfor the base and!post-exit cases of the induction. Thewhiletactic (process_while) triest_trivialon the two of them so trivial cases stay effort-free.The exit condition is kept as
bd = 1%r(the condition the rule always had, merely relocated); the exact need is1%r <= bd, and it can be relaxed to that at zero cost if preferred.Impact
while invon a<=judgement now yields four goals instead of two (body, prefix, exit bound, non-negativity); the last two are closed automatically when trivial (literal bounds such as1%r,0%rwithpost = false, or0%r <= bdin context). Awhilewhose old prefix goal was closed byauto/skipwith the exit condition inside the post-condition now closes the relocated goals separately.examples/PRG.eccloses the two relocated goals (one line each). No library file is affected.Test
tests/phoare-while-neg-bound.ec(invariantstrueandfalse): the remaining goal0%r <= -1%ris introduced withmove=> &hr _(which fails when the goal is not emitted) and asserted unprovable withfail (by smt()).make unitandmake stdlibpass;examples/PRG.eccompiles with the fixed binary.