Skip to content

perf: restrict and record option accesses during type class resolution - #14932

Draft
Kha wants to merge 11 commits into
masterfrom
push-pnlkkqrtolzm
Draft

perf: restrict and record option accesses during type class resolution#14932
Kha wants to merge 11 commits into
masterfrom
push-pnlkkqrtolzm

Conversation

@Kha

@Kha Kha commented Aug 26, 2026

Copy link
Copy Markdown
Member

This PR makes type class resolution cache entries depend on the options they observed, as a first prerequisite for more extensive caching.

A query now records every result-relevant option lookup (Lean.getRecordedOption), and an entry is served only while its recorded lookups give the same answers, so options no longer have to invalidate the cache wholesale (nor silently fail to). Options resolved once per query, such as the definitional-equality compatibility flags and the resource limits, are part of the cache key instead.

Acquiring the options directly is forbidden: getOptions panics while Core.Context.recordingDeps is set, so nothing can go unrecorded. Type class resolution is a closed system, so the few readers whose result cannot influence a cached entry acquire them through the new MonadOptions.getOptionsUnrestricted, each carrying its one-line argument (trace and profiler collection, message rendering, diagnostics counters, and limits whose excess throws and is never cached). The marker is scoped to the computation rather than carried by the options value or the environment, both of which outlive the query in contexts captured for later rendering.

@Kha

Kha commented Aug 26, 2026

Copy link
Copy Markdown
Member Author

!bench

@leanprover-radar

leanprover-radar commented Aug 26, 2026

Copy link
Copy Markdown

Benchmark results for e32b54b against b9c9eb9 are in. There are significant results. @Kha

  • 🟥 build exited with code 1
  • 🟥 other exited with code 1

No significant changes detected.

@Kha

Kha commented Aug 26, 2026

Copy link
Copy Markdown
Member Author

!bench

@Kha
Kha force-pushed the push-pnlkkqrtolzm branch from e32b54b to c71b1d3 Compare August 26, 2026 14:10
@leanprover-radar

leanprover-radar commented Aug 26, 2026

Copy link
Copy Markdown

Benchmark results for c71b1d3 against b9c9eb9 are in. There are significant results. @Kha

  • 🟥 build//instructions: +32.7G (+0.28%)

Medium changes (4🟥)

  • 🟥 size/all/.ir//bytes: +2MiB (+0.48%)
  • 🟥 size/all/.olean.private//bytes: +6MiB (+0.42%)
  • 🟥 size/compile/.out//bytes: +15MiB (+0.53%)
  • 🟥 size/libleanshared.so//bytes: +977kiB (+0.43%)

Small changes (190🟥)

  • 🟥 build/module/Init.Data.Array.Basic//instructions: +26.5M (+0.24%)
  • 🟥 build/module/Init.Data.Array.Erase//instructions: +21.9M (+0.31%)
  • 🟥 build/module/Init.Data.Array.Find//instructions: +23.7M (+0.24%)
  • 🟥 build/module/Init.Data.Array.Lemmas//instructions: +156.0M (+0.29%)
  • 🟥 build/module/Init.Data.Array.Lex.Lemmas//instructions: +40.9M (+0.43%)
  • 🟥 build/module/Init.Data.Array.MapIdx//instructions: +23.7M (+0.27%)
  • 🟥 build/module/Init.Data.Array.QSort.Basic//instructions: +36.9M (+0.37%)
  • 🟥 build/module/Init.Data.BitVec.Bitblast//instructions: +134.2M (+0.27%)
  • 🟥 build/module/Init.Data.BitVec.Lemmas//instructions: +341.4M (+0.29%)
  • 🟥 build/module/Init.Data.Int.DivMod.Lemmas//instructions: +122.4M (+0.33%)
  • 🟥 build/module/Init.Data.Iterators.Lemmas.Combinators.FilterMap//instructions: +51.6M (+0.27%)
  • 🟥 build/module/Init.Data.Iterators.Lemmas.Combinators.Monadic.FilterMap//instructions: +118.2M (+0.33%)
  • 🟥 build/module/Init.Data.Iterators.Lemmas.Consumers.Loop//instructions: +32.8M (+0.31%)
  • 🟥 build/module/Init.Data.Iterators.Lemmas.Consumers.Monadic.Loop//instructions: +38.8M (+0.32%)
  • 🟥 build/module/Init.Data.List.Find//instructions: +63.9M (+0.34%)
  • 🟥 build/module/Init.Data.List.Lemmas//instructions: +106.6M (+0.28%)
  • 🟥 build/module/Init.Data.List.MinMaxIdx//instructions: +32.1M (+0.33%)
  • 🟥 build/module/Init.Data.List.ToArray//instructions: +47.6M (+0.27%)
  • 🟥 build/module/Init.Data.Nat.Lemmas//instructions: +86.8M (+0.36%)
  • 🟥 build/module/Init.Data.Range.Polymorphic.IntLemmas//instructions: +108.4M (+0.41%)
  • and 169 more
  • and 1 hidden

