/- Copyright (c) 2020 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Leonardo de Moura, Sebastian Ullrich -/ import Std.ShareCommon import Lean.Util.CollectLevelParams import Lean.Util.FoldConsts import Lean.Util.CollectFVars import Lean.Elab.DeclModifiers import Lean.Elab.Binders namespace Lean namespace Elab namespace Command inductive DefKind | «def» | «theorem» | «example» | «opaque» | «abbrev» def DefKind.isTheorem : DefKind → Bool | DefKind.theorem => true | _ => false def DefKind.isDefOrAbbrevOrOpaque : DefKind → Bool | DefKind.def => true | DefKind.opaque => true | DefKind.abbrev => true | _ => false def DefKind.isExample : DefKind → Bool | DefKind.example => true | _ => false structure DefView := (kind : DefKind) (ref : Syntax) (modifiers : Modifiers) (declId : Syntax) (binders : Syntax) (type? : Option Syntax) (val : Syntax) def collectUsedFVars (ref : Syntax) (used : CollectFVars.State) (e : Expr) : TermElabM CollectFVars.State := do e ← Term.instantiateMVars ref e; pure $ collectFVars used e def collectUsedFVarsAtFVars (ref : Syntax) (used : CollectFVars.State) (fvars : Array Expr) : TermElabM CollectFVars.State := fvars.foldlM (fun used fvar => do fvarType ← Term.inferType ref fvar; collectUsedFVars ref used fvarType) used def removeUnused (ref : Syntax) (vars : Array Expr) (xs : Array Expr) (e : Expr) (eType : Expr) : TermElabM (LocalContext × LocalInstances × Array Expr) := do let used : CollectFVars.State := {}; used ← collectUsedFVars ref used eType; used ← collectUsedFVars ref used e; used ← collectUsedFVarsAtFVars ref used xs; localInsts ← Term.getLocalInsts; lctx ← Term.getLCtx; (lctx, localInsts, newVars, _) ← vars.foldrM (fun var (result : LocalContext × LocalInstances × Array Expr × CollectFVars.State) => let (lctx, localInsts, newVars, used) := result; if used.fvarSet.contains var.fvarId! then do varType ← Term.inferType ref var; used ← collectUsedFVars ref used varType; pure (lctx, localInsts, newVars.push var, used) else pure (lctx.erase var.fvarId!, localInsts.erase var.fvarId!, newVars, used)) (lctx, localInsts, #[], used); pure (lctx, localInsts, newVars.reverse) def withUsedWhen {α} (ref : Syntax) (vars : Array Expr) (xs : Array Expr) (e : Expr) (eType : Expr) (cond : Bool) (k : Array Expr → TermElabM α) : TermElabM α := if cond then do (lctx, localInsts, vars) ← removeUnused ref vars xs e eType; Term.withLCtx lctx localInsts $ k vars else k vars def withUsedWhen' {α} (ref : Syntax) (vars : Array Expr) (xs : Array Expr) (e : Expr) (cond : Bool) (k : Array Expr → TermElabM α) : TermElabM α := let dummyExpr := mkSort levelOne; withUsedWhen ref vars xs e dummyExpr cond k def mkDef (view : DefView) (declName : Name) (explictLevelNames : List Name) (vars : Array Expr) (xs : Array Expr) (type : Expr) (val : Expr) : TermElabM (Option Declaration) := do let ref := view.ref; Term.synthesizeSyntheticMVars; val ← Term.ensureHasType view.val type val; Term.synthesizeSyntheticMVars false; type ← Term.instantiateMVars ref type; val ← Term.instantiateMVars view.val val; if view.kind.isExample then pure none else withUsedWhen ref vars xs val type view.kind.isDefOrAbbrevOrOpaque $ fun vars => do type ← Term.mkForall ref xs type; type ← Term.mkForall ref vars type; val ← Term.mkLambda ref xs val; val ← Term.mkLambda ref vars val; type ← Term.levelMVarToParam type; val ← Term.levelMVarToParam val; type ← Term.instantiateMVars ref type; val ← Term.instantiateMVars view.val val; let shareCommonTypeVal : Std.ShareCommonM (Expr × Expr) := do { type ← Std.withShareCommon type; val ← Std.withShareCommon val; pure (type, val) }; let (type, val) := shareCommonTypeVal.run; Term.trace `Elab.definition.body ref $ fun _ => declName ++ " : " ++ type ++ " :=" ++ Format.line ++ val; let usedParams : CollectLevelParams.State := {}; let usedParams := collectLevelParams usedParams type; let usedParams := collectLevelParams usedParams val; let levelParams := sortDeclLevelParams explictLevelNames usedParams.params; match view.kind with | DefKind.theorem => -- TODO theorem elaboration in parallel pure $ some $ Declaration.thmDecl { name := declName, lparams := levelParams, type := type, value := Task.pure val } | DefKind.opaque => pure $ some $ Declaration.opaqueDecl { name := declName, lparams := levelParams, type := type, value := val, isUnsafe := view.modifiers.isUnsafe } | DefKind.abbrev => pure $ some $ Declaration.defnDecl { name := declName, lparams := levelParams, type := type, value := val, hints := ReducibilityHints.abbrev, isUnsafe := view.modifiers.isUnsafe } | DefKind.def => do env ← Term.getEnv; pure $ some $ Declaration.defnDecl { name := declName, lparams := levelParams, type := type, value := val, hints := ReducibilityHints.regular (getMaxHeight env val + 1), isUnsafe := view.modifiers.isUnsafe } | _ => unreachable! def elabDefVal (defVal : Syntax) (expectedType : Expr) : TermElabM Expr := do let kind := defVal.getKind; if kind == `Lean.Parser.Command.declValSimple then -- parser! " := " >> termParser Term.elabTerm (defVal.getArg 1) expectedType else if kind == `Lean.Parser.Command.declValEqns then Term.throwError defVal "equations have not been implemented yet" else Term.throwUnsupportedSyntax def elabDefLike (view : DefView) : CommandElabM Unit := let ref := view.ref; withDeclId view.declId $ fun name => do declName ← mkDeclName view.declId view.modifiers name; applyAttributes ref declName view.modifiers.attrs AttributeApplicationTime.beforeElaboration; explictLevelNames ← getLevelNames; decl? ← runTermElabM declName $ fun vars => Term.elabBinders view.binders.getArgs $ fun xs => match view.type? with | some typeStx => do type ← Term.elabType typeStx; Term.synthesizeSyntheticMVars false; type ← Term.instantiateMVars typeStx type; withUsedWhen' ref vars xs type view.kind.isTheorem $ fun vars => do val ← elabDefVal view.val type; mkDef view declName explictLevelNames vars xs type val | none => do { type ← Term.mkFreshTypeMVar view.binders; val ← elabDefVal view.val type; mkDef view declName explictLevelNames vars xs type val }; match decl? with | none => pure () | some decl => do addDecl ref decl; applyAttributes ref declName view.modifiers.attrs AttributeApplicationTime.afterTypeChecking; compileDecl ref decl; applyAttributes ref declName view.modifiers.attrs AttributeApplicationTime.afterCompilation @[init] private def regTraceClasses : IO Unit := do registerTraceClass `Elab.definition; pure () end Command end Elab end Lean