Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
c83f527
perf: a non-updating single-subst instantiateMVar
nomeata Jan 29, 2026
946773f
Please CI
nomeata Jan 29, 2026
3bbb693
Docstring
nomeata Jan 29, 2026
b133b76
File from wrong branch
nomeata Jan 29, 2026
293e980
Empty lines
nomeata Jan 29, 2026
5afae98
test: benchmark bvar-blind cross-depth cache for instantiateMVarsNoUp…
nomeata Feb 21, 2026
860b8fd
perf: C++ implementation of instantiateMVarsNoUpdate
nomeata Feb 27, 2026
c5d6267
perf: use instantiateMVarsNoUpdate as drop-in replacement
nomeata Feb 27, 2026
0cc92df
Undo changes to Injective
nomeata Feb 27, 2026
ae9230f
Merge branch 'nightly-with-mathlib' of https://github.com/leanprover/…
nomeata Feb 27, 2026
2c6ee17
perf: add instantiateAllMVars for call sites where all mvars are assi…
nomeata Feb 28, 2026
85c1556
perf: two-pass architecture and scope-tracked sharing for instantiate…
nomeata Mar 2, 2026
54aae0a
perf: make instantiateAllMVars universal and expose instantiateMVarsO…
nomeata Mar 2, 2026
87a814c
perf: wire instantiateAllMVarsSharing as default instantiateMVars
nomeata Mar 2, 2026
d4e4e7c
chore: suppress -Wdeprecated-copy in instantiate_mvars_no_update.cpp
nomeata Mar 2, 2026
cc31859
perf: flat cache with generation-based staleness for instantiateAllMV…
nomeata Mar 2, 2026
5d29c6f
fix: handle fvar shadowing in instantiateAllMVars flat cache
nomeata Mar 2, 2026
2068f0a
perf: skip pass 2 when no delayed assignments are present
nomeata Mar 2, 2026
e8d238b
chore: remove redundant scope_level check in cache_lookup
nomeata Mar 3, 2026
35f8206
perf: fuse direct and delayed mvar resolution into single-pass traversal
nomeata Mar 3, 2026
ec45386
Merge branch 'nightly-with-mathlib' of https://github.com/leanprover/…
nomeata Mar 3, 2026
f0562fa
Revert pass fusion, has perf issues
nomeata Mar 3, 2026
ff6cd50
perf: integrate resolvability tracking into pass 1 traversal cache
nomeata Mar 3, 2026
4a5773e
perf: compute delayed assignment resolvability via post-pass fixpoint
nomeata Mar 3, 2026
93f75de
perf: use replace_fvars in pass 2 to preserve sharing
nomeata Mar 3, 2026
d72b0d5
test: benchmark all instantiateMVars implementations
nomeata Mar 4, 2026
f0b69dc
perf: revert to fused pass 2, keep cached resolvability_checker
nomeata Mar 4, 2026
bc0324b
fix: stack-based scope cache for pass 2 fvar substitution
nomeata Mar 4, 2026
81deb0a
test: update expected output for beta-reduced delayed assignments
nomeata Mar 4, 2026
937b0fc
perf: lazy persistent-list cache for pass 2 scope tracking
nomeata Mar 4, 2026
cead466
chore: remove instantiateMVarsNoUpdate variant
nomeata Mar 5, 2026
56aa64e
refactor: extract scope-aware cache into scope_cache.h
nomeata Mar 5, 2026
3c17c8c
perf: reuse cached values across scopes in scope_cache insert
nomeata Mar 5, 2026
336c060
test: add cross-scope sharing test for scope_cache insert
nomeata Mar 5, 2026
5be0307
fix: check scope generation at all levels in scope_cache rewind
nomeata Mar 5, 2026
e2bbd74
refactor: remove m_global_cache from pass 2, use has_expr_mvar
nomeata Mar 5, 2026
f42e6dd
refactor: clarify pass 2 invariants for mvar and delayed assignment h…
nomeata Mar 5, 2026
5ce31b8
Manual comment tweaks
nomeata Mar 5, 2026
a61d5b6
refactor: merge resolvability checker into pass 2, simplify pass 1
nomeata Mar 5, 2026
bcf308b
Redundant checks
nomeata Mar 5, 2026
ad9be0f
refactor: add proper accessors for DelayedMetavarAssignment fields
nomeata Mar 5, 2026
99e9da1
doc: explain write-back behavior of pass 2 delayed mvar resolution
nomeata Mar 5, 2026
32948ac
refactor: make args buffer local to visit_delayed
nomeata Mar 5, 2026
07f526d
style: flip is_resolvable_pending branch to avoid negation
nomeata Mar 5, 2026
f67de66
refactor: assert fvar_subst_empty in non-resolvable delayed mvar path
nomeata Mar 5, 2026
9284c3c
refactor: overhaul terminology in instantiate_mvars_all.cpp
nomeata Mar 5, 2026
88b3086
doc: improve scope_cache documentation, make rewind private
nomeata Mar 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Lean/Elab/PreDefinition/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public import Lean.Util.NumApps
public import Lean.Meta.Eqns
public import Lean.Elab.RecAppSyntax
public import Lean.Elab.DefView
import Lean.Meta.InstMVarsAll

