lean4-htt/src/Lean/Meta/Tactic/Assert.lean
Sebastian Ullrich 416a8372cd
perf: shorten rebuild critical path by 19% (#9626)
This PR adjusts the import graph, primarily of `Lean`, such that the
worst case rebuild time of core (`lean` only) is below 3 minutes on the
speedcenter machine (not captured by benchmark yet).
2025-08-01 11:18:21 +00:00

150 lines
6 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Lean.Meta.Tactic.FVarSubst
public import Lean.Meta.Tactic.Intro
public import Lean.Meta.Tactic.Revert
public import Lean.Util.ForEachExpr
import Lean.Meta.AppBuilder
public section
namespace Lean.Meta
/--
Convert the given goal `Ctx |- target` into `Ctx |- type -> target`.
It assumes `val` has type `type` -/
def _root_.Lean.MVarId.assert (mvarId : MVarId) (name : Name) (type : Expr) (val : Expr) : MetaM MVarId :=
mvarId.withContext do
mvarId.checkNotAssigned `assert
let tag ← mvarId.getTag
let target ← mvarId.getType
let newType := Lean.mkForall name BinderInfo.default type target
let newMVar ← mkFreshExprSyntheticOpaqueMVar newType tag
mvarId.assign (mkApp newMVar val)
return newMVar.mvarId!
/-- Add the hypothesis `h : t`, given `v : t`, and return the new `FVarId`. -/
def _root_.Lean.MVarId.note (g : MVarId) (h : Name) (v : Expr) (t? : Option Expr := .none) :
MetaM (FVarId × MVarId) := do
(← g.assert h (← match t? with | some t => pure t | none => inferType v) v).intro1P
/--
Convert the given goal `Ctx |- target` into `Ctx |- let name : type := val; target`.
It assumes `val` has type `type` -/
def _root_.Lean.MVarId.define (mvarId : MVarId) (name : Name) (type : Expr) (val : Expr) : MetaM MVarId := do
mvarId.withContext do
mvarId.checkNotAssigned `define
let tag ← mvarId.getTag
let target ← mvarId.getType
let newType := Lean.mkLet name type val target
let newMVar ← mkFreshExprSyntheticOpaqueMVar newType tag
mvarId.assign newMVar
return newMVar.mvarId!
/--
Convert the given goal `Ctx |- target` into `Ctx |- (hName : type) -> hName = val -> target`.
It assumes `val` has type `type` -/
def _root_.Lean.MVarId.assertExt (mvarId : MVarId) (name : Name) (type : Expr) (val : Expr) (hName : Name := `h) : MetaM MVarId := do
mvarId.withContext do
mvarId.checkNotAssigned `assert
let tag ← mvarId.getTag
let target ← mvarId.getType
let u ← getLevel type
let hType := mkApp3 (mkConst `Eq [u]) type (mkBVar 0) val
let newType := Lean.mkForall name BinderInfo.default type $ Lean.mkForall hName BinderInfo.default hType target
let newMVar ← mkFreshExprSyntheticOpaqueMVar newType tag
let rflPrf ← mkEqRefl val
mvarId.assign (mkApp2 newMVar val rflPrf)
return newMVar.mvarId!
structure AssertAfterResult where
fvarId : FVarId
mvarId : MVarId
subst : FVarSubst
/--
Convert the given goal `Ctx |- target` into a goal containing `(userName : type)` after the local declaration with if `fvarId`.
It assumes `val` has type `type`, and that `type` is well-formed after `fvarId`.
Note that `val` does not need to be well-formed after `fvarId`. That is, it may contain variables that are defined after `fvarId`. -/
def _root_.Lean.MVarId.assertAfter (mvarId : MVarId) (fvarId : FVarId) (userName : Name) (type : Expr) (val : Expr) : MetaM AssertAfterResult := do
mvarId.checkNotAssigned `assertAfter
let (fvarIds, mvarId) ← mvarId.revertAfter fvarId
let mvarId ← mvarId.assert userName type val
let (fvarIdNew, mvarId) ← mvarId.intro1P
let (fvarIdsNew, mvarId) ← mvarId.introNP fvarIds.size
let mut subst := {}
for f in fvarIds, fNew in fvarIdsNew do
subst := subst.insert f (mkFVar fNew)
return { fvarId := fvarIdNew, mvarId, subst }
structure Hypothesis where
userName : Name
type : Expr
value : Expr
/-- The hypothesis' `BinderInfo` -/
binderInfo : BinderInfo := .default
/-- The hypothesis' `LocalDeclKind` -/
kind : LocalDeclKind := .default
/--
Convert the given goal `Ctx |- target` into `Ctx, (hs[0].userName : hs[0].type) ... |-target`.
It assumes `hs[i].val` has type `hs[i].type`. -/
def _root_.Lean.MVarId.assertHypotheses (mvarId : MVarId) (hs : Array Hypothesis) : MetaM (Array FVarId × MVarId) := do
if hs.isEmpty then
return (#[], mvarId)
else mvarId.withContext do
mvarId.checkNotAssigned `assertHypotheses
let tag ← mvarId.getTag
let target ← mvarId.getType
let targetNew := hs.foldr (init := target) fun h targetNew =>
.forallE h.userName h.type targetNew h.binderInfo
let mvarNew ← mkFreshExprSyntheticOpaqueMVar targetNew tag
let val := hs.foldl (init := mvarNew) fun val h => .app val h.value
mvarId.assign val
let (fvarIds, mvarId) ← mvarNew.mvarId!.introNP hs.size
mvarId.modifyLCtx fun lctx => Id.run do
let mut lctx := lctx
for h : i in *...hs.size do
let h := hs[i]
if h.kind != .default then
lctx := lctx.setKind fvarIds[i]! h.kind
pure lctx
return (fvarIds, mvarId)
/--
Replace hypothesis `hyp` in goal `g` with `proof : typeNew`.
The new hypothesis is given the same user name as the original,
it attempts to avoid reordering hypotheses, and the original is cleared if possible.
-/
-- adapted from Lean.Meta.replaceLocalDeclCore
def _root_.Lean.MVarId.replace (g : MVarId) (hyp : FVarId) (proof : Expr) (typeNew : Option Expr := none) :
MetaM AssertAfterResult :=
g.withContext do
let typeNew ← match typeNew with
| some t => pure t
| none => inferType proof
let ldecl ← hyp.getDecl
-- `typeNew` may contain variables that occur after `hyp`.
-- Thus, we use the auxiliary function `findMaxFVar` to ensure `typeNew` is well-formed
-- at the position we are inserting it.
let (_, ldecl') ← findMaxFVar typeNew |>.run ldecl
let result ← g.assertAfter ldecl'.fvarId ldecl.userName typeNew proof
(return { result with mvarId := ← result.mvarId.clear hyp }) <|> pure result
where
/-- Finds the `LocalDecl` for the FVar in `e` with the highest index. -/
findMaxFVar (e : Expr) : StateRefT LocalDecl MetaM Unit :=
e.forEach' fun e => do
if e.isFVar then
let ldecl' ← e.fvarId!.getDecl
modify fun ldecl => if ldecl'.index > ldecl.index then ldecl' else ldecl
return false
else
return e.hasFVar
end Lean.Meta