Kha and others added 2 commits August 27, 2026 13:45
This PR makes type class resolution cache entries depend on the options they observed: a query records every result-relevant option lookup (`Lean.getRecordedOption`), and an entry is served only while its recorded lookups give the same answers, so options no longer have to invalidate the cache wholesale (nor silently fail to). Options resolved once per query, such as the definitional-equality compatibility flags and the resource limits, are part of the cache key instead.

Acquiring the options plainly is what a running query forbids: `getOptions` panics while `Core.Context.recordingDeps` is set, so nothing can go unrecorded. Type class resolution is a closed system, so the few readers whose result cannot influence a cached entry acquire them through the new `MonadOptions.getOptionsUnrestricted`, each carrying its one-line argument (trace and profiler collection, message rendering, diagnostics counters, and limits whose excess throws and is never cached). The marker is scoped to the computation rather than carried by the options value or the environment, both of which outlive the query in contexts captured for later rendering.

The reachable set was measured rather than estimated: over the full `tests/elab` pile a recording query acquires the options 4.3M times from 17 source sites, all of them either audited unrestricted readers or the cache machinery itself.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This PR speeds up type class resolution by storing the definitional-equality flags and resource limits that partition the resolution cache as unboxed words rather than as pointers to records, so that hashing and comparing a cache key no longer walks thirteen boxed fields on every probe.

`SynthDefEqFlags` becomes a single `UInt32` bitfield with accessors, which the compiler erases to a bare word, and `SynthLimits` is inlined into `SynthInstanceCacheKey` as four `UInt64` fields. The key therefore carries five unboxed scalars instead of two pointers to freshly allocated records, which also removes two allocations per query.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Kha
Kha force-pushed the push-pnlkkqrtolzm branch from c71b1d3 to 7aa96c2 Compare August 27, 2026 13:55
@Kha

Kha commented Aug 27, 2026

Copy link
Copy Markdown
Member Author

!bench

@leanprover-radar

leanprover-radar commented Aug 27, 2026

Copy link
Copy Markdown

Benchmark results for 7aa96c2 against b9c9eb9 are in. There are significant results. @Kha

  • 🟥 build//instructions: +32.9G (+0.28%)

Medium changes (4🟥)

  • 🟥 size/all/.ir//bytes: +2MiB (+0.48%)
  • 🟥 size/all/.olean.private//bytes: +5MiB (+0.42%)
  • 🟥 size/compile/.out//bytes: +15MiB (+0.53%)
  • 🟥 size/libleanshared.so//bytes: +978kiB (+0.43%)

