/- 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.Meta variables {m : Type → Type} [MonadLiftT MetaM m] private def mkIdImp (e : Expr) : MetaM Expr := do let type ← inferType e let u ← getLevel type pure $ mkApp2 (mkConst `id [u]) type e /-- Return `id e` -/ def mkId (e : Expr) : m Expr := liftMetaM $ mkIdImp e def mkIdRhsImp (e : Expr) : MetaM Expr := do let type ← inferType e let u ← getLevel type pure $ mkApp2 (mkConst `idRhs [u]) type e /-- Return `idRhs e` -/ def mkIdRhs (e : Expr) : m Expr := liftMetaM $ mkIdRhsImp e private def mkExpectedTypeHintImp (e : Expr) (expectedType : Expr) : MetaM Expr := do let 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 $ mkExpectedTypeHintImp e expectedType private def mkEqImp (a b : Expr) : MetaM Expr := do let aType ← inferType a let 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 let aType ← inferType a let bType ← inferType b let 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 let aType ← inferType a let 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 let aType ← inferType a let 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 let hType ← inferType h whnfD hType private def hasTypeMsg (e type : Expr) : MessageData := m!"{indentExpr e}\nhas 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 let hType ← infer h match hType.eq? with | some (α, a, b) => do let 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 let hType₁ ← infer h₁ let hType₂ ← infer h₂ match hType₁.eq?, hType₂.eq? with | some (α, a, b), some (_, _, c) => do let 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 let hType ← infer h match hType.heq? with | some (α, a, β, b) => do let 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 := do if h₁.isAppOf `HEq.refl then pure h₂ else if h₂.isAppOf `HEq.refl then pure h₁ else do let hType₁ ← infer h₁ let hType₂ ← infer h₂ match hType₁.heq?, hType₂.heq? with | some (α, a, β, b), some (_, _, γ, c) => let 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 let hType ← infer h match hType.heq? with | some (α, a, β, b) => unless (← isDefEq α β) do throwAppBuilderException `eqOfHEq m!"heterogeneous equality types are not definitionally equal{indentExpr α}\nis not definitionally equal to{indentExpr β}" let u ← getLevel α pure $ mkApp4 (mkConst `eqOfHEq [u]) α a b h | _ => throwAppBuilderException `HEq.trans m!"heterogeneous equality proof expected{indentExpr h}" def mkEqOfHEq (h : Expr) : m Expr := liftMetaM $ mkEqOfHEqImp h private def mkCongrArgImp (f h : Expr) : MetaM Expr := do let hType ← infer h let fType ← infer f match fType.arrow?, hType.eq? with | some (α, β), some (_, a, b) => let u ← getLevel α; let 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 let hType ← infer h match hType.eq? with | some (ρ, f, g) => do let ρ ← whnfD ρ match ρ with | Expr.forallE n α β _ => do let β' := Lean.mkLambda n BinderInfo.default α β let u ← getLevel α let 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 let hType₁ ← infer h₁ let hType₂ ← infer h₂ match hType₁.eq?, hType₂.eq? with | some (ρ, f, g), some (α, a, b) => do let ρ ← whnfD ρ match ρ.arrow? with | some (_, β) => do let u ← getLevel α let 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 let mvarDecl ← getMVarDecl mvarId let mvarVal ← synthInstance mvarDecl.type assignExprMVar mvarId mvarVal let result ← instantiateMVars (mkAppN f args) if (← hasAssignableMVar result) then throwAppBuilderException methodName ("result contains metavariables" ++ indentExpr result) pure result private partial def mkAppMArgs (f : Expr) (fType : Expr) (xs : Array Expr) : MetaM Expr := let rec loop (type : Expr) (i : Nat) (j : Nat) (args : Array Expr) (instMVars : Array MVarId) : MetaM Expr := do if i >= xs.size then mkAppMFinal `mkAppM f args instMVars else match type with | Expr.forallE n d b c => let d := d.instantiateRevRange j args.size args match c.binderInfo with | BinderInfo.implicit => let mvar ← mkFreshExprMVar d MetavarKind.natural n loop b i j (args.push mvar) instMVars | BinderInfo.instImplicit => let mvar ← mkFreshExprMVar d MetavarKind.synthetic n loop b i j (args.push mvar) (instMVars.push mvar.mvarId!) | _ => let x := xs[i] let xType ← inferType x if (← isDefEq d xType) then loop b (i+1) j (args.push x) instMVars else throwAppTypeMismatch (mkAppN f args) x | type => let type := type.instantiateRevRange j args.size args let type ← whnfD type if type.isForall then loop type i args.size args instMVars else throwAppBuilderException `mkAppM m!"too many explicit arguments provided to{indentExpr f}\narguments{indentD xs}" loop fType 0 0 #[] #[] private def mkFun (constName : Name) : MetaM (Expr × Expr) := do let cinfo ← getConstInfo constName let us ← cinfo.lparams.mapM fun _ => mkFreshLevelMVar let f := mkConst constName us let fType := cinfo.instantiateTypeLevelParams us pure (f, fType) /-- Return the application `constName xs`. It tries to fill the implicit arguments before the last element in `xs`. Remark: ``mkAppM `arbitrary #[α]`` returns `@arbitrary.{u} α` without synthesizing the implicit argument occurring after `α`. Given a `x : (([Decidable p] → Bool) × Nat`, ``mkAppM `Prod.fst #[x]`` returns `@Prod.fst ([Decidable p] → Bool) Nat x` -/ def mkAppM (constName : Name) (xs : Array Expr) : m Expr := liftMetaM do traceCtx `Meta.appBuilder $ withNewMCtxDepth do let (f, fType) ← mkFun constName let r ← mkAppMArgs f fType xs trace[Meta.appBuilder]! "constName: {constName}, xs: {xs}, result: {r}" pure r 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 match xs.get ⟨i, h⟩ with | none => match c.binderInfo with | BinderInfo.instImplicit => do let mvar ← mkFreshExprMVar d MetavarKind.synthetic n mkAppOptMAux f xs (i+1) (args.push mvar) j (instMVars.push mvar.mvarId!) b | _ => do let mvar ← mkFreshExprMVar d MetavarKind.natural n mkAppOptMAux f xs (i+1) (args.push mvar) j instMVars b | some x => let xType ← inferType x if (← isDefEq d xType) then mkAppOptMAux f xs (i+1) (args.push x) j instMVars b else 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 let type ← whnfD type if type.isForall then mkAppOptMAux f xs 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 `Pure.pure {m : Type u → Type v} [Pure m] {α : Type u} (a : α) : m α`, ``` mkAppOptM `Pure.pure #[m, none, none, a] ``` returns a `Pure.pure` application if the instance `Pure m` can be synthesized, and the universes match. Note that, ``` mkAppM `Pure.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 let (f, fType) ← mkFun constName mkAppOptMAux f xs 0 #[] 0 #[] fType private def mkEqNDRecImp (motive h1 h2 : Expr) : MetaM Expr := do if h2.isAppOf `Eq.refl then pure h1 else let h2Type ← infer h2 match h2Type.eq? with | none => throwAppBuilderException `Eq.ndrec ("equality proof expected" ++ hasTypeMsg h2 h2Type) | some (α, a, b) => let u2 ← getLevel α let 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 := do if h2.isAppOf `Eq.refl then pure h1 else let h2Type ← infer h2 match h2Type.eq? with | none => throwAppBuilderException `Eq.rec ("equality proof expected" ++ indentExpr h2) | some (α, a, b) => let u2 ← getLevel α let 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 let type ← inferType h let type ← whnf type match type.eq? with | none => throwAppBuilderException `noConfusion ("equality expected" ++ hasTypeMsg h type) | some (α, a, b) => let α ← whnf α matchConstInduct α.getAppFn (fun _ => throwAppBuilderException `noConfusion ("inductive type expected" ++ indentExpr α)) fun v us => do let u ← getLevel target pure $ mkAppN (mkConst (Name.mkStr 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 `Pure.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 let type ← inferType s let type ← whnf type match type.getAppFn with | Expr.const structName us _ => let env ← getEnv unless isStructureLike env structName do 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 let r? ← fields.findSomeM? fun fieldName' => do match isSubobjectField? env structName fieldName' with | none => pure none | some _ => let parent ← mkProjectionImp s fieldName' (do let 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 nil cons xs) private def mkListLitImp (type : Expr) (xs : List Expr) : MetaM Expr := do let 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 let u ← getDecLevel type let listLit ← mkListLit type xs pure (mkApp (mkApp (mkConst `List.toArray [u]) type) listLit) def mkSorry (type : Expr) (synthetic : Bool) : m Expr := liftMetaM do let u ← getLevel type pure $ mkApp2 (mkConst `sorryAx [u]) type (toExpr synthetic) /-- Return `Decidable.decide p` -/ def mkDecide (p : Expr) : m Expr := mkAppOptM `Decidable.decide #[p, none] /-- Return a proof for `p : Prop` using `decide p` -/ def mkDecideProof (p : Expr) : m Expr := liftMetaM do let decP ← mkDecide p let decEqTrue ← mkEq decP (mkConst `Bool.true) let h ← mkEqRefl (mkConst `Bool.true) let 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] /-- Return `arbitrary α` -/ def mkArbitrary (α : Expr) : m Expr := mkAppOptM `arbitrary #[α, none] builtin_initialize registerTraceClass `Meta.appBuilder end Lean.Meta