Skip to content

feat: Meta.letToHave #8373

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Lean/Elab/MutualDef.lean
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,7 @@ where
finishElab headers
processDeriving headers
elabAsync header view declId := do
assert! view.kind.isTheorem
let env ← getEnv
let async ← env.addConstAsync declId.declName .thm (exportedKind := .axiom)
setEnv async.mainEnv
Expand Down
22 changes: 22 additions & 0 deletions src/Lean/Elab/PreDefinition/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Lean.Util.NumApps
import Lean.Meta.AbstractNestedProofs
import Lean.Meta.ForEachExpr
import Lean.Meta.Eqns
import Lean.Meta.LetToHave
import Lean.Elab.RecAppSyntax
import Lean.Elab.DefView
import Lean.Elab.PreDefinition.TerminationHint
Expand All @@ -20,6 +21,11 @@ namespace Lean.Elab
open Meta
open Term

register_builtin_option cleanup.letToHave : Bool := {
defValue := true
descr := "Enables transforming `let`s to `have`s when elaborating a declaration."
}

/--
A (potentially recursive) definition.
The elaborator converts it into Kernel definitions using many different strategies.
Expand Down Expand Up @@ -87,6 +93,21 @@ def applyAttributesOf (preDefs : Array PreDefinition) (applicationTime : Attribu
for preDef in preDefs do
applyAttributesAt preDef.declName preDef.modifiers.attrs applicationTime

/--
Applies `Meta.letToHave` to the values of defs, instances, and abbrevs.
Does not apply the transformation to values that are proofs, or to unsafe definitions.
-/
def letToHave (preDef : PreDefinition) : MetaM PreDefinition := withRef preDef.ref do
if !cleanup.letToHave.get (← getOptions) || preDef.modifiers.isUnsafe then
return preDef
else if preDef.kind matches .theorem | .example | .opaque then
return preDef
else if ← Meta.isProp preDef.type then
return preDef
else
let value ← Meta.letToHave preDef.value
return { preDef with value }

def abstractNestedProofs (preDef : PreDefinition) (cache := true) : MetaM PreDefinition := withRef preDef.ref do
if preDef.kind.isTheorem || preDef.kind.isExample then
pure preDef
Expand Down Expand Up @@ -124,6 +145,7 @@ private def reportTheoremDiag (d : TheoremVal) : TermElabM Unit := do
private def addNonRecAux (preDef : PreDefinition) (compile : Bool) (all : List Name) (applyAttrAfterCompilation := true) (cacheProofs := true) : TermElabM Unit :=
withRef preDef.ref do
let preDef ← abstractNestedProofs (cache := cacheProofs) preDef
let preDef ← letToHave preDef
let mkDefDecl : TermElabM Declaration :=
return Declaration.defnDecl {
name := preDef.declName, levelParams := preDef.levelParams, type := preDef.type, value := preDef.value
Expand Down
1 change: 1 addition & 0 deletions src/Lean/Elab/PreDefinition/Mutual.lean
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Cleans the right-hand-sides of the predefinitions, to prepare for inclusion in t
def cleanPreDef (preDef : PreDefinition) (cacheProofs := true) : MetaM PreDefinition := do
let preDef ← eraseRecAppSyntax preDef
let preDef ← abstractNestedProofs (cache := cacheProofs) preDef
let preDef ← letToHave preDef
return preDef

/--
Expand Down
1 change: 1 addition & 0 deletions src/Lean/Elab/PreDefinition/Structural/Main.lean
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def structuralRecursion (preDefs : Array PreDefinition) (termMeasure?s : Array (
unless preDef.kind.isTheorem do
unless (← isProp preDef.type) do
preDef ← abstractNestedProofs preDef
preDef ← letToHave preDef
/-
Don't save predefinition info for equation generator
for theorems and definitions that are propositions.
Expand Down
1 change: 1 addition & 0 deletions src/Lean/Meta.lean
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import Lean.Meta.Match
import Lean.Meta.ReduceEval
import Lean.Meta.Closure
import Lean.Meta.AbstractNestedProofs
import Lean.Meta.LetToHave
import Lean.Meta.ForEachExpr
import Lean.Meta.Transform
import Lean.Meta.PPGoal
Expand Down
6 changes: 5 additions & 1 deletion src/Lean/Meta/AppBuilder.lean
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,18 @@ def mkExpectedTypeHint (e : Expr) (expectedType : Expr) : MetaM Expr := do
/--
`mkLetFun x v e` creates the encoding for the `let_fun x := v; e` expression.
The expression `x` can either be a free variable or a metavariable, and the function suitably abstracts `x` in `e`.

If `usedLetOnly` is `true`, then returns `e` if `x` is unused in `e`.
-/
def mkLetFun (x : Expr) (v : Expr) (e : Expr) : MetaM Expr := do
def mkLetFun (x : Expr) (v : Expr) (e : Expr) (usedLetOnly : Bool := false) : MetaM Expr := do
-- If `x` is an `ldecl`, then the result of `mkLambdaFVars` is a let expression.
let ensureLambda : Expr → Expr
| .letE n t _ b _ => .lam n t b .default
| e@(.lam ..) => e
| _ => unreachable!
let f ← ensureLambda <$> mkLambdaFVars (usedLetOnly := false) #[x] e
if usedLetOnly && !f.bindingBody!.hasLooseBVars then
return f.bindingBody!
let ety ← inferType e
let α ← inferType x
let β ← ensureLambda <$> mkLambdaFVars (usedLetOnly := false) #[x] ety
Expand Down
Loading