Small changes (182🟥)

  • 🟥 build/module/Init.Data.Array.Basic//instructions: +26.4M (+0.24%)
  • 🟥 build/module/Init.Data.Array.Find//instructions: +25.6M (+0.26%)
  • 🟥 build/module/Init.Data.Array.Lemmas//instructions: +162.4M (+0.31%)
  • 🟥 build/module/Init.Data.Array.Lex.Lemmas//instructions: +39.9M (+0.42%)
  • 🟥 build/module/Init.Data.Array.MapIdx//instructions: +21.9M (+0.25%)
  • 🟥 build/module/Init.Data.Array.QSort.Basic//instructions: +35.0M (+0.35%)
  • 🟥 build/module/Init.Data.BitVec.Bitblast//instructions: +114.2M (+0.23%)
  • 🟥 build/module/Init.Data.BitVec.Lemmas//instructions: +305.2M (+0.26%)
  • 🟥 build/module/Init.Data.Int.DivMod.Lemmas//instructions: +129.4M (+0.35%)
  • 🟥 build/module/Init.Data.Iterators.Lemmas.Combinators.FilterMap//instructions: +52.2M (+0.27%)
  • 🟥 build/module/Init.Data.Iterators.Lemmas.Combinators.Monadic.FilterMap//instructions: +105.0M (+0.30%)
  • 🟥 build/module/Init.Data.Iterators.Lemmas.Consumers.Loop//instructions: +37.0M (+0.35%)
  • 🟥 build/module/Init.Data.Iterators.Lemmas.Consumers.Monadic.Loop//instructions: +40.2M (+0.33%)
  • 🟥 build/module/Init.Data.List.Find//instructions: +63.7M (+0.34%)
  • 🟥 build/module/Init.Data.List.Lemmas//instructions: +119.9M (+0.31%)
  • 🟥 build/module/Init.Data.List.MinMaxIdx//instructions: +30.6M (+0.32%)
  • 🟥 build/module/Init.Data.List.ToArray//instructions: +50.7M (+0.28%)
  • 🟥 build/module/Init.Data.Nat.Lemmas//instructions: +93.1M (+0.38%)
  • 🟥 build/module/Init.Data.Range.Polymorphic.IntLemmas//instructions: +109.7M (+0.41%)
  • 🟥 build/module/Init.Data.Range.Polymorphic.Lemmas//instructions: +169.8M (+0.39%)
  • and 161 more
  • and 1 hidden

…word

This PR removes a pointer field from `Meta.Context`, which is reconstructed on every `withConfig`/`withTransparency`-style scope, by storing the resolved definitional-equality flags as a sentinel-tagged `UInt32` instead of an `Option`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions github-actions Bot added toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN mathlib4-nightly-available A branch for this PR exists at leanprover-community/mathlib4-nightly-testing:lean-pr-testing-NNNN labels Aug 27, 2026
@mathlib-lean-pr-testing

mathlib-lean-pr-testing Bot commented Aug 27, 2026

Copy link
Copy Markdown

Mathlib CI status (docs):

  • 🟡 Mathlib branch lean-pr-testing-14932 build this PR didn't complete normally. (2026-08-27 14:23:04) View Log
  • ✅ Mathlib branch lean-pr-testing-14932 has successfully built against this PR. (2026-08-27 18:12:06) View Log
  • ✅ Mathlib branch lean-pr-testing-14932 has successfully built against this PR. (2026-08-28 13:50:13) View Log
  • ❗ Batteries/Mathlib CI will not be attempted unless your PR branches off the nightly-with-mathlib branch. Try git rebase 0e6944a825993b20d0834f2f825a3439bad58515 --onto 138ca9f20763523c4093baa092cf371e89535098. You can force Mathlib CI using the force-mathlib-ci label. (2026-08-31 16:15:21)

@leanprover-bot leanprover-bot added the builds-manual CI has verified that the Lean Language Reference builds against this PR label Aug 27, 2026
@leanprover-bot

leanprover-bot commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Reference manual CI status:

  • ✅ Reference manual branch lean-pr-testing-14932 has successfully built against this PR. (2026-08-27 14:24:25) View Log
  • 🟡 Reference manual branch lean-pr-testing-14932 build against this PR didn't complete normally. (2026-08-27 14:26:12) View Log
  • 🟡 Reference manual branch lean-pr-testing-14932 build against this PR didn't complete normally. (2026-08-27 17:20:48) View Log
  • ✅ Reference manual branch lean-pr-testing-14932 has successfully built against this PR. (2026-08-27 17:21:00) View Log
  • ✅ Reference manual branch lean-pr-testing-14932 has successfully built against this PR. (2026-08-28 13:07:00) View Log
  • 🟡 Reference manual branch lean-pr-testing-14932 build against this PR didn't complete normally. (2026-08-28 13:11:23) View Log
  • ❗ Reference manual CI will not be attempted unless your PR branches off the nightly-with-manual branch. Try git rebase 0e6944a825993b20d0834f2f825a3439bad58515 --onto e991a05e359a25988f49bff3ab8af986e959b866. You can force reference manual CI using the force-manual-ci label. (2026-08-31 16:15:23)

@Kha

Kha commented Aug 27, 2026

Copy link
Copy Markdown
Member Author

!bench

@leanprover-radar

leanprover-radar commented Aug 27, 2026

Copy link
Copy Markdown

