Skip to content

Commit 830be29

Browse files
authored
feat: generate equational theorems uniformly (#10734)
This PR follows upon #10606 and creates equational theorems uniformly from the unfold theorem, there is only one handler registered in `registerGetEqnsFn`. For now we keep `registerGetEqnsFn`, because it’s used by mathlib’s `irreducible_def`, but I’d like to get rid of it in the long term, relying only on `registerGetUnfoldEqnFn` for constructions that should unfold differently.
1 parent 2a8c031 commit 830be29

File tree

11 files changed

+275
-353
lines changed

11 files changed

+275
-353
lines changed

src/Lean/Elab/PreDefinition.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ public import Lean.Elab.PreDefinition.Structural
1111
public import Lean.Elab.PreDefinition.Main
1212
public import Lean.Elab.PreDefinition.MkInhabitant
1313
public import Lean.Elab.PreDefinition.WF
14+
public import Lean.Elab.PreDefinition.EqnsUtils
1415
public import Lean.Elab.PreDefinition.Eqns
15-
public import Lean.Elab.PreDefinition.Nonrec.Eqns
1616
public import Lean.Elab.PreDefinition.EqUnfold

src/Lean/Elab/PreDefinition/Eqns.lean

Lines changed: 173 additions & 258 deletions
Large diffs are not rendered by default.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/-
2+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Leonardo de Moura
5+
-/
6+
module
7+
8+
9+
prelude
10+
public import Lean.Meta.Basic
11+
import Lean.Meta.Tactic.Split
12+
import Lean.Meta.Match.MatchEqs
13+
import Lean.Meta.Tactic.SplitIf
14+
15+
/-!
16+
This module contains helpers useful to prove unfolding and/or equational theorems.
17+
-/
18+
19+
namespace Lean.Elab.Eqns
20+
open Meta
21+
22+
public def simpMatch? (mvarId : MVarId) : MetaM (Option MVarId) := do
23+
let mvarId' ← Split.simpMatchTarget mvarId
24+
if mvarId != mvarId' then return some mvarId' else return none
25+
26+
/--
27+
Simplify `if-then-expression`s in the goal target.
28+
If `useNewSemantics` is `true`, the flag `backward.split` is ignored.
29+
-/
30+
public def simpIf? (mvarId : MVarId) (useNewSemantics := false) : MetaM (Option MVarId) := do
31+
let mvarId' ← simpIfTarget mvarId (useDecide := true) (useNewSemantics := useNewSemantics)
32+
if mvarId != mvarId' then return some mvarId' else return none
33+
34+
/-- Try to close goal using `rfl` with smart unfolding turned off. -/
35+
public def tryURefl (mvarId : MVarId) : MetaM Bool :=
36+
withOptions (smartUnfolding.set · false) do
37+
try mvarId.refl; return true catch _ => return false
38+
39+
/-- Delta reduce the equation left-hand-side -/
40+
public def deltaLHS (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
41+
let target ← mvarId.getType'
42+
let some (_, lhs, rhs) := target.eq? | throwTacticEx `deltaLHS mvarId "equality expected"
43+
let some lhs ← delta? lhs | throwTacticEx `deltaLHS mvarId "failed to delta reduce lhs"
44+
mvarId.replaceTargetDefEq (← mkEq lhs rhs)
45+
46+
public def tryContradiction (mvarId : MVarId) : MetaM Bool := do
47+
mvarId.contradictionCore { genDiseq := true }
48+
49+
partial def whnfAux (e : Expr) : MetaM Expr := do
50+
let e ← whnfI e -- Must reduce instances too, otherwise it will not be able to reduce `(Nat.rec ... ... (OfNat.ofNat 0))`
51+
let f := e.getAppFn
52+
match f with
53+
| .proj _ _ s => return mkAppN (f.updateProj! (← whnfAux s)) e.getAppArgs
54+
| _ => return e
55+
56+
/-- Apply `whnfR` to lhs, return `none` if `lhs` was not modified -/
57+
public def whnfReducibleLHS? (mvarId : MVarId) : MetaM (Option MVarId) := mvarId.withContext do
58+
let target ← mvarId.getType'
59+
let some (_, lhs, rhs) := target.eq? | return none
60+
let lhs' ← whnfAux lhs
61+
if lhs' != lhs then
62+
return some (← mvarId.replaceTargetDefEq (← mkEq lhs' rhs))
63+
else
64+
return none
65+
66+
end Lean.Elab.Eqns

src/Lean/Elab/PreDefinition/Nonrec/Eqns.lean

Lines changed: 0 additions & 58 deletions
This file was deleted.

src/Lean/Elab/PreDefinition/PartialFixpoint/Eqns.lean

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ Authors: Leonardo de Moura
66
module
77

88
prelude
9-
public import Lean.Elab.PreDefinition.Eqns
109
public import Lean.Elab.PreDefinition.FixedParams
10+
import Lean.Elab.PreDefinition.EqnsUtils
1111
import Lean.Meta.ArgsPacker.Basic
1212
import Init.Data.Array.Basic
1313
import Init.Internal.Order.Basic
@@ -19,7 +19,11 @@ namespace Lean.Elab.PartialFixpoint
1919
open Meta
2020
open Eqns
2121

22-
public structure EqnInfo extends EqnInfoCore where
22+
public structure EqnInfo where
23+
declName : Name
24+
levelParams : List Name
25+
type : Expr
26+
value : Expr
2327
declNames : Array Name
2428
declNameNonRec : Name
2529
fixedParamPerms : FixedParamPerms
@@ -118,14 +122,7 @@ def getUnfoldFor? (declName : Name) : MetaM (Option Name) := do
118122
let some info := eqnInfoExt.find? env declName | return none
119123
return some (← mkUnfoldEq declName info)
120124

121-
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
122-
if let some info := eqnInfoExt.find? (← getEnv) declName then
123-
mkEqns declName info.declNames (tryRefl := false)
124-
else
125-
return none
126-
127125
builtin_initialize
128-
registerGetEqnsFn getEqnsFor?
129126
registerGetUnfoldEqnFn getUnfoldFor?
130127

131128
end Lean.Elab.PartialFixpoint

src/Lean/Elab/PreDefinition/Structural/Eqns.lean

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ module
77

88
prelude
99
public import Lean.Meta.Basic
10-
public import Lean.Elab.PreDefinition.Eqns
1110
public import Lean.Elab.PreDefinition.FixedParams
11+
import Lean.Elab.PreDefinition.EqnsUtils
1212
import Lean.Meta.Eqns
1313
import Lean.Meta.Tactic.Split
1414
import Lean.Meta.Tactic.Simp.Main
@@ -24,7 +24,11 @@ open Eqns
2424

2525
namespace Structural
2626

27-
public structure EqnInfo extends EqnInfoCore where
27+
public structure EqnInfo where
28+
declName : Name
29+
levelParams : List Name
30+
type : Expr
31+
value : Expr
2832
recArgPos : Nat
2933
declNames : Array Name
3034
fixedParamPerms : FixedParamPerms
@@ -53,6 +57,12 @@ where
5357
k brecOnApp x (mkAppN x extraArgs)
5458
throwError "could not find `.brecOn` application in{indentExpr e}"
5559

60+
def deltaRHS? (mvarId : MVarId) (declName : Name) : MetaM (Option MVarId) := mvarId.withContext do
61+
let target ← mvarId.getType'
62+
let some (_, lhs, rhs) := target.eq? | return none
63+
let some rhs ← delta? rhs.consumeMData (· == declName) | return none
64+
mvarId.replaceTargetDefEq (← mkEq lhs rhs)
65+
5666
/--
5767
Creates the proof of the unfolding theorem for `declName` with type `type`. It
5868
@@ -151,12 +161,6 @@ public def registerEqnsInfo (preDef : PreDefinition) (declNames : Array Name) (r
151161
modifyEnv fun env => eqnInfoExt.insert env preDef.declName
152162
{ preDef with recArgPos, declNames, fixedParamPerms }
153163

154-
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
155-
if let some info := eqnInfoExt.find? (← getEnv) declName then
156-
mkEqns declName info.declNames
157-
else
158-
return none
159-
160164
/-- Generate the "unfold" lemma for `declName`. -/
161165
def mkUnfoldEq (declName : Name) (info : EqnInfo) : MetaM Name := do
162166
let name := mkEqLikeNameFor (← getEnv) info.declName unfoldThmSuffix
@@ -190,7 +194,6 @@ def getStructuralRecArgPosImp? (declName : Name) : CoreM (Option Nat) := do
190194

191195

192196
builtin_initialize
193-
registerGetEqnsFn getEqnsFor?
194197
registerGetUnfoldEqnFn getUnfoldFor?
195198
registerTraceClass `Elab.definition.structural.eqns
196199

src/Lean/Elab/PreDefinition/WF/Eqns.lean

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ Authors: Leonardo de Moura
66
module
77

88
prelude
9-
public import Lean.Elab.PreDefinition.Eqns
109
public import Lean.Elab.PreDefinition.FixedParams
1110
public import Lean.Meta.ArgsPacker.Basic
11+
import Lean.Elab.PreDefinition.EqnsUtils
1212
import Lean.Meta.Tactic.Rewrite
1313
import Lean.Meta.Tactic.Split
1414
import Lean.Elab.PreDefinition.Basic
@@ -18,7 +18,11 @@ namespace Lean.Elab.WF
1818
open Meta
1919
open Eqns
2020

21-
public structure EqnInfo extends EqnInfoCore where
21+
public structure EqnInfo where
22+
declName : Name
23+
levelParams : List Name
24+
type : Expr
25+
value : Expr
2226
declNames : Array Name
2327
declNameNonRec : Name
2428
argsPacker : ArgsPacker
@@ -46,16 +50,6 @@ public def registerEqnsInfo (preDefs : Array PreDefinition) (declNameNonRec : Na
4650
eqnInfoExt.insert env preDef.declName { preDef with
4751
declNames, declNameNonRec, argsPacker, fixedParamPerms }
4852

49-
def getEqnsFor? (declName : Name) : MetaM (Option (Array Name)) := do
50-
if let some info := eqnInfoExt.find? (← getEnv) declName then
51-
mkEqns declName info.declNames (tryRefl := false)
52-
else
53-
return none
54-
55-
builtin_initialize
56-
registerGetEqnsFn getEqnsFor?
57-
58-
5953
/--
6054
This is a hack to fix fallout from #8519, where a non-exposed wfrec definition `foo`
6155
in a module would cause `foo.eq_def` to be defined eagerly and privately,

src/Lean/Elab/PreDefinition/WF/Unfold.lean

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ module
77

88
prelude
99
public import Lean.Elab.PreDefinition.Basic
10-
import Lean.Elab.PreDefinition.Eqns
10+
public import Lean.Meta.Tactic.Simp.Types
11+
import Lean.Elab.PreDefinition.EqnsUtils
1112
import Lean.Meta.Tactic.Apply
1213
import Lean.Meta.Tactic.Split
13-
public import Lean.Meta.Tactic.Simp.Types
1414
import Lean.Meta.Tactic.Simp.Main
1515
import Lean.Meta.Tactic.Simp.BuiltinSimprocs
1616
import Lean.Meta.Tactic.Delta

src/Lean/Meta/Eqns.lean

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,8 @@ builtin_initialize eqnsExt : EnvExtension EqnsExtState ←
172172
/--
173173
Simple equation theorem for nonrecursive definitions.
174174
-/
175-
private def mkSimpleEqThm (declName : Name) : MetaM (Option Name) := do
175+
def mkSimpleEqThm (declName : Name) (name : Name) : MetaM (Option Name) := do
176176
if let some (.defnInfo info) := (← getEnv).find? declName then
177-
let name := mkEqLikeNameFor (← getEnv) declName unfoldThmSuffix
178177
realizeConst declName name (doRealize name info)
179178
return some name
180179
else
@@ -313,7 +312,8 @@ def getUnfoldEqnFor? (declName : Name) (nonRec := false) : MetaM (Option Name) :
313312
return some r
314313
else
315314
if nonRec then
316-
return (← mkSimpleEqThm declName)
315+
let name := mkEqLikeNameFor (← getEnv) declName unfoldThmSuffix
316+
return (← mkSimpleEqThm declName name)
317317
return none
318318
if let some r := r? then
319319
unless r == unfoldName do
@@ -331,4 +331,7 @@ builtin_initialize
331331
return (← MetaM.run' <| getUnfoldEqnFor? declName (nonRec := true)).isSome
332332
return false
333333

334+
registerTraceClass `Elab.definition.eqns
335+
336+
334337
end Lean.Meta

stage0/src/stdlib_flags.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "util/options.h"
22

3+
// please update me
4+
35
namespace lean {
46
options get_default_options() {
57
options opts;

0 commit comments

Comments
 (0)