| /- | |
| Copyright (c) 2021 Microsoft Corporation. All rights reserved. | |
| Released under Apache 2.0 license as described in the file LICENSE. | |
| Authors: Dany Fabian | |
| -/ | |
| import Lean.Meta.Constructions | |
| import Lean.Meta.Match.Match | |
| namespace Lean.Meta.IndPredBelow | |
| open Match | |
| register_builtin_option maxBackwardChainingDepth : Nat := { | |
| defValue := 10 | |
| descr := "The maximum search depth used in the backwards chaining part of the proof of `brecOn` for inductive predicates." | |
| } | |
| /-- | |
| The context used in the creation of the `below` scheme for inductive predicates. | |
| -/ | |
| structure Context where | |
| motives : Array (Name × Expr) | |
| typeInfos : Array InductiveVal | |
| belowNames : Array Name | |
| headers : Array Expr | |
| numParams : Nat | |
| /-- | |
| Collection of variables used to keep track of the positions of binders in the construction | |
| of `below` motives and constructors. | |
| -/ | |
| structure Variables where | |
| target : Array Expr | |
| indVal : Array Expr | |
| params : Array Expr | |
| args : Array Expr | |
| motives : Array Expr | |
| innerType : Expr | |
| deriving Inhabited | |
| /-- | |
| Collection of variables used to keep track of the local context used in the `brecOn` proof. | |
| -/ | |
| structure BrecOnVariables where | |
| params : Array FVarId | |
| motives : Array FVarId | |
| indices : Array FVarId | |
| witness : FVarId | |
| indHyps : Array FVarId | |
| def mkContext (declName : Name) : MetaM Context := do | |
| let indVal ← getConstInfoInduct declName | |
| let typeInfos ← indVal.all.toArray.mapM getConstInfoInduct | |
| let motiveTypes ← typeInfos.mapM motiveType | |
| let motives ← motiveTypes.mapIdxM fun j motive => | |
| return (← motiveName motiveTypes j.val, motive) | |
| let headers ← typeInfos.mapM $ mkHeader motives indVal.numParams | |
| return { | |
| motives := motives | |
| typeInfos := typeInfos | |
| numParams := indVal.numParams | |
| headers := headers | |
| belowNames := indVal.all.toArray.map (· ++ `below) | |
| } | |
| where | |
| motiveName (motiveTypes : Array Expr) (i : Nat) : MetaM Name := | |
| if motiveTypes.size > 1 | |
| then mkFreshUserName s!"motive_{i.succ}" | |
| else mkFreshUserName "motive" | |
| mkHeader | |
| (motives : Array (Name × Expr)) | |
| (numParams : Nat) | |
| (indVal : InductiveVal) : MetaM Expr := do | |
| let header ← forallTelescopeReducing indVal.type fun xs t => do | |
| withNewBinderInfos (xs.map fun x => (x.fvarId!, BinderInfo.implicit)) $ | |
| mkForallFVars xs (← mkArrow (mkAppN (mkIndValConst indVal) xs) t) | |
| addMotives motives numParams header | |
| addMotives (motives : Array (Name × Expr)) (numParams : Nat) : Expr → MetaM Expr := | |
| motives.foldrM (fun (motiveName, motive) t => | |
| forallTelescopeReducing t fun xs s => do | |
| let motiveType ← instantiateForall motive xs[:numParams] | |
| withLocalDecl motiveName BinderInfo.implicit motiveType fun motive => do | |
| mkForallFVars (xs.insertAt! numParams motive) s) | |
| motiveType (indVal : InductiveVal) : MetaM Expr := | |
| forallTelescopeReducing indVal.type fun xs _ => do | |
| mkForallFVars xs (← mkArrow (mkAppN (mkIndValConst indVal) xs) (mkSort levelZero)) | |
| mkIndValConst (indVal : InductiveVal) : Expr := | |
| mkConst indVal.name $ indVal.levelParams.map mkLevelParam | |
| partial def mkCtorType | |
| (ctx : Context) | |
| (belowIdx : Nat) | |
| (originalCtor : ConstructorVal) : MetaM Expr := | |
| forallTelescopeReducing originalCtor.type fun xs t => addHeaderVars | |
| { innerType := t | |
| indVal := #[] | |
| motives := #[] | |
| params := xs[:ctx.numParams] | |
| args := xs[ctx.numParams:] | |
| target := xs[:ctx.numParams] } | |
| where | |
| addHeaderVars (vars : Variables) := do | |
| let headersWithNames ← ctx.headers.mapIdxM fun idx header => | |
| return (ctx.belowNames[idx]!, fun _ : Array Expr => pure header) | |
| withLocalDeclsD headersWithNames fun xs => | |
| addMotives { vars with indVal := xs } | |
| addMotives (vars : Variables) := do | |
| let motiveBuilders ← ctx.motives.mapM fun (motiveName, motiveType) => | |
| return (motiveName, BinderInfo.implicit, fun _ : Array Expr => | |
| instantiateForall motiveType vars.params) | |
| withLocalDecls motiveBuilders fun xs => | |
| modifyBinders { vars with target := vars.target ++ xs, motives := xs } 0 | |
| modifyBinders (vars : Variables) (i : Nat) := do | |
| if i < vars.args.size then | |
| let binder := vars.args[i]! | |
| let binderType ← inferType binder | |
| if (← checkCount binderType) then | |
| mkBelowBinder vars binder binderType fun indValIdx x => | |
| mkMotiveBinder vars indValIdx binder binderType fun y => | |
| withNewBinderInfos #[(binder.fvarId!, BinderInfo.implicit)] do | |
| modifyBinders { vars with target := vars.target ++ #[binder, x, y]} i.succ | |
| else modifyBinders { vars with target := vars.target.push binder } i.succ | |
| else rebuild vars | |
| rebuild (vars : Variables) := | |
| vars.innerType.withApp fun _ args => do | |
| let hApp := | |
| mkAppN | |
| (mkConst originalCtor.name $ ctx.typeInfos[0]!.levelParams.map mkLevelParam) | |
| (vars.params ++ vars.args) | |
| let innerType := mkAppN vars.indVal[belowIdx]! $ | |
| vars.params ++ vars.motives ++ args[ctx.numParams:] ++ #[hApp] | |
| let x ← mkForallFVars vars.target innerType | |
| return replaceTempVars vars x | |
| replaceTempVars (vars : Variables) (ctor : Expr) := | |
| let levelParams := | |
| ctx.typeInfos[0]!.levelParams.map mkLevelParam | |
| ctor.replaceFVars vars.indVal $ ctx.belowNames.map fun indVal => | |
| mkConst indVal levelParams | |
| checkCount (domain : Expr) : MetaM Bool := do | |
| let run (x : StateRefT Nat MetaM Expr) : MetaM (Expr × Nat) := StateRefT'.run x 0 | |
| let (_, cnt) ← run <| transform domain fun e => do | |
| if let some name := e.constName? then | |
| if let some _ := ctx.typeInfos.findIdx? fun indVal => indVal.name == name then | |
| modify (· + 1) | |
| return .continue | |
| if cnt > 1 then | |
| throwError "only arrows are allowed as premises. Multiple recursive occurrences detected:{indentExpr domain}" | |
| return cnt == 1 | |
| mkBelowBinder | |
| (vars : Variables) | |
| (binder : Expr) | |
| (domain : Expr) | |
| {α : Type} (k : Nat → Expr → MetaM α) : MetaM α := do | |
| forallTelescopeReducing domain fun xs t => do | |
| let fail _ := do | |
| throwError "only trivial inductive applications supported in premises:{indentExpr t}" | |
| t.withApp fun f args => do | |
| if let some name := f.constName? then | |
| if let some idx := ctx.typeInfos.findIdx? | |
| fun indVal => indVal.name == name then | |
| let hApp := mkAppN binder xs | |
| let t := | |
| mkAppN vars.indVal[idx]! $ | |
| vars.params ++ vars.motives ++ args[ctx.numParams:] ++ #[hApp] | |
| let newDomain ← mkForallFVars xs t | |
| withLocalDecl (←copyVarName binder.fvarId!) binder.binderInfo newDomain (k idx) | |
| else fail () | |
| else fail () | |
| mkMotiveBinder | |
| (vars : Variables) | |
| (indValIdx : Nat) | |
| (binder : Expr) | |
| (domain : Expr) | |
| {α : Type} (k : Expr → MetaM α) : MetaM α := do | |
| forallTelescopeReducing domain fun xs t => do | |
| t.withApp fun _ args => do | |
| let hApp := mkAppN binder xs | |
| let t := mkAppN vars.motives[indValIdx]! $ args[ctx.numParams:] ++ #[hApp] | |
| let newDomain ← mkForallFVars xs t | |
| withLocalDecl (←copyVarName binder.fvarId!) binder.binderInfo newDomain k | |
| copyVarName (oldName : FVarId) : MetaM Name := do | |
| let binderDecl ← oldName.getDecl | |
| mkFreshUserName binderDecl.userName | |
| def mkConstructor (ctx : Context) (i : Nat) (ctor : Name) : MetaM Constructor := do | |
| let ctorInfo ← getConstInfoCtor ctor | |
| let name := ctor.updatePrefix ctx.belowNames[i]! | |
| let type ← mkCtorType ctx i ctorInfo | |
| return { | |
| name := name | |
| type := type } | |
| def mkInductiveType | |
| (ctx : Context) | |
| (i : Fin ctx.typeInfos.size) | |
| (indVal : InductiveVal) : MetaM InductiveType := do | |
| return { | |
| name := ctx.belowNames[i]! | |
| type := ctx.headers[i]! | |
| ctors := (← indVal.ctors.mapM (mkConstructor ctx i)) | |
| } | |
| def mkBelowDecl (ctx : Context) : MetaM Declaration := do | |
| let lparams := ctx.typeInfos[0]!.levelParams | |
| return Declaration.inductDecl | |
| lparams | |
| (ctx.numParams + ctx.motives.size) | |
| (←ctx.typeInfos.mapIdxM $ mkInductiveType ctx).toList | |
| ctx.typeInfos[0]!.isUnsafe | |
| partial def backwardsChaining (m : MVarId) (depth : Nat) : MetaM Bool := do | |
| if depth = 0 then return false | |
| else | |
| m.withContext do | |
| let lctx ← getLCtx | |
| let mTy ← m.getType | |
| lctx.anyM fun localDecl => | |
| if localDecl.isAuxDecl then | |
| return false | |
| else | |
| commitWhen do | |
| let (mvars, _, t) ← forallMetaTelescope localDecl.type | |
| if ←isDefEq mTy t then | |
| m.assign (mkAppN localDecl.toExpr mvars) | |
| mvars.allM fun v => | |
| v.mvarId!.isAssigned <||> backwardsChaining v.mvarId! (depth - 1) | |
| else return false | |
| partial def proveBrecOn (ctx : Context) (indVal : InductiveVal) (type : Expr) : MetaM Expr := do | |
| let main ← mkFreshExprSyntheticOpaqueMVar type | |
| let (m, vars) ← intros main.mvarId! | |
| let [m] ← applyIH m vars | | |
| throwError "applying the induction hypothesis should only return one goal" | |
| let ms ← induction m vars | |
| let ms ← applyCtors ms | |
| let maxDepth := maxBackwardChainingDepth.get $ ←getOptions | |
| ms.forM (closeGoal maxDepth) | |
| instantiateMVars main | |
| where | |
| intros (m : MVarId) : MetaM (MVarId × BrecOnVariables) := do | |
| let (params, m) ← m.introNP indVal.numParams | |
| let (motives, m) ← m.introNP ctx.motives.size | |
| let (indices, m) ← m.introNP indVal.numIndices | |
| let (witness, m) ← m.intro1P | |
| let (indHyps, m) ← m.introNP ctx.motives.size | |
| return (m, ⟨params, motives, indices, witness, indHyps⟩) | |
| applyIH (m : MVarId) (vars : BrecOnVariables) : MetaM (List MVarId) := do | |
| match (← vars.indHyps.findSomeM? | |
| fun ih => do try pure <| some <| (← m.apply <| mkFVar ih) catch _ => pure none) with | |
| | some goals => pure goals | |
| | none => throwError "cannot apply induction hypothesis: {MessageData.ofGoal m}" | |
| induction (m : MVarId) (vars : BrecOnVariables) : MetaM (List MVarId) := do | |
| let params := vars.params.map mkFVar | |
| let motives := vars.motives.map mkFVar | |
| let levelParams := indVal.levelParams.map mkLevelParam | |
| let motives ← ctx.motives.mapIdxM fun idx (_, motive) => do | |
| let motive ← instantiateForall motive params | |
| forallTelescopeReducing motive fun xs _ => do | |
| mkLambdaFVars xs <| mkAppN (mkConst ctx.belowNames[idx]! levelParams) $ (params ++ motives ++ xs) | |
| let recursorInfo ← getConstInfo $ mkRecName indVal.name | |
| let recLevels := | |
| if recursorInfo.numLevelParams > levelParams.length | |
| then levelZero::levelParams | |
| else levelParams | |
| let recursor := mkAppN (mkConst recursorInfo.name $ recLevels) $ params ++ motives | |
| m.apply recursor | |
| applyCtors (ms : List MVarId) : MetaM $ List MVarId := do | |
| let mss ← ms.toArray.mapIdxM fun _ m => do | |
| let m ← introNPRec m | |
| (← m.getType).withApp fun below args => | |
| m.withContext do | |
| args.back.withApp fun ctor _ => do | |
| let ctorName := ctor.constName!.updatePrefix below.constName! | |
| let ctor := mkConst ctorName below.constLevels! | |
| let ctorInfo ← getConstInfoCtor ctorName | |
| let (mvars, _, _) ← forallMetaTelescope ctorInfo.type | |
| let ctor := mkAppN ctor mvars | |
| m.apply ctor | |
| return mss.foldr List.append [] | |
| introNPRec (m : MVarId) : MetaM MVarId := do | |
| if (← m.getType).isForall then introNPRec (← m.intro1P).2 else return m | |
| closeGoal (maxDepth : Nat) (m : MVarId) : MetaM Unit := do | |
| unless (← m.isAssigned) do | |
| let m ← introNPRec m | |
| unless (← backwardsChaining m maxDepth) do | |
| m.withContext do | |
| throwError "couldn't solve by backwards chaining ({``maxBackwardChainingDepth} = {maxDepth}): {MessageData.ofGoal m}" | |
| def mkBrecOnDecl (ctx : Context) (idx : Nat) : MetaM Declaration := do | |
| let type ← mkType | |
| let indVal := ctx.typeInfos[idx]! | |
| let name := indVal.name ++ brecOnSuffix | |
| return Declaration.thmDecl { | |
| name := name | |
| levelParams := indVal.levelParams | |
| type := type | |
| value := ←proveBrecOn ctx indVal type } | |
| where | |
| mkType : MetaM Expr := | |
| forallTelescopeReducing ctx.headers[idx]! fun xs _ => do | |
| let params := xs[:ctx.numParams] | |
| let motives := xs[ctx.numParams:ctx.numParams + ctx.motives.size].toArray | |
| let indices := xs[ctx.numParams + ctx.motives.size:] | |
| let motiveBinders ← ctx.motives.mapIdxM $ mkIH params motives | |
| withLocalDeclsD motiveBinders fun ys => do | |
| mkForallFVars (xs ++ ys) (mkAppN motives[idx]! indices) | |
| mkIH | |
| (params : Array Expr) | |
| (motives : Array Expr) | |
| (idx : Fin ctx.motives.size) | |
| (motive : Name × Expr) : MetaM $ Name × (Array Expr → MetaM Expr) := do | |
| let name := | |
| if ctx.motives.size > 1 | |
| then mkFreshUserName s!"ih_{idx.val.succ}" | |
| else mkFreshUserName "ih" | |
| let ih ← instantiateForall motive.2 params | |
| let mkDomain (_ : Array Expr) : MetaM Expr := | |
| forallTelescopeReducing ih fun ys _ => do | |
| let levels := ctx.typeInfos[idx]!.levelParams.map mkLevelParam | |
| let args := params ++ motives ++ ys | |
| let premise := | |
| mkAppN | |
| (mkConst ctx.belowNames[idx.val]! levels) args | |
| let conclusion := | |
| mkAppN motives[idx]! ys | |
| mkForallFVars ys (←mkArrow premise conclusion) | |
| return (←name, mkDomain) | |
| /-- Given a constructor name, find the indices of the corresponding `below` version thereof. -/ | |
| partial def getBelowIndices (ctorName : Name) : MetaM $ Array Nat := do | |
| let ctorInfo ← getConstInfoCtor ctorName | |
| let belowCtorInfo ← getConstInfoCtor (ctorName.updatePrefix $ ctorInfo.induct ++ `below) | |
| forallTelescopeReducing ctorInfo.type fun xs _ => do | |
| loop xs belowCtorInfo.type #[] 0 0 | |
| where | |
| loop | |
| (xs : Array Expr) | |
| (rest : Expr) | |
| (belowIndices : Array Nat) | |
| (xIdx yIdx : Nat) : MetaM $ Array Nat := do | |
| if xIdx ≥ xs.size then return belowIndices else | |
| let x := xs[xIdx]! | |
| let xTy ← inferType x | |
| let yTy := rest.bindingDomain! | |
| if (← isDefEq xTy yTy) then | |
| let rest ← instantiateForall rest #[x] | |
| loop xs rest (belowIndices.push yIdx) (xIdx + 1) (yIdx + 1) | |
| else | |
| forallBoundedTelescope rest (some 1) fun _ rest => | |
| loop xs rest belowIndices xIdx (yIdx + 1) | |
| private def belowType (motive : Expr) (xs : Array Expr) (idx : Nat) : MetaM $ Name × Expr := do | |
| (← inferType xs[idx]!).withApp fun type args => do | |
| let indName := type.constName! | |
| let indInfo ← getConstInfoInduct indName | |
| let belowArgs := args[:indInfo.numParams] ++ #[motive] ++ args[indInfo.numParams:] ++ #[xs[idx]!] | |
| let belowType := mkAppN (mkConst (indName ++ `below) type.constLevels!) belowArgs | |
| return (indName, belowType) | |
| /-- This function adds an additional `below` discriminant to a matcher application. | |
| It is used for modifying the patterns, such that the structural recursion can use the new | |
| `below` predicate instead of the original one and thus be used prove structural recursion. | |
| It takes as parameters: | |
| - matcherApp: a matcher application | |
| - belowMotive: the motive, that the `below` type should carry | |
| - below: an expression from the local context that is the `below` version of a predicate | |
| and can be used for structural recursion | |
| - idx: the index of the original predicate discriminant. | |
| It returns: | |
| - A matcher application as an expression | |
| - A side-effect for adding the matcher to the environment -/ | |
| partial def mkBelowMatcher | |
| (matcherApp : MatcherApp) | |
| (belowMotive : Expr) | |
| (below : Expr) | |
| (idx : Nat) : MetaM $ Expr × MetaM Unit := do | |
| let mkMatcherInput ← getMkMatcherInputInContext matcherApp | |
| let (indName, _, motive, matchType) ← | |
| forallBoundedTelescope mkMatcherInput.matchType mkMatcherInput.numDiscrs fun xs t => do | |
| let (indName, belowType) ← belowType belowMotive xs idx | |
| let matchType ← | |
| withLocalDeclD (←mkFreshUserName `h_below) belowType fun h_below => do | |
| mkForallFVars (xs.push h_below) t | |
| let motive ← newMotive belowType xs | |
| pure (indName, belowType.replaceFVars xs matcherApp.discrs, motive, matchType) | |
| let lhss ← mkMatcherInput.lhss.mapM <| addBelowPattern indName | |
| let alts ← mkMatcherInput.lhss.zip lhss |>.toArray.zip matcherApp.alts |>.mapIdxM fun idx ((oldLhs, lhs), alt) => do | |
| withExistingLocalDecls (oldLhs.fvarDecls ++ lhs.fvarDecls) do | |
| lambdaTelescope alt fun xs t => do | |
| let oldFVars := oldLhs.fvarDecls.toArray | |
| let fvars := lhs.fvarDecls.toArray.map (·.toExpr) | |
| let xs := | |
| -- special case: if we had no free vars, i.e. there was a unit added and no we do have free vars, we get rid of the unit. | |
| match oldFVars.size, fvars.size with | |
| | 0, _+1 => xs[1:] | |
| | _, _ => xs | |
| let t := t.replaceFVars xs[:oldFVars.size] fvars[:oldFVars.size] | |
| trace[Meta.IndPredBelow.match] "xs = {xs}; oldFVars = {oldFVars.map (·.toExpr)}; fvars = {fvars}; new = {fvars[:oldFVars.size] ++ xs[oldFVars.size:] ++ fvars[oldFVars.size:]}" | |
| let newAlt ← mkLambdaFVars (fvars[:oldFVars.size] ++ xs[oldFVars.size:] ++ fvars[oldFVars.size:]) t | |
| trace[Meta.IndPredBelow.match] "alt {idx}:\n{alt} ↦ {newAlt}" | |
| pure newAlt | |
| let matcherName ← mkFreshUserName mkMatcherInput.matcherName | |
| withExistingLocalDecls (lhss.foldl (init := []) fun s v => s ++ v.fvarDecls) do | |
| for lhs in lhss do | |
| trace[Meta.IndPredBelow.match] "{lhs.patterns.map (·.toMessageData)}" | |
| let res ← Match.mkMatcher { matcherName, matchType, discrInfos := mkArray (mkMatcherInput.numDiscrs + 1) {}, lhss } | |
| res.addMatcher | |
| -- if a wrong index is picked, the resulting matcher can be type-incorrect. | |
| -- we check here, so that errors can propagate higher up the call stack. | |
| check res.matcher | |
| let newApp := mkApp res.matcher motive | |
| let newApp := mkAppN newApp <| matcherApp.discrs.push below | |
| let newApp := mkAppN newApp alts | |
| return (newApp, res.addMatcher) | |
| where | |
| addBelowPattern (indName : Name) (lhs : AltLHS) : MetaM AltLHS := do | |
| withExistingLocalDecls lhs.fvarDecls do | |
| let patterns := lhs.patterns.toArray | |
| let originalPattern := patterns[idx]! | |
| let (fVars, belowPattern) ← convertToBelow indName patterns[idx]! | |
| withExistingLocalDecls fVars.toList do | |
| let patterns := patterns.push belowPattern | |
| let patterns := patterns.set! idx (←toInaccessible originalPattern) | |
| return { lhs with patterns := patterns.toList, fvarDecls := lhs.fvarDecls ++ fVars.toList } | |
| /-- | |
| this function changes the type of the pattern from the original type to the `below` version thereof. | |
| Most of the cases don't apply. In order to change the type and the pattern to be type correct, we don't | |
| simply recursively change all occurrences, but rather, we recursively change occurences of the constructor. | |
| As such there are only a few cases: | |
| - the pattern is a constructor from the original type. Here we need to: | |
| - replace the constructor | |
| - copy original recursive patterns and convert them to below and reintroduce them in the correct position | |
| - turn original recursive patterns inaccessible | |
| - introduce free variables as needed. | |
| - it is an `as` pattern. Here the contstructor could be hidden inside of it. | |
| - it is a variable. Here you we need to introduce a fresh variable of a different type. | |
| -/ | |
| convertToBelow (indName : Name) | |
| (originalPattern : Pattern) : MetaM $ Array LocalDecl × Pattern := do | |
| match originalPattern with | |
| | Pattern.ctor ctorName us params fields => | |
| let ctorInfo ← getConstInfoCtor ctorName | |
| let belowCtor ← getConstInfoCtor $ ctorName.updatePrefix $ ctorInfo.induct ++ `below | |
| let belowIndices ← IndPredBelow.getBelowIndices ctorName | |
| let belowIndices := belowIndices[ctorInfo.numParams:].toArray.map (· - belowCtor.numParams) | |
| -- belowFieldOpts starts off with an array of empty fields. | |
| -- We then go over pattern's fields and set the appropriate fields to values. | |
| -- In general, there are fewer `fields` than `belowFieldOpts`, because the | |
| -- `belowCtor` carries a `below`, a non-`below` and a `motive` version of each | |
| -- field that occurs in a recursive application of the inductive predicate. | |
| -- `belowIndices` is a mapping from non-`below` to the `below` version of each field. | |
| let mut belowFieldOpts := mkArray belowCtor.numFields none | |
| let fields := fields.toArray | |
| for fieldIdx in [:fields.size] do | |
| belowFieldOpts := belowFieldOpts.set! belowIndices[fieldIdx]! (some fields[fieldIdx]!) | |
| let belowParams := params.toArray.push belowMotive | |
| let belowCtorExpr := mkAppN (mkConst belowCtor.name us) belowParams | |
| let (additionalFVars, belowFields) ← transformFields belowCtorExpr indName belowFieldOpts | |
| withExistingLocalDecls additionalFVars.toList do | |
| let ctor := Pattern.ctor belowCtor.name us belowParams.toList belowFields.toList | |
| trace[Meta.IndPredBelow.match] "{originalPattern.toMessageData} ↦ {ctor.toMessageData}" | |
| return (additionalFVars, ctor) | |
| | Pattern.as varId p hId => | |
| let (additionalFVars, p) ← convertToBelow indName p | |
| return (additionalFVars, Pattern.as varId p hId) | |
| | Pattern.var varId => | |
| let var := mkFVar varId | |
| let (_, tgtType) ← belowType belowMotive #[var] 0 | |
| withLocalDeclD (←mkFreshUserName `h) tgtType fun h => do | |
| let localDecl ← getFVarLocalDecl h | |
| return (#[localDecl], Pattern.var h.fvarId!) | |
| | p => return (#[], p) | |
| transformFields belowCtor indName belowFieldOpts := | |
| let rec loop | |
| (belowCtor : Expr) | |
| (belowFieldOpts : Array $ Option Pattern) | |
| (belowFields : Array Pattern) | |
| (additionalFVars : Array LocalDecl) : MetaM (Array LocalDecl × Array Pattern) := do | |
| if belowFields.size ≥ belowFieldOpts.size then pure (additionalFVars, belowFields) else | |
| if let some belowField := belowFieldOpts[belowFields.size]! then | |
| let belowFieldExpr ← belowField.toExpr | |
| let belowCtor := mkApp belowCtor belowFieldExpr | |
| let patTy ← inferType belowFieldExpr | |
| patTy.withApp fun f _ => do | |
| let constName := f.constName? | |
| if constName == indName then | |
| let (fvars, transformedField) ← convertToBelow indName belowField | |
| withExistingLocalDecls fvars.toList do | |
| let belowFieldOpts := belowFieldOpts.set! (belowFields.size + 1) transformedField | |
| let belowField := | |
| match belowField with | |
| | Pattern.ctor .. => Pattern.inaccessible belowFieldExpr | |
| | _ => belowField | |
| loop belowCtor belowFieldOpts (belowFields.push belowField) (additionalFVars ++ fvars) | |
| else | |
| loop belowCtor belowFieldOpts (belowFields.push belowField) additionalFVars | |
| else | |
| let ctorType ← inferType belowCtor | |
| withLocalDeclD (←mkFreshUserName `a) ctorType.bindingDomain! fun a => do | |
| let localDecl ← getFVarLocalDecl a | |
| loop (mkApp belowCtor a) belowFieldOpts (belowFields.push $ Pattern.var a.fvarId!) (additionalFVars.push localDecl) | |
| loop belowCtor belowFieldOpts #[] #[] | |
| toInaccessible : Pattern → MetaM Pattern | |
| | Pattern.inaccessible p => return Pattern.inaccessible p | |
| | Pattern.var v => return Pattern.var v | |
| | p => return Pattern.inaccessible $ ←p.toExpr | |
| newMotive (belowType : Expr) (ys : Array Expr) : MetaM Expr := | |
| lambdaTelescope matcherApp.motive fun xs t => do | |
| let numDiscrs := matcherApp.discrs.size | |
| withLocalDeclD (←mkFreshUserName `h_below) (belowType.replaceFVars ys xs) fun h_below => do | |
| let motive ← mkLambdaFVars (xs[:numDiscrs] ++ #[h_below] ++ xs[numDiscrs:]) t | |
| trace[Meta.IndPredBelow.match] "motive := {motive}" | |
| return motive | |
| def findBelowIdx (xs : Array Expr) (motive : Expr) : MetaM $ Option (Expr × Nat) := do | |
| xs.findSomeM? fun x => do | |
| let xTy ← inferType x | |
| xTy.withApp fun f _ => | |
| match f.constName?, xs.indexOf? x with | |
| | some name, some idx => do | |
| if (← isInductivePredicate name) then | |
| let (_, belowTy) ← belowType motive xs idx | |
| let below ← mkFreshExprSyntheticOpaqueMVar belowTy | |
| try | |
| trace[Meta.IndPredBelow.match] "{←Meta.ppGoal below.mvarId!}" | |
| if (← backwardsChaining below.mvarId! 10) then | |
| trace[Meta.IndPredBelow.match] "Found below term in the local context: {below}" | |
| if (← xs.anyM (isDefEq below)) then pure none else pure (below, idx.val) | |
| else | |
| trace[Meta.IndPredBelow.match] "could not find below term in the local context" | |
| pure none | |
| catch _ => pure none | |
| else pure none | |
| | _, _ => pure none | |
| def mkBelow (declName : Name) : MetaM Unit := do | |
| if (← isInductivePredicate declName) then | |
| let x ← getConstInfoInduct declName | |
| if x.isRec then | |
| let ctx ← IndPredBelow.mkContext declName | |
| let decl ← IndPredBelow.mkBelowDecl ctx | |
| addDecl decl | |
| trace[Meta.IndPredBelow] "added {ctx.belowNames}" | |
| ctx.belowNames.forM Lean.mkCasesOn | |
| for i in [:ctx.typeInfos.size] do | |
| try | |
| let decl ← IndPredBelow.mkBrecOnDecl ctx i | |
| addDecl decl | |
| catch e => trace[Meta.IndPredBelow] "failed to prove brecOn for {ctx.belowNames[i]!}\n{e.toMessageData}" | |
| else trace[Meta.IndPredBelow] "Not recursive" | |
| else trace[Meta.IndPredBelow] "Not inductive predicate" | |
| builtin_initialize | |
| registerTraceClass `Meta.IndPredBelow | |
| registerTraceClass `Meta.IndPredBelow.match | |
| end Lean.Meta.IndPredBelow |