Benchmark results for 3fbd61b against b9c9eb9 are in. There are significant results. @Kha

  • 🟥 build//instructions: +27.7G (+0.24%)

Medium changes (4🟥)

  • 🟥 size/all/.ir//bytes: +2MiB (+0.49%)
  • 🟥 size/all/.olean.private//bytes: +5MiB (+0.42%)
  • 🟥 size/compile/.out//bytes: +14MiB (+0.50%)
  • 🟥 size/libleanshared.so//bytes: +932kiB (+0.41%)

Small changes (145🟥)

  • 🟥 build/module/Init.Data.Array.Lemmas//instructions: +115.3M (+0.22%)
  • 🟥 build/module/Init.Data.Array.Lex.Lemmas//instructions: +30.5M (+0.32%)
  • 🟥 build/module/Init.Data.BitVec.Lemmas//instructions: +258.2M (+0.22%)
  • 🟥 build/module/Init.Data.Int.DivMod.Lemmas//instructions: +103.8M (+0.28%)
  • 🟥 build/module/Init.Data.Iterators.Lemmas.Combinators.FilterMap//instructions: +35.6M (+0.19%)
  • 🟥 build/module/Init.Data.Iterators.Lemmas.Combinators.Monadic.FilterMap//instructions: +77.8M (+0.22%)
  • 🟥 build/module/Init.Data.List.Find//instructions: +49.0M (+0.26%)
  • 🟥 build/module/Init.Data.List.Lemmas//instructions: +73.2M (+0.19%)
  • 🟥 build/module/Init.Data.List.MinMaxIdx//instructions: +26.3M (+0.27%)
  • 🟥 build/module/Init.Data.Nat.Lemmas//instructions: +75.4M (+0.31%)
  • 🟥 build/module/Init.Data.Range.Polymorphic.Lemmas//instructions: +130.0M (+0.30%)
  • 🟥 build/module/Init.Data.Range.Polymorphic.NatLemmas//instructions: +120.2M (+0.36%)
  • 🟥 build/module/Init.Data.Range.Polymorphic.UInt//instructions: +13.3M (+0.31%)
  • 🟥 build/module/Init.Data.SInt.Bitwise//instructions: +30.4M (+0.26%)
  • 🟥 build/module/Init.Data.String.Decode//instructions: +56.7M (+0.23%)
  • 🟥 build/module/Init.Data.UInt.Bitwise//instructions: +57.6M (+0.27%)
  • 🟥 build/module/Lake.DSL.Attributes//instructions: +15.2M (+1.63%)
  • 🟥 build/module/Lake.DSL.Meta//instructions: +26.4M (+0.95%)
  • 🟥 build/module/Lake.Toml.Elab.Expression//instructions: +25.5M (+0.68%)
  • 🟥 build/module/Lean.AddDecl//instructions: +155.2M (+2.76%) (reduced significance based on absolute threshold)
  • and 124 more
  • and 1 hidden

mathlib-nightly-testing Bot pushed a commit to leanprover-community/batteries that referenced this pull request Aug 27, 2026
mathlib-nightly-testing Bot pushed a commit to leanprover-community/mathlib4-nightly-testing that referenced this pull request Aug 27, 2026
leanprover-bot added a commit to leanprover/reference-manual that referenced this pull request Aug 27, 2026
@mathlib-lean-pr-testing mathlib-lean-pr-testing Bot added the builds-mathlib CI has verified that Mathlib builds against this PR label Aug 27, 2026
@Kha

Kha commented Aug 28, 2026

Copy link
Copy Markdown
Member Author

!bench mathlib

@leanprover-radar

leanprover-radar commented Aug 28, 2026

Copy link
Copy Markdown

Benchmark results for leanprover-community/mathlib4-nightly-testing@dabd0b6 against leanprover-community/mathlib4-nightly-testing@dc2cdbd are in. There are significant results. @Kha

  • 🟥 build//instructions: +67.6G (+0.05%)

