@@ -32,6 +32,8 @@ structure Config where
3232 mono : Bool := false
3333 /-- Whether to eliminate `↔` in the Lean goal before sending it to the SMT solver. -/
3434 elimIff : Bool := true
35+ /-- Whether to replace `ite` instances in the Lean goal before sending it to the SMT solver. -/
36+ replaceIteInst : Bool := true
3537 /-- Whether to trust the result of the SMT solver. Closes the current goal with a `sorry` if the
3638 SMT solver returns `unsat`. **Warning** : use with caution, as this may lead to unsoundness.
3739 Additionally adds the translation from Lean to SMT to the trusted code base, which is not
@@ -71,11 +73,12 @@ def smt (cfg : Config) (mv : MVarId) (hs : Array Expr) : MetaM Result := mv.with
7173 let ⟨map₁, hs₁, mv₂⟩ ← (if cfg.mono then Preprocess.mono else Preprocess.pushHintsToCtx) mv₁ hs
7274 -- 2. Preprocess the hypotheses and goal.
7375 let ⟨map₂, hs₂, mv₂⟩ ← if cfg.elimIff then Preprocess.elimIff mv₂ hs₁ else pure ⟨map₁, hs₁, mv₂⟩
74- mv₂.withContext do
75- let goalType : Q(Prop ) ← mv₂.getType
76+ let ⟨map₃, hs₃, mv₃⟩ ← if cfg.replaceIteInst then Preprocess.replaceIteInst mv₂ hs₂ else pure ⟨map₂, hs₂, mv₂⟩
77+ mv₃.withContext do
78+ let goalType : Q(Prop ) ← mv₃.getType
7679 -- 3. Generate the SMT query.
7780 let (fvNames₁, fvNames₂) ← genUniqueFVarNames
78- let cmds ← prepareSmtQuery hs₂ .toList (← mv₂ .getType) fvNames₁
81+ let cmds ← prepareSmtQuery hs₃ .toList (← mv₃ .getType) fvNames₁
7982 let cmds := .setLogic "ALL" :: cmds
8083 if cfg.showQuery then
8184 logInfo m! "goal: { goalType} \n\n query:\n { Command.cmdsAsQuery (cmds ++ [.checkSat])} "
@@ -85,6 +88,7 @@ def smt (cfg : Config) (mv : MVarId) (hs : Array Expr) : MetaM Result := mv.with
8588 trace[smt] "goal: { goalType} "
8689 trace[smt] "\n query:\n { Command.cmdsAsQuery (cmds ++ [.checkSat])} "
8790 -- 4. Run the solver.
91+ -- throwError "here1"
8892 let res ← solve (Command.cmdsAsQuery cmds) cfg.timeout (defaultSolverOptions ++ cfg.extraSolverOptions)
8993 -- trace[ smt ] "\nresult: {res}"
9094 match res with
@@ -101,9 +105,12 @@ def smt (cfg : Config) (mv : MVarId) (hs : Array Expr) : MetaM Result := mv.with
101105 let ctx := { userNames := fvNames₂, native := cfg.native }
102106 let (uc, _) ← (uc.mapM Reconstruct.reconstructTerm).run ctx {}
103107 trace[smt] "unsat core: { uc} "
104- let ts₂ ← hs₂.mapM Meta.inferType
105- let uc := uc.filterMap fun p => ts₂.findIdx? (· == p) >>= (hs₂[·]?)
106- let uc := uc.filterMap (map₂[·]?)
108+ trace[smt] "unsat core: { mv₃} "
109+ -- throwError "here2"
110+ let ts₃ ← hs₃.mapM Meta.inferType
111+ let uc := uc.filterMap fun p => ts₃.findIdx? (· == p) >>= (hs₃[·]?)
112+ let uc := uc.filterMap (map₃[·]?)
113+ let uc := uc.flatten.filterMap (map₂[·]?)
107114 let uc := uc.flatten.filterMap (map₁[·]?)
108115 let uc := hs.filter uc.flatten.contains
109116 if cfg.trust then
@@ -112,10 +119,10 @@ def smt (cfg : Config) (mv : MVarId) (hs : Array Expr) : MetaM Result := mv.with
112119 return .unsat [] uc
113120 -- 7. Reconstruct proof.
114121 let (_, ps, p, hp, mvs) ← reconstructProof pf ctx
115- let mv₂ ← mv₂ .assert (← mkFreshId) p hp
116- let ⟨_, mv₂ ⟩ ← mv₂ .intro1
117- let mut gs ← mv₂ .apply (← Meta.mkAppOptM ``Prop .implies_of_not_and #[listExpr ps.dropLast q(Prop ), goalType])
118- mv₂ .withContext (gs.forM (·.assumption))
122+ let mv₃ ← mv₃ .assert (← mkFreshId) p hp
123+ let ⟨_, mv₃ ⟩ ← mv₃ .intro1
124+ let mut gs ← mv₃ .apply (← Meta.mkAppOptM ``Prop .implies_of_not_and #[listExpr ps.dropLast q(Prop ), goalType])
125+ mv₃ .withContext (gs.forM (·.assumption))
119126 mv.assign (.mvar mv₁)
120127 return .unsat mvs uc
121128 | .ok (.sat model) =>
@@ -242,11 +249,29 @@ def elabHints : TSyntax ``smtHints → TacticM (Std.HashMap Expr (TSyntax ``smtH
242249 | `(smtHints| ) => return ({}, #[])
243250 | _ => throwUnsupportedSyntax
244251
252+ /-- Returns `true` if `fv` corresponds to a proposition in the local context. -/
253+ def isPropHyp (fv : FVarId) : MetaM Bool := do
254+ let localDecl ← fv.getDecl
255+ unless localDecl.isImplementationDetail do
256+ if ← pure !(isNonEmpty localDecl.type) <&&> Meta.isProp localDecl.type then
257+ return true
258+ return false
259+ where
260+ isNonEmpty (e : Expr) : Bool :=
261+ match e with
262+ | .app (.const ``Nonempty _) _ => true
263+ | .forallE _ _ b _ => isNonEmpty b
264+ | _ => false
265+
245266def evalSmtCore (cfg : TSyntax ``Parser.Tactic.optConfig) (hs : TSyntax ``smtHints) := withMainContext do
246267 let cfg ← elabConfig cfg
247- let mv ← Tactic.getMainGoal
248268 let (map, hs) ← elabHints hs
249- let res ← Smt.smt cfg mv hs
269+ let mv ← Tactic.getMainGoal
270+ let (fvs, mv) ← mv.intros
271+ Tactic.replaceMainGoal [mv]
272+ let fvs ← liftM (mv.withContext (fvs.filterM isPropHyp))
273+ let hs' := fvs.map Expr.fvar
274+ let res ← Smt.smt cfg mv (hs ++ hs')
250275 match res with
251276 | .sat none =>
252277 throwError "unable to prove goal, either it is false or you need to provide more facts. Try adding '+model' config option to display a potential counter-example (experimental)."
@@ -260,6 +285,7 @@ def evalSmtCore (cfg : TSyntax ``Parser.Tactic.optConfig) (hs : TSyntax ``smtHin
260285 throwError "unable to prove goal, either it is false or you need to provide more facts. Here is a potential counter-example:\n {md}"
261286 | .unsat mvs uc =>
262287 Tactic.replaceMainGoal mvs
288+ let uc := uc.filter (!hs'.contains ·)
263289 let uc := uc.filterMap map.get?
264290 let uc := uc.toList.eraseDups.toArray
265291 return uc
0 commit comments