fix(phl): reject a bound written by the prefix in the upper-bound while rule - #1131
Open
Yiping106283 wants to merge 1 commit into
Open
fix(phl): reject a bound written by the prefix in the upper-bound while rule#1131Yiping106283 wants to merge 1 commit into
while rule#1131Yiping106283 wants to merge 1 commit into
Conversation
…ile` rule
Summary: the pHL `while` tactic (invariant only, `<=`) accepted
`phoare[P.p : true ==> true] <= (if P.y = 0 then 1%r else 0%r)` for
`y <- 0; while (x < 1) { x <- x + 1; }`, which is false whenever `P.y <> 0`
initially (the procedure terminates with probability 1). This is distinct
from EasyCrypt#1102: the goals introduced for EasyCrypt#1102 (exit bound, non-negativity)
hold for this bound with the invariant `P.y = 0`.
Root cause (src/phl/ecPhlWhile.ml, `t_bdhoare_while_rev_r`): the bound of
the conclusion is interpreted in the initial memory, while the body goal
(`bdhoare[w : inv ==> post] <= bd => bdhoare[c; w : inv /\ e ==> post] <= bd`)
interprets it in the memory the loop starts from. Nothing prevented the
statements preceding the loop from writing the bound, in which case the two
differ.
Fix: the tactic rejects a bound that depends on variables written by the
statements preceding the loop, as `while ... : k eps`
(`t_bdhoare_while_rev_geq_r`) already does for its own arguments. A more
permissive alternative is a non-modification goal in the style of `seq`
(`forall r, hoare[s : pre /\ r = bd ==> r = bd]`).
Test: tests/phoare-while-prefix-bound.ec (`fail (while (true))`).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Sep 11, 2026
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 the
whilepart of #1124.Summary
The pHL
whiletactic (invariant only,<=) acceptedphoare[P.p : true ==> true] <= (if P.y = 0 then 1%r else 0%r)fory <- 0; while (x < 1) { x <- x + 1; }, which is false wheneverP.y <> 0initially (the procedure terminates with probability 1). This is distinct from #1102: the goals introduced for #1102 (exit bound, non-negativity) hold for this bound with the invariantP.y = 0, and the two fixes are independent (this one applies onmainwith or without the #1102 PR #1130).Root cause (
src/phl/ecPhlWhile.ml,t_bdhoare_while_rev_r)The bound of the conclusion is interpreted in the initial memory, while the body goal (
bdhoare[w : inv ==> post] <= bd => bdhoare[c; w : inv /\ e ==> post] <= bd) interprets it in the memory the loop starts from. Nothing prevented the statements preceding the loop from writing the bound, in which case the two differ. Thecallandrnd phi d1 d2 d3 d4rules have the same shape (#1124);rnd Eis not affected, as it already binds the bound to a fresh local when the prefix writes it.Fix
The tactic rejects a bound that depends on variables written by the statements preceding the loop (
PV.indep env (s_write env rem_s) (PV.fv env m bd), error "The bound cannot depend on variables written by the statements preceding the loop"), aswhile ... : k eps(t_bdhoare_while_rev_geq_r) already does for its own arguments. This is over-strict for true judgements such asphoare[P.p : P.y = 0 ==> true] <= (P.y%r + 1%r)(workaround:conseqto a constant bound first). A more permissive, equally sound alternative is aseq-style non-modification goalforall r, hoare[s : pre /\ r = bd ==> r = bd](condnminsrc/phl/ecPhlSeq.ml), tried witht_pl_trivialand left to the user otherwise; the hard error was chosen for consistency within the file and can be switched if preferred.Impact
while invon a<=judgement now fails with a tactic error when the bound mentions a variable written before the loop. No library or example file is affected.Test
tests/phoare-while-prefix-bound.ec:fail (while (true))on the false judgement above (with the boundP.y%r + 1%r).make unitandmake stdlibpass.