Small changes (21🟥)

  • 🟥 build/module/Aesop.Builder.Apply//instructions: +22.3M (+1.30%)
  • 🟥 build/module/Aesop.Forward.State.ApplyGoalDiff//instructions: +30.9M (+1.51%)
  • 🟥 build/module/Aesop.Forward.State.Initial//instructions: +29.4M (+1.49%)
  • 🟥 build/module/Aesop.Frontend.Command//instructions: +37.8M (+0.78%)
  • 🟥 build/module/Aesop.Main//instructions: +134.3M (+3.60%) (reduced significance based on absolute threshold)
  • 🟥 build/module/Aesop.Saturate//instructions: +736.1M (+5.23%) (reduced significance based on absolute threshold)
  • 🟥 build/module/Aesop.Search.RuleSelection//instructions: +40.3M (+1.69%)
  • 🟥 build/module/Aesop.Util.Tactic.Unfold//instructions: +30.0M (+1.29%)
  • 🟥 build/module/Batteries.CodeAction.Deprecated//instructions: +26.6M (+1.05%)
  • 🟥 build/module/Batteries.Lean.LawfulMonad//instructions: +22.4M (+1.44%)
  • 🟥 build/module/Batteries.Recycling.RBTree//instructions: +23.5M (+1.62%)
  • 🟥 build/module/Batteries.Tactic.Alias//instructions: +48.7M (+0.84%)
  • 🟥 build/module/Batteries.Tactic.NoMatch//instructions: +29.6M (+1.12%)
  • 🟥 build/module/Batteries.Tactic.PrintPrefix//instructions: +36.2M (+1.05%)
  • 🟥 build/module/LeanSearchClient.LoogleSyntax//instructions: +34.1M (+0.77%)
  • 🟥 build/module/LeanSearchClient.Syntax//instructions: +69.8M (+1.09%)
  • 🟥 build/module/Mathlib.Order.Std//instructions: +61.9M (+1.18%)
  • 🟥 build/module/Mathlib.Tactic.AdaptationNote//instructions: +19.0M (+0.75%)
  • 🟥 build/module/Mathlib.Tactic.GRewrite.Elab//instructions: +134.9M (+1.58%)
  • 🟥 build/module/Mathlib.Util.AddRelatedDecl//instructions: +67.7M (+1.17%)
  • and 1 more

`Core.instMonadOptionsCoreM.getOptions` is inlined at every `getOptions` call site in the compiler, and `panic!` is `@[inline]`, so each of those sites grew a private copy of the tripwire's message constants: 265 copies of the 173-byte message ended up in `libleanshared.so`, along with a `panic._at_….spec_N` specialization and four `_closed_N` constants per site. Moving the panic into a `@[noinline]` `where` helper leaves one copy.

Measured against the merge base, over the whole stage1 stdlib: `libleanshared.so` +0.42% -> +0.15%, `.olean.private` +0.42% -> +0.25%, `.ir` +0.47% -> +0.31%. Per module (`trace.Compiler.stat` sum of `decl.size`), `Lean/Compiler/LCNF/Simp` +2.40% -> +1.06% and `Lean/Elab/App` +0.84% -> +0.58%, with the extra compiled declarations gone entirely in most modules. `Init` modules were and remain unaffected.

Co-Authored-By: Claude <noreply@anthropic.com>
@Kha

Kha commented Aug 28, 2026

Copy link
Copy Markdown
Member Author

!bench

@leanprover-radar

leanprover-radar commented Aug 28, 2026

Copy link
Copy Markdown

Benchmark results for 2d09aa2 against b9c9eb9 are in. There are significant results. @Kha

  • 🟥 build//instructions: +24.5G (+0.21%)

Large changes (3🟥)

  • 🟥 compiled/rbmap_checkpoint//instructions: +1.7G (+13.55%)
  • 🟥 compiled/rbmap_checkpoint//task-clock: +293ms (+19.86%)
  • 🟥 compiled/rbmap_checkpoint//wall-clock: +304ms (+20.51%)

Medium changes (1✅)

  • compiled/rbmap_checkpoint//maxrss: -2MiB (-0.07%)

