169 lines
7.5 KiB
Text
169 lines
7.5 KiB
Text
/-
|
|
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
|
Released under Apache 2.0 license as described in the file LICENSE.
|
|
Authors: Leonardo de Moura
|
|
-/
|
|
import Lean.Util.SCC
|
|
import Lean.Meta.AbstractNestedProofs
|
|
import Lean.Meta.Transform
|
|
import Lean.Elab.Term
|
|
import Lean.Elab.RecAppSyntax
|
|
import Lean.Elab.DefView
|
|
import Lean.Elab.PreDefinition.MkInhabitant
|
|
|
|
namespace Lean.Elab
|
|
open Meta
|
|
open Term
|
|
|
|
/--
|
|
A (potentially recursive) definition.
|
|
The elaborator converts it into Kernel definitions using many different strategies.
|
|
-/
|
|
structure PreDefinition where
|
|
ref : Syntax
|
|
kind : DefKind
|
|
levelParams : List Name
|
|
modifiers : Modifiers
|
|
declName : Name
|
|
type : Expr
|
|
value : Expr
|
|
deriving Inhabited
|
|
|
|
def instantiateMVarsAtPreDecls (preDefs : Array PreDefinition) : TermElabM (Array PreDefinition) :=
|
|
preDefs.mapM fun preDef => do
|
|
pure { preDef with type := (← instantiateMVars preDef.type), value := (← instantiateMVars preDef.value) }
|
|
|
|
private def levelMVarToParamPreDeclsAux (preDefs : Array PreDefinition) : StateRefT Nat TermElabM (Array PreDefinition) :=
|
|
preDefs.mapM fun preDef => do
|
|
pure { preDef with type := (← levelMVarToParam' preDef.type), value := (← levelMVarToParam' preDef.value) }
|
|
|
|
def levelMVarToParamPreDecls (preDefs : Array PreDefinition) : TermElabM (Array PreDefinition) :=
|
|
(levelMVarToParamPreDeclsAux preDefs).run' 1
|
|
|
|
private def getLevelParamsPreDecls (preDefs : Array PreDefinition) (scopeLevelNames allUserLevelNames : List Name) : TermElabM (List Name) := do
|
|
let mut s : CollectLevelParams.State := {}
|
|
for preDef in preDefs do
|
|
s := collectLevelParams s preDef.type
|
|
s := collectLevelParams s preDef.value
|
|
match sortDeclLevelParams scopeLevelNames allUserLevelNames s.params with
|
|
| Except.error msg => throwError msg
|
|
| Except.ok levelParams => pure levelParams
|
|
|
|
def fixLevelParams (preDefs : Array PreDefinition) (scopeLevelNames allUserLevelNames : List Name) : TermElabM (Array PreDefinition) := do
|
|
-- We used to use `shareCommon` here, but is was a bottleneck
|
|
let levelParams ← getLevelParamsPreDecls preDefs scopeLevelNames allUserLevelNames
|
|
let us := levelParams.map mkLevelParam
|
|
let fixExpr (e : Expr) : Expr :=
|
|
e.replace fun c => match c with
|
|
| Expr.const declName _ _ => if preDefs.any fun preDef => preDef.declName == declName then some $ Lean.mkConst declName us else none
|
|
| _ => none
|
|
pure $ preDefs.map fun preDef =>
|
|
{ preDef with
|
|
type := fixExpr preDef.type,
|
|
value := fixExpr preDef.value,
|
|
levelParams := levelParams }
|
|
|
|
def applyAttributesOf (preDefs : Array PreDefinition) (applicationTime : AttributeApplicationTime) : TermElabM Unit := do
|
|
for preDef in preDefs do
|
|
applyAttributesAt preDef.declName preDef.modifiers.attrs applicationTime
|
|
|
|
def abstractNestedProofs (preDef : PreDefinition) : MetaM PreDefinition :=
|
|
if preDef.kind.isTheorem || preDef.kind.isExample then
|
|
pure preDef
|
|
else do
|
|
let value ← Meta.abstractNestedProofs preDef.declName preDef.value
|
|
pure { preDef with value := value }
|
|
|
|
/- Auxiliary method for (temporarily) adding pre definition as an axiom -/
|
|
def addAsAxiom (preDef : PreDefinition) : MetaM Unit := do
|
|
withRef preDef.ref do
|
|
addDecl <| Declaration.axiomDecl { name := preDef.declName, levelParams := preDef.levelParams, type := preDef.type, isUnsafe := preDef.modifiers.isUnsafe }
|
|
|
|
private def shouldGenCodeFor (preDef : PreDefinition) : Bool :=
|
|
!preDef.kind.isTheorem && !preDef.modifiers.isNoncomputable
|
|
|
|
private def compileDecl (decl : Declaration) : TermElabM Bool := do
|
|
try
|
|
Lean.compileDecl decl
|
|
catch ex =>
|
|
if (← read).isNoncomputableSection then
|
|
return false
|
|
else
|
|
throw ex
|
|
return true
|
|
|
|
private def addNonRecAux (preDef : PreDefinition) (compile : Bool) : TermElabM Unit :=
|
|
withRef preDef.ref do
|
|
let preDef ← abstractNestedProofs preDef
|
|
let env ← getEnv
|
|
let decl :=
|
|
match preDef.kind with
|
|
| DefKind.«theorem» =>
|
|
Declaration.thmDecl { name := preDef.declName, levelParams := preDef.levelParams, type := preDef.type, value := preDef.value }
|
|
| DefKind.«opaque» =>
|
|
Declaration.opaqueDecl { name := preDef.declName, levelParams := preDef.levelParams, type := preDef.type, value := preDef.value,
|
|
isUnsafe := preDef.modifiers.isUnsafe }
|
|
| DefKind.«abbrev» =>
|
|
Declaration.defnDecl { name := preDef.declName, levelParams := preDef.levelParams, type := preDef.type, value := preDef.value,
|
|
hints := ReducibilityHints.«abbrev»,
|
|
safety := if preDef.modifiers.isUnsafe then DefinitionSafety.unsafe else DefinitionSafety.safe }
|
|
| _ => -- definitions and examples
|
|
Declaration.defnDecl { name := preDef.declName, levelParams := preDef.levelParams, type := preDef.type, value := preDef.value,
|
|
hints := ReducibilityHints.regular (getMaxHeight env preDef.value + 1),
|
|
safety := if preDef.modifiers.isUnsafe then DefinitionSafety.unsafe else DefinitionSafety.safe }
|
|
addDecl decl
|
|
withSaveInfoContext do -- save new env
|
|
addTermInfo preDef.ref (← mkConstWithLevelParams preDef.declName) (isBinder := true)
|
|
applyAttributesOf #[preDef] AttributeApplicationTime.afterTypeChecking
|
|
if compile && shouldGenCodeFor preDef then
|
|
unless (← compileDecl decl) do
|
|
return ()
|
|
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
|
|
|
|
def addAndCompileNonRec (preDef : PreDefinition) : TermElabM Unit := do
|
|
addNonRecAux preDef true
|
|
|
|
def addNonRec (preDef : PreDefinition) : TermElabM Unit := do
|
|
addNonRecAux preDef false
|
|
|
|
/--
|
|
Eliminate recursive application annotations containing syntax. These annotations are used by the well-founded recursion module
|
|
to produce better error messages. -/
|
|
def eraseRecAppSyntax (e : Expr) : CoreM Expr :=
|
|
Core.transform e (post := fun e => TransformStep.done <| if (getRecAppSyntax? e).isSome then e.mdataExpr! else e)
|
|
|
|
def addAndCompileUnsafe (preDefs : Array PreDefinition) (safety := DefinitionSafety.unsafe) : TermElabM Unit :=
|
|
withRef preDefs[0].ref do
|
|
let decl := Declaration.mutualDefnDecl <| ← preDefs.toList.mapM fun preDef => return {
|
|
name := preDef.declName
|
|
levelParams := preDef.levelParams
|
|
type := preDef.type
|
|
value := (← eraseRecAppSyntax preDef.value)
|
|
safety := safety
|
|
hints := ReducibilityHints.opaque
|
|
}
|
|
addDecl decl
|
|
withSaveInfoContext do -- save new env
|
|
for preDef in preDefs do
|
|
addTermInfo preDef.ref (← mkConstWithLevelParams preDef.declName) (isBinder := true)
|
|
applyAttributesOf preDefs AttributeApplicationTime.afterTypeChecking
|
|
unless (← compileDecl decl) do
|
|
return ()
|
|
applyAttributesOf preDefs AttributeApplicationTime.afterCompilation
|
|
return ()
|
|
|
|
def addAndCompilePartialRec (preDefs : Array PreDefinition) : TermElabM Unit := do
|
|
if preDefs.all shouldGenCodeFor then
|
|
addAndCompileUnsafe (safety := DefinitionSafety.partial) <| preDefs.map fun preDef =>
|
|
{ preDef with
|
|
declName := Compiler.mkUnsafeRecName preDef.declName,
|
|
value := preDef.value.replace fun e => match e with
|
|
| Expr.const declName us _ =>
|
|
if preDefs.any fun preDef => preDef.declName == declName then
|
|
some $ mkConst (Compiler.mkUnsafeRecName declName) us
|
|
else
|
|
none
|
|
| _ => none,
|
|
modifiers := {} }
|
|
|
|
end Lean.Elab
|