Revise the SMT encoding of top-level prop definitions #3505
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.
[Following up on #3440]
Say you define:
The existing behavior for this in the SMT encoding is to produce something like
In other words, with a trigger for proving that
some_pred x : prop
, this axiom introduces an equation in scope definingsome_pred x
in terms of its "deeply embedded" SMT representation as an application of l_forall to a lambda term.This is overkill in many cases, especially when it can be determined from the head-constructor of the definition that
some_pred x
is trivially a prop.The interaction of this behavior with context_pruning is especially unfortunate, since it means that as soon as
some_pred
is reachable (and Prims.unit and Prims.subtype_of, but these are almost always reachable), the deep embedding ofsome_pred
also becomes reachable and context pruning ends up pulling in a whole bunch of definitions l_quant_interp etc. for the deeply embedded quantifier that are unnecessary.So, this PR changes the behavior to the following:
When encoding a top-level "logical" definition like
some_pred x
, if the definition is a sub-singleton (syntactically determined from its head constructor), then instead of the axiom above, we now generateThe result is that when context pruning, we no longer needlessly bring deeply embedded quantified facts into scope, just by mentioned top-level logical symbols.
Impact
I have a green on check-world: https://github.com/FStarLang/FStar/actions/runs/11097438556
Many files that were already using context pruning verify much faster now:
E.g.,
FStar.Sequence.Base.fst: The context is in some cases half as big as previously, with no l_quant_interp facts retained. I observe a significant end-to-end verification time improvement (4.5s now vs 7.2s previously)
Kuiper.MatMul.fst: Previously 8.7s now 4.7s
etc.
There were only two meaningful regressions:
In F* Bug2172: We were relying on the deeply embedded quantifier to bring some constants into scope which results in instantiating an existential quantifier. This is very unpredictable and not something that one should expect to work. I've changed the test case accordingly.
In Pulse: ArraySwap.Proof, the symptom is very similar to Bug2172, where a doubly nested quantifier triggers in an unexpected way to make the proof work. This needed some adjusting. A better fix would be to write a proper pattern on those quantifiers, rather than leaving it to be picked heuristically by Z3.
Note, I've bumped the checked file version, since this changes the SMT encoding and cached declarations in checked files need to be invalidated.
That said, the old behavior is retained with
--ext retain_old_prop_typing
Future improvements
If we get rid of the logical type altogether, then the prop-typing of top-level definitions should just follow from regular typing axioms. When that happens, we should be able to remove this special-casing of prop-typing altogether.