Small changes (104🟥)

  • 🟥 build/module/Init.Data.Array.Lemmas//instructions: +116.7M (+0.22%)
  • 🟥 build/module/Init.Data.Array.Lex.Lemmas//instructions: +35.5M (+0.37%)
  • 🟥 build/module/Init.Data.BitVec.Lemmas//instructions: +263.8M (+0.23%)
  • 🟥 build/module/Init.Data.Format.Basic//instructions: +13.6M (+0.58%)
  • 🟥 build/module/Init.Data.Iterators.Lemmas.Combinators.FilterMap//instructions: +39.9M (+0.21%)
  • 🟥 build/module/Init.Data.Iterators.Lemmas.Combinators.Monadic.FilterMap//instructions: +81.4M (+0.23%)
  • 🟥 build/module/Init.Data.List.Find//instructions: +45.8M (+0.24%)
  • 🟥 build/module/Init.Data.List.Lemmas//instructions: +80.0M (+0.21%)
  • 🟥 build/module/Init.Data.List.MinMaxIdx//instructions: +21.6M (+0.23%)
  • 🟥 build/module/Init.Data.Nat.Lemmas//instructions: +76.0M (+0.31%)
  • 🟥 build/module/Init.Data.Range.Polymorphic.IntLemmas//instructions: +83.5M (+0.31%)
  • 🟥 build/module/Init.Data.Range.Polymorphic.Lemmas//instructions: +124.7M (+0.29%)
  • 🟥 build/module/Init.Data.String.Decode//instructions: +57.0M (+0.23%)
  • 🟥 build/module/Init.Data.UInt.Bitwise//instructions: +52.4M (+0.25%)
  • 🟥 build/module/Lake.DSL.Attributes//instructions: +9.8M (+1.04%)
  • 🟥 build/module/Lake.Toml.Elab.Expression//instructions: +20.5M (+0.55%)
  • 🟥 build/module/Lean.AddDecl//instructions: +106.9M (+1.90%) (reduced significance based on absolute threshold)
  • 🟥 build/module/Lean.Compiler.LCNF.Main//instructions: +85.9M (+1.61%)
  • 🟥 build/module/Lean.Compiler.LCNF.Visibility//instructions: +42.2M (+0.87%)
  • 🟥 build/module/Lean.CoreM//instructions: +515.8M (+6.75%) (reduced significance based on *//lines)
  • and 83 more
  • and 1 hidden

… resolution cache

`validateDeps?` ignored its `Environment` argument and always reported the entry as not re-stamped, so `findCachedResult?`'s re-insertion branch was unreachable; both collapse into a `List.find?` over `validOptionAccesses`.

The `SynthInstanceCache` and `Meta.Cache` docstrings described an environment-dependency tier that does not exist here, naming `SynthDepLog`, `Lean.EnvExtension.TCResolutionAccess` and `synthInstanceCacheExt`; the former also carried a duplicated half-sentence from a bad edit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mathlib-nightly-testing Bot pushed a commit to leanprover-community/batteries that referenced this pull request Aug 28, 2026
mathlib-nightly-testing Bot pushed a commit to leanprover-community/mathlib4-nightly-testing that referenced this pull request Aug 28, 2026
leanprover-bot added a commit to leanprover/reference-manual that referenced this pull request Aug 28, 2026
@Kha

Kha commented Aug 28, 2026

Copy link
Copy Markdown
Member Author

!bench mathlib

@leanprover-radar

leanprover-radar commented Aug 28, 2026

Copy link
Copy Markdown

Benchmark results for leanprover-community/mathlib4-nightly-testing@200ab0a against leanprover-community/mathlib4-nightly-testing@dc2cdbd are in. There are significant results. @Kha

  • 🟥 build//instructions: +112.2G (+0.08%)

