fix: Fix antecedent in induction hypothesis #6367
Merged
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.
This PR fixes a soundness issue in automatically generated induction hypotheses.
Fixes #6366
Description of the problem and fix
The problem reported in #6366 was due to a missing antecedent in automatically generated induction hypotheses. For a lemma
the induction hypothesis was previously generated as
This is incorrect, because it gives access to
Pre#CanCall(y)even ifIs(y, X)does not hold. It also gives access toPre#CanCall(y)for anyy, not justys that are smaller thanx; that seems unintentional, but not a soundness issue.The proper way to formulate the induction hypothesis is
This PR makes this change.
Effects of the previous bug
This bug was present in every induction hypothesis generated by auto-induction and so could have had an effect on every
lemma. To exploit the bug, the following conditions would have had to hold:Is(y, X)predicate for typeXdistinguishesyfrom other values of the representation type in Boogie, andIn the reported bug, all 3 apply.
The Dafny type
Xfor which the induction hypothesis was intended isseq<SeqNat>. It has the same underlying Boogie type as the Dafny typeseq<Nat>, since both are essentially a "sequence ofBox" in the Boogie encoding. If such a sequence is nonempty, then its first element has typeSeqNatorNat, respectively. The various constructors of adatatypeare represented as functions with disjoint ranges. The axiomatization is actually stricter; it says that thedatatypeconstructors of all types have disjoint ranges, and thus aSeqNatvalue is known to be different from aNatvalue.The "can call" predicate for each of the two
LessThanfunctions implies theIs(_, X)predicate for its first argument. More precisely, it says that the argument is a value produced by theSeqNatorNatconstructor, respectively. (This is included in the "can call" predicate as a "free fact" for every single-constructordatatype.) By the previous bullet, this distinguishes aSeqNatfrom aNat.The lemma precondition
LessThan(ss, 30) && LessThan(ss, 239)is cruicial, because its "can call" predicate looks likeand this is then used in the induction hypothesis. The occurrence of the
LessThan(ss, 30)is just in a disjunct, so it will not always be in the SMT solver's context. However, if the SMT solver happens to start its case study with the left disjunct,LessThan(ss, 30), then it will continue to instantiate quantifiers based on this term, even if thessis not of the intended type. Lemma learning in SMT solvers today ignores the matching patterns of quantifiers, and therefore the solver will learn the proof offalseon the left branch and will then apply that learnt lemma on the right branch, too. In contrast, if the SMT solver happens to start with the right disjunct, then it will not be able to complete the proof, so an error will be reported. For this reason (which can be called "lack of confluence"), small changes to the repro can mask the soundness issue (i.e., can cause the SMT solver not to find the proof).By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.