forked from leanprover/lean4
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLevelDefEq.lean
124 lines (113 loc) · 4.65 KB
/
LevelDefEq.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
/-
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean.Util.CollectMVars
import Lean.Meta.Basic
import Lean.Meta.InferType
import Lean.Meta.DecLevel
namespace Lean.Meta
/--
Return true iff `lvl` occurs in `max u_1 ... u_n` and `lvl != u_i` for all `i in [1, n]`.
That is, `lvl` is a proper level subterm of some `u_i`. -/
private def strictOccursMax (lvl : Level) : Level → Bool
| Level.max u v => visit u || visit v
| _ => false
where
visit : Level → Bool
| Level.max u v => visit u || visit v
| u => u != lvl && lvl.occurs u
/-- `mkMaxArgsDiff mvarId (max u_1 ... (mvar mvarId) ... u_n) v` => `max v u_1 ... u_n` -/
private def mkMaxArgsDiff (mvarId : LMVarId) : Level → Level → Level
| Level.max u v, acc => mkMaxArgsDiff mvarId v <| mkMaxArgsDiff mvarId u acc
| l@(Level.mvar id), acc => if id != mvarId then mkLevelMax' acc l else acc
| l, acc => mkLevelMax' acc l
/--
Solve `?m =?= max ?m v` by creating a fresh metavariable `?n`
and assigning `?m := max ?n v` -/
private def solveSelfMax (mvarId : LMVarId) (v : Level) : MetaM Unit := do
assert! v.isMax
let n ← mkFreshLevelMVar
assignLevelMVar mvarId <| mkMaxArgsDiff mvarId v n
private def postponeIsLevelDefEq (lhs : Level) (rhs : Level) : MetaM Unit := do
let ref ← getRef
let ctx ← read
trace[Meta.isLevelDefEq.stuck] "{lhs} =?= {rhs}"
modifyPostponed fun postponed => postponed.push { lhs := lhs, rhs := rhs, ref := ref, ctx? := ctx.defEqCtx? }
private def isMVarWithGreaterDepth (v : Level) (mvarId : LMVarId) : MetaM Bool :=
match v with
| Level.mvar mvarId' => return (← mvarId'.getLevel) > (← mvarId.getLevel)
| _ => return false
mutual
private partial def solve (u v : Level) : MetaM LBool := do
match u, v with
| Level.mvar mvarId, _ =>
if (← mvarId.isReadOnly) then
return LBool.undef
else if (← isMVarWithGreaterDepth v mvarId) then
-- If both `u` and `v` are both metavariables, but depth of v is greater, then we assign `v := u`.
-- This can only happen when levelAssignDepth is set to a smaller value than depth (e.g. during TC synthesis)
assignLevelMVar v.mvarId! u
return LBool.true
else if !u.occurs v then
assignLevelMVar u.mvarId! v
return LBool.true
else if v.isMax && !strictOccursMax u v then
solveSelfMax u.mvarId! v
return LBool.true
else
return LBool.undef
| _, Level.mvar .. => return LBool.undef -- Let `solve v u` to handle this case
| Level.zero, Level.max v₁ v₂ =>
Bool.toLBool <$> (isLevelDefEqAux levelZero v₁ <&&> isLevelDefEqAux levelZero v₂)
| Level.zero, Level.imax _ v₂ =>
Bool.toLBool <$> isLevelDefEqAux levelZero v₂
| Level.zero, Level.succ .. => return LBool.false
| Level.succ u, v =>
if v.isParam then
return LBool.false
else if u.isMVar && u.occurs v then
return LBool.undef
else
match (← Meta.decLevel? v) with
| some v => Bool.toLBool <$> isLevelDefEqAux u v
| none => return LBool.undef
| _, _ => return LBool.undef
@[export lean_is_level_def_eq]
partial def isLevelDefEqAuxImpl : Level → Level → MetaM Bool
| Level.succ lhs, Level.succ rhs => isLevelDefEqAux lhs rhs
| lhs, rhs =>
withTraceNode `Meta.isLevelDefEq (return m!"{exceptBoolEmoji ·} {lhs} =?= {rhs}") do
if lhs.getLevelOffset == rhs.getLevelOffset then
return lhs.getOffset == rhs.getOffset
else
let lhs' ← instantiateLevelMVars lhs
let lhs' := lhs'.normalize
let rhs' ← instantiateLevelMVars rhs
let rhs' := rhs'.normalize
if lhs != lhs' || rhs != rhs' then
isLevelDefEqAux lhs' rhs'
else
let r ← solve lhs rhs;
if r != LBool.undef then
return r == LBool.true
else
let r ← solve rhs lhs;
if r != LBool.undef then
return r == LBool.true
else if !(← hasAssignableLevelMVar lhs <||> hasAssignableLevelMVar rhs) then
let ctx ← read
if ctx.config.isDefEqStuckEx && (lhs.isMVar || rhs.isMVar) then do
trace[Meta.isLevelDefEq.stuck] "{lhs} =?= {rhs}"
Meta.throwIsDefEqStuck
else
return false
else
postponeIsLevelDefEq lhs rhs
return true
end
builtin_initialize
registerTraceClass `Meta.isLevelDefEq
registerTraceClass `Meta.isLevelDefEq.stuck (inherited := true)
end Lean.Meta