public section

Expand Down Expand Up @@ -48,7 +49,7 @@ Applies `Lean.instantiateMVars` to the types of values of each predefinition.
-/
def instantiateMVarsAtPreDecls (preDefs : Array PreDefinition) : TermElabM (Array PreDefinition) :=
preDefs.mapM fun preDef => do
pure { preDef with type := (← instantiateMVars preDef.type), value := (← instantiateMVars preDef.value) }
pure { preDef with type := (← instantiateAllMVars preDef.type), value := (← instantiateAllMVars preDef.value) }

/--
Applies `Lean.Elab.Term.levelMVarToParam` to the types of each predefinition.
Expand Down
50 changes: 50 additions & 0 deletions src/Lean/Meta/InstMVarsAll.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner
-/

module

prelude
public import Lean.Meta.Basic

namespace Lean.Meta

@[extern "lean_instantiate_expr_mvars_original"]
private opaque instantiateMVarsOriginalImp (mctx : MetavarContext) (e : Expr) :
MetavarContext × Expr

@[extern "lean_instantiate_expr_mvars_all"]
private opaque instantiateAllMVarsImp (mctx : MetavarContext) (e : Expr) :
MetavarContext × Expr

@[extern "lean_instantiate_expr_mvars_all_sharing"]
private opaque instantiateAllMVarsSharingImp (mctx : MetavarContext) (e : Expr) :
MetavarContext × Expr

/-- The original single-pass `instantiateMVars` implementation, exposed for benchmarking
independently of which implementation is the default. -/
public def instantiateMVarsOriginal (e : Expr) : MetaM Expr := do
if !e.hasMVar then return e
let (mctx, eNew) := instantiateMVarsOriginalImp (← getMCtx) e
modifyMCtx fun _ => mctx; return eNew

/-- Like `instantiateMVars` but uses a fused two-pass approach.
Pass 1 resolves direct mvar assignments with write-back.
Pass 2 resolves delayed assignments with a fused fvar substitution,
avoiding separate `replace_fvars` calls. Preserves sharing using
a flat cache with lazy staleness detection via persistent scope
generation snapshots. -/
public def instantiateAllMVars (e : Expr) : MetaM Expr := do
if !e.hasMVar then return e
let (mctx, eNew) := instantiateAllMVarsImp (← getMCtx) e
modifyMCtx fun _ => mctx; return eNew

/-- Alias for `instantiateAllMVars`. -/
public def instantiateAllMVarsSharing (e : Expr) : MetaM Expr := do
if !e.hasMVar then return e
let (mctx, eNew) := instantiateAllMVarsSharingImp (← getMCtx) e
modifyMCtx fun _ => mctx; return eNew