Small changes (1✅, 38🟥)

  • 🟥 build/module/Aesop.Forward.State.ApplyGoalDiff//instructions: +18.2M (+0.89%)
  • 🟥 build/module/Aesop.Forward.State.Initial//instructions: +18.0M (+0.91%)
  • 🟥 build/module/Aesop.Main//instructions: +86.6M (+2.32%) (reduced significance based on absolute threshold)
  • 🟥 build/module/Aesop.Saturate//instructions: +508.2M (+3.61%) (reduced significance based on absolute threshold)
  • 🟥 build/module/Aesop.Search.RuleSelection//instructions: +25.3M (+1.06%)
  • 🟥 build/module/Aesop.Util.Basic//instructions: +58.5M (+0.74%)
  • 🟥 build/module/Batteries.CodeAction.Basic//instructions: +48.2M (+1.71%)
  • 🟥 build/module/Batteries.CodeAction.Misc//instructions: +54.4M (+0.70%)
  • 🟥 build/module/Batteries.Data.Float.Basic//instructions: +29.1M (+1.70%)
  • 🟥 build/module/Batteries.Data.Nat.Bitwise//instructions: +59.8M (+6.11%) (reduced significance based on absolute threshold)
  • 🟥 build/module/Batteries.Data.String.Basic//instructions: +25.3M (+1.97%)
  • 🟥 build/module/Batteries.Linter//instructions: +29.3M (+2.20%)
  • 🟥 build/module/Batteries.Tactic.Alias//instructions: +40.4M (+0.69%)
  • 🟥 build/module/Batteries.Tactic.Lint//instructions: +21.3M (+1.57%)
  • 🟥 build/module/Batteries.Tactic.NoMatch//instructions: +20.6M (+0.78%)
  • 🟥 build/module/Batteries.Tactic.PrintPrefix//instructions: +33.0M (+0.95%)
  • 🟥 build/module/LeanSearchClient.LoogleSyntax//instructions: +25.5M (+0.58%)
  • 🟥 build/module/LeanSearchClient.Syntax//instructions: +52.7M (+0.83%)
  • 🟥 build/module/Mathlib.Algebra.Field.Power//instructions: +42.7M (+1.53%)
  • 🟥 build/module/Mathlib.Algebra.Lie.Basis//instructions: +100.4M (+2.42%)
  • and 19 more

Kha and others added 6 commits August 28, 2026 14:47
`withOptions` hands its transformer the ambient options, so the transformer may derive the value of an option that the search later reads through `getRecordedOption`. The dependency log then records that derived value while `findCachedResult?` validates against the ambient one, and nothing constrains what the transformer would have produced in the validating context, so an entry can be reused where it does not hold. `MonadWithOptions CoreM` now reports a violation when a transformer runs inside a recording computation, and `withSetOption`/`withSetOptionByName` provide the form whose new value cannot depend on the ambient options. The two call sites reachable from a search, the `Meta.isLevelDefEq` trace message and `mkUnfoldAxiomsNote`, use them.

The transformer body moves to `Core.withOptionsUnrestricted`, kept `@[inline]` so that codegen is unchanged: instance bodies are inlined at statically resolved call sites, and the generated C for modules calling `withOptions` has the same number of `Kernel.isDiagnosticsEnabled` calls as before. The panic is kept out of line for the same reason as the one in `getOptions`.

The guard is partial. Seven sites write `Core.Context.options` directly and bypass it, of which `realizeConst`'s `realizeAndReport` is the one that could plausibly run under a search. `withSetOption` also cannot enforce that its value is independent of the ambient options, only make the dependency explicit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The flags occupy bits 0 to 8 and `armedBit` bit 15, so `UInt16` fits them exactly, while `UInt32` left a sixteen-bit hole above the sentinel that the docstring did not account for. The layout is now written down: bits 0 to 8 are the flags, 9 to 14 are free for further ones, and 15 is `armedBit`.

This does not shrink either object that holds the word. `Meta.Context`'s scalar area goes from 8 to 6 bytes and `SynthInstanceCacheKey`'s from 36 to 34, but both objects round into the same 8-byte bucket, 72 and 80 bytes respectively.

`toContextWord` now also records that its result is not interchangeable with the flags it came from: the derived `BEq` and `Hashable` compare raw `bits`, so the armed word and the un-armed one are distinct values denoting the same flags, and only the un-armed form may be stored in `SynthInstanceCacheKey.defEqFlags`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A dependency log entry was the value a lookup returned, which is the value in effect where the lookup happened, while a cache entry is validated against the ambient options where the query started. Those differ for a lookup made under an options write that the query itself opened, and because the log is deduplicated by name, such a lookup could take the slot of the ambient one and leave the entry validating in contexts the recorded computation never ran in.

`RecordedDeps` now carries the options in effect when recording started, and a lookup is a dependency only when it answers as that watermark does. A lookup served by a write inside the query answers differently and is not recorded at all, which is also correct: its value is independent of the ambient options, so the entry is reusable regardless of them rather than pinned to the written value. The log no longer depends on the order the lookups happen in. `RecordedDeps.mergeInto` applies the same test against the enclosing query's watermark, so a nested query's scope-local value is not injected into a log that is validated without that scope.

This mirrors how declaration dependencies are handled, where a change whose target was born after the recorded `constBirthGen` is skipped because the computation cannot have observed it.

