/- Copyright (c) 2019 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Leonardo de Moura -/ import Lean.Structure import Lean.Util.Recognizers import Lean.Meta.SynthInstance import Lean.Meta.Check namespace Lean namespace Meta variables {m : Type → Type} [MonadLiftT MetaM m] private def mkIdImp (e : Expr) : MetaM Expr := do type ← inferType e; u ← getLevel type; pure $ mkApp2 (mkConst `id [u]) type e /-- Return `id e` -/ def mkId (e : Expr) : m Expr := liftMetaM $ mkIdImp e private def mkExpectedTypeHint (e : Expr) (expectedType : Expr) : MetaM Expr := do u ← getLevel expectedType; pure $ mkApp2 (mkConst `id [u]) expectedType e /-- Given `e` s.t. `inferType e` is definitionally equal to `expectedType`, return term `@id expectedType e`. -/ def mkExpectedTypeHint (e : Expr) (expectedType : Expr) : m Expr := liftMetaM $ mkExpectedTypeHint e expectedType private def mkEqImp (a b : Expr) : MetaM Expr := do aType ← inferType a; u ← getLevel aType; pure $ mkApp3 (mkConst `Eq [u]) aType a b def mkEq (a b : Expr) : m Expr := liftMetaM $ mkEqImp a b private def mkHEqImp (a b : Expr) : MetaM Expr := do aType ← inferType a; bType ← inferType b; u ← getLevel aType; pure $ mkApp4 (mkConst `HEq [u]) aType a bType b def mkHEq (a b : Expr) : m Expr := liftMetaM $ mkHEqImp a b private def mkEqReflImp (a : Expr) : MetaM Expr := do aType ← inferType a; u ← getLevel aType; pure $ mkApp2 (mkConst `Eq.refl [u]) aType a def mkEqRefl (a : Expr) : m Expr := liftMetaM $ mkEqReflImp a private def mkHEqReflImp (a : Expr) : MetaM Expr := do aType ← inferType a; u ← getLevel aType; pure $ mkApp2 (mkConst `HEq.refl [u]) aType a def mkHEqRefl (a : Expr) : m Expr := liftMetaM $ mkHEqReflImp a private def infer (h : Expr) : MetaM Expr := do hType ← inferType h; whnfD hType private def hasTypeMsg (e type : Expr) : MessageData := indentExpr e ++ Format.line ++ "has type" ++ indentExpr type private def throwAppBuilderException {α} (op : Name) (msg : MessageData) : MetaM α := throwError ("AppBuilder for '" ++ op ++ "', " ++ msg) private def mkEqSymmImp (h : Expr) : MetaM Expr := if h.isAppOf `Eq.refl then pure h else do hType ← infer h; match hType.eq? with | some (α, a, b) => do u ← getLevel α; pure $ mkApp4 (mkConst `Eq.symm [u]) α a b h | none => throwAppBuilderException `Eq.symm ("equality proof expected" ++ hasTypeMsg h hType) def mkEqSymm (h : Expr) : m Expr := liftMetaM $ mkEqSymmImp h private def mkEqTransImp (h₁ h₂ : Expr) : MetaM Expr := if h₁.isAppOf `Eq.refl then pure h₂ else if h₂.isAppOf `Eq.refl then pure h₁ else do hType₁ ← infer h₁; hType₂ ← infer h₂; match hType₁.eq?, hType₂.eq? with | some (α, a, b), some (_, _, c) => do u ← getLevel α; pure $ mkApp6 (mkConst `Eq.trans [u]) α a b c h₁ h₂ | none, _ => throwAppBuilderException `Eq.trans ("equality proof expected" ++ hasTypeMsg h₁ hType₁) | _, none => throwAppBuilderException `Eq.trans ("equality proof expected" ++ hasTypeMsg h₂ hType₂) def mkEqTrans (h₁ h₂ : Expr) : m Expr := liftMetaM $ mkEqTransImp h₁ h₂ private def mkHEqSymmImp (h : Expr) : MetaM Expr := if h.isAppOf `HEq.refl then pure h else do hType ← infer h; match hType.heq? with | some (α, a, β, b) => do u ← getLevel α; pure $ mkApp5 (mkConst `HEq.symm [u]) α β a b h | none => throwAppBuilderException `HEq.symm ("heterogeneous equality proof expected" ++ hasTypeMsg h hType) def mkHEqSymm (h : Expr) : m Expr := liftMetaM $ mkHEqSymmImp h private def mkHEqTransImp (h₁ h₂ : Expr) : MetaM Expr := if h₁.isAppOf `HEq.refl then pure h₂ else if h₂.isAppOf `HEq.refl then pure h₁ else do hType₁ ← infer h₁; hType₂ ← infer h₂; match hType₁.heq?, hType₂.heq? with | some (α, a, β, b), some (_, _, γ, c) => do u ← getLevel α; pure $ mkApp8 (mkConst `HEq.trans [u]) α β γ a b c h₁ h₂ | none, _ => throwAppBuilderException `HEq.trans ("heterogeneous equality proof expected" ++ hasTypeMsg h₁ hType₁) | _, none => throwAppBuilderException `HEq.trans ("heterogeneous equality proof expected" ++ hasTypeMsg h₂ hType₂) def mkHEqTrans (h₁ h₂ : Expr) : m Expr := liftMetaM $ mkHEqTransImp h₁ h₂ private def mkEqOfHEqImp (h : Expr) : MetaM Expr := do hType ← infer h; match hType.heq? with | some (α, a, β, b) => do unlessM (isDefEq α β) $ throwAppBuilderException `eqOfHEq ("heterogeneous equality types are not definitionally equal" ++ indentExpr α ++ Format.line ++ "is not definitionally equal to" ++ indentExpr β); u ← getLevel α; pure $ mkApp4 (mkConst `eqOfHEq [u]) α a b h | _ => throwAppBuilderException `HEq.trans ("heterogeneous equality proof expected" ++ indentExpr h) def mkEqOfHEq (h : Expr) : m Expr := liftMetaM $ mkEqOfHEqImp h private def mkCongrArgImp (f h : Expr) : MetaM Expr := do hType ← infer h; fType ← infer f; match fType.arrow?, hType.eq? with | some (α, β), some (_, a, b) => do u ← getLevel α; v ← getLevel β; pure $ mkApp6 (mkConst `congrArg [u, v]) α β a b f h | none, _ => throwAppBuilderException `congrArg ("non-dependent function expected" ++ hasTypeMsg f fType) | _, none => throwAppBuilderException `congrArg ("equality proof expected" ++ hasTypeMsg h hType) def mkCongrArg (f h : Expr) : m Expr := liftMetaM $ mkCongrArgImp f h private def mkCongrFunImp (h a : Expr) : MetaM Expr := do hType ← infer h; match hType.eq? with | some (ρ, f, g) => do ρ ← whnfD ρ; match ρ with | Expr.forallE n α β _ => do let β' := Lean.mkLambda n BinderInfo.default α β; u ← getLevel α; v ← getLevel (mkApp β' a); pure $ mkApp6 (mkConst `congrFun [u, v]) α β' f g h a | _ => throwAppBuilderException `congrFun ("equality proof between functions expected" ++ hasTypeMsg h hType) | _ => throwAppBuilderException `congrFun ("equality proof expected" ++ hasTypeMsg h hType) def mkCongrFun (h a : Expr) : m Expr := liftMetaM $ mkCongrFunImp h a private def mkCongrImp (h₁ h₂ : Expr) : MetaM Expr := do hType₁ ← infer h₁; hType₂ ← infer h₂; match hType₁.eq?, hType₂.eq? with | some (ρ, f, g), some (α, a, b) => do ρ ← whnfD ρ; match ρ.arrow? with | some (_, β) => do u ← getLevel α; v ← getLevel β; pure $ mkApp8 (mkConst `congr [u, v]) α β f g a b h₁ h₂ | _ => throwAppBuilderException `congr ("non-dependent function expected" ++ hasTypeMsg h₁ hType₁) | none, _ => throwAppBuilderException `congr ("equality proof expected" ++ hasTypeMsg h₁ hType₁) | _, none => throwAppBuilderException `congr ("equality proof expected" ++ hasTypeMsg h₂ hType₂) def mkCongr (h₁ h₂ : Expr) : m Expr := liftMetaM $ mkCongrImp h₁ h₂ private def mkAppMFinal (methodName : Name) (f : Expr) (args : Array Expr) (instMVars : Array MVarId) : MetaM Expr := do instMVars.forM $ fun mvarId => do { mvarDecl ← getMVarDecl mvarId; mvarVal ← synthInstance mvarDecl.type; assignExprMVar mvarId mvarVal }; result ← instantiateMVars (mkAppN f args); whenM (hasAssignableMVar result) $ throwAppBuilderException methodName ("result contains metavariables" ++ indentExpr result); pure result private partial def mkAppMAux (f : Expr) (xs : Array Expr) : Nat → Array Expr → Nat → Array MVarId → Expr → MetaM Expr | i, args, j, instMVars, Expr.forallE n d b c => do let d := d.instantiateRevRange j args.size args; match c.binderInfo with | BinderInfo.implicit => do mvar ← mkFreshExprMVar d MetavarKind.natural n; mkAppMAux i (args.push mvar) j instMVars b | BinderInfo.instImplicit => do mvar ← mkFreshExprMVar d MetavarKind.synthetic n; mkAppMAux i (args.push mvar) j (instMVars.push mvar.mvarId!) b | _ => if h : i < xs.size then do let x := xs.get ⟨i, h⟩; xType ← inferType x; condM (isDefEq d xType) (mkAppMAux (i+1) (args.push x) j instMVars b) (throwAppTypeMismatch (mkAppN f args) x) else mkAppMFinal `mkAppM f args instMVars | i, args, j, instMVars, type => do let type := type.instantiateRevRange j args.size args; type ← whnfD type; if type.isForall then mkAppMAux i args args.size instMVars type else if i == xs.size then mkAppMFinal `mkAppM f args instMVars else throwAppBuilderException `mkAppM ("too many explicit arguments provided to" ++ indentExpr f ++ Format.line ++ "arguments" ++ xs) private def mkFun (constName : Name) : MetaM (Expr × Expr) := do cinfo ← getConstInfo constName; us ← cinfo.lparams.mapM $ fun _ => mkFreshLevelMVar; let f := mkConst constName us; let fType := cinfo.instantiateTypeLevelParams us; pure (f, fType) def mkAppM (constName : Name) (xs : Array Expr) : m Expr := liftMetaM do traceCtx `Meta.appBuilder $ withNewMCtxDepth $ do (f, fType) ← mkFun constName; mkAppMAux f xs 0 #[] 0 #[] fType private partial def mkAppOptMAux (f : Expr) (xs : Array (Option Expr)) : Nat → Array Expr → Nat → Array MVarId → Expr → MetaM Expr | i, args, j, instMVars, Expr.forallE n d b c => do let d := d.instantiateRevRange j args.size args; if h : i < xs.size then do match xs.get ⟨i, h⟩ with | none => match c.binderInfo with | BinderInfo.instImplicit => do mvar ← mkFreshExprMVar d MetavarKind.synthetic n; mkAppOptMAux (i+1) (args.push mvar) j (instMVars.push mvar.mvarId!) b | _ => do mvar ← mkFreshExprMVar d MetavarKind.natural n; mkAppOptMAux (i+1) (args.push mvar) j instMVars b | some x => do xType ← inferType x; condM (isDefEq d xType) (mkAppOptMAux (i+1) (args.push x) j instMVars b) (throwAppTypeMismatch (mkAppN f args) x) else mkAppMFinal `mkAppOptM f args instMVars | i, args, j, instMVars, type => do let type := type.instantiateRevRange j args.size args; type ← whnfD type; if type.isForall then mkAppOptMAux i args args.size instMVars type else if i == xs.size then mkAppMFinal `mkAppOptM f args instMVars else do let xs : Array Expr := xs.foldl (fun r x? => match x? with | none => r | some x => r.push x) #[]; throwAppBuilderException `mkAppOptM ("too many arguments provided to" ++ indentExpr f ++ Format.line ++ "arguments" ++ xs) /-- Similar to `mkAppM`, but it allows us to specify which arguments are provided explicitly using `Option` type. Example: Given `HasPure.pure {m : Type u → Type v} [HasPure m] {α : Type u} (a : α) : m α`, ``` mkAppOptM `HasPure.pure #[m, none, none, a] ``` returns a `HasPure.pure` application if the instance `HasPure m` can be synthesized, and the universes match. Note that, ``` mkAppM `HasPure.pure #[a] ``` fails because the only explicit argument `(a : α)` is not sufficient for inferring the remaining arguments, we would need the expected type. -/ def mkAppOptM (constName : Name) (xs : Array (Option Expr)) : m Expr := liftMetaM do traceCtx `Meta.appBuilder $ withNewMCtxDepth $ do (f, fType) ← mkFun constName; mkAppOptMAux f xs 0 #[] 0 #[] fType private def mkEqNDRecImp (motive h1 h2 : Expr) : MetaM Expr := if h2.isAppOf `Eq.refl then pure h1 else do h2Type ← infer h2; match h2Type.eq? with | none => throwAppBuilderException `Eq.ndrec ("equality proof expected" ++ hasTypeMsg h2 h2Type) | some (α, a, b) => do u2 ← getLevel α; motiveType ← infer motive; match motiveType with | Expr.forallE _ _ (Expr.sort u1 _) _ => pure $ mkAppN (mkConst `Eq.ndrec [u1, u2]) #[α, a, motive, h1, b, h2] | _ => throwAppBuilderException `Eq.ndrec ("invalid motive" ++ indentExpr motive) def mkEqNDRec (motive h1 h2 : Expr) : m Expr := liftMetaM $ mkEqNDRecImp motive h1 h2 private def mkEqRecImp (motive h1 h2 : Expr) : MetaM Expr := if h2.isAppOf `Eq.refl then pure h1 else do h2Type ← infer h2; match h2Type.eq? with | none => throwAppBuilderException `Eq.rec ("equality proof expected" ++ indentExpr h2) | some (α, a, b) => do u2 ← getLevel α; motiveType ← infer motive; match motiveType with | Expr.forallE _ _ (Expr.forallE _ _ (Expr.sort u1 _) _) _ => pure $ mkAppN (mkConst `Eq.rec [u1, u2]) #[α, a, motive, h1, b, h2] | _ => throwAppBuilderException `Eq.rec ("invalid motive" ++ indentExpr motive) def mkEqRec (motive h1 h2 : Expr) : m Expr := liftMetaM $ mkEqRecImp motive h1 h2 def mkEqMP (eqProof pr : Expr) : m Expr := mkAppM `Eq.mp #[eqProof, pr] def mkEqMPR (eqProof pr : Expr) : m Expr := mkAppM `Eq.mpr #[eqProof, pr] private def mkNoConfusionImp (target : Expr) (h : Expr) : MetaM Expr := do type ← inferType h; type ← whnf type; match type.eq? with | none => throwAppBuilderException `noConfusion ("equality expected" ++ hasTypeMsg h type) | some (α, a, b) => do α ← whnf α; matchConstInduct α.getAppFn (fun _ => throwAppBuilderException `noConfusion ("inductive type expected" ++ indentExpr α)) fun v us => do u ← getLevel target; pure $ mkAppN (mkConst (mkNameStr v.name "noConfusion") (u :: us)) (α.getAppArgs ++ #[target, a, b, h]) def mkNoConfusion (target : Expr) (h : Expr) : m Expr := liftMetaM $ mkNoConfusionImp target h def mkPure (monad : Expr) (e : Expr) : m Expr := mkAppOptM `HasPure.pure #[monad, none, none, e] /-- `mkProjection s fieldName` return an expression for accessing field `fieldName` of the structure `s`. Remark: `fieldName` may be a subfield of `s`. -/ private partial def mkProjectionImp : Expr → Name → MetaM Expr | s, fieldName => do type ← inferType s; type ← whnf type; match type.getAppFn with | Expr.const structName us _ => do env ← getEnv; unless (isStructureLike env structName) $ throwAppBuilderException `mkProjection ("structure expected" ++ hasTypeMsg s type); match getProjFnForField? env structName fieldName with | some projFn => let params := type.getAppArgs; pure $ mkApp (mkAppN (mkConst projFn us) params) s | none => do let fields := getStructureFields env structName; r? ← fields.findSomeM? fun fieldName' => match isSubobjectField? env structName fieldName' with | none => pure none | some _ => do { parent ← mkProjectionImp s fieldName'; (do r ← mkProjectionImp parent fieldName; pure $ some r) <|> pure none }; match r? with | some r => pure r | none => throwAppBuilderException `mkProjectionn ("invalid field name '" ++ toString fieldName ++ "' for" ++ hasTypeMsg s type) | _ => throwAppBuilderException `mkProjectionn ("structure expected" ++ hasTypeMsg s type) def mkProjection (s : Expr) (fieldName : Name) : m Expr := liftMetaM $ mkProjectionImp s fieldName private def mkListLitAux (nil : Expr) (cons : Expr) : List Expr → Expr | [] => nil | x::xs => mkApp (mkApp cons x) (mkListLitAux xs) private def mkListLitImp (type : Expr) (xs : List Expr) : MetaM Expr := do u ← getDecLevel type; let nil := mkApp (mkConst `List.nil [u]) type; match xs with | [] => pure nil | _ => let cons := mkApp (mkConst `List.cons [u]) type; pure $ mkListLitAux nil cons xs def mkListLit (type : Expr) (xs : List Expr) : m Expr := liftMetaM $ mkListLitImp type xs def mkArrayLit (type : Expr) (xs : List Expr) : m Expr := liftMetaM do u ← getDecLevel type; listLit ← mkListLit type xs; pure (mkApp (mkApp (mkConst `List.toArray [u]) type) listLit) def mkSorry (type : Expr) (synthetic : Bool) : m Expr := liftMetaM do u ← getLevel type; pure $ mkApp2 (mkConst `sorryAx [u]) type (toExpr synthetic) /-- Return a proof for `p : Prop` using `decide p` -/ def mkDecideProof (p : Expr) : m Expr := liftMetaM do decP ← mkAppM `Decidable.decide #[p]; decEqTrue ← mkEq decP (mkConst `Bool.true); h ← mkEqRefl (mkConst `Bool.true); h ← mkExpectedTypeHint h decEqTrue; mkAppM `ofDecideEqTrue #[h] /-- Return `a < b` -/ def mkLt (a b : Expr) : m Expr := mkAppM `HasLess.Less #[a, b] /-- Return `a <= b` -/ def mkLe (a b : Expr) : m Expr := mkAppM `HasLessEq.LessEq #[a, b] end Meta end Lean