/- 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.Elab.Attributes import Lean.Elab.Binders import Lean.Elab.DeclModifiers import Lean.Elab.SyntheticMVars namespace Lean namespace Elab namespace Term open Meta structure LetRecDeclView := (ref : Syntax) (attrs : Array Attribute) (shortDeclName : Name) (declName : Name) (numParams : Nat) (type : Expr) (mvar : Expr) -- auxiliary metavariable used to lift the 'let rec' (valStx : Syntax) structure LetRecView := (decls : Array LetRecDeclView) (body : Syntax) /- group ("let " >> nonReservedSymbol "rec ") >> sepBy1 (group (optional «attributes» >> letDecl)) ", " >> "; " >> termParser -/ private def mkLetRecDeclView (letRec : Syntax) : TermElabM LetRecView := do decls ← (letRec.getArg 1).getArgs.getSepElems.mapM fun attrDeclStx => do { let attrStx := attrDeclStx.getArg 0; attrs ← elabAttrs attrStx; let decl := (attrDeclStx.getArg 1).getArg 0; if decl.isOfKind `Lean.Parser.Term.letPatDecl then throwErrorAt decl "patterns are not allowed in 'let rec' expressions" else if decl.isOfKind `Lean.Parser.Term.letIdDecl || decl.isOfKind `Lean.Parser.Term.letEqnsDecl then do let shortDeclName := decl.getIdAt 0; currDeclName? ← getDeclName?; let declName := currDeclName?.getD Name.anonymous ++ shortDeclName; checkNotAlreadyDeclared declName; applyAttributes declName attrs AttributeApplicationTime.beforeElaboration; let binders := (decl.getArg 1).getArgs; let typeStx := expandOptType decl (decl.getArg 2); (type, numParams) ← elabBinders binders fun xs => do { type ← elabType typeStx; type ← mkForallFVars xs type; pure (type, xs.size) }; mvar ← mkFreshExprMVar type MetavarKind.syntheticOpaque; valStx ← if decl.isOfKind `Lean.Parser.Term.letIdDecl then pure $ decl.getArg 4 else liftMacroM $ expandMatchAltsIntoMatch decl (decl.getArg 4); pure { ref := decl, attrs := attrs, shortDeclName := shortDeclName, declName := declName, numParams := numParams, type := type, mvar := mvar, valStx := valStx : LetRecDeclView } else throwUnsupportedSyntax }; pure { decls := decls, body := letRec.getArg 3 } private partial def withAuxLocalDeclsAux {α} (views : Array LetRecDeclView) (k : Array Expr → TermElabM α) : Nat → Array Expr → TermElabM α | i, fvars => if h : i < views.size then let view := views.get ⟨i, h⟩; withLetDecl view.shortDeclName view.type view.mvar fun fvar => withAuxLocalDeclsAux (i+1) (fvars.push fvar) else k fvars private def withAuxLocalDecls {α} (views : Array LetRecDeclView) (k : Array Expr → TermElabM α) : TermElabM α := withAuxLocalDeclsAux views k 0 #[] private def elabLetRecDeclValues (view : LetRecView) : TermElabM (Array Expr) := view.decls.mapM fun view => do forallBoundedTelescope view.type view.numParams fun xs type => withDeclName view.declName do value ← elabTermEnsuringType view.valStx type; mkLambdaFVars xs value private def abortIfContainsSyntheticSorry (e : Expr) : TermElabM Unit := do e ← instantiateMVars e; when e.hasSyntheticSorry $ throwAbort private def registerLetRecsToLift (views : Array LetRecDeclView) (fvars : Array Expr) (values : Array Expr) : TermElabM Unit := do lctx ← getLCtx; let toLift := views.mapIdx fun i view => { ref := view.ref, fvarId := (fvars.get! i).fvarId!, attrs := view.attrs, shortDeclName := view.shortDeclName, declName := view.declName, lctx := lctx, type := view.type, val := values.get! i, mvarId := view.mvar.mvarId! : LetRecToLift }; modify fun s => { s with letRecsToLift := toLift.toList ++ s.letRecsToLift } @[builtinTermElab «letrec»] def elabLetRec : TermElab := fun stx expectedType? => do view ← mkLetRecDeclView stx; withAuxLocalDecls view.decls fun fvars => do values ← elabLetRecDeclValues view; body ← elabTermEnsuringType view.body expectedType?; registerLetRecsToLift view.decls fvars values; mkLetFVars fvars body end Term end Elab end Lean