No behavior changes today: the options that reach a log (`backward.synthInstance.canonInstances`, `synthInstance.maxSize`, `maxSynthPendingDepth`) are disjoint from those written inside a search (`diagnostics`, `pp.instantiateMVars`), so no lookup answers differently from the watermark. The situation also cannot be reached from Lean source, as nothing gives user code a foothold inside a search.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`sameDepIdentity` decides whether a freshly computed type class resolution entry replaces an existing one for the same key or joins it in a list. Its documented meaning is the same option lookups with the same answers, but it is `Array` equality, which is elementwise in order, and the recorded order is the order the search happened to reach each option in rather than a property of the dependency set. Two searches of one key that reach a nested query or a `synthPending` at different points record the same set in a different order, so the new log fails the identity test, both are kept, and every later lookup for that key walks and validates the redundant entry.

`insertCachedResult` now sorts the accesses by name, in the same rewrite that already clears the recording watermark, so both operands of `sameDepIdentity` are canonical and comparing the arrays compares the sets. Names are unique within a log, so the order is total. Nothing else depends on the order: `validOptionAccesses` is an `all`, and `RecordedDeps.mergeInto` deduplicates by name membership.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`getRecordedBoolOption` has no call sites, here or on the environment-dependency stack that builds on this one, so unlike the `validateDeps?` parameters it is not scaffolding for later work. `getRecordedOption` covers every option the search reads.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Kha

Kha commented Aug 31, 2026

Copy link
Copy Markdown
Member Author

!bench

@leanprover-radar

leanprover-radar commented Aug 31, 2026

Copy link
Copy Markdown

Benchmark results for a660d9e against 0e6944a are in. There are significant results. @Kha

  • 🟥 build//instructions: +23.1G (+0.20%)

Small changes (75🟥)

  • 🟥 build/module/Init.Data.Array.Lemmas//instructions: +104.7M (+0.20%)
  • 🟥 build/module/Init.Data.Iterators.Lemmas.Combinators.FilterMap//instructions: +45.3M (+0.24%)
  • 🟥 build/module/Init.Data.Iterators.Lemmas.Combinators.Monadic.FilterMap//instructions: +70.4M (+0.20%)
  • 🟥 build/module/Init.Data.Nat.Lemmas//instructions: +71.5M (+0.30%)
  • 🟥 build/module/Init.Data.Range.Polymorphic.Lemmas//instructions: +139.2M (+0.33%)
  • 🟥 build/module/Init.Data.String.Decode//instructions: +60.8M (+0.25%)
  • 🟥 build/module/Lake.Toml.Elab.Expression//instructions: +18.0M (+0.49%)
  • 🟥 build/module/Lean.AddDecl//instructions: +109.9M (+1.98%)
  • 🟥 build/module/Lean.Compiler.LCNF.Main//instructions: +99.5M (+1.91%)
  • 🟥 build/module/Lean.Compiler.LCNF.Visibility//instructions: +43.6M (+0.93%)
  • 🟥 build/module/Lean.CoreM//instructions: +709.2M (+9.51%) (reduced significance based on *//lines)
  • 🟥 build/module/Lean.DefEqAttrib//instructions: +26.2M (+1.17%)
  • 🟥 build/module/Lean.DocString.Add//instructions: +38.7M (+0.83%)
  • 🟥 build/module/Lean.Elab.App//instructions: +144.5M (+0.38%)
  • 🟥 build/module/Lean.Elab.Binders//instructions: +51.9M (+0.36%)
  • 🟥 build/module/Lean.Elab.BuiltinEvalCommand//instructions: +86.7M (+1.33%)
  • 🟥 build/module/Lean.Elab.BuiltinTerm//instructions: +83.1M (+0.75%)
  • 🟥 build/module/Lean.Elab.DocString.Builtin.Keywords//instructions: +121.8M (+0.82%)
  • 🟥 build/module/Lean.Elab.DocString.Builtin//instructions: +303.1M (+0.77%)
  • 🟥 build/module/Lean.Elab.DocString//instructions: +207.1M (+0.52%)
  • and 55 more

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

builds-manual CI has verified that the Lean Language Reference builds against this PR builds-mathlib CI has verified that Mathlib builds against this PR mathlib4-nightly-available A branch for this PR exists at leanprover-community/mathlib4-nightly-testing:lean-pr-testing-NNNN toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants