Skip to content

Commit a44dd71

Browse files
Scott Morrisonleodemoura
Scott Morrison
authored andcommitted
feat: add flag for apply to defer failed typeclass syntheses as goals
1 parent 82196b5 commit a44dd71

File tree

3 files changed

+48
-7
lines changed

3 files changed

+48
-7
lines changed

src/Lean/Meta/Tactic/Apply.lean

+16-7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ structure ApplyConfig where
2727
-/
2828
synthAssignedInstances := true
2929
/--
30+
If `allowSynthFailures` is `true`, then `apply` will return instance implicit arguments
31+
for which typeclass search failed as new goals.
32+
-/
33+
allowSynthFailures := false
34+
/--
3035
If `approx := true`, then we turn on `isDefEq` approximations. That is, we use
3136
the `approxDefEq` combinator.
3237
-/
@@ -47,15 +52,18 @@ def getExpectedNumArgs (e : Expr) : MetaM Nat := do
4752
private def throwApplyError {α} (mvarId : MVarId) (eType : Expr) (targetType : Expr) : MetaM α :=
4853
throwTacticEx `apply mvarId m!"failed to unify{indentExpr eType}\nwith{indentExpr targetType}"
4954

50-
def synthAppInstances (tacticName : Name) (mvarId : MVarId) (newMVars : Array Expr) (binderInfos : Array BinderInfo) (synthAssignedInstances : Bool) : MetaM Unit :=
55+
def synthAppInstances (tacticName : Name) (mvarId : MVarId) (newMVars : Array Expr) (binderInfos : Array BinderInfo)
56+
(synthAssignedInstances : Bool) (allowSynthFailures : Bool) : MetaM Unit :=
5157
newMVars.size.forM fun i => do
5258
if binderInfos[i]!.isInstImplicit then
5359
let mvar := newMVars[i]!
5460
if synthAssignedInstances || !(← mvar.mvarId!.isAssigned) then
5561
let mvarType ← inferType mvar
56-
let mvarVal ← synthInstance mvarType
57-
unless (← isDefEq mvar mvarVal) do
58-
throwTacticEx tacticName mvarId "failed to assign synthesized instance"
62+
try
63+
let mvarVal ← synthInstance mvarType
64+
unless (← isDefEq mvar mvarVal) do
65+
throwTacticEx tacticName mvarId "failed to assign synthesized instance"
66+
catch e => unless allowSynthFailures do throw e
5967

6068
def appendParentTag (mvarId : MVarId) (newMVars : Array Expr) (binderInfos : Array BinderInfo) : MetaM Unit := do
6169
let parentTag ← mvarId.getTag
@@ -76,8 +84,9 @@ If `synthAssignedInstances` is `true`, then `apply` will synthesize instance imp
7684
even if they have assigned by `isDefEq`, and then check whether the synthesized value matches the
7785
one inferred. The `congr` tactic sets this flag to false.
7886
-/
79-
def postprocessAppMVars (tacticName : Name) (mvarId : MVarId) (newMVars : Array Expr) (binderInfos : Array BinderInfo) (synthAssignedInstances := true) : MetaM Unit := do
80-
synthAppInstances tacticName mvarId newMVars binderInfos synthAssignedInstances
87+
def postprocessAppMVars (tacticName : Name) (mvarId : MVarId) (newMVars : Array Expr) (binderInfos : Array BinderInfo)
88+
(synthAssignedInstances := true) (allowSynthFailures := false) : MetaM Unit := do
89+
synthAppInstances tacticName mvarId newMVars binderInfos synthAssignedInstances allowSynthFailures
8190
-- TODO: default and auto params
8291
appendParentTag mvarId newMVars binderInfos
8392

@@ -163,7 +172,7 @@ def _root_.Lean.MVarId.apply (mvarId : MVarId) (e : Expr) (cfg : ApplyConfig :=
163172
let (_, _, eType) ← forallMetaTelescopeReducing eType (some rangeNumArgs.start)
164173
throwApplyError mvarId eType targetType
165174
let (newMVars, binderInfos) ← go rangeNumArgs.start
166-
postprocessAppMVars `apply mvarId newMVars binderInfos cfg.synthAssignedInstances
175+
postprocessAppMVars `apply mvarId newMVars binderInfos cfg.synthAssignedInstances cfg.allowSynthFailures
167176
let e ← instantiateMVars e
168177
mvarId.assign (mkAppN e newMVars)
169178
let newMVars ← newMVars.filterM fun mvar => not <$> mvar.mvarId!.isAssigned

tests/lean/2273.lean

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import Lean
2+
3+
class P (n : Nat)
4+
5+
theorem foo (n : Nat) [P n] : True := trivial
6+
7+
-- This should fail, as by default `apply` does not allow synthesis failures.
8+
example : True := by
9+
apply foo 37
10+
11+
open Lean Meta Elab Tactic Term
12+
13+
/--
14+
In mathlib4 we add the syntax:
15+
16+
`apply (config := cfg) e` is like `apply e` but allows you to provide a configuration
17+
`cfg : ApplyConfig` to pass to the underlying apply operation.
18+
19+
For this test we just hard code the `allowSynthFailures` option into `apply'`.
20+
-/
21+
elab "apply'" e:term : tactic => do
22+
evalApplyLikeTactic (·.apply · { allowSynthFailures := true }) e
23+
24+
def instP (n : Nat) : P n := {}
25+
26+
example : True := by
27+
apply' foo
28+
apply instP
29+
exact 37
30+

tests/lean/2273.lean.expected.out

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2273.lean:9:8-9:14: error: failed to synthesize instance
2+
P 37

0 commit comments

Comments
 (0)