Commit graph

40396 commits

Author SHA1 Message Date
Henrik Böving
a3fff15212
perf: optimize String.compare (#13796)
This PR optimizes `String.compare` to turn it into 1 instead of 2
`memcmp` calls.
2026-05-20 12:57:48 +00:00
Lean stage0 autoupdater
34df732066 chore: update stage0 2026-05-20 11:40:02 +00:00
Henrik Böving
8398048832
chore: preparation for String.compare optimization (#13795)
This PR takes the first step towards only doing one `memcmp` call per
`compare` on `String`.
2026-05-20 10:52:48 +00:00
Julia Markus Himmel
fa2384726f
chore: clean up string processing (#13785)
This PR improves some code involving strings in order to remove usage of
`String.length` and raw string positions.
2026-05-20 10:39:40 +00:00
Mac Malone
727b4aad2b
fix: lake: noRelease test flakiness (#13786)
This PR fixes flaky output checks in Lake's `tests/noRelease`.
2026-05-19 23:35:28 +00:00
Henrik Böving
1f23107ad9
perf: dec specialization (#13788)
This PR generates specialized code for invoking `dec` on values whose
shape is known. This puts branch prediction pressure off
`lean_dec_ref_cold` as the shape of the constructor should now be
compiled into the executable.
2026-05-19 19:56:34 +00:00
Garmelon
0a75e1d92f
chore: CI: fix stage2 release build (#13790)
If we're using the downloaded LLVM release and building stage2/stage3,
we need to also copy stage1 to stage2/stage3 respectively before
continuing the build. Previously, the CI did not do this in every case.
Now we do it unconditionally because the overhead is small enough.
2026-05-19 19:23:57 +00:00
Kyle Miller
29adf42309
refactor: use builtin command elaborators for configuration evaluation (part 2) (#13780)
This PR is part 2 to #13779. It completes the transition of the
configuration evaluation metaprograms into being builtin elaborators.

After this, building stage1 will not invoke the interpreter when
generating configuration evaluators. A tradeoff is that changes to
ConfigEval will not affect stage1 builds, but these changes ought to be
infrequent.
2026-05-19 18:16:21 +00:00
Lean stage0 autoupdater
731d0c4878 chore: update stage0 2026-05-19 17:02:51 +00:00
Julia Markus Himmel
690d27ebc7
chore: clean up string processing (#13789)
This PR eliminates some more uses of `String.length`.
2026-05-19 16:17:42 +00:00
Kyle Miller
e38c698392
refactor: use builtin command elaborators for configuration evaluation (#13779)
This PR makes the command elaborators for configuration evaluation
metaprogramming be builtins, to avoid bootstrapping ABI issues in core
Lean due to the interpreter evaluating large parts of the elaborator
before all builtin initializers are run. (This is part 1; #13780 will be
applied after a stage0 update.)
2026-05-19 16:08:11 +00:00
Kyle Miller
3bb1493139
refactor: remove use of liftCommandElabM from Coinductive (#13782)
This PR eliminates the use of `liftCommandElabM` from the coinductive
module.

Context: `liftCommandElabM` was decided to be deprecated in issue
#10674.
2026-05-19 10:53:44 +00:00
Kyle Miller
a0de9024e9
refactor: avoid liftCommandElabM in @[ext] (#13781)
This PR eliminates the use of `liftCommandElabM` in the `@[ext]`
attribute, since it is unnecessary — all the computations can stay in
`TermElabM`.

Context: `liftCommandElabM` was decided to be deprecated in issue
#10674.
2026-05-19 10:53:07 +00:00
Sebastian Ullrich
e09155b6f9
chore: CI: fold Linux Lake into Linux release (#13759)
Now that they both use Lake, there's little reason for the duplication
2026-05-18 22:52:22 +00:00
Joachim Breitner
ebbbec00f7
refactor: dispatch try? builtins via @[builtin_try_tactic] (#13766)
This PR moves the `evalSuggest` combinator and trace-handler dispatch
from a hardcoded `match` on syntax kinds to the existing
`tryTacticElabAttribute` registration mechanism, bringing `try?`'s
extensibility model in line with normal tactics and interactive `grind`.

Each built-in combinator (`<;>`, `first`, `paren`, `try`, `attempt_all`,
`attempt_all_par`, `first_par`, `seq1`) and trace handler (`grind?`,
`simp?`, `simp_all?`, `exact?`) is now registered as
`@[builtin_try_tactic <kind>]`. `evalSuggestImpl` becomes a thin
attribute-driven dispatcher mirroring `evalTactic`/`evalGrindTactic`.

The post-run "no unsolved goals in terminal mode" check is no longer
applied centrally by the dispatcher. Handlers that may leave goals
open (`simp?`, `simp_all?`, the atomic fallback) call the new
`checkTerminalGoals` helper themselves; handlers that close the goal
by construction (`grind?`, `exact?`) and combinators that delegate via
`evalSuggest` need no explicit call.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 19:41:24 +00:00
Joachim Breitner
705ba6404b
refactor: extract try? suggestions via TryThisInfo (#13774)
This PR makes `try?`'s `expandUserTactic` walk the info tree for
`TryThisInfo`
nodes (introduced in #10524) instead of parsing the rendered `Try this:`
message
text. The previous approach scraped lines prefixed with ` [apply] ` from
the
message log, which would break the moment that wire format changed.

The user tactic is run under `withSaveInfoContext` so the in-flight
trees come
out wrapped with a `PartialContextInfo.commandCtx`, which is what
`InfoTree.foldInfo` needs in order to descend; the structured
suggestions can
then be read directly from the `TryThisInfo` nodes that
`TryThis.addSuggestion`
already pushes. The prior manual `Core.setMessageLog` save/restore dance
is no
longer needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 18:41:55 +00:00
Kim Morrison
6d5ec050f4
fix: instantiate metavariables when collecting a goal's constants (#13748)
This PR fixes premise selection silently dropping relevant premises when
the goal was reached via `induction`.

`MVarId.getRelevantConstants` and `MVarId.getConstants` walked the raw
goal type returned by `getType`, which after `induction` is `?motive
arg` with `?motive` an assigned-but-unsubstituted metavariable. The
constant fold treats the metavariable as an opaque leaf, so every
constant in the goal predicate is lost. Instantiating metavariables
first recovers them.

Thanks to Xavier Généreux for the report.

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 15:52:13 +00:00
Kyle Miller
5d22886aff
refactor: app elaborator refactoring and improvements (#13762)
This PR does some refactoring of the function application elaborator,
and it improves `trace.Elab.app` tracing. It also improves asymptotic
complexity by more carefully substituting arguments into the function's
type and by changing how named argument dependency suppression is
implemented. For dot notation, it now constructs base projections
directly rather than using the app elaborator. It fixes a bug in the eta
args feature where more explicit arguments would be turned into implicit
arguments than expected, and it improves expected type propagation by
following the rules from the main app elaborator.
2026-05-18 14:41:09 +00:00
Kim Morrison
3f3a26c53c
feat: filter MePo candidates to theorems and order output by iteration (#13750)
This PR refines MePo premise selection so that (1) candidates are
restricted to theorems, matching the convention already used by
`SineQuaNon` and `SymbolFrequency`, and (2) the result is ordered
lexicographically by `(iteration, score)` rather than by score alone.

Before the theorem filter, `mepoSelector` returned functions, recursors,
and casts (e.g. `Int.add`, `Eq.ndrec`, `cast`, `proof_irrel`) alongside
real premises. With it, those go away and the obvious lemmas — e.g.
`Int.add_comm` for `a + b = b + a` — rank well above the remaining
noise.

The `(iteration, score)` ordering preserves the iteration priority of
the original Meng–Paulson MePo: an iter-1 premise scoring 0.6 always
ranks above an iter-2 premise scoring 1.0, since later iterations score
against a strictly larger `relevant` set and a higher threshold, so
their raw scores are not comparable.

Thanks to Xavier Généreux for the report. Further tuning of the score
function (the remaining problem: small generic theorems still score 1.0)
is tracked in https://github.com/leanprover/lean4/issues/13749.

Depends on https://github.com/leanprover/lean4/pull/13747.

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 14:15:06 +00:00
Mac Malone
2c38bb3306
fix: lake: rm examples/hello use in tests/env (#13769)
This PR an intermittent test failure in the Lake environment test caused
by its reuse of the hello test's configuration. The tests are now
properly independent.
2026-05-18 13:11:03 +00:00
Kim Morrison
8f508e3c91
fix: include zetaUnused in Meta.Config cache key (#13772)
This PR closes https://github.com/leanprover/lean4/issues/13770 by
including `Config.zetaUnused` in `Config.toKey`. Without this, two
configs that differ only in `zetaUnused` share a `WHNF`/`isDefEq` cache
key, so reductions performed under one setting can be returned for the
other. The new bit sits at position 22, immediately above `zetaHave`.

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 12:47:19 +00:00
Kim Morrison
a68d753729
fix: widen transparency field to 3 bits in Meta.Config cache key (#13768)
This PR fixes a long-standing bug in `Meta.Config.toKey` and
`Context.setTransparency` where `TransparencyMode` was packed into only
2 bits of the cache key, even though it has 5 constructors (`.all`,
`.default`, `.reducible`, `.instances`, `.none`). The `.none` case
(value `4`, i.e. `0b100`) overlapped with the `foApprox` bit, so
configurations differing only in transparency vs. `foApprox` could
collide in the `isDefEq`/`WHNF` cache, and `Context.setTransparency`
corrupted the neighbouring bit when switching to/from `.none`.

The fix widens the transparency field to 3 bits and shifts every
subsequent flag up by one. Split out from
https://github.com/leanprover/lean4/pull/13637 where the same fix was
needed anyway to accommodate a 6th transparency constructor.

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 10:51:59 +00:00
Wojciech Nawrocki
9df1caeb77
feat: add ToJson/FromJson for Unit (#13525)
This PR adds `FromJson`/`ToJson` instances for `Unit` - encoded as `{}`
- and documentation for `FromJson`/`ToJson`.
2026-05-18 10:03:30 +00:00
Kim Morrison
385efbbaa7
fix: drop stale .reverse in MePo premise selector (#13747)
This PR fixes the MePo premise selector returning its lowest-scoring
premises instead of its best ones.

Since https://github.com/leanprover/lean4/pull/10917 `mepo` returns
suggestions in score-descending order, but `mepoSelector` still applied
a `.reverse` that predated that change. The `.reverse` flipped the order
to score-ascending, so the subsequent `.take config.maxSuggestions` kept
the `maxSuggestions` worst premises.

Thanks to Xavier Généreux for the report.

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 19:37:49 +00:00
Thomas R. Murrills
2bdc67756c
chore: remove addInfo flag from elabSetOption (#13736)
This PR removes the `addInfo` flag from `elabSetOption` introduced in
#11313, instead allowing callers to use `withEnableInfoTree` to control
whether infotrees are added.
2026-05-17 17:28:27 +00:00
Kyle Miller
ce5814043b
feat: add MessageData.withExprHover (#13763)
This PR adds `MessageData.withExprHover`, for creating messages that
show information about an expression when hovered over. A
`withExprHoverM` variant captures the current local context.
2026-05-17 13:39:49 +00:00
Kyle Miller
43ef70db63
fix: pp.universes should not affect constants with no universe levels (#13761)
This PR fixes an issue where the `pp.universes` option would cause
constants with no universes to not use unexpanders or dot notation. For
example, `p ↔ q` would pretty print as `Iff p q` even though `Iff` has
no universe levels.
2026-05-17 01:41:04 +00:00
Kyle Miller
5dea2142c3
feat: improve performance of instantiateBetaRevRange (#13758)
This PR improves `Expr.instantiateBetaRevRange` to be more efficient in
the common case where lambda functions are not being instantiated, and
it increases expression sharing in applications.

The motivation is that we would like to use this function more
pervasively in elaboration, so that users do not need to write `dsimp
only` as frequently in applications that involve higher-order functions,
plus `inferType` uses it so there is a UX inconsistency when the
elaborator is not using it.
2026-05-16 20:55:18 +00:00
Sebastian Ullrich
ef2dc0f66a
chore: CI: build everything with Lake (#13721) 2026-05-16 17:11:25 +00:00
Mac Malone
d64eaf4597
chore: CI: verify lake cache step (#13735)
This PR adds a CI step to the build job which verifies that the uploaded
Lake cache can be downloaded and used without issue.
2026-05-16 16:08:38 +00:00
Sebastian Ullrich
895752dc2e
feat: add Lean.CompactedRegion.save/read supporting cross-file object sharing (#13185)
This PR adds new incremental module serialization functions that
save/load a single module at a time with explicit sharing via dep
regions and compactor state, generalizing the existing batch
saveModuleDataParts API.

Two sharing mechanisms that can be mixed:
- `CompactedRegion` dep regions for sharing with loaded regions
- `CompactorState` for same-process chaining (pre-loaded `m_obj_table`)
2026-05-16 12:53:20 +00:00
Kim Morrison
1b8f1f140c
feat: add ByteArray indexing and set! lemmas (#13457)
This PR adds the missing `ByteArray` push and `set!` lemmas that are
still carried locally in `ZipForStd.ByteArray` downstream.

These lemmas provide one coherent API layer for reasoning about
`ByteArray` pushes and `set!` updates on top of the existing upstream
append, extract, and size lemmas. Previous downstream code had to keep
local compatibility lemmas for these common proof steps.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 06:28:43 +00:00
Kim Morrison
c993d3f237
feat: add Dyadic.divAtPrec for round-down dyadic division at given precision (#13654)
This PR adds `Dyadic.divAtPrec a b prec`, returning the greatest dyadic
with precision at most `prec` which is less than or equal to `a/b` (and
`0` when `b = 0`). Mirroring the existing `invAtPrec`, the
characterising lemmas `divAtPrec_mul_le` and `lt_divAtPrec_add_inc_mul`
are also provided.

Compared to composing `invAtPrec` with multiplication, `divAtPrec`
rounds `a/b` down uniformly (no sign-dependent rounding flip), states
the precision at the output rather than requiring callers to back-solve
from `‖a‖`, and fuses the bignum division with renormalisation.
`invAtPrec` remains the right primitive when the reciprocal is reused.

Closes https://github.com/leanprover/lean4/issues/13653.

🤖 Prepared with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 05:47:39 +00:00
Sebastian Ullrich
d5bcec28e5
chore: adjust dev-release preset (#13741)
Use same output directory as `release` to avoid
lean-toolchain/CLAUDE.md/... issues, rename preset accordingly
2026-05-16 05:32:16 +00:00
Sebastian Ullrich
1708293920
feat: shake: precise reasons in --explain output (#13740)
This PR extends `lake shake --explain` to also cover reasons for keeping
imports that go beyond direct references, such as shake annotations.
2026-05-15 20:24:36 +00:00
Sebastian Ullrich
2acdaafcfe
chore: CI: fix jira-sync (#13739) 2026-05-15 09:55:25 +00:00
Lean stage0 autoupdater
6749463072 chore: update stage0 2026-05-15 09:14:30 +00:00
Mac Malone
f8b7f30f5c
fix: change plugin separator to = (#13737)
This PR changes the separator between the plugin file name and the
initialization function in `--plugin` from `:` to `=`. This prevents
clashes with the `:` in drive prefixes on Windows.
2026-05-15 08:28:21 +00:00
Kyle Miller
047f6aaf89
feat: efficient tactic configuration elaborators and configurability (#13651)
This PR replaces the previous tactic configuration system with a
significantly more efficient one that supports custom configuration
syntaxes and processing. On a simple benchmark, configuration evaluation
takes 6.2% of the time it used to. The `declare_config_elab` command
generates a configuration elaborator that now directly constructs
configuration objects; previously it relied on `Meta.evalExpr'`, which
involved running a configuration through the full term elaboration,
compilation, and evaluation processes. The generated configuration
elaborators now also have the capability to do direct `Syntax`
evaluation in common cases, skipping term elaboration. Furthermore, the
elaborator accepts configurations more liberally: any user-defined
syntax that has the form of an `optConfig`-style configuration or
configuration item (including, e.g., `namedArgument`s) is accepted.
Import `Lean.Elab.ConfigEval` to use the system; see this module for
some documentation in addition to the docstrings in
`Lean.Elab.ConfigEval.Commands`. Furthermore, the `simp` tactic now also
has `(user.optionName := ...)` user configuration options, which can be
declared using a global `tactic.simp.user.optionName` option; use
`getUserConfigOption` and `withUserConfig` to access and set these in
metaprograms.

Other features:
- `declare_config_elab` creates a function that exposes an `init`
parameter for the configuration that will be modified. It also now has a
`where` clause, enabling defining custom handlers for specific keys.
- Elaborators can be given additional binders, to make parameterized
elaborators. This is used by `simp` and `grind ` to support multiple
default configurations with the correct expected type for `(config :=
...)` elaboration.
- The `EvalTerm` class supports direct evaluation of `Syntax`, skipping
term elaboration. The system will attempt to automatically derive this
class when generating the elaborator.
- In case `EvalTerm` does not recognize the term, then the syntax is
elaborated to an expression and an `EvalExpr` instance is applied to
evaluate the expression. The system will similarly automatically derive
these instances if possible.
- Automatic derivation is *transitive*. It is able to seek instances
through other instances; e.g. if it needs an `EvalTerm (List T)`
instance it will be able to reduce this to seeking an `EvalTerm T`
instance.
- The system is designed to be flexible, and the various components can
be combined to construct a configuration elaborator. There are also now
`declare_core_config_elab` and `declare_term_config_elab` for
conveniently generating elaborators for `CoreM` and `TermElabM`. The
difference is that the first takes an explicit flag for whether to log
exceptions, and the second uses the current `errToSorry` state.
**Warning:** if you use the `TermElabM` one from `TacticM`, it will be
unaware of the current `recover` state. The only differences between
these macros are the ways error recovery is handled per monad.

Other changes:
- `#reduce` tactic configuration now makes use of this system and has
more options
- The module `Lean.Elab.Tactic.ConfigSetter` is removed; the
`declare_config_elab`-family macros subsume its functionality.
- The module `Lean.Elab.Tactic.Config` is deprecated and will be
removed; migration notes appear in the module docs there. Import
`Lean.Elab.ConfigEval` instead.
- One of the mvcgen benchmarks got significantly slower, but it turned
out to be caused by the new tactic configuration elaboration no longer
resetting the MetaM caches. Adding an explicit `resetCache` into the
test driver fixed the benchmark.

### Notes for metaprogram authors

If you are using the module system, you just need a `meta import
Lean.Elab.ConfigEval` to use the macros, and it should serve as a
drop-in replacement to the previous system so long as

1. your configuration type is a `structure` with no parameters, indices,
or universes (only `Type` is supported);
2. default values are self-contained and not dependent on other fields;
and
3. all fields have types that are composed from `Option`, `List`,
`Array`, `String`, `DataValue`, and inductive types in `Type` with no
parameters or indices, whose fields are similarly composed.

The macros synthesize a self-contained configuration elaboration
procedure, analyzing the `EvalTerm` and `EvalExpr` instances that are
currently available or can be automatically derived. These are the
components of the system:
- `EvalTerm` instances provide `Term → TermElabM α` functions for
evaluation of raw syntax; these handle the common cases where an option
value is a identifier, application, or other simple expression. They are
responsible for adding TermInfo when info is enabled, to support hovers.
One can invoke derivation of a `EvalTerm T` instance with the
`ensure_eval_term_instance T` command (after `open scoped
Lean.Elab.ConfigEval`).
- `EvalExpr` instances provide `Expr → TermElabM α` functions for
evaluation of elaborated expressions; these handle cases where an option
value may require reduction to evaluate. Similarly, one can invoke
derivation of an `EvalExpr T` instance with the
`ensure_eval_expr_instance T` command. If needed, there's also
`derive_eval_expr_instance_using_meta_eval T` for creating a
`Meta.evalExpr'`-based evaluator.
- Functions like `ConfigEval.evalExprWithElab` compose `EvalTerm` and
`EvalExpr` instances into a single procedure that first attempts
`EvalTerm`, and, if that fails, applies the standard term elaborator and
then attempts `EvalExpr`. This way term elaboration can be skipped in
all but uncommon cases.
- Configuration item interpretation is through `ConfigEval.foldConfigM`,
which is a procedure with a lax specification for what counts as a
configuration item, calling the provided function on each recognized
configuration item. The idea is:
  - Null nodes are lists of configurations
- One-argument nodes are considered to be wrappers like `optConfig` or
`configItem`
- Two-argument nodes of the form `("+"<|>"-") (atom<|>ident)` are
considered to be boolean options
- Five-argument nodes of the form `"(" (atom<|>ident) ":=" syntax ")"`
are considered to be general configuration items. (It only checks for
the presence of `(` and that there are two-to-five arguments.)
  - Bare atoms are considered to be positive boolean options
- Configuration evaluation then uses `EvalConfigItem.set` on each item
produced by the fold, for an `EvalConfigItem` structure defined for the
given configuration type. The `def_eval_config_item` command can be used
to generate this structure. It analyzes which `EvalTerm` and `EvalExpr`
instances exist and derives missing ones, then builds an efficient
procedure to process configuration items and apply evaluators.
- Lastly, there are the `declare_core_config_elab`,
`declare_term_config_elab`, `declare_config_elab`, and
`declare_command_config_elab` macros for conveniently running the
`def_eval_config_item` command and constructing a self-contained
elaboration function.

The derivation procedures analyze which `EvalTerm`/`EvalExpr` instances
already exist and only derive the "leaf" instances that are necessary to
construct `EvalTerm` and `EvalExpr` instances. The derived instances are
made `private local` — since configuration elaborators are meant to be
self-contained, we decided not to let the additional instances be a side
effect of the macros. The instances can be globally added by manually
using the `ensure_*` commands.

The macros support making parameterized elaborators with arbitrary
additional binders. See `make_elab_grind_config` and
`make_elab_simp_config` in core Lean for examples of generating a single
elaborator that's used with multiple default value configurations.

To see how to create a key handler that matches all configuration keys
with a given prefix, see `make_elab_simp_config`.

There is a todo item at `Lean.Elab.ConfigEval.ReflectConfigItems` for
reflecting configurations back to syntax, which is not yet supported.

### Performance evaluation

A legacy configuration parser was temporarily added to
`Lean.Elab.Tactic.Grind.Config` using `declare_term_config_elab_legacy
elabGrindConfigLegacy Grind.Config`, and then this file was used for
measuring elaboration time:

```lean
import Lean

open Lean Elab Meta Tactic Parser

def cfgs : Array Syntax := Unhygienic.run do
  return #[
    ← `(Tactic.optConfig| ),
    ← `(Tactic.optConfig| +clean),
    ← `(Tactic.optConfig| +trace +markInstances -lookahead -useSorry),
    ← `(Tactic.optConfig| (trace := true) (markInstances := true) (lookahead := false) (useSorry := false)),
    ← `(Tactic.optConfig| -trace (splits := 20) +revert (maxSuggestions := some 3) (ematch := 2)),
    ← `(Tactic.optConfig| (gen := 5) -reducible +splitImp -funCC),
    ← `(Tactic.optConfig| (config := { trace := true, lookahead := false, maxSuggestions := some 3 })),
  ]

def testGrindElab (cfgs : Array Syntax) (n : Nat) : TacticM Unit := do
  profileitM Exception "test grind elab" (← getOptions) do
    let mut ematch := 0
    for _ in [0:n] do
      for cfg in cfgs do
        let c ← Tactic.elabGrindConfig cfg
        ematch := ematch + c.ematch
    logInfo m!"sum = {ematch}"

def testGrindElabLegacy (cfgs : Array Syntax) (n : Nat) : TacticM Unit := do
  profileitM Exception "test grind elab legacy" (← getOptions) do
    let mut ematch := 0
    for _ in [0:n] do
      for cfg in cfgs do
        let c ← Tactic.elabGrindConfigLegacy cfg
        ematch := ematch + c.ematch
    logInfo m!"sum = {ematch}"

def runTest (info : Bool) (test : TacticM Unit) : TermElabM Unit := do
  withEnableInfoTree info do
    let mvar ← mkFreshExprMVar none
    discard <| Tactic.run mvar.mvarId! test

set_option maxHeartbeats 0
set_option profiler true
set_option profiler.threshold 1
def iters : Nat := 1000
#eval runTest false <| testGrindElab cfgs iters
#eval runTest true <| testGrindElab cfgs iters
#eval runTest false <| testGrindElabLegacy cfgs iters
#eval runTest true <| testGrindElabLegacy cfgs iters
```

A representative output is
```
test grind elab took 315ms
test grind elab took 333ms
test grind elab legacy took 5.22s
test grind elab legacy took 5.33s
```
Computing `(315.0 + 333.0) / (5220 + 5330)` and rounding up to the
nearest tenth gives the 6.2% figure.

---

The #13426 draft PR includes some LSP modifications to support
completions for `simp` user configuration options.
2026-05-14 17:20:57 +00:00
Wojciech Różowski
f459c1436e
feat: upstream unusedDecidableInType linter (#13688)
This PR upstreams `unusedDecidableInType` linter from mathlib.

Stacked on top of #11313.

---------

Co-authored-by: thorimur <thomasmurrills@gmail.com>
Co-authored-by: thorimur <68410468+thorimur@users.noreply.github.com>
2026-05-14 11:56:22 +00:00
MoritzBeroRoos
48729abf4a
feat: better error messages and more complete logic for checkImpossibleInstance and checkNonClassInstance (#13550)
This PR improves the logic and performance of the
`checkImpossibleInstance` function to detect more arguments that are
impossible to
infer for typeclass synthesis. It also improves the formatting of the
error messages for `checkImpossibleInstance` and `checkNonClassInstance`
to be more readable.

This integrates [independent (temporarily merged)
work](https://github.com/leanprover-community/batteries/pull/1717) on
these former Batteries linters which has been reverted in Batteries
because of the unprecedented upstreaming of these linters.

We also disable these checks (if the declaration contains any `sorry`s
so that partially completing instances is still possible without being
terrorized by obvious errors. We also swap the order of the checks, such
that now `checkNonClassInstance` is called first - it would be wrong to
warn for synthesis impossibility if the return type isn't even
synthesizable because it is not class-valued.

Co-authored by: @thorimur

---------

Co-authored-by: Wojciech Rozowski <wojciech@lean-fro.org>
2026-05-14 09:40:03 +00:00
Joachim Breitner
80a85cd3d9
fix: respect fixed-parameter permutation when reporting structural recursion failures (#13730)
This PR fixes a regression introduced in #7166 where, after fixed and
varying
parameters were allowed to be reordered, three places in
`Lean.Elab.Structural.FindRecArg` still indexed the concatenation `xs ++
ys`
with `recArgInfo.recArgPos` even though `recArgPos` refers to the
original
parameter order. With fixed parameters interleaved with the structural
argument, this picked the wrong element: error messages named the wrong
parameter, and `argsInGroup`'s nested-inductive recognition silently
rejected
otherwise-valid mutual definitions.

All three sites now use `recArgInfo.fixedParamPerm.buildArgs xs ys` so
the
index matches `recArgPos` and `indicesPos`.

Closes #13729.
2026-05-14 08:17:13 +00:00
Kyle Miller
803553a556
feat: additional fieldinfo in structure instance notation (#13728)
This PR improves hovers and completions for compound field names in
structure instance notation. Previously a field like `x.fst` would only
have information associated to `x` attached to the entire syntax, but
now `x` and `fst` are treated separately.

Additionally, the `trace.Elab.struct` trace class is now
`trace.Elab.structInst`, and the messages have been cleaned up and
reorganized. Furthermore, `ExceptToTraceResult` has an `Expr` instance
that shows a failure emoji when there are synthetic sorries. Lastly,
`Syntax.identComponents` has an early return for names with at most one
component, saving a re-parsing step.
2026-05-14 00:11:05 +00:00
pandaman
bba3868b3b
fix: omit empty moreLeancArgs when leanc flags are unset (#13723)
This PR fixes toml_escape so it does not emit moreLeancArgs = [""] when
internal leanc flags are both empty (the combined placeholder was only
whitespace). This led GCC to see an extra empty argv entry and fail with
a confusing linker error.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-13 14:00:35 +00:00
Joachim Breitner
6279ae98c7
test: resurrect try cancellation tests (#13707)
This PR resurrects the tests about cancellation of `try?` tactics.

They were flaky before. By introducing an option
(`debug.tactic.try.onlyUserSuggestions`) to make these tests not run
`exact?` as part of `try?`, and thus be generally faster and use less
memory, the flakiness seems to have disappeared, at least in
non-`master` testing. We’ll see.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 13:54:48 +00:00
Wojciech Różowski
793cd14b38
feat: improve message of unusedVariables linter (#13715)
This PR improves the message of `unusedVariables` linter, by replacing
potentially confusing "unused variable `x`" message with "Variable name
`x` is not explicitly referenced. The binding can be removed (if unused)
or named `_` (if used implicitly)."

Related issue: https://github.com/leanprover/lean4/issues/13269
2026-05-13 09:40:18 +00:00
Sofia Rodrigues
ed0d50fcf0
fix: timing issues and race conditions in ContextAsync.race (#13718)
This PR fixes tests in context_async.lean by removing all the issues
with Async.sleep and IO.sleep and improving how ContextAsync.race works.
2026-05-13 01:25:01 +00:00
Joachim Breitner
cc103d8ed6
chore: remove worker_crash test (#13720)
This test was of low value (testing only test infrastructure) and has
non-deterministic output ordering. Let’s just get rid of it.
2026-05-12 22:21:11 +00:00
Joachim Breitner
7a6c59a357
fix: abort waitFor server_interactive tests promptly on file worker fatal error (#13710)
This PR makes the test-only `waitForMessage` helper abort promptly
when the Lean language server reports a fatalError, instead of
blocking until the outer test framework's timeout kills the process.

`waitForMessage` is used to implement the `waitFor` directive in
`tests/server_interactive` tests: it reads server messages in a loop
and returns when it sees a `textDocument/publishDiagnostics`
notification containing a given message. It has no pending request,
so the watchdog's `responseError` (which already terminates
`collectDiagnostics` and `waitForILeans` on a fatalError) doesn't
help here, and it would silently discard `$/lean/fileProgress`
notifications. When the file worker crashes or its header processing
fails fatally (e.g. an unresolved import), the awaited diagnostic
will never be emitted -- so `waitForMessage` would block indefinitely
and report nothing useful, which made several CI failures of
server_interactive tests look like generic 1500s timeouts. With this
change, `waitForMessage` also reacts to `$/lean/fileProgress` with
`fatalError` kind and throws a descriptive error.

The detection is scoped to `waitForMessage` only, not the underlying
`Ipc.readMessage`, so that other IPC paths (notably
`importCompletion.lean`, which intentionally elaborates a file with
an incomplete `import` line and observes the resulting fatalError
fileProgress while still using the alive worker for completion
requests) keep working unchanged. As part of this PR, `waitForMessage`
has been moved out of `Lean.Lsp.Ipc` and into
`Lean.Server.Test.Runner` next to its sole caller `processWaitFor`,
since it is a test-driver helper rather than a general-purpose IPC
primitive (it discards all unrelated messages, which would be unsafe
outside of a test driver).

To exercise the new behavior, `tests/server_interactive/run_test.sh`
now sources `<file>.init.sh` and uses `check_exit_is "${TEST_EXIT:-0}"`,
matching the convention in `tests/compile/`. The new
`worker_crash.lean` test forces the file worker to die via
`IO.Process.forceExit 9` from inside an `elab` command and then
issues a `waitFor` directive that can never be satisfied;
`TEST_EXIT=1` and `.out.expected` capture the abort path. Without
this PR the test would sit at the framework timeout; with this PR it
returns in well under a second.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 20:53:17 +00:00
Sebastian Ullrich
d33a771ea3
test: always clean full .lake (#13703)
Ensures we don't reuse outdated config oleans
2026-05-12 16:25:00 +00:00