-
Notifications
You must be signed in to change notification settings - Fork 465
/
Copy pathSyntheticMVars.lean
536 lines (490 loc) · 25.8 KB
/
SyntheticMVars.lean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
/-
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Sebastian Ullrich
-/
prelude
import Lean.Meta.Tactic.Util
import Lean.Util.ForEachExpr
import Lean.Util.OccursCheck
import Lean.Elab.Tactic.Basic
namespace Lean.Elab.Term
open Tactic (TacticM evalTactic getUnsolvedGoals withTacticInfoContext)
open Meta
/-- Auxiliary function used to implement `synthesizeSyntheticMVars`. -/
private def resumeElabTerm (stx : Syntax) (expectedType? : Option Expr) (errToSorry := true) : TermElabM Expr :=
-- Remark: if `ctx.errToSorry` is already false, then we don't enable it. Recall tactics disable `errToSorry`
withReader (fun ctx => { ctx with errToSorry := ctx.errToSorry && errToSorry }) do
elabTerm stx expectedType? false
/--
Try to elaborate `stx` that was postponed by an elaboration method using `Expection.postpone`.
It returns `true` if it succeeded, and `false` otherwise.
It is used to implement `synthesizeSyntheticMVars`. -/
private def resumePostponed (savedContext : SavedContext) (stx : Syntax) (mvarId : MVarId) (postponeOnError : Bool) : TermElabM Bool :=
withRef stx <| mvarId.withContext do
let s ← saveState
try
withSavedContext savedContext do
let mvarDecl ← getMVarDecl mvarId
let expectedType ← instantiateMVars mvarDecl.type
withInfoHole mvarId do
let result ← resumeElabTerm stx expectedType (!postponeOnError)
/- We must ensure `result` has the expected type because it is the one expected by the method that postponed stx.
That is, the method does not have an opportunity to check whether `result` has the expected type or not. -/
let result ← withRef stx <| ensureHasType expectedType result
/- We must perform `occursCheck` here since `result` may contain `mvarId` when it has synthetic `sorry`s. -/
if (← occursCheck mvarId result) then
mvarId.assign result
return true
else
return false
catch
| ex@(.internal id _) =>
if id == postponeExceptionId then
s.restore (restoreInfo := true)
return false
else
throw ex
| ex@(.error ..) =>
if postponeOnError then
s.restore (restoreInfo := true)
return false
else
logException ex
return true
/--
Similar to `synthesizeInstMVarCore`, but makes sure that `instMVar` local context and instances
are used. It also logs any error message produced. -/
private def synthesizePendingInstMVar (instMVar : MVarId) (extraErrorMsg? : Option MessageData := none): TermElabM Bool :=
instMVar.withContext do
try
synthesizeInstMVarCore instMVar (extraErrorMsg? := extraErrorMsg?)
catch
| ex@(.error ..) => logException ex; return true
| _ => unreachable!
/--
Try to synthesize `mvarId` by starting using a default instance with the give privority.
This method succeeds only if the metavariable of fully synthesized.
Remark: In the past, we would return a list of pending TC problems, but this was problematic since
a default instance may create subproblems that cannot be solved.
Remark: The new approach also has limitations because other pending metavariables are not taken into account
while backtraking. That is, we fail to synthesize `mvarId` because we reach subproblems that are stuck,
but we could "unstuck" them if we tried to solve other pending metavariables. Considering all pending metavariables
into a single backtracking search seems to be too expensive, and potentially generate incomprehensible error messages.
This is particularly true if we consider pending metavariables for "postponed" elaboration steps.
Here is an example that demonstrate this issue. The example considers we are using the old `binrel%` elaborator which was
disconnected from `binop%`.
```
example (a : Int) (b c : Nat) : a = ↑b - ↑c := sorry
```
We have two pending coercions for the `↑` and `HSub ?m.220 ?m.221 ?m.222`.
When we did not use a backtracking search here, then the homogenous default instance for `HSub`.
```
instance [Sub α] : HSub α α α where
```
would be applied first, and would propagate the expected type `Int` to the pending coercions which would now be unblocked.
Instead of performing a backtracking search that considers all pending metavariables, we improved the `binrel%` elaborator.
-/
private partial def synthesizeUsingDefaultPrio (mvarId : MVarId) (prio : Nat) : TermElabM Bool :=
mvarId.withContext do
let mvarType ← mvarId.getType
match (← isClass? mvarType) with
| none => return false
| some className =>
match (← getDefaultInstances className) with
| [] => return false
| defaultInstances =>
for (defaultInstance, instPrio) in defaultInstances do
if instPrio == prio then
if (← synthesizeUsingDefaultInstance mvarId defaultInstance) then
return true
return false
where
synthesizeUsingDefault (mvarId : MVarId) : TermElabM Bool := do
for prio in (← getDefaultInstancesPriorities) do
if (← synthesizeUsingDefaultPrio mvarId prio) then
return true
return false
synthesizePendingInstMVar' (mvarId : MVarId) : TermElabM Bool :=
commitWhen <| mvarId.withContext do
try
synthesizeInstMVarCore mvarId
catch _ =>
return false
synthesizeUsingInstancesStep (mvarIds : List MVarId) : TermElabM (List MVarId) :=
mvarIds.filterM fun mvarId => do
if (← synthesizePendingInstMVar' mvarId) then
return false
else
return true
synthesizeUsingInstances (mvarIds : List MVarId) : TermElabM (List MVarId) := do
let mvarIds' ← synthesizeUsingInstancesStep mvarIds
if mvarIds'.length < mvarIds.length then
synthesizeUsingInstances mvarIds'
else
return mvarIds'
synthesizeUsingDefaultInstance (mvarId : MVarId) (defaultInstance : Name) : TermElabM Bool :=
commitWhen do
let candidate ← mkConstWithFreshMVarLevels defaultInstance
let (mvars, bis, _) ← forallMetaTelescopeReducing (← inferType candidate)
let candidate := mkAppN candidate mvars
trace[Elab.defaultInstance] "{toString (mkMVar mvarId)}, {mkMVar mvarId} : {← inferType (mkMVar mvarId)} =?= {candidate} : {← inferType candidate}"
/- The `coeAtOutParam` feature may mark output parameters of local instances as `syntheticOpaque`.
This kind of parameter is not assignable by default. We use `withAssignableSyntheticOpaque` to workaround this behavior
when processing default instances. TODO: try to avoid `withAssignableSyntheticOpaque`. -/
if (← withAssignableSyntheticOpaque <| isDefEqGuarded (mkMVar mvarId) candidate) then
-- Succeeded. Collect new TC problems
trace[Elab.defaultInstance] "isDefEq worked {mkMVar mvarId} : {← inferType (mkMVar mvarId)} =?= {candidate} : {← inferType candidate}"
let mut pending := []
for i in [:bis.size] do
if bis[i]! == BinderInfo.instImplicit then
pending := mvars[i]!.mvarId! :: pending
synthesizePending pending
else
return false
synthesizeSomeUsingDefault? (mvarIds : List MVarId) : TermElabM (Option (List MVarId)) := do
match mvarIds with
| [] => return none
| mvarId :: mvarIds =>
if (← synthesizeUsingDefault mvarId) then
return mvarIds
else if let some mvarIds' ← synthesizeSomeUsingDefault? mvarIds then
return mvarId :: mvarIds'
else
return none
synthesizePending (mvarIds : List MVarId) : TermElabM Bool := do
let mvarIds ← synthesizeUsingInstances mvarIds
if mvarIds.isEmpty then return true
let some mvarIds ← synthesizeSomeUsingDefault? mvarIds | return false
synthesizePending mvarIds
/-- Used to implement `synthesizeUsingDefault`. This method only consider default instances with the given priority. -/
private def synthesizeSomeUsingDefaultPrio (prio : Nat) : TermElabM Bool := do
let rec visit (pendingMVars : List MVarId) (pendingMVarsNew : List MVarId) : TermElabM Bool := do
match pendingMVars with
| [] => return false
| mvarId :: pendingMVars =>
let some mvarDecl ← getSyntheticMVarDecl? mvarId | visit pendingMVars (mvarId :: pendingMVarsNew)
match mvarDecl.kind with
| .typeClass .. => -- TODO: use `errorMsg?` in `typeClass`.
if (← withRef mvarDecl.stx <| synthesizeUsingDefaultPrio mvarId prio) then
modify fun s => { s with pendingMVars := pendingMVars.reverse ++ pendingMVarsNew }
return true
else
visit pendingMVars (mvarId :: pendingMVarsNew)
| _ => visit pendingMVars (mvarId :: pendingMVarsNew)
/- Recall that s.pendingMVars is essentially a stack. The first metavariable was the last one created.
We want to apply the default instance in reverse creation order. Otherwise,
`toString 0` will produce a `OfNat String _` cannot be synthesized error. -/
visit (← get).pendingMVars.reverse []
/--
Apply default value to any pending synthetic metavariable of kind `SyntheticMVarKind.withDefault`
Return true if something was synthesized. -/
private def synthesizeUsingDefault : TermElabM Bool := do
let prioSet ← getDefaultInstancesPriorities
/- Recall that `prioSet` is stored in descending order -/
for prio in prioSet do
if (← synthesizeSomeUsingDefaultPrio prio) then
return true
return false
/--
We use this method to report typeclass (and coercion) resolution problems that are "stuck".
That is, there is nothing else to do, and we don't have enough information to synthesize them using TC resolution.
-/
def reportStuckSyntheticMVar (mvarId : MVarId) (ignoreStuckTC := false) : TermElabM Unit := do
let some mvarSyntheticDecl ← getSyntheticMVarDecl? mvarId | return ()
withRef mvarSyntheticDecl.stx do
match mvarSyntheticDecl.kind with
| .typeClass extraErrorMsg? =>
let extraErrorMsg := extraMsgToMsg extraErrorMsg?
unless ignoreStuckTC do
mvarId.withContext do
let mvarDecl ← getMVarDecl mvarId
unless (← MonadLog.hasErrors) do
throwError "typeclass instance problem is stuck, it is often due to metavariables{indentExpr mvarDecl.type}{extraErrorMsg}"
| .coe header expectedType e f? =>
mvarId.withContext do
throwTypeMismatchError header expectedType (← inferType e) e f?
m!"failed to create type class instance for{indentExpr (← getMVarDecl mvarId).type}"
| _ => unreachable! -- TODO handle other cases.
/--
Report an error for each synthetic metavariable that could not be resolved.
Remark: we set `ignoreStuckTC := true` when elaborating `simp` arguments.
-/
private def reportStuckSyntheticMVars (ignoreStuckTC := false) : TermElabM Unit := do
let pendingMVars ← modifyGet fun s => (s.pendingMVars, { s with pendingMVars := [] })
for mvarId in pendingMVars do
reportStuckSyntheticMVar mvarId ignoreStuckTC
private def getSomeSyntheticMVarsRef : TermElabM Syntax := do
for mvarId in (← get).pendingMVars do
if let some decl ← getSyntheticMVarDecl? mvarId then
if decl.stx.getPos?.isSome then
return decl.stx
return .missing
/--
Generate an nicer error message for stuck universe constraints.
-/
private def throwStuckAtUniverseCnstr : TermElabM Unit := do
-- This code assumes `entries` is not empty. Note that `processPostponed` uses `exceptionOnFailure` to guarantee this property
let entries ← getPostponed
let mut found : HashSet (Level × Level) := {}
let mut uniqueEntries := #[]
for entry in entries do
let mut lhs := entry.lhs
let mut rhs := entry.rhs
if Level.normLt rhs lhs then
(lhs, rhs) := (rhs, lhs)
unless found.contains (lhs, rhs) do
found := found.insert (lhs, rhs)
uniqueEntries := uniqueEntries.push entry
for i in [1:uniqueEntries.size] do
logErrorAt uniqueEntries[i]!.ref (← mkLevelStuckErrorMessage uniqueEntries[i]!)
throwErrorAt uniqueEntries[0]!.ref (← mkLevelStuckErrorMessage uniqueEntries[0]!)
/--
Try to solve postponed universe constraints, and throws an exception if there are stuck constraints.
Remark: in previous versions, each `isDefEq u v` invocation would fail if there
were pending universe level constraints. With this old approach, we were not able
to process
```
Functor.map Prod.fst (x s)
```
because after elaborating `Prod.fst` and trying to ensure its type
match the expected one, we would be stuck at the universe constraint:
```
u =?= max u ?v
```
Another benefit of using `withoutPostponingUniverseConstraints` is better error messages. Instead
of getting a mysterious type mismatch constraint, we get a list of
universe constraints the system is stuck at.
-/
private def processPostponedUniverseContraints : TermElabM Unit := do
unless (← processPostponed (mayPostpone := false) (exceptionOnFailure := true)) do
throwStuckAtUniverseCnstr
/--
Remove `mvarId` from the `syntheticMVars` table. We use this method after
the metavariable has been synthesized.
-/
private def markAsResolved (mvarId : MVarId) : TermElabM Unit :=
modify fun s => { s with syntheticMVars := s.syntheticMVars.erase mvarId }
/--
Auxiliary type for `synthesizeSyntheticMVars`. It specifies
whether pending synthetic metavariables can be postponed or not.
-/
inductive PostponeBehavior where
/--
Any kind of pending synthetic metavariable can be postponed.
Universe constrains may also be postponed.
-/
| yes
/--
Pending synthetic metavariables cannot be postponed.
-/
| no
/--
Synthectic metavariables associated with type class resolution can be postponed.
Motivation: this kind of metavariable are not synthethic opaque, and can be assigned by `isDefEq`.
Unviverse constraints can also be postponed.
-/
| «partial»
deriving Inhabited, Repr, BEq
def PostponeBehavior.ofBool : Bool → PostponeBehavior
| true => .yes
| false => .no
mutual
/--
Try to synthesize a term `val` using the tactic code `tacticCode`, and then assign `mvarId := val`.
The `tacticCode` syntax comprises the whole `by ...` expression.
If `report := false`, then `runTactic` will not capture exceptions nor will report unsolved goals. Unsolved goals become exceptions.
-/
partial def runTactic (mvarId : MVarId) (tacticCode : Syntax) (report := true) : TermElabM Unit := withoutAutoBoundImplicit do
instantiateMVarDeclMVars mvarId
/-
TODO: consider using `runPendingTacticsAt` at `mvarId` local context and target type.
Issue #1380 demonstrates that the goal may still contain pending metavariables.
It happens in the following scenario we have a term `foo A (by tac)` where `A` has been postponed
and contains nested `by ...` terms. The pending metavar list contains two metavariables: ?m1 (for `A`) and
`?m2` (for `by tac`). When `A` is resumed, it creates a new metavariable `?m3` for the nested `by ...` term in `A`.
`?m3` is after `?m2` in the to-do list. Then, we execute `by tac` for synthesizing `?m2`, but its type depends on
`?m3`. We have considered putting `?m3` at `?m2` place in the to-do list, but this is not super robust.
The ideal solution is to make sure a tactic "resolves" all pending metavariables nested in their local context and target type
before starting tactic execution. The procedure would be a generalization of `runPendingTacticsAt`. We can try to combine
it with `instantiateMVarDeclMVars` to make sure we do not perform two traversals.
Regarding issue #1380, we addressed the issue by avoiding the elaboration postponement step. However, the same issue can happen
in more complicated scenarios.
-/
tryCatchRuntimeEx
(do let remainingGoals ← withInfoHole mvarId <| Tactic.run mvarId do
withTacticInfoContext tacticCode do
-- also put an info node on the `by` keyword specifically -- the token may be `canonical` and thus shown in the info
-- view even though it is synthetic while a node like `tacticCode` never is (#1990)
withTacticInfoContext tacticCode[0] do
withNarrowedArgTacticReuse (argIdx := 1) (evalTactic ·) tacticCode
synthesizeSyntheticMVars (postpone := .no)
unless remainingGoals.isEmpty do
if report then
reportUnsolvedGoals remainingGoals
else
throwError "unsolved goals\n{goalsToMessageData remainingGoals}")
fun ex => do
if report && (← read).errToSorry then
for mvarId in (← getMVars (mkMVar mvarId)) do
mvarId.admit
logException ex
else
throw ex
/-- Try to synthesize the given pending synthetic metavariable. -/
private partial def synthesizeSyntheticMVar (mvarId : MVarId) (postponeOnError : Bool) (runTactics : Bool) : TermElabM Bool := do
let some mvarSyntheticDecl ← getSyntheticMVarDecl? mvarId | return true -- The metavariable has already been synthesized
withRef mvarSyntheticDecl.stx do
match mvarSyntheticDecl.kind with
| .typeClass extraErrorMsg? => synthesizePendingInstMVar mvarId extraErrorMsg?
| .coe _header? expectedType e _f? => mvarId.withContext do
if (← withDefault do isDefEq (← inferType e) expectedType) then
-- Types may be defeq now due to mvar assignments, type class
-- defaulting, etc.
if (← occursCheck mvarId e) then
mvarId.assign e
return true
if let .some coerced ← coerce? e expectedType then
if (← occursCheck mvarId coerced) then
mvarId.assign coerced
return true
return false
-- NOTE: actual processing at `synthesizeSyntheticMVarsAux`
| .postponed savedContext => resumePostponed savedContext mvarSyntheticDecl.stx mvarId postponeOnError
| .tactic tacticCode savedContext =>
withSavedContext savedContext do
if runTactics then
runTactic mvarId tacticCode
return true
else
return false
/--
Try to synthesize the current list of pending synthetic metavariables.
Return `true` if at least one of them was synthesized. -/
private partial def synthesizeSyntheticMVarsStep (postponeOnError : Bool) (runTactics : Bool) : TermElabM Bool := do
let ctx ← read
traceAtCmdPos `Elab.resuming fun _ =>
m!"resuming synthetic metavariables, mayPostpone: {ctx.mayPostpone}, postponeOnError: {postponeOnError}"
let pendingMVars := (← get).pendingMVars
let numSyntheticMVars := pendingMVars.length
-- We reset `pendingMVars` because new synthetic metavariables may be created by `synthesizeSyntheticMVar`.
modify fun s => { s with pendingMVars := [] }
-- Recall that `pendingMVars` is a list where head is the most recent pending synthetic metavariable.
-- We use `filterRevM` instead of `filterM` to make sure we process the synthetic metavariables using the order they were created.
-- It would not be incorrect to use `filterM`.
let remainingPendingMVars ← pendingMVars.filterRevM fun mvarId => do
-- We use `traceM` because we want to make sure the metavar local context is used to trace the message
traceM `Elab.postpone (mvarId.withContext do addMessageContext m!"resuming {mkMVar mvarId}")
let succeeded ← synthesizeSyntheticMVar mvarId postponeOnError runTactics
if succeeded then markAsResolved mvarId
trace[Elab.postpone] if succeeded then format "succeeded" else format "not ready yet"
pure !succeeded
-- Merge new synthetic metavariables with `remainingPendingMVars`, i.e., metavariables that still couldn't be synthesized
modify fun s => { s with pendingMVars := s.pendingMVars ++ remainingPendingMVars }
return numSyntheticMVars != remainingPendingMVars.length
/--
Try to process pending synthetic metavariables.
If `postpone == .no`,then `pendingMVars` is `[]` after executing this method.
If `postpone == .partial`, then `pendingMVars` contains only `.tc` and `.coe` kinds.
It keeps executing `synthesizeSyntheticMVarsStep` while progress is being made.
If `postpone != .yes`, then it applies default instances to `SyntheticMVarKind.typeClass` (if available)
metavariables that are still unresolved, and then tries to resolve metavariables
with `postponeOnError == false`. That is, we force them to produce error messages and/or commit to
a "best option". If, after that, we still haven't made progress, we report "stuck" errors If `postpone == .no`.
Remark: we set `ignoreStuckTC := true` when elaborating `simp` arguments. Then,
pending TC problems become implicit parameters for the simp theorem.
-/
partial def synthesizeSyntheticMVars (postpone := PostponeBehavior.yes) (ignoreStuckTC := false) : TermElabM Unit := do
let rec loop (_ : Unit) : TermElabM Unit := do
withRef (← getSomeSyntheticMVarsRef) <| withIncRecDepth do
unless (← get).pendingMVars.isEmpty do
if ← synthesizeSyntheticMVarsStep (postponeOnError := false) (runTactics := false) then
loop ()
else if postpone != .yes then
/- Resume pending metavariables with "elaboration postponement" disabled.
We postpone elaboration errors in this step by setting `postponeOnError := true`.
Example:
```
#check let x := ⟨1, 2⟩; Prod.fst x
```
The term `⟨1, 2⟩` can't be elaborated because the expected type is not know.
The `x` at `Prod.fst x` is not elaborated because the type of `x` is not known.
When we execute the following step with "elaboration postponement" disabled,
the elaborator fails at `⟨1, 2⟩` and postpones it, and succeeds at `x` and learns
that its type must be of the form `Prod ?α ?β`.
Recall that we postponed `x` at `Prod.fst x` because its type it is not known.
We the type of `x` may learn later its type and it may contain implicit and/or auto arguments.
By disabling postponement, we are essentially giving up the opportunity of learning `x`s type
and assume it does not have implicit and/or auto arguments. -/
if ← withoutPostponing <| synthesizeSyntheticMVarsStep (postponeOnError := true) (runTactics := false) then
loop ()
else if ← synthesizeUsingDefault then
loop ()
else if ← withoutPostponing <| synthesizeSyntheticMVarsStep (postponeOnError := false) (runTactics := false) then
loop ()
else if ← synthesizeSyntheticMVarsStep (postponeOnError := false) (runTactics := true) then
loop ()
else if postpone == .no then
reportStuckSyntheticMVars ignoreStuckTC
loop ()
if postpone == .no then
processPostponedUniverseContraints
end
def synthesizeSyntheticMVarsNoPostponing (ignoreStuckTC := false) : TermElabM Unit :=
synthesizeSyntheticMVars (postpone := .no) (ignoreStuckTC := ignoreStuckTC)
/-- Keep invoking `synthesizeUsingDefault` until it returns false. -/
private partial def synthesizeUsingDefaultLoop : TermElabM Unit := do
if (← synthesizeUsingDefault) then
synthesizeSyntheticMVars (postpone := .yes)
synthesizeUsingDefaultLoop
def synthesizeSyntheticMVarsUsingDefault : TermElabM Unit := do
synthesizeSyntheticMVars (postpone := .yes)
synthesizeUsingDefaultLoop
private partial def withSynthesizeImp (k : TermElabM α) (postpone : PostponeBehavior) : TermElabM α := do
let pendingMVarsSaved := (← get).pendingMVars
modify fun s => { s with pendingMVars := [] }
try
let a ← k
synthesizeSyntheticMVars (postpone := postpone)
if postpone == .yes then
synthesizeUsingDefaultLoop
return a
finally
modify fun s => { s with pendingMVars := s.pendingMVars ++ pendingMVarsSaved }
/--
Execute `k`, and synthesize pending synthetic metavariables created while executing `k` are solved.
If `mayPostpone == false`, then all of them must be synthesized.
Remark: even if `mayPostpone == true`, the method still uses `synthesizeUsingDefault` -/
@[inline] def withSynthesize [MonadFunctorT TermElabM m] [Monad m] (k : m α) (postpone := PostponeBehavior.no) : m α :=
monadMap (m := TermElabM) (withSynthesizeImp · postpone) k
private partial def withSynthesizeLightImp (k : TermElabM α) : TermElabM α := do
let pendingMVarsSaved := (← get).pendingMVars
modify fun s => { s with pendingMVars := [] }
try
let a ← k
synthesizeSyntheticMVars (postpone := .yes)
return a
finally
modify fun s => { s with pendingMVars := s.pendingMVars ++ pendingMVarsSaved }
/-- Similar to `withSynthesize`, but uses `postpone := .true`, does not use use `synthesizeUsingDefault` -/
@[inline] def withSynthesizeLight [MonadFunctorT TermElabM m] [Monad m] (k : m α) : m α :=
monadMap (m := TermElabM) (withSynthesizeLightImp ·) k
/-- Elaborate `stx`, and make sure all pending synthetic metavariables created while elaborating `stx` are solved. -/
def elabTermAndSynthesize (stx : Syntax) (expectedType? : Option Expr) : TermElabM Expr :=
withRef stx do
instantiateMVars (← withSynthesize <| elabTerm stx expectedType?)
/--
Collect unassigned metavariables at `e` that have associated tactic blocks, and then execute them using `runTactic`.
We use this method at the `match .. with` elaborator when it cannot be postponed anymore, but it is still waiting
the result of a tactic block.
-/
def runPendingTacticsAt (e : Expr) : TermElabM Unit := do
for mvarId in (← getMVars e) do
let mvarId ← getDelayedMVarRoot mvarId
if let some { kind := .tactic tacticCode savedContext, .. } ← getSyntheticMVarDecl? mvarId then
withSavedContext savedContext do
runTactic mvarId tacticCode
markAsResolved mvarId
builtin_initialize
registerTraceClass `Elab.resume
end Lean.Elab.Term