@@ -319,25 +319,74 @@ elab "bmc" : tactic => withMainContext do
319319 trace[veil.smt] "{ tac} "
320320 evalTactic $ tac
321321
322- /-- Tactic to solve `sat_trace` goals. -/
323- elab "bmc_sat" : tactic => withMainContext do
324- let prep_tac ← `(tactic|
325- (try simplify_all);
326- (try
327- negate_goal;
328- simp only [Classical.exists_elim, Classical.not_not];
329- (unhygienic intros);
330- sdestruct_hyps);
331- (try simplify_all);
332- /- Needed to work around [ lean-smt#100 ] (https://github.com/ufmg-smite/lean-smt/issues/100) -/
333- (try rename_binders)
334- )
335- trace[veil.smt] "{ prep_tac} "
336- evalTactic prep_tac
337- if (← getUnsolvedGoals).length != 0 then
338- /- After preparing the context, call `sauto` on it. -/
339- withMainContext do
340- let idents ← getPropsInContext
341- let auto_tac ← `(tactic| admit_if_satisfiable [$[$idents:ident],*])
342- trace[veil.smt] "{ auto_tac} "
343- evalTactic auto_tac
322+ open Lean.Meta in
323+ def bmcSat : TacticM Unit := withMainContext do
324+ let originalGoal ← Tactic.getMainGoal
325+ -- Operate on a duplicated goal
326+ let goal' ← mkFreshExprMVar (← Tactic.getMainTarget)
327+ replaceMainGoal [goal'.mvarId!]
328+ run `(tactic| simplify_all)
329+ if (← getUnsolvedGoals).length == 0 then
330+ trace[veil.info] "goal is solved by initial simplification"
331+ originalGoal.admit (synthetic := false )
332+ else
333+ trace[veil.info] "goal is not solved by initial simplification"
334+ existIntoForall
335+ let simpLemmas := mkSimpLemmas $ #[`smtSimp].map mkIdent
336+ withMainContext do run `(tactic| unhygienic intros; sdestruct_hyps; (try simp only [$simpLemmas,*]))
337+ if (← getUnsolvedGoals).length == 0 then
338+ trace[veil.info] "goal is solved by existential elimination"
339+ originalGoal.admit (synthetic := false )
340+ else
341+ trace[veil.info] "proceeding with SMT"
342+ trace[veil.info] "goal: { ← Tactic.getMainTarget} "
343+ admitIfSat
344+ if (← getUnsolvedGoals).length == 0 then
345+ originalGoal.admit (synthetic := false )
346+ else
347+ throwError "goal is not solved by SMT"
348+ where
349+ existIntoForall := withMainContext do
350+ let goalType' ← turnExistsIntoForall (← Tactic.getMainTarget)
351+ let goal' ← mkFreshExprMVar goalType'
352+ replaceMainGoal [goal'.mvarId!]
353+ /-- UNSAFE: admits the goal if it is satisfiable, taking into account
354+ all hypotheses in the context. -/
355+ admitIfSat : TacticM Unit := withMainContext do
356+ let opts ← getOptions
357+ let mv ← Tactic.getMainGoal
358+ let idents ← getPropsInContext
359+ let hints ← `(Smt.Tactic.smtHints|[$[$idents:ident],*])
360+ let hs ← Smt.Tactic.parseHints ⟨hints⟩
361+ let withTimeout := veil.smt.timeout.get opts
362+ -- IMPORTANT: `prepareLeanSmtQuery` (in `Smt.prepareSmtQuery`) negates
363+ -- the goal (it's designed for validity checking), so we negate it here
364+ -- to counter-act this.
365+ -- NOTE: we don't respect `veil.smt.translator` here, since we want to
366+ -- print a readable model, which requires `lean-smt`.
367+ let mv' ← mkFreshExprMVar (mkNot $ ← Tactic.getMainTarget)
368+ let leanSmtQueryString ← Veil.SMT.prepareLeanSmtQuery mv'.mvarId! hs
369+ let res ← Veil.SMT.querySolver leanSmtQueryString withTimeout (retryOnUnknown := true )
370+ match res with
371+ | .Sat .none => mv.admit (synthetic := false )
372+ | .Sat (some modelString) =>
373+ -- try to generate a readable model, using `lean-smt`
374+ let resStr := match ← Veil.SMT.getReadableModel leanSmtQueryString withTimeout (minimize := veil.smt.model.minimize.get opts) with
375+ | .some fostruct => s! "{ fostruct} "
376+ | .none => s! "(could not get readable model)\n { modelString} "
377+ logInfo resStr
378+ mv.admit (synthetic := false )
379+ | .Unknown reason => throwError "{Veil.SMT.unknownGoalStr}{if reason.isSome then s!" : {reason.get!}" else ""}"
380+ | .Failure reason => throwError "{Veil.SMT.failureGoalStr}{if reason.isSome then s!" : {reason.get!}" else ""}"
381+ | .Unsat => throwError "{Veil.SMT.satGoalStr}"
382+
383+ /-- Tactic to solve `sat trace` goals.
384+
385+ Given a goal of the form `∃ x, P x` (valid), we prove it by showing `P c`
386+ is _satisfiable_ for some constant `c`.
387+
388+ In principle, we could use the model returned by the SMT solver to
389+ instantiate the existential quantifiers, and thus avoid the need to
390+ trust the solver, but this is not implemented yet.
391+ -/
392+ elab "bmc_sat" : tactic => bmcSat
0 commit comments