Commit graph

12241 commits

Author SHA1 Message Date
Garmelon
9e5c86eac9
chore: move more radar bench logic to this repo (#13633)
These commands were previously executed in the radar-bench-lean4
repository, but are now moved here for additional control.
2026-05-04 18:21:22 +00:00
Mac Malone
326f43aa3a
fix: lake: meta import transitive import artifacts (#13600)
This PR fixes a Lake issue where the IR for a `meta import`'s transitive
imports was not included in the import artifacts Lake provided to Lean
(e.g., via `--setup`). When using the Lake artifact cache, this could
produce "missing data file" errors due to absent IR.

Closes #13419

---------

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-05-03 17:44:46 +00:00
Leonardo de Moura
316c39ffe4
fix: grind congruence-table invariant for lazy ite branches (#13624)
This PR fixes a `grind` congruence-table invariant violation that could
panic
when an `ite` branch was internalized lazily (after the condition became
`True`
or `False`) and that branch's equivalence class was later merged with
another.

`Internalize.lean` has a special case for `ite` that internalizes only
the
condition; the `then`/`else` branches are skipped and only internalized
later
on demand by `propagateIte`. The on-demand path (`applyCongrFun`) called
`internalize` for the branch but never called `registerParent` to add
the
parent `ite` to the branch's parent set in the e-graph. Subsequent
merges of
the branch's equivalence class then skipped re-hashing the `ite` in the
congruence table, leaving an orphan entry whose `congr` chain no longer
matched
the table's representative.

The fix adds the explicit `registerParent e rhs` that the standard
`for arg in args` loop in `Internalize.lean` would have made for an
ordinary
application argument; we are simply mirroring that pattern lazily. The
same
helper is reused by `propagateDIte`, but with parent registration
disabled
(controlled by a new `ite : Bool` parameter): for `dite` the `rhs`
propagated
upwards is a *constructed* reduction (built via `mkApp` from `e`'s
children,
possibly post-`preprocess`), not a structural argument of `e`, so
registering
`e` as its parent would be incorrect. The lambda branches of a `dite`
are
already eagerly internalized as parents of `e` by `Internalize.lean`, so
this
case does not need the fix.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 17:27:54 +00:00
Leonardo de Moura
ee8acc14e2
fix: grind cast internalization order (#13625)
This PR fixes a `grind` internal error triggered when `cast` (or
`Eq.rec`, `Eq.ndrec`, `Eq.recOn`) is applied to an argument that has not
yet been internalized. `pushCastHEqs` was emitting `e ≍ a` before
internalizing the args of `e`, so the `rhs` of the heq had no enode and
the debug sanity check tripped. The call now runs after the args are
internalized.
2026-05-03 16:32:10 +00:00
Leonardo de Moura
1b23b051f3
fix: missing proof hints in grind propagators (#13623)
This PR fixes proof construction issues in the `grind` projection
propagators.
2026-05-03 14:51:03 +00:00
Leonardo de Moura
2d79ec2883
fix: another grind AC invariant (#13622)
This PR fixes another issue in the `grind` AC invariant checker.
2026-05-03 13:33:13 +00:00
Leonardo de Moura
fe3c7394fd
fix: grind AC invariant (#13614)
This PR fixes the invariant in `grind` AC. equations in the todo queue
are not fully simplified.
2026-05-03 02:19:51 +00:00
Leonardo de Moura
030397785c
fix: missing case in processLevel at SymM (#13612)
This PR improves the universe unifier used by `SymM`.
2026-05-02 17:52:43 +00:00
Sebastian Ullrich
508a113242
feat: add .instances-transparency type-check diagnostics (#13368)
This PR adds infrastructure to help diagnose cases where tactics like
`unfold`
leave the goal in a state that is type-correct only at `.default`
transparency,
causing `rw`/`simp` to fail at `.instances` transparency.

Changes:
- Add a `transparency` parameter to `Meta.check` (defaults to `.all`)
- Add `withInstancesTypeCheckNote` which appends a
lazy note to tactic errors when the target is not type-correct at
`.instances`
- Wrap `rw`, `simp`, `dsimp`, and `simp_all` at the Elab level
- Add opt-in `linter.tacticCheckInstances` that proactively checks every
  tactic goal and reports semireducible defs that should be marked
`@[implicit_reducible]`, using diagnostic counter diffing between
`.default`
  and `.instances` checks
2026-05-02 12:17:51 +00:00
Sebastian Ullrich
4b8e74b27a
chore: disable flaky test (#13603) 2026-05-02 11:58:17 +00:00
Sebastian Ullrich
06ac472859
feat: detect further sub-instances in wrapInstance (#13536)
This PR extends the procedure behind `inferInstanceAs`/`def ...
deriving` to continue recursion through the class graph even when a
(local) instance to wrap was found in order to re-use already-wrapped
instance of subclasses.
2026-05-02 11:55:04 +00:00
Sebastian Ullrich
c53c4b4d2e
fix: prevent private default instances from leaking into public scope (#13596)
This PR fixes private(ly imported) default instances from accidentally
being used in public signatures, leading to follow-up errors.

As reported at
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/Elaboration.20of.20heterogeneous.20mul.20with.20the.20module.20system
2026-05-02 07:53:44 +00:00
David Thrane Christiansen
659249a7df
fix: always fail on unsolved metavariables after Verso docstring elab (#13574)
This PR ensures consistent metavariable behavior between Verso
docstrings and Verso moduledocs by sharing more code between their
elaborators. It also improves the error message when a metavariable leak
is prevented.
2026-05-01 22:34:52 +00:00
Leonardo de Moura
53db221124
fix: disable model-based theory combination in grind derived tactics (#13593)
This PR disables model-based theory combination (`mbtc`) in `grind`'s
`NoopConfig`, which is the base configuration used by the derived
tactics `lia`, `linarith`, `cutsat`, `order`, and `ring`. Without this
fix, these tactics could engage in wasteful reasoning via theory
combination, causing them to run for a long time (or hit the
deterministic timeout) on problems they are not designed to solve. With
this fix, these tactics fail quickly on out-of-scope problems, as
expected.

Closes #13573.
2026-05-01 01:45:17 +00:00
Leonardo de Moura
a9946fe4ac
fix: case-split on arithmetic implications with And/Or antecedents (#13590)
This PR makes `lia` (and `grind`'s arithmetic case-split heuristic)
recognize
implications whose antecedent is an `And` or `Or` of arithmetic
predicates as
relevant case-split candidates. Previously, `Arith.isRelevantPred` only
matched
`Not`, `LE`, `LT`, `Eq`, and `Dvd`. With `splitImp := false` (the
default),
implications `p → q` are added as split candidates only when `p` is
arith-relevant, so a hypothesis like `(b ≤ e ∧ e < b + c → a ≤ e ∧ e < a
+ d)`
was never registered as a candidate. cutsat/lia would then find a
satisfying
assignment for the constraints it had been told about, but that
assignment
would not necessarily satisfy the original implication, yielding the bad
counterexample reported in #13575.

After this change, `isRelevantPred` recurses through `And` and `Or`
(returning
`true` if either operand is relevant), so the implication is split,
modus
ponens fires in the True branch, and cutsat/lia closes the False branch
via the
disjunction over negated atoms.

Closes #13575.
2026-04-30 23:15:34 +00:00
Leonardo de Moura
427e3bcdbc
fix: limit ring solver polynomial degree in grind (#13585)
This PR adds a `ringMaxDegree` configuration option (default `1024`)
that bounds the maximum degree of polynomials processed by the `grind`
ring solver. Equality constraints whose polynomial exceeds this
threshold are discarded (with an issue reported once per goal),
preventing pathological degree explosion on inputs such as `r ^ (2 ^ 250
- 1)`.

This PR also introduces `Poly.simpM?`, a monadic version of `Poly.simp?`
built on the existing safe arithmetic primitives (`mulMonM`, `combineM`,
`mulConstM`) in `Grind.Arith.CommRing.SafePoly`. The previous
reflection-oriented `Poly.simp?` in `Sym.Arith.Poly` lacked the abort
mechanisms needed during proof search, so the simplification path used
by `EqCnstr` now goes through the safe variant. A regression test
`tests/elab/grind_ring_degree_explosion.lean` ensures `grind` fails
quickly on high-degree problems.
2026-04-30 14:00:00 +00:00
Kyle Miller
19baa470e5
feat: MVarId.assertAfter fvar alias info, MVarId.replace mvar dependencies, specialize tactic using eta arguments (#13528)
This PR gives the `specialize` tactic the ability to instantiate
universal quantifiers other than the first using `specialize h (y := v)`
syntax. It also fixes an issue where `MVarId.assertAfter` did not record
variable alias information, and an issue where `MVarId.replace` and
`MVarId.replaceLocalDecl` did not take metavariables into account when
calculating dependencies. Additionally it fixes some uninstantiated
metavariables bugs, including one in the Infoview tactic state
hypothesis diff.

The `specialize` tactic now uses `Lean.MVarId.replace` to simplify the
implementation, and as a consequence it tries to keep the specialized
hypothesis close to its original spot in the local context.

Additional metaprogramming API:
- `Lean.Expr.getLambdaBody` to accompany `Lean.Expr.getNumHeadLambdas`
- `Lean.LocalContext.setType`, `Lean.MetavarContext.setFVarType`,
`Lean.MVarId.setFVarType`
- `Lean.MVarId.assertAfter'` to assert a new hypothesis as early as
possibly in the context where it is well-formed, as a frontend to
`Lean.MVarId.assertAfter`, which assumes the new hypothesis is
well-formed

Breaking change: metaprograms cannot assume that `MVarId`s change if
metavariables are assigned. For example, the `change` tactic will no
longer change `MVarId`s if the only effect is incidental metavariable
assignments.

Mathlib impact: this revealed many `dsimp`s that did nothing and could
be deleted.

Closes #9893
2026-04-30 10:36:29 +00:00
Joachim Breitner
9efe4283e2
fix: propagate parent cancel token to parallel tactic subtasks (#13428)
This PR fixes parallel tactic combinators (`attempt_all_par`,
`first_par`) leaking their subtasks when the server cancels elaboration
on re-elaboration. Subtasks spawned via `CoreM.asTask` (and its
`MetaM`/`TermElabM`/`TacticM` variants) get a fresh `IO.CancelToken`,
which previously had no link to the parent token; `cancelRec` would set
the command-level token but the children kept running.

The fix is one line in `CoreM.asTask`: when a parent token is in scope,
register `cancelToken.set` as an `onSet` callback on the parent.
Server-level cancellation now flows down to every parallel subtask, and
`Core.checkInterrupted` inside the child sees the token set as expected.

Fixes #13300.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-30 09:22:33 +00:00
Wojciech Różowski
2b5d154a4c
feat: upstream unnecessarySeqFocus linter (#13540)
This PR upstreams `unnecessarySeqFocus` linter from batteries to core
lean as a "clippy" linter - i.e. one that is disabled by default and
executed by `lake lint --clippy`.

---------

Co-authored-by: Mac Malone <tydeu@hatpress.net>
Co-authored-by: Thomas R. Murrills <68410468+thorimur@users.noreply.github.com>
2026-04-29 14:51:10 +00:00
Sebastian Graf
e6f44eeeec
test: regression tests for do issues fixed by the new elaborator (#13541)
This PR adds regression tests for `do`-notation issues that the new
elaborator fixes:

* `tests/elab/doNotation7.lean` collects reproducers for #2663, #2676,
#3126, #5607, #6426, and #8119.
* `tests/elab/12229.lean` covers the `logInfo` and `Std.TreeMap`
reproducers from #12229.
2026-04-29 14:37:23 +00:00
Wojciech Różowski
2ba4c55a84
feat: upstream dupNamespace environment linter (#13538)
This PR upstreams `dupNamespace` linter from batteries to work with new
core environment linting framework, as a "clippy" linter - i.e. one that
is not enabled by default.

Stacked on top of #13513.

---------

Co-authored-by: Mac Malone <tydeu@hatpress.net>
Co-authored-by: Thomas R. Murrills <68410468+thorimur@users.noreply.github.com>
2026-04-29 12:50:40 +00:00
Leonardo de Moura
897e556d90
feat: add E-matching diagnostics to grind (#13558)
This PR adds the option `grind.ematch.diagnostics`, which tracks how
E-matching theorem instances depend on each other. When enabled, `grind`
records, for every new theorem instance, the set of previous instances
whose generated terms participated in the match. This produces a
hyper-graph `{thm_1, ..., thm_n} => thm` describing the provenance of
each instantiation.

The hyper-graph is stored in `Grind.Result` so downstream tooling can
inspect it. The trace class `trace.grind.ematch.diagnostics.compact`
prints a compact textual view of the hyper-graph, restricted to
constant-name origins. Example output:

```
  [grind.ematch.diagnostics.compact] ️ instances
    [inst] [] => th1
    [inst] [th1] => th3
    [inst] [th1] => th2
    [inst] [th2, th3] => th4
    [inst] [th4] => th5
```

The implementation stores an `ematchDiagSource` field on each `ENode`
and threads a `withEmatchDiagSource` reader through fact assertion so
that newly internalized terms inherit the origin of the instance that
produced them. During E-matching, `Choice` collects the sources of every
matched argument, and the resulting set becomes the predecessor set of
the new instance.
2026-04-29 12:17:55 +00:00
Joachim Breitner
b763ab8a5e
refactor: keep IO.CancelToken task private, resolve promise before setting flag (#13569)
This PR addresses two review points on `IO.CancelToken`:

* `set` now resolves the underlying promise *before* writing the `Bool`
  fast-path flag, so observing `isSet = true` implies any synchronously
chained `onSet` callback has already run. The previous order (flag
first,
then resolve) was a subtle footgun: code seeing `isSet = true` could not
  rely on the cancellation task having fired.
* The underlying promise and the task it produces are kept private. The
prior `task : Task (Option Unit)` accessor is removed; consumers should
use `onSet` to react to cancellation. A comment on the structure records
  that re-exposing the task in the future requires re-auditing the order
  in `set` for races between the promise and the `Bool` flag.

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

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 11:12:54 +00:00
Leonardo de Moura
5852865c92
fix: beta reduction in grind must respect generation threshold (#13560)
This PR fixes a bug in `propagateBetaEqs` (in
`Lean.Meta.Tactic.Grind.Beta`)
where new equalities/terms introduced by beta reduction were added to
the goal
without checking the generation threshold. The generation of the new
fact
is the maximum generation of the lambda, the function `f`, and its
arguments, plus one. Without the threshold check, beta reduction can
cascade indefinitely on self-similar lambdas such as
`(fun b => f (b + 1)) = fun b => f b`, which kept producing
`f n = f (n + 1)` for every `n`. The fix aggregates argument generations
before the threshold check and bails out when the resulting generation
reaches `maxGeneration`.
2026-04-28 21:51:14 +00:00
Joachim Breitner
c36b0fb165
refactor: make CancelToken Promise-based (#13303)
This PR moves `IO.CancelToken` from `Init.System.IO` to its own file
`Init.System.CancelToken`, backed by `IO.Promise Unit` instead of
`IO.Ref Bool`. This enables non-polling cancellation propagation: the
token's underlying promise can be used directly with `IO.waitAny`, and
callbacks can be registered to fire when cancellation is requested.

The structure carries both the promise *and* a plain `IO.Ref Bool` flag,
set in lockstep by `set`. `isSet` reads the flag directly (used on hot
paths like `Core.checkInterrupted`); `task`/`onSet` go through the
promise. The avoids a ~0.4% regression that a pure-promise
representation introduced.

API additions:

- `CancelToken.task : Task (Option Unit)`. Returns the underlying
promise's `result?` task directly — the same task object on every call,
so further `Task.map`/`BaseIO.bindTask` dependencies can be safely
attached. Resolves with `some ()` when `set` is called, or `none` if the
token is dropped without ever being set.
- `CancelToken.onSet : BaseIO Unit → BaseIO Unit`. Registers a callback
that runs synchronously on the cancelling thread when `set` is called
(or immediately if the token is already set). Implemented via
`BaseIO.chainTask` on `result?`, so no fresh `Task.map` per call and no
GC hazard.

Runtime cleanup:

- Add `LEAN_TASK_STATE_{WAITING,RUNNING,FINISHED}` constants in `lean.h`
matching `IO.TaskState`.
- Factor `lean::promise_is_resolved` inline in `object.h`, replacing
three open-coded `lean_io_get_task_state_core(...) == 2` checks (in
`interrupt.cpp`, `uv/timer.cpp`, `uv/signal.cpp`).
- Drop the manual `inc_ref(g_cancel_tk)` in `check_interrupted`; the
token is owned by the enclosing `scope_cancel_tk` for the duration of
the call (documented).
- Replace the bare `lean_always_assert(g_task_manager)` in
`lean_promise_new` with an explicit `lean_internal_panic` carrying a
message that names `Promise.new`, identifies the typical trigger
(`initialize` blocks, transitively via `IO.CancelToken.new`), and
recommends lazy construction. Without this, users got an opaque "LEAN
ASSERTION VIOLATION ... Condition: g_task_manager" with no actionable
hint.

Behavioural notes documented inline:

- `new` cannot be called from `initialize` blocks (task manager not
running yet); construct lazily.
- `task` documents the dropped-promise case (`none`) and steers callers
to `onSet` for callback chaining.

A consumer of `onSet` for parent → child cancel-token propagation in
parallel tactic combinators is in #13428 (fixes #13300).

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 21:50:54 +00:00
Wojciech Różowski
1a15db69ec
feat: lake: add support for running text linters from lake lint (#13513)
This PR extends `lake lint --builtin-lint` to also support text linters
(i.e. those using `logLint`/`logLintIf`), in addition to the environment
linters added in #13431. Text-linter warnings emitted during the build
are persisted into each module's `.olean` via a new
`Lean.Linter.lintLogExt` environment extension; `lake lint` re-runs the
build for the target modules and reads the entries back, reporting them
alongside the environment linter output.

---------

Co-authored-by: Mac Malone <tydeu@hatpress.net>
Co-authored-by: Thomas R. Murrills <68410468+thorimur@users.noreply.github.com>
2026-04-28 15:09:04 +00:00
Joachim Breitner
432d11541b
feat: add try? => tac syntax and parallel cancellation test (#13301)
This PR adds a `try? => tac` syntax that runs `evalSuggest` directly on
a given tactic, useful for testing the `try?` machinery in isolation. It
also adds a server_interactive test (`cancellation_par.lean`) that
demonstrates a cancellation bug with parallel tactic combinators.

The test contrasts three combinators:
- **`first`** (sequential): cancellation works correctly — the tactic
runs on the main elaboration thread and shares its cancel token.
- **`attempt_all_par`** (parallel): cancellation is broken — the subtask
spawned via `asTask` gets a fresh cancel token that is never set on
re-elaboration.
- **`first_par`** (parallel): same bug as `attempt_all_par`.

The test uses a `check_cancel <label>` helper tactic that detects leaked
cancel tokens without any timing dependency: the second invocation (from
re-elaboration) signals the first, which then checks whether its cancel
token was set.

Related issue: #13300

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 10:00:35 +00:00
Sebastian Graf
5b252f2c3d
fix: surface meaningful pattern errors inside do-notation (#13542)
This PR replaces the catch-all "unsupported pattern in syntax match"
error that the new `do` elaborator produces for typical pattern mistakes
(#2215, #8304, #10393) with the proper diagnostics from the regular
pattern-var collector (e.g. "Invalid pattern: Expected a constructor or
constant marked with `[match_pattern]`", "ambiguous pattern, use fully
qualified name"), pointing at the offending pattern.

`getPatternVarsEx` / `getPatternsVarsEx` in `Lean.Elab.Do` now try the
syntax-quotation collector first (cheaply handling identifiers, holes,
and antiquotations) and fall back to the regular pattern-var collector
for everything else. When both fail, the regular collector's error wins
via `<|>` semantics.

The legacy `do` elaborator is intentionally left untouched, so the
existing `tests/elab/doSyntaxPatternError.lean` guards (which capture
the cryptic messages produced under legacy default) are unchanged. They
will need updating when the new `do` elaborator becomes default.

Fixes #2215, #8304, and #10393 for the new `do` elaborator.
2026-04-27 21:12:04 +00:00
Sebastian Ullrich
7f5fac9d9f
feat: add warn.redundantExpose for redundant @[expose]/@[no_expose] attributes (#13359)
This PR adds a `linter.redundantExpose` option (default `true`) that
warns when `@[expose]` or `@[no_expose]` attributes have no effect:

- `@[expose]` on `abbrev` (always exposed) or non-Prop `instance`
(always exposed)
- `@[expose]` on a `def` inside an `@[expose] section` (already exposed
by the section)
- `@[expose]`/`@[no_expose]` in a non-`module` file (no module system)
- `@[no_expose]` on a declaration that wouldn't be exposed by default

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 10:33:58 +00:00
Joachim Breitner
ac9a1cb415
feat: add @[backward_defeq] attribute and local useBackward simp option (#13492)
This PR introduces stricter inference for the `@[defeq]` attribute and a
companion `@[backward_defeq]` attribute that preserves the pre-PR
behavior
as an opt-in.

### What changed

* `@[defeq]` is now inferred only when the equation holds at
  `.instances` transparency (the transparency `dsimp` operates at).
* `@[backward_defeq]` is the old set: every theorem whose `rfl` proof
the legacy inference would have accepted is tagged `@[backward_defeq]`,
  so `defeq ⊆ backward_defeq` holds by construction.
* The option `backward.defeqAttrib.useBackward` (default `false`) makes
  `dsimp` also use `@[backward_defeq]` theorems, restoring the pre-PR
  behavior for a specific proof or file.
* The option is eqn-affecting: its value at the point of a function's
  definition is recorded so that the equation lemmas later generated for
  that function use the same value, regardless of the ambient option at
  the use site.

### Mathlib adaption

A companion adaption branch (`lean-pr-testing-backward-defeq-attrib` on
mathlib4) builds cleanly against this PR and passes `lake test` without
warnings. Most adaption changes are scoped
`set_option backward.defeqAttrib.useBackward true in` additions on the
failing declarations; a small number of files needed proof-level edits
where the stored form of a `dsimp%`/`@[reassoc]`/`@[elementwise]`
/`@[simps]`/`@[to_app]`-generated lemma had drifted under the stricter
regime.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 10:07:59 +00:00
Sebastian Graf
4d7b7dd8e6
feat: support while let in do blocks via unified condition syntax (#13534)
This PR generalizes the `while` syntax in `do` blocks so that the
condition can be any `doIfCond`, the same condition form already
accepted by `if`. As a result, `while let pat := e do …` and `while let
pat ← e do …` are now supported in addition to `while cond do …` and
`while h : cond do …`. The previously separate `doWhile` and `doWhileH`
parsers and their accompanying macros are unified into a single
`doWhile` parser whose macro delegates to the existing `doIf`
desugaring.
2026-04-27 09:23:36 +00:00
Leonardo de Moura
3c6317b6d7
feat: notify satellite solvers about asserted equalities in grind (#13532)
This PR notifies satellite solvers about asserted equalities `lhs = rhs`
even though `lhs = rhs` is not internalized in the E-graph (an existing
optimization). The notification lets solvers that do not inspect
equivalence classes (such as the homomorphism extension) react to
asserted equalities directly. It fires before the equivalence-class
merge so that solvers that mark `lhs` and `rhs` as their internal terms
have them registered before `Solvers.mergeTerms` fires `processNewEq`.

`cutsat` opts out of the notification when the equality has not been
internalized, since it already handles equalities through its `newEq`
handler. The homomorphism demo opts in by forcing `e` to be
internalized, enabling its rewrite rules to apply to asserted equalities
(e.g., `add b b = b` rewrites via `a = b ↔ toInt a = toInt b`).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 19:45:15 +00:00
Sebastian Ullrich
e843e5a155
feat: add [no_fallback] attribute for tactic elaborators and macros (#13523)
This PR allows tactic macros and elaborators to opt out of automatic
fallback to previous macros/elabs on failure. `throwUnsupportedSyntax`
is unaffected.
2026-04-26 13:59:54 +00:00
Sebastian Ullrich
a21d3b1ef7
test: copy srcHash test data to temp dir before modifying (#13524)
Mirrors the fix from #13134 for `ver_clash`.
2026-04-26 08:13:39 +00:00
Mac Malone
24bef91f9a
test: tests/lake/run_test.sh (#13501)
This PR removes the Makefile-based Lake test runner and replaces it with
`run_test.sh` and `run_clean.sh`. These are still not designed for use
with the `with_*.sh` runners, but this a step closer to that
eventuality.

It also adds a line to `CLAUDE.md` informing Claude how to build and run
a single Lake test.
2026-04-25 04:36:08 +00:00
Joachim Breitner
5b87ab6625
feat: use explicit allowlist instead of transparency bump in whnfMatcher (#13363)
This PR replaces the transparency bump from `.reducible` to `.instances`
in `whnfMatcher` with an explicit allowlist in `canUnfoldAtMatcher`.
Previously, `whnfMatcher` would unfold all `implicitReducible`
definitions and all `fromClass` projections when reducing match
discriminants. This made it impossible to mark definitions as
`implicit_reducible` without silently affecting match reduction
behavior.

The new `canUnfoldAtMatcher` delegates to `canUnfoldDefault` first
(respecting the ambient transparency), then allows unfolding of
`match_pattern`-attributed definitions, and finally checks an explicit
allowlist:

- `OfNat.ofNat` — numeric literals in match discriminants
- `NatCast.natCast` — `↑m` coercions (pervasive in Int proofs)
- `Zero.zero`, `One.one` — `0`/`1` class projections in match
discriminants
- `Fin.ofNat`, `HMod.hMod`, `Mod.mod` — Fin literal reduction
- `decEq`, `Nat.decEq` — decidable equality
- `Char.ofNat`, `Char.ofNatAux` — character literals
- `String.decEq`, `List.hasDecEq` — string/list equality
- `UInt{8,16,32,64}.{ofNat,decEq}` — unsigned integer literals and
equality

The key change is removing the blanket `implicitReducible` and
`fromClass` checks, so that marking definitions as `implicit_reducible`
no longer silently affects match reduction.

Additionally, `reduceMatcher?` and `reduceRecMatcher?` now call
`consumeMData` on their input to handle mdata-wrapped matcher
expressions.

Mathlib adaptation: the removal of the `fromClass` projection check
means class projections like `CategoryStruct.comp`, `CategoryStruct.id`,
`Min.min` etc. are no longer auto-unfolded in match discriminants.
Affected proofs add these projections explicitly to `simp`/`dsimp`
calls.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 13:50:30 +00:00
Leonardo de Moura
45bdae81c9
test: grind homomorphism predicates (#13520)
This PR extends the `grind` homomorphism demo with predicates to be
applied atoms.
2026-04-24 08:19:04 +00:00
Sofia Rodrigues
2e48cd293a
refactor: move Async and Http from Internal to Std (#13511)
This PR moves Async and Http from Internal to Std
2026-04-23 19:55:22 +00:00
Sebastian Graf
e3d42400ce
feat: inject unreachable! after break-less repeat (#13506)
This PR appends `unreachable!` to the expansion of `break`-less `repeat`
when the expected result type does not unify with `PUnit`. The
continuation then has a polymorphic value, so the enclosing do block's
result type is inferred without a user-written filler, and `ControlInfo`
for break-less `repeat` can report `noFallthrough` honestly — dead-code
warnings on subsequent elements are now actionable.

Co-authored-by: Rob23oba <robin.arnez@web.de>
2026-04-23 07:16:03 +00:00
Sebastian Graf
525021c01e
feat: pluggable pure/bind builders for do elaboration (#13507)
This PR exposes the `Pure.pure` / `Bind.bind` applications emitted by
the `do` elaborator as pluggable closures, so external surface syntaxes
(e.g. an `ido` notation for indexed monads) can reuse the full `do`
machinery while emitting alternate constants.

`Context` carries a new `DoOps` record (wrapped via an opaque `DoOpsRef`
to break the cycle with `DoElabM`) with `mkPureApp`, `mkBindApp`, and
`isPureApp?` fields. `mkPureApp` and `mkBindApp` become thin
dispatchers; the original bodies move to `DoOps.default`. `isPureApp?`
returns the pure value as an `Expr` rather than a `Bool`, so overrides
aren't locked into `Pure.pure`'s 4-argument layout. A new `elabDoWith`
entry point takes a `DoOps` plus a `doSeq`; `elabDo` is now `elabDoWith
.default` applied to a matched ``(do $doSeq)``.

Control-flow features (`mut`, `return`, `break`, `continue`, `for`) and
the transformer stack (`StateT`, `OptionT`, `ExceptT`, `EarlyReturnT`,
`BreakT`, `ContinueT`) remain hard-coded to `Monad`; generalising them
is deferred to a follow-up. A new
`tests/elab/doNotationPluggableOps.lean` registers an Atkey-style
indexed monad and an `ido` surface syntax that drives `elabDoWith`,
covering the forms of `do` that are supported under the minimal scope.
2026-04-23 07:15:25 +00:00
Mac Malone
30a3fde8aa
feat: lake: empty build detection & --allow-empty (#13500)
This PR adds a check for empty `lake build` invocations (as an empty
build usually indicates a misconfiguration). Builds with no jobs will
now print "Nothing to build." and invocations of `lake build` with no
default targets configured will produce a warning. This will be promoted
to an error in the future. The warning (and future error) can be
suppressed with the new `--allow-empty` CLI option.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 03:27:29 +00:00
Kyle Miller
48c7a4f7d9
feat: informative metavariable hovers, better delayed assignment pretty printing (#13446)
This PR improves metavariable pretty printing and their hovers in the
InfoView. The hovers in the InfoView now include information about
specific metavariables — it includes information such as the kind of the
metavariable, whether it is a blocked delayed assignment and which
metavariables it is blocked on, and the differences in what variables
exist the metavariable's local context. Additionally, named
metavariables now pretty print with tombstones if they are inaccessible.
Delayed assignment pretty printing now more reliably follows chains of
assignments to find the pending metavariable.

**Example hovers**

Hovering over a natural metavariable:
> `?m.7 : Sort ?u.14`
> A metavariable representing an expression that should be solved for by
unification during the elaboration process. They are created during
elaboration as placeholders for implicit arguments and by `_`
placeholder syntax.
>
> Variables absent from this metavariable's local context: `a`, `b`, `y`

Hovering over `?baz`, a tactic goal:
> `?baz : ∀ (a : ?m.7) (a : ?m.8), True`
> A metavariable representing a tactic goal or an expression whose
elaboration is still pending. They usually act like constants until they
are completely solved for. They can be created using `?_` and `?n`
synthetic placeholder syntax.
>
> Variables absent from this metavariable's local context: `a`, `b`, `y`

Hovering over `?refine_1`, which appears under a binder:
> `?m.4 x : Nat`
> A metavariable representing a tactic goal or an expression whose
elaboration is still pending. They usually act like constants until they
are completely solved for. They can be created using `?_` and `?n`
synthetic placeholder syntax.
>
> This metavariable appears here via a *delayed assignment*.
Substitution is delayed until the metavariable's value contains no
metavariables, since all occurrences of the variables from its local
context will need to be replaced with expressions that are valid in the
current context.
>
> Additional variable in this metavariable's local context: `x`

Hovering over `?m.4`:
> `?m.4 : Nat → Nat`
> Part of the encoding of the *delayed assignment* mechanism. Represents
the metavariable `?refine_1`, which has additional local context
variables. Substitution is delayed until the metavariable's value
contains no metavariables, since all occurrences of the variables from
its local context will need to be replaced with expressions that are
valid in the current context.
>
> Additional variable in this metavariable's local context: `x`

The middle paragraph for `?refine_1` when it has been partially
assigned:
> This metavariable has been assigned, but it is a *delayed assignment*.
Substitution is delayed until the metavariable's value contains no
metavariables, since all occurrences of the variables from its local
context will need to be replaced with expressions that are valid in the
current context. Substitution is awaiting assignment of the following
metavariable: `?foo`
2026-04-23 01:43:55 +00:00
Wojciech Różowski
87c123bb1b
feat: lake: add support for running environment linters (#13431)
This PR adds builtin environment linting support to Lake, accessible via
`lake lint` flags. It also introduces two builtin linters upstreamed
from Mathlib (`defLemma` and `checkUnivs`) and a `builtinLint` package
configuration option.

Builtin linting is triggered via flags on `lake lint`:
- `--builtin-lint`: run default builtin linters (in addition to the lint
driver if configured)
- `--builtin-only`: run only builtin linters, skip the lint driver
- `--clippy`: run only non-default (clippy) linters
- `--lint-all`: run all builtin linters (default + clippy)
- `--lint-only <name>`: run a specific builtin linter by name
- Using `--clippy`, `--lint-all`, or `--lint-only` implicitly enables
builtin lint mode

The `builtinLint` package option is a tristate (`Option Bool`):
- `true`: always run builtin lints via `lake lint`; when a lint driver
is also configured, builtin lints run first, then the driver, and the
command fails if either reports errors.
- `false`: never run builtin lints automatically; `lake check-lint`
fails unless a lint driver is configured.
- `none` (default): currently equivalent to `false`; in a future
release, `none` will fall back to builtin lints when no lint driver is
configured.

The linter framework introduces a `LintScope` enum (`.default`,
`.clippy`, `.all`) replacing the previous boolean `clippy` parameter in
`getChecks` and `formatLinterResults`. A `@[builtin_nolint]` attribute
(available without imports) allows suppressing specific linters per
declaration.

---------

Co-authored-by: Mac Malone <tydeu@hatpress.net>
Co-authored-by: Thomas R. Murrills <68410468+thorimur@users.noreply.github.com>
2026-04-22 18:17:41 +00:00
Sebastian Graf
cae4decead
test: speed up bench/mvcgen/sym ctest entry (#13498)
This PR drops `LEAN_NUM_THREADS=1` from the `run_test.sh` of
`bench/mvcgen/sym`. The single-threaded restriction was originally there
to get reproducible benchmark timings, but `run_test.sh` runs as a test
rather than a benchmark, and we do not care about timing reproducibility
for tests. Allowing the default thread count cuts the wall time of what
was the slowest ctest entry from ~30s to ~20s.
2026-04-22 13:41:07 +00:00
Sebastian Graf
a1240f7b80
fix: correct alternative-fold base in do match (#13491)
This PR fixes the `ControlInfo` inference for a do-block `match`: the
fold over the match arms started from `ControlInfo.pure` (defaults to
`numRegularExits := 1`, `noFallthrough := false`), but `alternative`
sums `numRegularExits` and ANDs `noFallthrough`, so the fold identity is
`{ numRegularExits := 0, noFallthrough := true }`. With the wrong base,
a `match` whose arms all `break`/`continue`/`return` reported
`numRegularExits = 1` and `noFallthrough = false`, suppressing the
dead-code warning on the continuation after the match. The fix corrects
both the inference handler in `InferControlInfo.lean` and the fold in
`elabDoMatchCore`.
2026-04-22 13:25:30 +00:00
Sebastian Graf
2b99012545
feat: split ControlInfo.noFallthrough from syntactic numRegularExits (#13502)
This PR splits `ControlInfo`'s dead-code signal in two.
`numRegularExits` is now purely syntactic: how many times the block
wires its continuation into the elaborated expression, consumed by
`withDuplicableCont` as a join-point duplication trigger (`> 1`). The
new `noFallthrough : Bool` asserts that the next doElem in the enclosing
sequence is semantically irrelevant; `false` asserts nothing. Invariant:
`numRegularExits = 0 → noFallthrough`; the converse does not hold.
`sequence` derives `noFallthrough := a.noFallthrough || b.noFallthrough`
(and aggregates syntactic fields unconditionally); `alternative` derives
it as `a.noFallthrough && b.noFallthrough`. The dead-code warning gate
in `withDuplicableCont` and `ControlLifter.ofCont` now reads
`noFallthrough`.
2026-04-22 12:32:11 +00:00
Leonardo de Moura
e542810e79
test: grind homomorphism demo (#13497)
This PR adds an example for the Lean hackathon in Paris. It demonstrates
how users can implement https://hackmd.io/Qd0nkWdzQImVe7TDGSAGbA
2026-04-21 21:17:32 +00:00
Sebastian Graf
f32106283f
fix: pin repeat's numRegularExits at 1 to match for (#13494)
This PR stops the `repeat` inference handler from reporting
`numRegularExits := 0` for break-less bodies. For break-less `repeat`
the loop never terminates normally, so `0` looks more accurate
semantically, but the loop expression still has type `m Unit` and the do
block's continuation after the loop is what carries that type. Reporting
`0` makes the elaborator flag that continuation as dead code, yet there
is no way for the user to remove it that is also type correct — unless
the enclosing do block's monadic result type happens to be `Unit`.
Pinning `numRegularExits` at `1` (matching `for ... in`) eliminates
those spurious warnings.
2026-04-21 16:15:19 +00:00
Robert J. Simmons
bf269ce250
fix: preserve nesting level across empty doc snippet nesting (#13489)
This PR fixes a bug where the nesting level in Verso Docstrings is
forgotten when there's a doc comment with no headers.

It changes the `terminalNesting` of `VersoModuleDocs` to be recomputed
rather than stored in the structure; we never want it to be anything
besides the default value, and it's easy to accidentally break this
invariant.

Closes #13485
2026-04-21 12:58:52 +00:00
Marc Huisinga
25bab8bcc4
feat: server-side support for incremental diagnostics (#13260)
This PR adds server-side support for incremental diagnostics via a new
`isIncremental` field on `PublishDiagnosticsParams` that is only used by
the language server when clients set `incrementalDiagnosticSupport` in
`LeanClientCapabilities`.

### Context

The goal of this new feature is to avoid quadratic reporting of
diagnostics.

LSP has two means of reporting diagnostics; pull diagnostics (where the
client decides when to fetch the diagnostics of a project) and push
diagnostics (where the server decides when to update the set of
diagnostics of a file in the client).
Pull diagnostics have the inherent problem that clients need to
heuristically decide when the set of diagnostics should be updated, and
that diagnostics can only be incrementally reported per file, so the
Lean language server has always stuck with push diagnostics instead.
In principle, push diagnostics were also intended to only be reported
once for a full file, but all major language clients also support
replacing the old set of diagnostics for a file when a new set of
diagnostics is reported for the same version of the file, so we have
always reported diagnostics incrementally while the file is being
processed in this way.
However, this approach has a major limitation: all notifications must be
a full set of diagnostics, which means that we have to report a
quadratic amount of diagnostics while processing a file to the end.

### Semantics

When `LeanClientCapabilities.incrementalDiagnosticSupport` is set, the
language server will set `PublishDiagnosticsParams.isIncremental` when
it is reporting a set of diagnostics that should simply be appended to
the previously reported set of diagnostics instead of replacing it.
Specifically, clients implementing this new feature should implement the
following behaviour:
- If `PublishDiagnosticsParams.isIncremental` is `false` or the field is
missing, the current diagnostic report for a specific document should
replace the previous diagnostic report for that document instead of
appending to it. This is identical to the current behavior before this
PR.
- If `PublishDiagnosticsParams.isIncremental` is `true`, the current
diagnostic report for a specific document should append to the previous
diagnostic report for that document instead of replacing it.
- Versions should be ignored when deciding whether to replace or append
to a previous set of diagnostics. The language server ensures that the
`isIncremental` flag is set correctly.

### Client-side implementation

A client-side implementation for the VS Code extension can be found at
[vscode-lean4#752](https://github.com/leanprover/vscode-lean4/pull/752).

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Wojciech Nawrocki <13901751+Vtec234@users.noreply.github.com>
2026-04-21 12:48:15 +00:00