end Lean.Meta
24 changes: 24 additions & 0 deletions src/Lean/MetavarContext.lean
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,12 @@ def MetavarContext.getDelayedMVarAssignmentCore? (mctx : MetavarContext) (mvarId
def MetavarContext.getDelayedMVarAssignmentExp (mctx : MetavarContext) (mvarId : MVarId) : Option DelayedMetavarAssignment :=
mctx.dAssignment.find? mvarId

@[export lean_delayed_mvar_assignment_fvars]
def DelayedMetavarAssignment.fvarsExp (d : DelayedMetavarAssignment) : Array Expr := d.fvars

@[export lean_delayed_mvar_assignment_mvar_id_pending]
def DelayedMetavarAssignment.mvarIdPendingExp (d : DelayedMetavarAssignment) : MVarId := d.mvarIdPending

def getDelayedMVarAssignment? [Monad m] [MonadMCtx m] (mvarId : MVarId) : m (Option DelayedMetavarAssignment) :=
return (← getMCtx).getDelayedMVarAssignmentCore? mvarId

Expand Down Expand Up @@ -1483,6 +1489,24 @@ def levelMVarToParam (mctx : MetavarContext) (alreadyUsedPred : Name → Bool) (
def getExprAssignmentDomain (mctx : MetavarContext) : Array MVarId :=
mctx.eAssignment.foldl (init := #[]) fun a mvarId _ => Array.push a mvarId

/--
Abstract the given fvars from an unassigned mvar by creating a delayed-assigned mvar.
Returns `(mctx', result)` where `result` has the fvars replaced by `values`.
This can be used when encountering an unassigned mvar with an active fvar
substitution.
-/
@[export lean_abstract_mvar_fvars]
def abstractMVarFVars (mctx : MetavarContext) (mvarId : MVarId) (fvars : Array Expr) (values : Array Expr) : MetavarContext × Expr :=
match mctx.decls.find? mvarId with
| none => (mctx, mkMVar mvarId)
| some _mvarDecl =>
let ngen : NameGenerator := { namePrefix := `_noUpdate, idx := mctx.mvarCounter }
let ctx : MkBinding.Context := { quotContext := `_root_, preserveOrder := false }
let state : MkBinding.State := { mctx, nextMacroScope := 0, ngen }
match (MkBinding.elimMVarDeps fvars (mkMVar mvarId) ctx).run state with
| .ok e s => (s.mctx, Expr.replaceFVars e fvars values)
| .error _ s => (s.mctx, mkMVar mvarId)

end MetavarContext

namespace MVarId
Expand Down
1 change: 1 addition & 0 deletions src/kernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ add_library(
inductive.cpp
trace.cpp
instantiate_mvars.cpp
instantiate_mvars_all.cpp
)
23 changes: 20 additions & 3 deletions src/kernel/instantiate_mvars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ extern "C" LEAN_EXPORT object * lean_instantiate_level_mvars(object * m, object

extern "C" object * lean_get_mvar_assignment(obj_arg mctx, obj_arg mid);
extern "C" object * lean_get_delayed_mvar_assignment(obj_arg mctx, obj_arg mid);
extern "C" object * lean_delayed_mvar_assignment_fvars(obj_arg d);
extern "C" object * lean_delayed_mvar_assignment_mvar_id_pending(obj_arg d);
extern "C" object * lean_assign_mvar(obj_arg mctx, obj_arg mid, obj_arg val);
typedef object_ref delayed_assignment;

Expand All @@ -116,6 +118,14 @@ option_ref<delayed_assignment> get_delayed_mvar_assignment(metavar_ctx & mctx, n
return option_ref<delayed_assignment>(lean_get_delayed_mvar_assignment(mctx.to_obj_arg(), mid.to_obj_arg()));
}

array_ref<expr> delayed_assignment_fvars(delayed_assignment const & d) {
return array_ref<expr>(lean_delayed_mvar_assignment_fvars(d.to_obj_arg()));
}

name delayed_assignment_mvar_id_pending(delayed_assignment const & d) {
return name(lean_delayed_mvar_assignment_mvar_id_pending(d.to_obj_arg()));
}

expr replace_fvars(expr const & e, array_ref<expr> const & fvars, expr const * rev_args) {
size_t sz = fvars.size();
if (sz == 0)
Expand Down Expand Up @@ -290,8 +300,8 @@ class instantiate_mvars_fn {
metavariables, we replace the free variables `fvars` in `newVal` with the first
`fvars.size` elements of `args`.
*/
array_ref<expr> fvars(cnstr_get(d.get_val().raw(), 0), true);
name mid_pending(cnstr_get(d.get_val().raw(), 1), true);
array_ref<expr> fvars = delayed_assignment_fvars(d.get_val());
name mid_pending = delayed_assignment_mvar_id_pending(d.get_val());
if (fvars.size() > get_app_num_args(e)) {
/*
We don't have sufficient arguments for instantiating the free variables `fvars`.
Expand Down Expand Up @@ -362,12 +372,19 @@ class instantiate_mvars_fn {
expr operator()(expr const & e) { return visit(e); }
};

extern "C" LEAN_EXPORT object * lean_instantiate_expr_mvars(object * m, object * e) {
/* The original single-pass implementation, exposed for benchmarking. */
extern "C" LEAN_EXPORT object * lean_instantiate_expr_mvars_original(object * m, object * e) {
metavar_ctx mctx(m);
expr e_new = instantiate_mvars_fn(mctx)(expr(e));
object * r = alloc_cnstr(0, 2, 0);
cnstr_set(r, 0, mctx.steal());
cnstr_set(r, 1, e_new.steal());
return r;
}

extern "C" LEAN_EXPORT object * lean_instantiate_expr_mvars_all_sharing(object * m, object * e);

extern "C" LEAN_EXPORT object * lean_instantiate_expr_mvars(object * m, object * e) {
return lean_instantiate_expr_mvars_all_sharing(m, e);
}
}
Loading
Loading