forked from leanprover/lean4
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathInjective.lean
170 lines (151 loc) · 7.08 KB
/
Injective.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
/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
import Lean.Meta.Transform
import Lean.Meta.Tactic.Injection
import Lean.Meta.Tactic.Apply
import Lean.Meta.Tactic.Refl
import Lean.Meta.Tactic.Cases
import Lean.Meta.Tactic.Subst
import Lean.Meta.Tactic.Simp.Types
import Lean.Meta.Tactic.Assumption
namespace Lean.Meta
private def mkAnd? (args : Array Expr) : Option Expr := Id.run do
if args.isEmpty then
return none
else
let mut result := args.back
for arg in args.reverse[1:] do
result := mkApp2 (mkConst ``And) arg result
return result
def elimOptParam (type : Expr) : CoreM Expr := do
Core.transform type fun e =>
if e.isAppOfArity ``optParam 2 then
return TransformStep.visit (e.getArg! 0)
else
return .continue
private partial def mkInjectiveTheoremTypeCore? (ctorVal : ConstructorVal) (useEq : Bool) : MetaM (Option Expr) := do
let us := ctorVal.levelParams.map mkLevelParam
let type ← elimOptParam ctorVal.type
forallBoundedTelescope type ctorVal.numParams fun params type =>
forallTelescope type fun args1 resultType => do
let jp (args2 args2New : Array Expr) : MetaM (Option Expr) := do
let lhs := mkAppN (mkAppN (mkConst ctorVal.name us) params) args1
let rhs := mkAppN (mkAppN (mkConst ctorVal.name us) params) args2
let eq ← mkEq lhs rhs
let mut eqs := #[]
for arg1 in args1, arg2 in args2 do
let arg1Type ← inferType arg1
if !(← isProp arg1Type) && arg1 != arg2 then
eqs := eqs.push (← mkEqHEq arg1 arg2)
if let some andEqs := mkAnd? eqs then
let result ← if useEq then
mkEq eq andEqs
else
mkArrow eq andEqs
mkForallFVars params (← mkForallFVars args1 (← mkForallFVars args2New result))
else
return none
let rec mkArgs2 (i : Nat) (type : Expr) (args2 args2New : Array Expr) : MetaM (Option Expr) := do
if h : i < args1.size then
match (← whnf type) with
| Expr.forallE n d b _ =>
let arg1 := args1.get ⟨i, h⟩
if arg1.occurs resultType then
mkArgs2 (i + 1) (b.instantiate1 arg1) (args2.push arg1) args2New
else
withLocalDecl n (if useEq then BinderInfo.default else BinderInfo.implicit) d fun arg2 =>
mkArgs2 (i + 1) (b.instantiate1 arg2) (args2.push arg2) (args2New.push arg2)
| _ => throwError "unexpected constructor type for '{ctorVal.name}'"
else
jp args2 args2New
if useEq then
mkArgs2 0 type #[] #[]
else
withNewBinderInfos (params.map fun param => (param.fvarId!, BinderInfo.implicit)) <|
withNewBinderInfos (args1.map fun arg1 => (arg1.fvarId!, BinderInfo.implicit)) <|
mkArgs2 0 type #[] #[]
private def mkInjectiveTheoremType? (ctorVal : ConstructorVal) : MetaM (Option Expr) :=
mkInjectiveTheoremTypeCore? ctorVal false
private def injTheoremFailureHeader (ctorName : Name) : MessageData :=
m!"failed to prove injectivity theorem for constructor '{ctorName}', use 'set_option genInjectivity false' to disable the generation"
private def throwInjectiveTheoremFailure {α} (ctorName : Name) (mvarId : MVarId) : MetaM α :=
throwError "{injTheoremFailureHeader ctorName}{indentD <| MessageData.ofGoal mvarId}"
private def solveEqOfCtorEq (ctorName : Name) (mvarId : MVarId) (h : FVarId) : MetaM Unit := do
match (← injection mvarId h) with
| InjectionResult.solved => unreachable!
| InjectionResult.subgoal mvarId .. =>
(← mvarId.splitAnd).forM fun mvarId =>
unless (← mvarId.assumptionCore) do
throwInjectiveTheoremFailure ctorName mvarId
private def mkInjectiveTheoremValue (ctorName : Name) (targetType : Expr) : MetaM Expr :=
forallTelescopeReducing targetType fun xs type => do
let mvar ← mkFreshExprSyntheticOpaqueMVar type
solveEqOfCtorEq ctorName mvar.mvarId! xs.back.fvarId!
mkLambdaFVars xs mvar
def mkInjectiveTheoremNameFor (ctorName : Name) : Name :=
ctorName ++ `inj
private def mkInjectiveTheorem (ctorVal : ConstructorVal) : MetaM Unit := do
let some type ← mkInjectiveTheoremType? ctorVal
| return ()
let value ← mkInjectiveTheoremValue ctorVal.name type
let name := mkInjectiveTheoremNameFor ctorVal.name
addDecl <| Declaration.thmDecl {
name
levelParams := ctorVal.levelParams
type := (← instantiateMVars type)
value := (← instantiateMVars value)
}
def mkInjectiveEqTheoremNameFor (ctorName : Name) : Name :=
ctorName ++ `injEq
private def mkInjectiveEqTheoremType? (ctorVal : ConstructorVal) : MetaM (Option Expr) :=
mkInjectiveTheoremTypeCore? ctorVal true
private def mkInjectiveEqTheoremValue (ctorName : Name) (targetType : Expr) : MetaM Expr := do
forallTelescopeReducing targetType fun xs type => do
let mvar ← mkFreshExprSyntheticOpaqueMVar type
let [mvarId₁, mvarId₂] ← mvar.mvarId!.apply (mkConst ``Eq.propIntro)
| throwError "unexpected number of subgoals when proving injective theorem for constructor '{ctorName}'"
let (h, mvarId₁) ← mvarId₁.intro1
let (_, mvarId₂) ← mvarId₂.intro1
solveEqOfCtorEq ctorName mvarId₁ h
let mvarId₂ ← mvarId₂.casesAnd
if let some mvarId₂ ← mvarId₂.substEqs then
try mvarId₂.refl catch _ => throwError (injTheoremFailureHeader ctorName)
mkLambdaFVars xs mvar
private def mkInjectiveEqTheorem (ctorVal : ConstructorVal) : MetaM Unit := do
let some type ← mkInjectiveEqTheoremType? ctorVal
| return ()
let value ← mkInjectiveEqTheoremValue ctorVal.name type
let name := mkInjectiveEqTheoremNameFor ctorVal.name
addDecl <| Declaration.thmDecl {
name
levelParams := ctorVal.levelParams
type := (← instantiateMVars type)
value := (← instantiateMVars value)
}
addSimpTheorem (ext := simpExtension) name (post := true) (inv := false) AttributeKind.global (prio := eval_prio default)
register_builtin_option genInjectivity : Bool := {
defValue := true
descr := "generate injectivity theorems for inductive datatype constructors"
}
def mkInjectiveTheorems (declName : Name) : MetaM Unit := do
if (← getEnv).contains ``Eq.propIntro && genInjectivity.get (← getOptions) && !(← isInductivePredicate declName) then
let info ← getConstInfoInduct declName
unless info.isUnsafe do
-- We need to reset the local context here because `solveEqOfCtorEq` uses
-- `assumptionCore`, which can reference "outside" free variables that
-- were not introduced by `mkInjective(Eq)Theorem` and are not abstracted
-- by `mkLambdaFVars`, thus adding a declaration with free variables.
-- See https://github.com/leanprover/lean4/issues/2188
withLCtx {} {} do
for ctor in info.ctors do
withTraceNode `Meta.injective (fun _ => return m!"{ctor}") do
let ctorVal ← getConstInfoCtor ctor
if ctorVal.numFields > 0 then
mkInjectiveTheorem ctorVal
mkInjectiveEqTheorem ctorVal
builtin_initialize
registerTraceClass `Meta.injective
end Lean.Meta