-
Notifications
You must be signed in to change notification settings - Fork 16
feat(bv_decide): veir_bv_decide_upto tactic #912
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
tobiasgrosser
wants to merge
1
commit into
main
Choose a base branch
from
tobias/bv_upto
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -37,3 +37,117 @@ then calls `bv_decide` to automatically close the goal. | |||||||||||||
| -/ | ||||||||||||||
| @[expose] macro "veir_bv_decide" : tactic => | ||||||||||||||
| `(tactic| ((veir_bv_normalize <;> bv_decide))) | ||||||||||||||
|
|
||||||||||||||
| open Lean Elab Tactic Meta | ||||||||||||||
|
|
||||||||||||||
| public section | ||||||||||||||
|
|
||||||||||||||
| /-- | ||||||||||||||
| Runs a TacticM sequence on a specific target goal (MVarId). | ||||||||||||||
| If the tactic fails or does not fully close the goal, it rolls back any partial state changes. | ||||||||||||||
| -/ | ||||||||||||||
| meta def runTactic (mvarId : MVarId) (tac : TacticM Unit) : TermElabM (Option (List MVarId)) := do | ||||||||||||||
| let mctx ← getMCtx | ||||||||||||||
| try | ||||||||||||||
| let leftover ← Tactic.run mvarId tac | ||||||||||||||
| if leftover.isEmpty then | ||||||||||||||
| return some [] | ||||||||||||||
| else | ||||||||||||||
| setMCtx mctx | ||||||||||||||
| return none | ||||||||||||||
| catch _ => | ||||||||||||||
| setMCtx mctx | ||||||||||||||
| return none | ||||||||||||||
|
|
||||||||||||||
| /-- | ||||||||||||||
| Core loop that splits `x < n` (represented as `Nat.le (Nat.succ x) n`) | ||||||||||||||
| or `x ≤ n` (represented as `Nat.le x n`) directly in MetaM. | ||||||||||||||
| Returns a list of all subgoals that failed to solve automatically. | ||||||||||||||
| -/ | ||||||||||||||
| meta partial def unrollNatCases (mvarId : MVarId) (hId : FVarId) (solver : TacticM Unit) : TacticM (List MVarId) := do | ||||||||||||||
| mvarId.withContext do | ||||||||||||||
| let localDecl ← hId.getDecl | ||||||||||||||
| let type ← whnf localDecl.type | ||||||||||||||
|
|
||||||||||||||
| match type.getAppFnArgs with | ||||||||||||||
| -- Under whnf, both `<` and `≤` resolve to the primitive `Nat.le` relation | ||||||||||||||
| | (``Nat.le, #[_, n]) => | ||||||||||||||
| let nVal ← whnf n | ||||||||||||||
| if nVal.rawNatLit?.isSome then | ||||||||||||||
| -- FIX: We no longer run the solver on the abstract parent goal here. | ||||||||||||||
| -- This prevents the SMT/SAT solver from running on generic, non-instantiated bitwidths | ||||||||||||||
| -- and avoids the "potentially spurious counterexample" error entirely. | ||||||||||||||
|
|
||||||||||||||
| -- Split this level directly | ||||||||||||||
| let subgoals ← mvarId.cases hId | ||||||||||||||
| let mut remainingGoals := [] | ||||||||||||||
| for subgoal in subgoals do | ||||||||||||||
| if subgoal.fields.isEmpty then | ||||||||||||||
| -- This is the 'refl' case (variable instantiated to a concrete numeral). | ||||||||||||||
| -- We run the solver directly on this fully instantiated subgoal. | ||||||||||||||
| let solverRes ← runTactic subgoal.mvarId solver | ||||||||||||||
| if solverRes.isSome then | ||||||||||||||
| remainingGoals := remainingGoals ++ [] | ||||||||||||||
| else | ||||||||||||||
| remainingGoals := remainingGoals ++ [subgoal.mvarId] | ||||||||||||||
|
Comment on lines
+89
to
+92
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
As the then branch is a no-op |
||||||||||||||
| else | ||||||||||||||
| -- This is the 'step' case (the remaining upper bound). | ||||||||||||||
| -- Search the fields to find the actual recursive Nat.le hypothesis | ||||||||||||||
| let unclosed ← subgoal.mvarId.withContext do | ||||||||||||||
| let mut newHId? : Option FVarId := none | ||||||||||||||
| for field in subgoal.fields do | ||||||||||||||
| if let .fvar fvarId := field then | ||||||||||||||
| let fieldType ← whnf (← fvarId.getType) | ||||||||||||||
| if fieldType.isAppOf ``Nat.le then | ||||||||||||||
| newHId? := some fvarId | ||||||||||||||
| break | ||||||||||||||
|
|
||||||||||||||
| match newHId? with | ||||||||||||||
| | some newHId => | ||||||||||||||
| unrollNatCases subgoal.mvarId newHId solver | ||||||||||||||
| | none => | ||||||||||||||
| -- Fallback: if we somehow couldn't find the Nat.le hypothesis, run the solver | ||||||||||||||
| let solverRes ← runTactic subgoal.mvarId solver | ||||||||||||||
| if solverRes.isSome then | ||||||||||||||
| pure [] | ||||||||||||||
| else | ||||||||||||||
| pure [subgoal.mvarId] | ||||||||||||||
| remainingGoals := remainingGoals ++ unclosed | ||||||||||||||
| return remainingGoals | ||||||||||||||
| else | ||||||||||||||
| -- If upper bound is zero (contradiction case), let omega clean it up | ||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the case of a non-literal symbol, rather than the 0 case? |
||||||||||||||
| let omegaRes ← runTactic mvarId (evalTactic (← `(tactic| omega))) | ||||||||||||||
| if omegaRes.isSome then | ||||||||||||||
| return [] | ||||||||||||||
| else | ||||||||||||||
| return [mvarId] | ||||||||||||||
| | _ => | ||||||||||||||
| -- Fallback to omega if it's some other relation | ||||||||||||||
| let omegaRes ← runTactic mvarId (evalTactic (← `(tactic| omega))) | ||||||||||||||
| if omegaRes.isSome then | ||||||||||||||
| return [] | ||||||||||||||
| else | ||||||||||||||
| return [mvarId] | ||||||||||||||
|
|
||||||||||||||
| syntax (name := veirBvDecideUpto) "veir_bv_decide_upto " ident : tactic | ||||||||||||||
|
|
||||||||||||||
| @[tactic veirBvDecideUpto] | ||||||||||||||
| meta def evalVeirBvDecideUpto : Tactic := fun stx => do | ||||||||||||||
| let hId ← match stx with | ||||||||||||||
| | `(tactic| veir_bv_decide_upto $h:ident) => pure h.getId | ||||||||||||||
| | _ => throwUnsupportedSyntax | ||||||||||||||
|
|
||||||||||||||
| let mvarId ← getMainGoal | ||||||||||||||
|
|
||||||||||||||
| mvarId.withContext do | ||||||||||||||
| let lctx ← getLCtx | ||||||||||||||
| let localDecl ← match lctx.findFromUserName? hId with | ||||||||||||||
| | some d => pure d | ||||||||||||||
| | none => throwError "Hypothesis {hId} not found" | ||||||||||||||
|
|
||||||||||||||
| let solver : TacticM Unit := do | ||||||||||||||
| evalTactic (← `(tactic| veir_bv_decide)) | ||||||||||||||
|
|
||||||||||||||
| -- Collect all goals that could not be proved | ||||||||||||||
| let leftoverGoals ← unrollNatCases mvarId localDecl.fvarId solver | ||||||||||||||
| setGoals leftoverGoals | ||||||||||||||
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this comment for? Is it for an issue that we should add, or should the comment be removed?