Compare commits

..

23 Commits

Author SHA1 Message Date
Sebastian Graf
aa1144602b feat: add letConfig syntax to do block let/have parsers (#13250)
This PR extends the `doLet`, `doLetElse`, `doLetArrow`, and `doHave`
parsers to accept `letConfig` (e.g. `(eq := h)`, `+nondep`, `+usedOnly`,
`+zeta`), matching the syntax of term-level `let`/`have`. The
elaborators are adjusted to handle the shifted syntax indices but do not
yet process the configuration; that will be done in a follow-up PR after
stage0 is updated, allowing the use of proper quotation patterns.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 09:40:44 +00:00
Sebastian Graf
ffc2c0ab1a chore: remove hardcoded maxSteps limit in Sym mvcgen' (#13252)
This PR removes a FIXME in Sym-based mvcgen' concerning a hardcoded step
limit for grind simplification. I tested that this is no longer
necessary even at highest setting for the GetThrowSetGrind benchmark.
2026-04-02 09:16:43 +00:00
Sebastian Ullrich
8dc4c16fce fix: correct String cases codegen to use String.toByteArray (#13242)
This PR fixes the compiler handling of pattern matching on the `String`
constructor to conform to the new `String` representation.
2026-04-02 08:17:20 +00:00
Kyle Miller
861bc19e0c feat: allow dotted function notation to use @ and explicit universes (#13245)
This PR extends Lean syntax for dotted function notation (`.f`) to add
support for explicit mode (`@.f`), explicit universes (`.f.{u,v}`), and
both simultaneously (`@.f.{u,v}`). This also includes a fix for a bug
involving overloaded functions, where it used to give erroneous
deprecation warnings about declarations that the function did not
elaborate to.

Closes #10984
2026-04-02 03:12:54 +00:00
Kyle Miller
8f1c18d9f4 feat: pretty print level metavariables using index (#13030)
This PR improves pretty printing of level metavariables: they now print
with a per-definition index rather than their per-module internal
identifiers. Furthermore, `+` is printed uniformly in level expressions
with surrounding spaces. **Breaking metaprogramming change:** level
pretty printing should use `delabLevel` or `MessageData.ofLevel`;
functions such as `format` or `toString` do not have access to the
indices, since they are stored in the current metacontext. Absent index
information, metavariables print with the raw internal identifier as
`?_mvar.nnn`. **Note:** The heartbeat counter also increases quicker due
to counting allocations that record level metavariable indices. In some
tests we needed to increase `maxHeartbeats` by 20–50% to compensate,
without a corresponding slowdown.
2026-04-01 22:34:29 +00:00
Henrik Böving
097f3ebdbc perf: use memcmp for ByteArray equality (#13235)
This PR uses `std::memcmp` for `ByteArray` `BEq` and `DecidableEq`.

Implementation is done in the same way as `String` but adapted to scalar
arrays.
2026-04-01 15:30:03 +00:00
Joachim Breitner
861f722844 fix: handle multi-discriminant casesOn in WF unfold equation generation (#13232)
This PR fixes a panic when compiling mutually recursive definitions that
use `casesOn` on indexed inductive types (e.g. `Vect`). The
`splitMatchOrCasesOn` function in `WF.Unfold` asserted
`matcherInfo.numDiscrs = 1`, but for indexed types the casesOn recursor
has multiple discriminants (indices + major premise). The fix uses the
last discriminant (the major premise) and lets the `cases` tactic handle
index discriminants automatically.

Closes #13015

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

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 15:23:13 +00:00
Wojciech Różowski
eac9315962 feat: add deprecated_module (#13002)
This PR adds a `deprecated_module` command that marks the current module
as deprecated. When another module imports a deprecated module, a
warning is emitted during elaboration suggesting replacement imports.

Example usage:
```lean
deprecated_module "use NewModule instead" (since := "2026-03-30")
```

The warning message is optional but recommended. The `since` parameter
is required. Warnings can be disabled, by setting
`linter.deprecated.module` option to false in the command line. Because
the check happens when importing , using `set_option
linter.deprecated.module` in the source file won't affect the warnings.
Instead, a whole file can be marked not to display depreciation
warnings, by putting a comment `deprecated_module: ignore` next to
`module` keyword. Similarly, individual keywords can be silenced.

A `#show_deprecated_modules` command is also provided for inspecting
which modules in the current environment are deprecated.
`linter.deprecated.module` has no effect on this command, and hence one
can view deprecated modules, even when having warnings silenced.
2026-04-01 14:40:43 +00:00
Robin Arnez
8b52f4e8f7 fix: make FirstTokens.seq (.optTokens _) .unknown return .unkown (#13205)
This PR fixes `FirstTokens.seq (.optTokens s) .unknown` to return
`.unknown`. This occurs e.g. when an optional (with first tokens
`.optTokens s`) is followed by a parser category (with first tokens
`.unknown`). Previously `FirstTokens.seq` returned `.optTokens s`,
ignoring the fact that the optional may be empty and then the parser
category may have any first token. The correct behavior here is to
return `.unknown`, which indicates that the first token may be anything.

Closes #13203
2026-04-01 13:21:26 +00:00
Joachim Breitner
402a6096b9 fix: add checkSystem calls to long-running elaboration paths (#13220)
This PR adds `checkSystem` calls to several code paths that can run for
extended periods without checking for cancellation, heartbeat limits, or
stack overflow. This improves responsiveness of the cancellation
mechanism
in the language server.

Affected paths:
- `simpLoop` step loop (`Simp/Main.lean`)
- `simp` rewrite candidate loops (`Rewrite.lean`)
- `simpAppUsingCongr` argument traversal (`Types.lean`)
- `synthesizeSyntheticMVarsStep` mvar loop (`SyntheticMVars.lean`)
- `abstractNestedProofs` visitor (`AbstractNestedProofs.lean`)
- `transform`/`transformWithCache` visitors (`Transform.lean`)
- LCNF compiler pass runner loop (`LCNF/Main.lean`)
- LCNF checker recursive traversal (`LCNF/Check.lean`)
- `whnfImp` top-level reduction (`WHNF.lean`)

Intentionally *not* instrumented (too hot, measurable regression):
- `whnfCore.go` inner recursion
- `simpImpl` entry point (redundant with `simpLoop`)
- LCNF `simp` inner recursion (0.4% regression on `big_do`)

Also adds a docstring to `checkInterrupted` clarifying its relationship
to
`checkSystem`.

Found using `LEAN_CHECK_SYSTEM_INTERVAL_MS` monitoring from #13218.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 12:54:57 +00:00
Lean stage0 autoupdater
978bde4a0f chore: update stage0 2026-04-01 12:56:25 +00:00
Garmelon
8aa0c21bf8 chore: begin development cycle for v4.31.0 (#13230) 2026-04-01 12:23:00 +00:00
Sebastian Ullrich
1aa860af33 fix: avoid heartbeat timeout in symbolFrequencyExt export (#13202)
This PR fixes a heartbeat timeout from an environment extension at the
end of the file that cannot be avoided by raising the limit.

Fixes #12989
2026-04-01 10:54:13 +00:00
Wojciech Różowski
cdd982a030 feat: add deprecated_syntax (#13108)
This PR adds a `deprecated_syntax` command that marks syntax kinds as
deprecated. When deprecated syntax is elaborated (in terms, tactics, or
commands), a linter warning is emitted. The warning is also emitted
during quotation precheck when a macro definition uses deprecated syntax
in its expansion.

The `deprecated_syntax` command takes a syntax node kind, an optional
message, and a `(since := "...")` clause. Deprecation warnings correctly
attribute the warning to macro call sites when the deprecated syntax is
produced by macro expansion, including through nested macro chains.

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2026-04-01 10:51:59 +00:00
Sebastian Ullrich
f11d137a30 feat: add unlock_limits command to disable all resource limits (#13211)
This PR adds an `unlock_limits` command that sets `maxHeartbeats`,
`maxRecDepth`, and `synthInstance.maxHeartbeats` to 0, disabling all
core resource limits. Also makes `maxRecDepth 0` mean "no limit"
(matching the existing behavior of `maxHeartbeats 0`).
2026-04-01 09:26:13 +00:00
Kim Morrison
fc0cf68539 fix: make -DLEAN_VERSION_* CMake overrides actually work (#13226)
This PR updates `release_checklist.py` to handle the `CACHE STRING ""`
suffix on CMake version variables. The `CACHE STRING` format was
introduced in the `releases/v4.30.0` branch, but the script's parsing
wasn't updated to match, causing false failures.

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-01 05:31:23 +00:00
Kim Morrison
46a0a0eb59 chore: add safety notes to release command (#13225)
This PR adds two safety notes to the Claude Code release command:
- Mathlib bump branches live on `mathlib4-nightly-testing`, not the main
`mathlib4` repo
- Never force-update remote refs without explicit user confirmation

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 05:01:00 +00:00
Joachim Breitner
916004bd3c fix: add checkSystem calls to hasAssignableMVar (#13219)
This PR moves `hasAssignableMVar`, `hasAssignableLevelMVar`, and
`isLevelMVarAssignable` from `MetavarContext.lean` to a new
`Lean.Meta.HasAssignableMVar` module, changing them from generic `[Monad
m] [MonadMCtx m]` functions to `MetaM` functions. This enables adding
`checkSystem` calls in the recursive traversal, which ensures
cancellation and heartbeat checks happen during what can be a very
expensive computation.

All callers of these functions were already in `MetaM`, so this change
is safe. The motivating case is the `4595_slowdown.lean` test, where
`hasAssignableMVar` (with `PersistentHashMap.find?` lookups on
`mctx.lDepth`) was the dominant CPU cost during elaboration of category
theory definitions. Without `checkSystem` calls, cancellation requests
could be delayed by over 2 seconds.

The test file `4595_slowdown.lean` gets a slightly increased
`maxHeartbeats` limit because `checkSystem` now detects heartbeat
exhaustion mid-traversal rather than after the function returns.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-31 19:27:15 +00:00
Nadja Yang
a145b9c11a chore: use FetchContent for mimalloc source acquisition (#13196)
FetchContent provides configure-time source acquisition with
`FETCHCONTENT_SOURCE_DIR_MIMALLOC` for sandboxed builds, replacing the
empty-command ExternalProject pattern.

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

---------

Co-authored-by: Joscha <joscha@plugh.de>
2026-03-31 18:00:39 +00:00
Garmelon
67b6e815b9 chore: strip binaries only in release builds (#13208)
This commit ensures binaries are only stripped in the `release` build
preset, not in any of the other presets.

Since `release` is used for development, the commit adds a non-stripping
copy called `dev` that can be used via `cmake --preset dev`.
2026-03-31 17:18:43 +00:00
Kim Morrison
33c3604b87 fix: use correct shared library directory for Lake plugin on Windows (#13128)
This PR fixes the Windows dev build by using
`CMAKE_RELATIVE_LIBRARY_OUTPUT_DIRECTORY` instead of the hardcoded
`lib/lean` path for the Lake plugin. On Windows, DLLs must be placed
next to executables in `bin/`, but the plugin path was hardcoded to
`lib/lean`, causing stage0 DLLs to not be found.

The `CMAKE_RELATIVE_LIBRARY_OUTPUT_DIRECTORY` variable was introduced
precisely for this purpose — it resolves to `bin` on Windows and
`lib/lean` elsewhere.

Closes #13126

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2026-03-31 16:33:58 +00:00
Sebastian Graf
504e099c5d test: lazy let-binding unfolding in sym mvcgen (#13210)
This PR replaces eager let-expression zeta-reduction in the sym-based
mvcgen with on-demand unfolding that mirrors the production mvcgen's
behavior.

Previously, all let-expressions in the program head were immediately
zeta-reduced. Now, let-expressions are hoisted to the top of the goal
target, and the value is only inlined if it is duplicable (literals,
fvars, consts, `OfNat.ofNat`). Complex values are introduced into the
local context via `introsSimp`, preserving SymM's maximal sharing
invariants, and unfolded on demand when the fvar later appears as the
program head.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-31 15:29:11 +00:00
Sofia Rodrigues
17795b02ee fix: missing borrow annotations in Std.Internal.UV.System (#13172)
This PR adds borrow annotations in `Std.Internal.UV.System`.
2026-03-31 15:10:23 +00:00
209 changed files with 1359 additions and 342 deletions

View File

@@ -157,6 +157,16 @@ Note: `gh pr checks --watch` exits as soon as ALL checks complete (pass or fail)
fail while others are still running, `--watch` will continue until everything settles, then exit
with a non-zero code. So a background `--watch` finishing = all checks done; check which failed.
## Mathlib Bump Branches
Mathlib `bump/v4.X.0` branches live on the **fork** `leanprover-community/mathlib4-nightly-testing`,
NOT on `leanprover-community/mathlib4`.
## Never Force-Update Remote Refs Without Confirmation
Never force-update an existing remote branch or tag via `git push --force` or the GitHub API
without explicit user confirmation.
## Error Handling
**CRITICAL**: If something goes wrong or a command fails:

View File

@@ -143,7 +143,7 @@ jobs:
CMAKE_MAJOR=$(grep -E "^set\(LEAN_VERSION_MAJOR " src/CMakeLists.txt | grep -oE '[0-9]+')
CMAKE_MINOR=$(grep -E "^set\(LEAN_VERSION_MINOR " src/CMakeLists.txt | grep -oE '[0-9]+')
CMAKE_PATCH=$(grep -E "^set\(LEAN_VERSION_PATCH " src/CMakeLists.txt | grep -oE '[0-9]+')
CMAKE_IS_RELEASE=$(grep -m 1 -E "^set\(LEAN_VERSION_IS_RELEASE " src/CMakeLists.txt | sed -nE 's/^set\(LEAN_VERSION_IS_RELEASE ([0-9]+)\).*/\1/p')
CMAKE_IS_RELEASE=$(grep -m 1 -E "^set\(LEAN_VERSION_IS_RELEASE " src/CMakeLists.txt | grep -oE '[0-9]+' | head -1)
# Expected values from tag parsing
TAG_MAJOR="${{ steps.set-release.outputs.LEAN_VERSION_MAJOR }}"

View File

@@ -6,6 +6,6 @@ vscode:
- leanprover.lean4
tasks:
- name: Release build
init: cmake --preset release
- name: Build
init: cmake --preset dev
command: make -C build/release -j$(nproc || sysctl -n hw.logicalcpu)

View File

@@ -1,4 +1,6 @@
cmake_minimum_required(VERSION 3.21)
include(ExternalProject)
include(FetchContent)
if(NOT CMAKE_GENERATOR MATCHES "Makefiles")
message(FATAL_ERROR "Only makefile generators are supported")
@@ -34,7 +36,6 @@ foreach(var ${vars})
endif()
endforeach()
include(ExternalProject)
project(LEAN CXX C)
if(NOT (DEFINED STAGE0_CMAKE_EXECUTABLE_SUFFIX))
@@ -119,17 +120,16 @@ if(NOT CMAKE_SYSTEM_NAME MATCHES "Emscripten")
endif()
if(USE_MIMALLOC)
ExternalProject_Add(
FetchContent_Declare(
mimalloc
PREFIX mimalloc
GIT_REPOSITORY https://github.com/microsoft/mimalloc
GIT_TAG v2.2.3
# just download, we compile it as part of each stage as it is small
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
# Unnecessarily deep directory structure, but it saves us from a complicated
# stage0 update for now. If we ever update the other dependencies like
# cadical, it might be worth reorganizing the directory structure.
SOURCE_DIR "${CMAKE_BINARY_DIR}/mimalloc/src/mimalloc"
)
list(APPEND EXTRA_DEPENDS mimalloc)
FetchContent_MakeAvailable(mimalloc)
endif()
if(NOT STAGE1_PREV_STAGE)

View File

@@ -8,16 +8,26 @@
"configurePresets": [
{
"name": "release",
"displayName": "Default development optimized build config",
"displayName": "Release build config",
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/release"
},
{
"name": "dev",
"displayName": "Default development optimized build config",
"cacheVariables": {
"STRIP_BINARIES": "OFF"
},
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/dev"
},
{
"name": "debug",
"displayName": "Debug build config",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"LEAN_EXTRA_CXX_FLAGS": "-DLEAN_DEFAULT_THREAD_STACK_SIZE=16*1024*1024",
"CMAKE_BUILD_TYPE": "Debug"
"STRIP_BINARIES": "OFF"
},
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/debug"
@@ -26,7 +36,8 @@
"name": "reldebug",
"displayName": "Release with assertions enabled",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithAssert"
"CMAKE_BUILD_TYPE": "RelWithAssert",
"STRIP_BINARIES": "OFF"
},
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/reldebug"
@@ -38,6 +49,7 @@
"LEAN_EXTRA_CXX_FLAGS": "-fsanitize=address,undefined -DLEAN_DEFAULT_THREAD_STACK_SIZE=16*1024*1024",
"LEANC_EXTRA_CC_FLAGS": "-fsanitize=address,undefined",
"LEAN_EXTRA_LINKER_FLAGS": "-fsanitize=address,undefined -fsanitize-link-c++-runtime",
"STRIP_BINARIES": "OFF",
"SMALL_ALLOCATOR": "OFF",
"USE_MIMALLOC": "OFF",
"BSYMBOLIC": "OFF",
@@ -58,6 +70,10 @@
"name": "release",
"configurePreset": "release"
},
{
"name": "dev",
"configurePreset": "dev"
},
{
"name": "debug",
"configurePreset": "debug"
@@ -81,6 +97,11 @@
"configurePreset": "release",
"output": {"outputOnFailure": true, "shortProgress": true}
},
{
"name": "dev",
"configurePreset": "dev",
"output": {"outputOnFailure": true, "shortProgress": true}
},
{
"name": "debug",
"configurePreset": "debug",

View File

@@ -30,6 +30,9 @@ cd lean4
cmake --preset release
make -C build/release -j$(nproc || sysctl -n hw.logicalcpu)
```
For development, `cmake --preset dev` is recommended instead.
You can replace `$(nproc || sysctl -n hw.logicalcpu)` with the desired parallelism amount.
The above commands will compile the Lean library and binaries into the

View File

@@ -311,16 +311,16 @@ def check_cmake_version(repo_url, branch, version_major, version_minor, github_t
print(f" ❌ Could not retrieve {cmake_file_path} from {branch}")
return False
expected_lines = [
f"set(LEAN_VERSION_MAJOR {version_major})",
f"set(LEAN_VERSION_MINOR {version_minor})",
f"set(LEAN_VERSION_PATCH 0)",
f"set(LEAN_VERSION_IS_RELEASE 1)"
expected_patterns = [
(f"LEAN_VERSION_MAJOR", rf"^set\(LEAN_VERSION_MAJOR\s+{version_major}[\s)]", f"set(LEAN_VERSION_MAJOR {version_major} ...)"),
(f"LEAN_VERSION_MINOR", rf"^set\(LEAN_VERSION_MINOR\s+{version_minor}[\s)]", f"set(LEAN_VERSION_MINOR {version_minor} ...)"),
(f"LEAN_VERSION_PATCH", rf"^set\(LEAN_VERSION_PATCH\s+0[\s)]", f"set(LEAN_VERSION_PATCH 0 ...)"),
(f"LEAN_VERSION_IS_RELEASE", rf"^set\(LEAN_VERSION_IS_RELEASE\s+1[\s)]", f"set(LEAN_VERSION_IS_RELEASE 1 ...)"),
]
for line in expected_lines:
if not any(l.strip().startswith(line) for l in content.splitlines()):
print(f" ❌ Missing or incorrect line in {cmake_file_path}: {line}")
for name, pattern, display in expected_patterns:
if not any(re.match(pattern, l.strip()) for l in content.splitlines()):
print(f" ❌ Missing or incorrect line in {cmake_file_path}: {display}")
return False
print(f" ✅ CMake version settings are correct in {cmake_file_path}")
@@ -343,11 +343,11 @@ def check_stage0_version(repo_url, branch, version_major, version_minor, github_
for line in content.splitlines():
stripped = line.strip()
if stripped.startswith("set(LEAN_VERSION_MAJOR "):
actual = stripped.split()[-1].rstrip(")")
actual = stripped.split()[1].rstrip(")")
if actual != str(version_major):
errors.append(f"LEAN_VERSION_MAJOR: expected {version_major}, found {actual}")
elif stripped.startswith("set(LEAN_VERSION_MINOR "):
actual = stripped.split()[-1].rstrip(")")
actual = stripped.split()[1].rstrip(")")
if actual != str(version_minor):
errors.append(f"LEAN_VERSION_MINOR: expected {version_minor}, found {actual}")

View File

@@ -8,7 +8,7 @@ endif()
include(ExternalProject)
project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4 CACHE STRING "")
set(LEAN_VERSION_MINOR 30 CACHE STRING "")
set(LEAN_VERSION_MINOR 31 CACHE STRING "")
set(LEAN_VERSION_PATCH 0 CACHE STRING "")
set(LEAN_VERSION_IS_RELEASE 0 CACHE STRING "") # This number is 1 in the release revision, and 0 otherwise.
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
@@ -80,6 +80,7 @@ option(CCACHE "use ccache" ON)
option(SPLIT_STACK "SPLIT_STACK" OFF)
# When OFF we disable LLVM support
option(LLVM "LLVM" OFF)
option(STRIP_BINARIES "Strip produced binaries" ON)
# When ON we include githash in the version string
option(USE_GITHASH "GIT_HASH" ON)

View File

@@ -20,12 +20,20 @@ universe u
namespace ByteArray
deriving instance BEq for ByteArray
@[extern "lean_sarray_dec_eq"]
def beq (lhs rhs : @& ByteArray) : Bool :=
lhs.data == rhs.data
instance : BEq ByteArray where
beq := beq
attribute [ext] ByteArray
instance : DecidableEq ByteArray :=
fun _ _ => decidable_of_decidable_of_iff ByteArray.ext_iff.symm
@[extern "lean_sarray_dec_eq"]
def decEq (lhs rhs : @& ByteArray) : Decidable (lhs = rhs) :=
decidable_of_decidable_of_iff ByteArray.ext_iff.symm
instance : DecidableEq ByteArray := decEq
instance : Inhabited ByteArray where
default := empty

View File

@@ -232,6 +232,7 @@ partial def checkCases (c : Cases .pure) : CheckM Unit := do
withParams params do check k
partial def check (code : Code .pure) : CheckM Unit := do
checkSystem "LCNF check"
match code with
| .let decl k => checkLetDecl decl; withFVarId decl.fvarId do check k
| .fun decl k =>

View File

@@ -188,6 +188,7 @@ where
profileitM Exception profilerName ( getOptions) do
let mut state : (pu : Purity) × Array (Decl pu) := inPhase, decls
for pass in passes do
checkSystem "LCNF compiler"
state withTraceNode `Compiler (fun _ => return m!"compiler phase: {pass.phase}, pass: {pass.name}") do
let decls withPhase pass.phase do
state.fst.withAssertPurity pass.phase.toPurity fun h => do

View File

@@ -146,7 +146,7 @@ Similar to the default `Lean.withIncRecDepth`, but include the `inlineStack` in
@[inline] def withIncRecDepth (x : SimpM α) : SimpM α := do
let curr MonadRecDepth.getRecDepth
let max MonadRecDepth.getMaxRecDepth
if curr == max then
if max != 0 && curr == max then
throwMaxRecDepth
else
MonadRecDepth.withRecDepth (curr+1) x

View File

@@ -279,13 +279,13 @@ partial def casesFloatArrayToMono (c : Cases .pure) (_ : c.typeName == ``FloatAr
let k k.toMono
return .let decl k
/-- Eliminate `cases` for `String. -/
/-- Eliminate `cases` for `String`. -/
partial def casesStringToMono (c : Cases .pure) (_ : c.typeName == ``String) : ToMonoM (Code .pure) := do
assert! c.alts.size == 1
let .alt _ ps k := c.alts[0]! | unreachable!
eraseParams ps
let p := ps[0]!
let decl := { fvarId := p.fvarId, binderName := p.binderName, type := anyExpr, value := .const ``String.toList [] #[.fvar c.discr] }
let decl := { fvarId := p.fvarId, binderName := p.binderName, type := anyExpr, value := .const ``String.toByteArray [] #[.fvar c.discr] }
modifyLCtx fun lctx => lctx.addLetDecl decl
let k k.toMono
return .let decl k

View File

@@ -453,6 +453,9 @@ Throws an internal interrupt exception if cancellation has been requested. The e
caught by `try catch` but is intended to be caught by `Command.withLoggingExceptions` at the top
level of elaboration. In particular, we want to skip producing further incremental snapshots after
the exception has been thrown.
Like `checkSystem` but without the global heartbeat check, for callers that have their own
heartbeat tracking (e.g. `SynthInstance`).
-/
@[inline] def checkInterrupted : CoreM Unit := do
if let some tk := ( read).cancelTk? then

View File

@@ -0,0 +1,45 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Wojciech Różowski
-/
module
prelude
public import Lean.Compiler.ModPkgExt
public section
namespace Lean
structure DeprecatedModuleEntry where
message? : Option String := none
since? : Option String := none
deriving Inhabited
register_builtin_option linter.deprecated.module : Bool := {
defValue := true
descr := "if true, generate warnings when importing deprecated modules"
}
builtin_initialize deprecatedModuleExt : ModuleEnvExtension <| Option DeprecatedModuleEntry
registerModuleEnvExtension <| pure none
def Environment.getDeprecatedModuleByIdx? (env : Environment) (idx : ModuleIdx) : Option DeprecatedModuleEntry :=
deprecatedModuleExt.getStateByIdx? env idx |>.join
def Environment.setDeprecatedModule (entry : Option DeprecatedModuleEntry) (env : Environment) : Environment :=
deprecatedModuleExt.setState env entry
def formatDeprecatedModuleWarning (env : Environment) (idx : ModuleIdx) (modName : Name)
(entry : DeprecatedModuleEntry) : String :=
let msg := entry.message?.getD ""
let replacements := env.header.moduleData[idx.toNat]!.imports.filter fun imp =>
imp.module != `Init
let lines := replacements.foldl (init := "") fun acc imp =>
acc ++ s!"import {imp.module}\n"
s!"{msg}\n\
'{modName}' has been deprecated: please replace this import by\n\n\
{lines}"
end Lean

View File

@@ -1832,13 +1832,15 @@ To infer a namespace from the expected type, we do the following operations:
- if the type is of the form `c x₁ ... xₙ` with `c` a constant, then try using `c` as the namespace,
and if that doesn't work, try unfolding the expression and continuing.
-/
private partial def resolveDottedIdentFn (idRef : Syntax) (id : Name) (expectedType? : Option Expr) : TermElabM (List (Expr × Syntax × List Syntax)) := do
private partial def resolveDottedIdentFn (idRef : Syntax) (id : Name) (explicitUnivs : List Level) (expectedType? : Option Expr) : TermElabM (List (Expr × Syntax × List Syntax)) := do
unless id.isAtomic do
throwError "Invalid dotted identifier notation: The name `{id}` must be atomic"
tryPostponeIfNoneOrMVar expectedType?
let some expectedType := expectedType?
| throwNoExpectedType
addCompletionInfo <| CompletionInfo.dotId idRef id ( getLCtx) expectedType?
-- We will check deprecations in `elabAppFnResolutions`.
withoutCheckDeprecated do
withForallBody expectedType fun resultType => do
go resultType expectedType #[]
where
@@ -1878,8 +1880,10 @@ where
|>.filter (fun (_, fieldList) => fieldList.isEmpty)
|>.map Prod.fst
if !candidates.isEmpty then
candidates.mapM fun resolvedName => return ( mkConst resolvedName, getRef, [])
candidates.mapM fun resolvedName => return ( mkConst resolvedName explicitUnivs, getRef, [])
else if let some (fvar, []) resolveLocalName fullName then
unless explicitUnivs.isEmpty do
throwInvalidExplicitUniversesForLocal fvar
return [(fvar, getRef, [])]
else
throwUnknownIdentifierAt ( getRef) (declHint := fullName) <| m!"Unknown constant `{.ofConstName fullName}`"
@@ -1919,6 +1923,10 @@ private partial def elabAppFn (f : Syntax) (lvals : List LVal) (namedArgs : Arra
let some idx := idxStx.isFieldIdx?
| throwError "Internal error: Unexpected field index syntax `{idxStx}`"
elabAppFn e (LVal.fieldIdx idxStx idx :: lvals) namedArgs args expectedType? explicit ellipsis overloaded acc
let elabDottedIdent (id : Syntax) (explicitUnivs : List Level) (explicit : Bool) : TermElabM (Array (TermElabResult Expr)) := do
let res withRef f <| resolveDottedIdentFn id id.getId.eraseMacroScopes explicitUnivs expectedType?
-- Use (forceTermInfo := true) because we want to record the result of .ident resolution even in patterns
elabAppFnResolutions f res lvals namedArgs args expectedType? explicit ellipsis overloaded acc (forceTermInfo := true)
match f with
| `($(e).$idx:fieldIdx) => elabFieldIdx e idx explicit
| `($e |>.$idx:fieldIdx) => elabFieldIdx e idx explicit
@@ -1934,16 +1942,17 @@ private partial def elabAppFn (f : Syntax) (lvals : List LVal) (namedArgs : Arra
| `($id:ident.{$us,*}) => do
let us elabExplicitUnivs us
elabAppFnId id us lvals namedArgs args expectedType? explicit ellipsis overloaded acc
| `(@$id:ident) =>
elabAppFn id lvals namedArgs args expectedType? (explicit := true) ellipsis overloaded acc
| `(@$_:ident.{$_us,*}) =>
| `(.$id:ident) => elabDottedIdent id [] explicit
| `(.$id:ident.{$us,*}) =>
let us elabExplicitUnivs us
elabDottedIdent id us explicit
| `(@$_:ident)
| `(@$_:ident.{$_us,*})
| `(@.$_:ident)
| `(@.$_:ident.{$_us,*}) =>
elabAppFn (f.getArg 1) lvals namedArgs args expectedType? (explicit := true) ellipsis overloaded acc
| `(@$_) => throwUnsupportedSyntax -- invalid occurrence of `@`
| `(_) => throwError "A placeholder `_` cannot be used where a function is expected"
| `(.$id:ident) =>
let res withRef f <| resolveDottedIdentFn id id.getId.eraseMacroScopes expectedType?
-- Use (forceTermInfo := true) because we want to record the result of .ident resolution even in patterns
elabAppFnResolutions f res lvals namedArgs args expectedType? explicit ellipsis overloaded acc (forceTermInfo := true)
| _ => do
let catchPostpone := !overloaded
/- If we are processing a choice node, then we should use `catchPostpone == false` when elaborating terms.
@@ -2086,13 +2095,15 @@ private def elabAtom : TermElab := fun stx expectedType? => do
@[builtin_term_elab explicit] def elabExplicit : TermElab := fun stx expectedType? =>
match stx with
| `(@$_:ident) => elabAtom stx expectedType? -- Recall that `elabApp` also has support for `@`
| `(@$_:ident.{$_us,*}) => elabAtom stx expectedType?
| `(@$(_).$_:fieldIdx) => elabAtom stx expectedType?
| `(@$(_).$_:ident) => elabAtom stx expectedType?
| `(@($t)) => elabTerm t expectedType? (implicitLambda := false) -- `@` is being used just to disable implicit lambdas
| `(@$t) => elabTerm t expectedType? (implicitLambda := false) -- `@` is being used just to disable implicit lambdas
| _ => throwUnsupportedSyntax
| `(@$_:ident) => elabAtom stx expectedType? -- Recall that `elabApp` also has support for `@`
| `(@$_:ident.{$_us,*}) => elabAtom stx expectedType?
| `(@$(_).$_:fieldIdx) => elabAtom stx expectedType?
| `(@$(_).$_:ident) => elabAtom stx expectedType?
| `(@.$_:ident) => elabAtom stx expectedType?
| `(@.$_:ident.{$_us,*}) => elabAtom stx expectedType?
| `(@($t)) => elabTerm t expectedType? (implicitLambda := false) -- `@` is being used just to disable implicit lambdas
| `(@$t) => elabTerm t expectedType? (implicitLambda := false) -- `@` is being used just to disable implicit lambdas
| _ => throwUnsupportedSyntax
@[builtin_term_elab choice] def elabChoice : TermElab := elabAtom
@[builtin_term_elab proj] def elabProj : TermElab := elabAtom

View File

@@ -9,10 +9,12 @@ prelude
public import Lean.Meta.Reduce
public import Lean.Elab.Eval
public import Lean.Elab.Command
import Lean.Elab.DeprecatedSyntax
public import Lean.Elab.Open
import Init.Data.Nat.Order
import Init.Data.Order.Lemmas
import Init.System.Platform
import Lean.DeprecatedModule
public section
@@ -512,6 +514,15 @@ def failIfSucceeds (x : CommandElabM Unit) : CommandElabM Unit := do
modify fun s => { s with maxRecDepth := maxRecDepth.get options }
modifyScope fun scope => { scope with opts := options }
@[builtin_command_elab «unlock_limits»] def elabUnlockLimits : CommandElab := fun _ => do
let opts getOptions
let opts := maxHeartbeats.set opts 0
let opts := maxRecDepth.set opts 0
let opts := synthInstance.maxHeartbeats.set opts 0
modifyScope ({ · with opts })
-- update cached value as well
modify ({ · with maxRecDepth := 0 })
open Lean.Parser.Command.InternalSyntax in
@[builtin_macro Lean.Parser.Command.«in»] def expandInCmd : Macro
| `($cmd₁ in%$tk $cmd₂) =>
@@ -706,4 +717,54 @@ where
let env getEnv
IO.eprintln ( env.dbgFormatAsyncState)
/-- Elaborate `deprecated_module`, marking the current module as deprecated. -/
@[builtin_command_elab Parser.Command.deprecated_module]
def elabDeprecatedModule : CommandElab
| `(Parser.Command.deprecated_module| deprecated_module $[$msg?]? $[(since := $since?)]?) => do
let message? := msg?.map TSyntax.getString
let since? := since?.map TSyntax.getString
if (deprecatedModuleExt.getState ( getEnv)).isSome then
logWarning "module is already marked as deprecated"
if since?.isNone then
logWarning "`deprecated_module` should specify the date or library version \
at which the deprecation was introduced, using `(since := \"...\")`"
modifyEnv fun env => env.setDeprecatedModule (some { message?, since? })
| _ => throwUnsupportedSyntax
/-- Elaborate `#show_deprecated_modules`, displaying all deprecated modules. -/
@[builtin_command_elab Parser.Command.showDeprecatedModules]
def elabShowDeprecatedModules : CommandElab := fun _ => do
let env getEnv
let mut parts : Array String := #["Deprecated modules\n"]
for h : idx in [:env.header.moduleNames.size] do
if let some entry := env.getDeprecatedModuleByIdx? idx then
let modName := env.header.moduleNames[idx]
let msg := match entry.message? with
| some str => s!"message '{str}'"
| none => "no message"
let replacements := env.header.moduleData[idx]!.imports.filter fun imp =>
imp.module != `Init
parts := parts.push s!"'{modName}' deprecates to\n{replacements.map (·.module)}\nwith {msg}\n"
-- Also show the current module's deprecation if set.
if let some entry := deprecatedModuleExt.getState env then
let modName := env.mainModule
let msg := match entry.message? with
| some str => s!"message '{str}'"
| none => "no message"
let replacements := env.imports.filter fun imp =>
imp.module != `Init
parts := parts.push s!"'{modName}' deprecates to\n{replacements.map (·.module)}\nwith {msg}\n"
logInfo (String.intercalate "\n" parts.toList)
@[builtin_command_elab Parser.Command.deprecatedSyntax] def elabDeprecatedSyntax : CommandElab := fun stx => do
let id := stx[1]
let kind liftCoreM <| checkSyntaxNodeKindAtNamespaces id.getId ( getCurrNamespace)
let text? := if stx[2].isNone then none else stx[2][0].isStrLit?
let since? := if stx[3].isNone then none else stx[3][3].isStrLit?
if since?.isNone then
logWarning "`deprecated_syntax` should specify the date or library version at which the \
deprecation was introduced, using `(since := \"...\")`"
modifyEnv fun env =>
deprecatedSyntaxExt.addEntry env { kind, text?, since? }
end Lean.Elab.Command

View File

@@ -168,12 +168,25 @@ def elabDoArrow (letOrReassign : LetOrReassign) (stx : TSyntax [``doIdDecl, ``do
elabDoElem ( `(doElem| $pattern:term := $x)) dec
| _ => throwUnsupportedSyntax
/-- Backward-compatible index for `doLet`/`doLetArrow`/`doLetElse`: if `letConfig` is present at
index 2, the decl is at index 3; otherwise (old-shape syntax from stage0 macros), it's at index 2. -/
private def doLetDeclIdx (stx : Syntax) : Nat :=
if stx[2].isOfKind ``Parser.Term.letConfig then 3 else 2
/-- Backward-compatible index for `doHave`: if `letConfig` is present at
index 1, the decl is at index 2; otherwise (old-shape syntax), it's at index 1. -/
private def doHaveDeclIdx (stx : Syntax) : Nat :=
if stx[1].isOfKind ``Parser.Term.letConfig then 2 else 1
@[builtin_doElem_elab Lean.Parser.Term.doLet] def elabDoLet : DoElab := fun stx dec => do
let `(doLet| let $[mut%$mutTk?]? $decl:letDecl) := stx | throwUnsupportedSyntax
-- "let " >> optional "mut " >> letConfig >> letDecl
let mutTk? := stx.raw[1].getOptional?
let decl : TSyntax ``letDecl := stx.raw[doLetDeclIdx stx.raw]
elabDoLetOrReassign (.let mutTk?) decl dec
@[builtin_doElem_elab Lean.Parser.Term.doHave] def elabDoHave : DoElab := fun stx dec => do
let `(doHave| have $decl:letDecl) := stx | throwUnsupportedSyntax
-- "have" >> letConfig >> letDecl
let decl : TSyntax ``letDecl := stx.raw[doHaveDeclIdx stx.raw]
elabDoLetOrReassign .have decl dec
@[builtin_doElem_elab Lean.Parser.Term.doLetRec] def elabDoLetRec : DoElab := fun stx dec => do
@@ -199,7 +212,14 @@ def elabDoArrow (letOrReassign : LetOrReassign) (stx : TSyntax [``doIdDecl, ``do
| _ => throwUnsupportedSyntax
@[builtin_doElem_elab Lean.Parser.Term.doLetElse] def elabDoLetElse : DoElab := fun stx dec => do
let `(doLetElse| let $[mut%$mutTk?]? $pattern := $rhs | $otherwise $(body?)?) := stx | throwUnsupportedSyntax
-- "let " >> optional "mut " >> letConfig >> termParser >> " := " >> termParser >>
-- (checkColGe >> " | " >> doSeqIndent) >> optional (checkColGe >> doSeqIndent)
let mutTk? := stx.raw[1].getOptional?
let offset := doLetDeclIdx stx.raw -- 3 if letConfig present, 2 otherwise
let pattern : Term := stx.raw[offset]
let rhs : Term := stx.raw[offset + 2]
let otherwise : TSyntax ``doSeqIndent := stx.raw[offset + 4]
let body? := stx.raw[offset + 5].getOptional?.map fun s => (s : TSyntax ``doSeqIndent)
let letOrReassign := LetOrReassign.let mutTk?
let vars getPatternVarsEx pattern
letOrReassign.checkMutVars vars
@@ -211,7 +231,9 @@ def elabDoArrow (letOrReassign : LetOrReassign) (stx : TSyntax [``doIdDecl, ``do
elabDoElem ( `(doElem| match $rhs:term with | $pattern => $body:doSeqIndent | _ => $otherwise:doSeqIndent)) dec
@[builtin_doElem_elab Lean.Parser.Term.doLetArrow] def elabDoLetArrow : DoElab := fun stx dec => do
let `(doLetArrow| let $[mut%$mutTk?]? $decl) := stx | throwUnsupportedSyntax
-- "let " >> optional "mut " >> letConfig >> (doIdDecl <|> doPatDecl)
let mutTk? := stx.raw[1].getOptional?
let decl : TSyntax [``doIdDecl, ``doPatDecl] := stx.raw[doLetDeclIdx stx.raw]
elabDoArrow (.let mutTk?) decl dec
@[builtin_doElem_elab Lean.Parser.Term.doReassignArrow] def elabDoReassignArrow : DoElab := fun stx dec => do

View File

@@ -10,6 +10,7 @@ public import Lean.Meta.Diagnostics
public import Lean.Elab.Binders
public import Lean.Elab.Command.Scope
public import Lean.Elab.SetOption
import Lean.Elab.DeprecatedSyntax
public meta import Lean.Parser.Command
public section
@@ -468,6 +469,7 @@ where go := do
else withTraceNode `Elab.command (fun _ => return stx) (tag :=
-- special case: show actual declaration kind for `declaration` commands
(if stx.isOfKind ``Parser.Command.declaration then stx[1] else stx).getKind.toString) do
checkDeprecatedSyntax stx ( read).macroStack
let s get
match ( liftMacroM <| expandMacroImpl? s.env stx) with
| some (decl, stxNew?) =>

View File

@@ -0,0 +1,71 @@
/-
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Wojciech Różowski
-/
module
prelude
public import Lean.MonadEnv
public import Lean.Linter.Basic
public import Lean.Elab.Util
public section
namespace Lean.Linter
register_builtin_option linter.deprecated.syntax : Bool := {
defValue := true
descr := "if true, generate warnings when deprecated syntax is used"
}
end Lean.Linter
namespace Lean.Elab
/-- Entry recording that a syntax kind has been deprecated. -/
structure SyntaxDeprecationEntry where
/-- The syntax node kind that is deprecated. -/
kind : SyntaxNodeKind
/-- Optional deprecation message. -/
text? : Option String := none
/-- Optional version or date at which the syntax was deprecated. -/
since? : Option String := none
builtin_initialize deprecatedSyntaxExt :
SimplePersistentEnvExtension SyntaxDeprecationEntry (NameMap SyntaxDeprecationEntry)
registerSimplePersistentEnvExtension {
addImportedFn := mkStateFromImportedEntries (fun m e => m.insert e.kind e) {}
addEntryFn := fun m e => m.insert e.kind e
}
/--
Check whether `stx` is a deprecated syntax kind, and if so, emit a warning.
If `macroStack` is non-empty, the warning is attributed to the macro call site rather than the
syntax itself.
-/
def checkDeprecatedSyntax [Monad m] [MonadEnv m] [MonadLog m] [MonadOptions m]
[AddMessageContext m] [MonadRef m] (stx : Syntax) (macroStack : MacroStack) : m Unit := do
let env getEnv
let kind := stx.getKind
if let some entry := (deprecatedSyntaxExt.getState env).find? kind then
let extraMsg := match entry.text? with
| some text => m!": {text}"
| none => m!""
match macroStack with
| { before := macroStx, .. } :: { before := callerStx, .. } :: _ =>
let expandedFrom :=
if callerStx.getKind != macroStx.getKind then
m!" (expanded from '{callerStx.getKind}')"
else m!""
Linter.logLintIf Linter.linter.deprecated.syntax macroStx
m!"macro '{macroStx.getKind}'{expandedFrom} produces deprecated syntax '{kind}'{extraMsg}"
| { before := macroStx, .. } :: [] =>
Linter.logLintIf Linter.linter.deprecated.syntax macroStx
m!"macro '{macroStx.getKind}' produces deprecated syntax '{kind}'{extraMsg}"
| [] =>
Linter.logLintIf Linter.linter.deprecated.syntax stx
m!"syntax '{kind}' has been deprecated{extraMsg}"
end Lean.Elab

View File

@@ -169,15 +169,29 @@ partial def ofElem (stx : TSyntax `doElem) : TermElabM ControlInfo := do
let bodyInfo match body? with | none => pure {} | some body => ofSeq body
return otherwiseInfo.alternative bodyInfo
| _ =>
let handlers := controlInfoElemAttribute.getEntries ( getEnv) stx.raw.getKind
-- Backward compat: quotation patterns above may fail for doLet/doHave/doLetElse/doLetArrow
-- when the parser shape includes letConfig (not present in stage0 quotations).
let kind := stx.raw.getKind
if kind == ``Parser.Term.doLet || kind == ``Parser.Term.doHave || kind == ``Parser.Term.doLetRec then
return .pure
if kind == ``Parser.Term.doLetElse then
let offset := if stx.raw[2].isOfKind ``Parser.Term.letConfig then 3 else 2
let otherwise? := stx.raw[offset + 4].getOptional?.map (· : _ TSyntax ``doSeqIndent)
let body? := stx.raw[offset + 5].getOptional?.map (· : _ TSyntax ``doSeqIndent)
return ofLetOrReassign #[] none otherwise? body?
if kind == ``Parser.Term.doLetArrow then
let declIdx := if stx.raw[2].isOfKind ``Parser.Term.letConfig then 3 else 2
let decl : TSyntax [``doIdDecl, ``doPatDecl] := stx.raw[declIdx]
return ofLetOrReassignArrow false decl
let handlers := controlInfoElemAttribute.getEntries ( getEnv) kind
for handler in handlers do
let res catchInternalId unsupportedSyntaxExceptionId
(some <$> handler.value stx)
(fun _ => pure none)
if let some info := res then return info
throwError
"No `ControlInfo` inference handler found for `{stx.raw.getKind}` in syntax {indentD stx}\n\
Register a handler with `@[doElem_control_info {stx.raw.getKind}]`."
"No `ControlInfo` inference handler found for `{kind}` in syntax {indentD stx}\n\
Register a handler with `@[doElem_control_info {kind}]`."
partial def ofLetOrReassignArrow (reassignment : Bool) (decl : TSyntax [``doIdDecl, ``doPatDecl]) : TermElabM ControlInfo := do
match decl with

View File

@@ -36,6 +36,18 @@ private def getDoSeq (doStx : Syntax) : Syntax :=
def elabLiftMethod : TermElab := fun stx _ =>
throwErrorAt stx "invalid use of `(<- ...)`, must be nested inside a 'do' expression"
/-- Backward-compatible index for `doLet`/`doLetArrow`/`doLetElse`: if `letConfig` is present at
index 2, the decl is at index 3; otherwise (old-shape syntax from stage0 quotations), it's at
index 2. -/
private def doLetDeclIdx (stx : Syntax) : Nat :=
if stx[2].isOfKind ``Parser.Term.letConfig then 3 else 2
/-- Backward-compatible index for `doHave`: if `letConfig` is present at
index 1, the decl is at index 2; otherwise (old-shape syntax from stage0 quotations), it's at
index 1. -/
private def doHaveDeclIdx (stx : Syntax) : Nat :=
if stx[1].isOfKind ``Parser.Term.letConfig then 2 else 1
/-- Return true if we should not lift `(<- ...)` actions nested in the syntax nodes with the given kind. -/
private def liftMethodDelimiter (k : SyntaxNodeKind) : Bool :=
k == ``Parser.Term.do ||
@@ -76,9 +88,9 @@ private def liftMethodForbiddenBinder (stx : Syntax) : Bool :=
else if k == ``Parser.Term.let then
letDeclHasBinders stx[1]
else if k == ``Parser.Term.doLet then
letDeclHasBinders stx[2]
letDeclHasBinders stx[doLetDeclIdx stx]
else if k == ``Parser.Term.doLetArrow then
letDeclArgHasBinders stx[2]
letDeclArgHasBinders stx[doLetDeclIdx stx]
else
false
@@ -701,12 +713,12 @@ def getLetDeclVars (letDecl : Syntax) : TermElabM (Array Var) := do
throwError "unexpected kind of let declaration"
def getDoLetVars (doLet : Syntax) : TermElabM (Array Var) :=
-- leading_parser "let " >> optional "mut " >> letDecl
getLetDeclVars doLet[2]
-- leading_parser "let " >> optional "mut " >> letConfig >> letDecl
getLetDeclVars doLet[doLetDeclIdx doLet]
def getDoHaveVars (doHave : Syntax) : TermElabM (Array Var) :=
-- leading_parser "have" >> letDecl
getLetDeclVars doHave[1]
-- leading_parser "have" >> letConfig >> letDecl
getLetDeclVars doHave[doHaveDeclIdx doHave]
def getDoLetRecVars (doLetRec : Syntax) : TermElabM (Array Var) := do
-- letRecDecls is an array of `(group (optional attributes >> letDecl))`
@@ -727,9 +739,9 @@ def getDoPatDeclVars (doPatDecl : Syntax) : TermElabM (Array Var) := do
let pattern := doPatDecl[0]
getPatternVarsEx pattern
-- leading_parser "let " >> optional "mut " >> (doIdDecl <|> doPatDecl)
-- leading_parser "let " >> optional "mut " >> letConfig >> (doIdDecl <|> doPatDecl)
def getDoLetArrowVars (doLetArrow : Syntax) : TermElabM (Array Var) := do
let decl := doLetArrow[2]
let decl := doLetArrow[doLetDeclIdx doLetArrow]
if decl.getKind == ``Parser.Term.doIdDecl then
return #[getDoIdDeclVar decl]
else if decl.getKind == ``Parser.Term.doPatDecl then
@@ -1060,14 +1072,14 @@ def seqToTerm (action : Syntax) (k : Syntax) : M Syntax := withRef action <| wit
def declToTerm (decl : Syntax) (k : Syntax) : M Syntax := withRef decl <| withFreshMacroScope do
let kind := decl.getKind
if kind == ``Parser.Term.doLet then
let letDecl := decl[2]
let letDecl := decl[doLetDeclIdx decl]
`(let $letDecl:letDecl; $k)
else if kind == ``Parser.Term.doLetRec then
let letRecToken := decl[0]
let letRecDecls := decl[1]
return mkNode ``Parser.Term.letrec #[letRecToken, letRecDecls, mkNullNode, k]
else if kind == ``Parser.Term.doLetArrow then
let arg := decl[2]
let arg := decl[doLetDeclIdx decl]
if arg.getKind == ``Parser.Term.doIdDecl then
let id := arg[0]
let type := expandOptType id arg[1]
@@ -1415,7 +1427,7 @@ mutual
/-- Generate `CodeBlock` for `doLetArrow; doElems`
`doLetArrow` is of the form
```
"let " >> optional "mut " >> (doIdDecl <|> doPatDecl)
"let " >> optional "mut " >> letConfig >> (doIdDecl <|> doPatDecl)
```
where
```
@@ -1424,7 +1436,7 @@ mutual
```
-/
partial def doLetArrowToCode (doLetArrow : Syntax) (doElems : List Syntax) : M CodeBlock := do
let decl := doLetArrow[2]
let decl := doLetArrow[doLetDeclIdx doLetArrow]
if decl.getKind == ``Parser.Term.doIdDecl then
let y := decl[0]
checkNotShadowingMutable #[y]
@@ -1475,11 +1487,12 @@ mutual
throwError "unexpected kind of `do` declaration"
partial def doLetElseToCode (doLetElse : Syntax) (doElems : List Syntax) : M CodeBlock := do
-- "let " >> optional "mut " >> termParser >> " := " >> termParser >> (checkColGt >> " | " >> doSeq) >> optional doSeq
let pattern := doLetElse[2]
let val := doLetElse[4]
let elseSeq := doLetElse[6]
let bodySeq := doLetElse[7][0]
-- "let " >> optional "mut " >> letConfig >> termParser >> " := " >> termParser >> (checkColGt >> " | " >> doSeq) >> optional doSeq
let offset := doLetDeclIdx doLetElse
let pattern := doLetElse[offset]
let val := doLetElse[offset + 2]
let elseSeq := doLetElse[offset + 4]
let bodySeq := doLetElse[offset + 5][0]
let contSeq if isMutableLet doLetElse then
let vars ( getPatternVarsEx pattern).mapM fun var => `(doElem| let mut $var := $var)
pure (vars ++ (getDoSeqElems bodySeq).toArray)

View File

@@ -9,6 +9,7 @@ prelude
public import Lean.Parser.Module
meta import Lean.Parser.Module
import Lean.Compiler.ModPkgExt
public import Lean.DeprecatedModule
public section
@@ -42,12 +43,66 @@ def HeaderSyntax.toModuleHeader (stx : HeaderSyntax) : ModuleHeader where
abbrev headerToImports := @HeaderSyntax.imports
/--
Check imported modules for deprecation and emit warnings.
The `-- deprecated_module: ignore` comment can be placed on the `module` keyword to suppress
all warnings, or on individual `import` statements to suppress specific ones.
This follows the same pattern as `-- shake: keep` in Lake shake.
The `headerStx?` parameter carries the header syntax used for checking trailing comments.
When called from the Language Server, the main header syntax may have its trailing trivia
stripped by `unsetTrailing` for caching purposes, so `origHeaderStx?` can supply the original
(untrimmed) syntax to preserve `-- deprecated_module: ignore` annotations on the last import.
-/
def checkDeprecatedImports
(env : Environment) (imports : Array Import) (opts : Options)
(inputCtx : Parser.InputContext) (startPos : String.Pos.Raw) (messages : MessageLog)
(headerStx? : Option HeaderSyntax := none)
(origHeaderStx? : Option HeaderSyntax := none)
: MessageLog := Id.run do
let mut opts := opts
let mut ignoreDeprecatedImports : NameSet := {}
if let some headerStx := origHeaderStx? <|> headerStx? then
match headerStx with
| `(Parser.Module.header| $[module%$moduleTk]? $[prelude%$_]? $importsStx*) =>
if moduleTk.any (·.getTrailing?.any (·.toString.contains "deprecated_module: ignore")) then
opts := linter.deprecated.module.set opts false
for impStx in importsStx do
if impStx.raw.getTrailing?.any (·.toString.contains "deprecated_module: ignore") then
match impStx with
| `(Parser.Module.import| $[public%$_]? $[meta%$_]? import $[all%$_]? $n) =>
ignoreDeprecatedImports := ignoreDeprecatedImports.insert n.getId
| _ => pure ()
| _ => pure ()
if !linter.deprecated.module.get opts then
return messages
imports.foldl (init := messages) fun messages imp =>
if ignoreDeprecatedImports.contains imp.module then
messages
else
match env.getModuleIdx? imp.module with
| some idx =>
match env.getDeprecatedModuleByIdx? idx with
| some entry =>
let pos := inputCtx.fileMap.toPosition startPos
messages.add {
fileName := inputCtx.fileName
pos := pos
severity := .warning
data := .tagged ``deprecatedModuleExt <| formatDeprecatedModuleWarning env idx imp.module entry
}
| none => messages
| none => messages
def processHeaderCore
(startPos : String.Pos.Raw) (imports : Array Import) (isModule : Bool)
(opts : Options) (messages : MessageLog) (inputCtx : Parser.InputContext)
(trustLevel : UInt32 := 0) (plugins : Array System.FilePath := #[]) (leakEnv := false)
(mainModule := Name.anonymous) (package? : Option PkgId := none)
(arts : NameMap ImportArtifacts := {})
(headerStx? : Option HeaderSyntax := none)
(origHeaderStx? : Option HeaderSyntax := none)
: IO (Environment × MessageLog) := do
let level := if isModule then
if Elab.inServer.get opts then
@@ -66,6 +121,7 @@ def processHeaderCore
let pos := inputCtx.fileMap.toPosition startPos
pure (env, messages.add { fileName := inputCtx.fileName, data := toString e, pos := pos })
let env := env.setMainModule mainModule |>.setModulePackage package?
let messages := checkDeprecatedImports env imports opts inputCtx startPos messages headerStx? origHeaderStx?
return (env, messages)
/--
@@ -82,6 +138,7 @@ backwards compatibility measure not compatible with the module system.
: IO (Environment × MessageLog) := do
processHeaderCore header.startPos header.imports header.isModule
opts messages inputCtx trustLevel plugins leakEnv mainModule
(headerStx? := header)
def parseImports (input : String) (fileName : Option String := none) : IO (Array Import × Position × MessageLog) := do
let fileName := fileName.getD "<input>"

View File

@@ -73,8 +73,9 @@ def splitMatchOrCasesOn (mvarId : MVarId) (e : Expr) (matcherInfo : MatcherInfo)
if ( isMatcherApp e) then
Split.splitMatch mvarId e
else
assert! matcherInfo.numDiscrs = 1
let discr := e.getAppArgs[matcherInfo.numParams + 1]!
-- For casesOn, the last discriminant is the major premise;
-- `cases` will handle any index discriminants automatically.
let discr := e.getAppArgs[matcherInfo.numParams + matcherInfo.numDiscrs]!
assert! discr.isFVar
let subgoals mvarId.cases discr.fvarId!
return subgoals.map (·.mvarId) |>.toList

View File

@@ -7,6 +7,7 @@ module
prelude
public import Lean.Elab.Quotation.Util
import Lean.Elab.DeprecatedSyntax
public section
@@ -56,6 +57,14 @@ unsafe builtin_initialize precheckAttribute : KeyedDeclsAttribute Precheck ←
}
partial def precheck : Precheck := fun stx => do
-- Check for deprecated syntax kinds in quotations
if let some entry := (deprecatedSyntaxExt.getState ( getEnv)).find? stx.getKind then
let extraMsg := match entry.text? with
| some text => m!": {text}"
| none => m!""
withRef stx do
Linter.logLintIf Linter.linter.deprecated.syntax stx
m!"quotation uses deprecated syntax '{stx.getKind}'{extraMsg}"
if let p::_ := precheckAttribute.getValues ( getEnv) stx.getKind then
if catchInternalId unsupportedSyntaxExceptionId (do withRef stx <| p stx; pure true) (fun _ => pure false) then
return

View File

@@ -582,6 +582,7 @@ mutual
-- We use `filterRevM` instead of `filterM` to make sure we process the synthetic metavariables using the order they were created.
-- It would not be incorrect to use `filterM`.
let remainingPendingMVars pendingMVars.filterRevM fun mvarId => do
checkSystem "synthesize pending MVars"
-- We use `traceM` because we want to make sure the metavar local context is used to trace the message
traceM `Elab.postpone (mvarId.withContext do addMessageContext m!"resuming {mkMVar mvarId}")
let succeeded synthesizeSyntheticMVar mvarId postponeOnError runTactics

View File

@@ -8,6 +8,7 @@ module
prelude
public import Lean.Meta.Tactic.Util
public import Lean.Elab.Term
import Lean.Elab.DeprecatedSyntax
import Init.Omega
public section
@@ -192,6 +193,7 @@ partial def evalTactic (stx : Syntax) : TacticM Unit := do
Term.withoutTacticIncrementality true <| withTacticInfoContext stx do
stx.getArgs.forM evalTactic
else withTraceNode `Elab.step (fun _ => return stx) (tag := stx.getKind.toString) do
checkDeprecatedSyntax stx ( readThe Term.Context).macroStack
let evalFns := tacticElabAttribute.getEntries ( getEnv) stx.getKind
let macros := macroAttribute.getEntries ( getEnv) stx.getKind
if evalFns.isEmpty && macros.isEmpty then

View File

@@ -7,6 +7,8 @@ module
prelude
public import Lean.Elab.Tactic.Grind.Basic
import Lean.Elab.Tactic.ConfigSetter
import Lean.Elab.DeprecatedSyntax -- shake: skip (workaround for `mkConfigSetter` failing to interpret `deprecatedSyntaxExt`, to be fixed)
public section
namespace Lean.Elab.Tactic.Grind

View File

@@ -9,6 +9,7 @@ prelude
public import Lean.Meta.Coe
public import Lean.Util.CollectLevelMVars
public import Lean.Linter.Deprecated
import Lean.Elab.DeprecatedSyntax
public import Lean.Elab.Attributes
public import Lean.Elab.Level
public import Lean.Elab.PreDefinition.TerminationHint
@@ -1794,6 +1795,7 @@ private partial def elabTermAux (expectedType? : Option Expr) (catchExPostpone :
withTraceNode `Elab.step (fun _ => return m!"expected type: {expectedType?}, term\n{stx}")
(tag := stx.getKind.toString) do
checkSystem "elaborator"
checkDeprecatedSyntax stx ( read).macroStack
let env getEnv
let result match ( liftMacroM (expandMacroImpl? env stx)) with
| some (decl, stxNew?) =>
@@ -2122,11 +2124,14 @@ private def mkConsts (candidates : List (Name × List String)) (explicitLevels :
let const withoutCheckDeprecated <| mkConst declName explicitLevels
return (const, projs) :: result
def throwInvalidExplicitUniversesForLocal {α} (e : Expr) : TermElabM α :=
throwError "invalid use of explicit universe parameters, `{e}` is a local variable"
def resolveName (stx : Syntax) (n : Name) (preresolved : List Syntax.Preresolved) (explicitLevels : List Level) (expectedType? : Option Expr := none) : TermElabM (List (Expr × List String)) := do
addCompletionInfo <| CompletionInfo.id stx stx.getId (danglingDot := false) ( getLCtx) expectedType?
if let some (e, projs) resolveLocalName n then
unless explicitLevels.isEmpty do
throwError "invalid use of explicit universe parameters, `{e}` is a local variable"
throwInvalidExplicitUniversesForLocal e
return [(e, projs)]
let preresolved := preresolved.filterMap fun
| .decl n projs => some (n, projs)

View File

@@ -245,7 +245,7 @@ We use this combinator to prevent stack overflows.
@[inline] def withIncRecDepth [Monad m] [MonadError m] [MonadRecDepth m] (x : m α) : m α := do
let curr MonadRecDepth.getRecDepth
let max MonadRecDepth.getMaxRecDepth
if curr == max then
if max != 0 && curr == max then
throwMaxRecDepthAt ( getRef)
else
MonadRecDepth.withRecDepth (curr+1) x

View File

@@ -478,11 +478,11 @@ where
}
result? := some {
parserState
processedSnap := ( processHeader trimmedStx parserState)
processedSnap := ( processHeader trimmedStx stx parserState)
}
}
processHeader (stx : HeaderSyntax) (parserState : Parser.ModuleParserState) :
processHeader (stx : HeaderSyntax) (origStx : HeaderSyntax) (parserState : Parser.ModuleParserState) :
LeanProcessingM (SnapshotTask HeaderProcessedSnapshot) := do
let ctx read
SnapshotTask.ofIO none none (.some 0, ctx.endPos) <|
@@ -498,6 +498,7 @@ where
let (headerEnv, msgLog) Elab.processHeaderCore (leakEnv := true)
stx.startPos setup.imports setup.isModule setup.opts .empty ctx.toInputContext
setup.trustLevel setup.plugins setup.mainModuleName setup.package? setup.importArts
(headerStx? := stx) (origHeaderStx? := origStx)
let stopTime := ( IO.monoNanosNow).toFloat / 1000000000
let diagnostics := ( Snapshot.Diagnostics.ofMessageLog msgLog)
if msgLog.hasErrors then

View File

@@ -441,18 +441,27 @@ def Result.imax : Result → Result → Result
| f, Result.imaxNode Fs => Result.imaxNode (f::Fs)
| f₁, f₂ => Result.imaxNode [f₁, f₂]
def toResult (l : Level) (mvars : Bool) : Result :=
structure Context where
mvars : Bool
lIndex? : LMVarId Option Nat
abbrev M := ReaderM Context
def toResult (l : Level) : M Result := do
match l with
| zero => Result.num 0
| succ l => Result.succ (toResult l mvars)
| max l₁ l₂ => Result.max (toResult l₁ mvars) (toResult l₂ mvars)
| imax l₁ l₂ => Result.imax (toResult l₁ mvars) (toResult l₂ mvars)
| param n => Result.leaf n
| zero => return Result.num 0
| succ l => return Result.succ ( toResult l)
| max l₁ l₂ => return Result.max ( toResult l₁) ( toResult l₂)
| imax l₁ l₂ => return Result.imax ( toResult l₁) ( toResult l₂)
| param n => return Result.leaf n
| mvar n =>
if mvars then
Result.leaf <| n.name.replacePrefix `_uniq (Name.mkSimple "?u")
if !( read).mvars then
return Result.leaf `_
else if let some i := ( read).lIndex? n then
return Result.leaf <| Name.num (Name.mkSimple "?u") (i + 1)
else
Result.leaf `_
-- Undefined mvar, use internal name
return Result.leaf <| n.name.replacePrefix `_uniq (Name.mkSimple "?_mvar")
private def parenIfFalse : Format Bool Format
| f, true => f
@@ -469,7 +478,7 @@ mutual
| Result.offset f 0, r => format f r
| Result.offset f (k+1), r =>
let f' := format f false;
parenIfFalse (f' ++ "+" ++ Std.format (k+1)) r
parenIfFalse (f' ++ " + " ++ Std.format (k+1)) r
| Result.maxNode fs, r => parenIfFalse (Format.group <| "max" ++ formatLst fs) r
| Result.imaxNode fs, r => parenIfFalse (Format.group <| "imax" ++ formatLst fs) r
end
@@ -487,20 +496,20 @@ protected partial def Result.quote (r : Result) (prec : Nat) : Syntax.Level :=
end PP
protected def format (u : Level) (mvars : Bool) : Format :=
(PP.toResult u mvars).format true
protected def format (u : Level) (mvars : Bool) (lIndex? : LMVarId Option Nat) : Format :=
(PP.toResult u) |>.run { mvars, lIndex? } |>.format true
instance : ToFormat Level where
format u := Level.format u (mvars := true)
format u := Level.format u (mvars := true) (lIndex? := fun _ => none)
instance : ToString Level where
toString u := Format.pretty (format u)
protected def quote (u : Level) (prec : Nat := 0) (mvars : Bool := true) : Syntax.Level :=
(PP.toResult u (mvars := mvars)).quote prec
protected def quote (u : Level) (prec : Nat := 0) (mvars : Bool := true) (lIndex? : LMVarId Option Nat) : Syntax.Level :=
(PP.toResult u) |>.run { mvars, lIndex? } |>.quote prec
instance : Quote Level `level where
quote := Level.quote
quote := Level.quote (lIndex? := fun _ => none)
end Level

View File

@@ -66,7 +66,11 @@ Helper function for running `MetaM` code during module export, when there is not
Panics on errors.
-/
unsafe def _root_.Lean.Environment.unsafeRunMetaM [Inhabited α] (env : Environment) (x : MetaM α) : α :=
match unsafeEIO ((((withoutExporting x).run' {} {}).run' { fileName := "symbolFrequency", fileMap := default } { env })) with
match unsafeEIO ((((withoutExporting x).run' {} {}).run'
{ fileName := "symbolFrequency", fileMap := default
-- avoid triggering since limit cannot be raised here
maxHeartbeats := 0 }
{ env })) with
| Except.ok a => a
| Except.error ex => panic! match unsafeIO ex.toMessageData.toString with
| Except.ok s => s

View File

@@ -244,7 +244,7 @@ def ofLevel (l : Level) : MessageData :=
.ofLazy
(fun ctx? => do
let msg ofFormat <$> match ctx? with
| .none => pure (format l)
| .none => pure (l.format true (fun _ => none))
| .some ctx => ppLevel ctx l
return Dynamic.mk msg)
(fun _ => false)

View File

@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
module
prelude
public import Lean.Meta.Basic
public import Lean.Meta.HasAssignableMVar
public import Lean.Meta.LevelDefEq
public import Lean.Meta.WHNF
public import Lean.Meta.InferType

View File

@@ -70,6 +70,7 @@ structure Context where
abbrev M := ReaderT Context $ MonadCacheT ExprStructEq Expr MetaM
partial def visit (e : Expr) : M Expr := do
checkSystem "abstract nested proofs"
if e.isAtomic then
pure e
else

View File

@@ -8,6 +8,7 @@ prelude
public import Lean.Meta.SynthInstance
public import Lean.Meta.DecLevel
import Lean.Meta.CtorRecognizer
public import Lean.Meta.HasAssignableMVar
import Lean.Structure
import Init.Omega
public section

View File

@@ -0,0 +1,54 @@
/-
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Lean.Meta.Basic
public section
open Lean
namespace Lean.Meta
def hasAssignableLevelMVar : Level MetaM Bool
| .succ lvl => pure lvl.hasMVar <&&> hasAssignableLevelMVar lvl
| .max lvl₁ lvl₂ => (pure lvl₁.hasMVar <&&> hasAssignableLevelMVar lvl₁) <||> (pure lvl₂.hasMVar <&&> hasAssignableLevelMVar lvl₂)
| .imax lvl₁ lvl₂ => (pure lvl₁.hasMVar <&&> hasAssignableLevelMVar lvl₁) <||> (pure lvl₂.hasMVar <&&> hasAssignableLevelMVar lvl₂)
| .mvar mvarId => isLevelMVarAssignable mvarId
| .zero => return false
| .param _ => return false
/-- Return `true` iff expression contains a metavariable that can be assigned. -/
partial def hasAssignableMVar (e : Expr) : MetaM Bool :=
if !e.hasMVar then
return false
else
go e |>.run' {}
where
go (e : Expr) : StateRefT ExprSet MetaM Bool :=
match e with
| .const _ lvls => lvls.anyM (liftM <| hasAssignableLevelMVar ·)
| .sort lvl => liftM <| hasAssignableLevelMVar lvl
| .app f a => do checkSystem "hasAssignableMVar"; visit f <||> visit a
| .letE _ t v b _ => do checkSystem "hasAssignableMVar"; visit t <||> visit v <||> visit b
| .forallE _ d b _ => do checkSystem "hasAssignableMVar"; visit d <||> visit b
| .lam _ d b _ => do checkSystem "hasAssignableMVar"; visit d <||> visit b
| .fvar _ => return false
| .bvar _ => return false
| .lit _ => return false
| .mdata _ e => visit e
| .proj _ _ e => visit e
| .mvar mvarId => mvarId.isAssignable
visit (e : Expr) : StateRefT ExprSet MetaM Bool := do
if !e.hasMVar then return false
if ( get).contains e then return false
modify fun s => s.insert e
go e
end Lean.Meta
end

View File

@@ -8,6 +8,7 @@ module
prelude
public import Lean.Util.CollectMVars
public import Lean.Meta.DecLevel
public import Lean.Meta.HasAssignableMVar
public section

View File

@@ -18,6 +18,7 @@ import Lean.Meta.Sym.AlphaShareBuilder
import Lean.Meta.Sym.Offset
import Lean.Meta.Sym.Eta
import Lean.Meta.Sym.Util
import Lean.Meta.HasAssignableMVar
import Init.Data.List.MapIdx
import Init.Data.Nat.Linear
import Std.Do.Triple.Basic

View File

@@ -14,6 +14,7 @@ import Lean.Meta.Tactic.Grind.SynthInstance
import Lean.Meta.Tactic.Grind.Simp
import Init.Grind.Util
import Init.Omega
public import Lean.Meta.HasAssignableMVar
public section
namespace Lean.Meta.Grind
namespace EMatch

View File

@@ -714,7 +714,6 @@ where
set_option compiler.ignoreBorrowAnnotation true in
@[export lean_simp]
def simpImpl (e : Expr) : SimpM Result := withIncRecDepth do
checkSystem "simp"
if ( isProof e) then
return { expr := e }
trace[Meta.Tactic.simp.heads] "{repr e.toHeadIndex}"

View File

@@ -12,6 +12,7 @@ public import Lean.Meta.Tactic.Simp.Arith
public import Lean.Meta.Tactic.Simp.Attr
public import Lean.Meta.BinderNameHint
import Lean.Meta.WHNF
public import Lean.Meta.HasAssignableMVar
public section
namespace Lean.Meta.Simp
/--
@@ -218,6 +219,7 @@ where
else
let candidates := candidates.insertionSort fun e₁ e₂ => e₁.1.priority > e₂.1.priority
for (thm, numExtraArgs) in candidates do
checkSystem "simp"
if inErasedSet thm then continue
if rflOnly then
unless thm.rfl do
@@ -245,6 +247,7 @@ where
else
let candidates := candidates.insertionSort fun e₁ e₂ => e₁.priority > e₂.priority
for thm in candidates do
checkSystem "simp"
unless inErasedSet thm || (rflOnly && !thm.rfl) do
let result? withNewMCtxDepth do
let val thm.getValue

View File

@@ -722,6 +722,7 @@ def simpAppUsingCongr (e : Expr) : SimpM Result := do
if i == 0 then
simp f
else
checkSystem "simp"
let i := i - 1
let .app f a := e | unreachable!
let fr visit f i

View File

@@ -50,6 +50,7 @@ partial def transform {m} [Monad m] [MonadLiftT CoreM m] [MonadControlT CoreM m]
let _ : MonadLiftT (ST IO.RealWorld) m := { monadLift := fun x => liftM (m := CoreM) (liftM (m := ST IO.RealWorld) x) }
let rec visit (e : Expr) : MonadCacheT ExprStructEq Expr m Expr :=
checkCache { val := e : ExprStructEq } fun _ => Core.withIncRecDepth do
Core.checkSystem "transform"
let rec visitPost (e : Expr) : MonadCacheT ExprStructEq Expr m Expr := do
match ( post e) with
| .done e => pure e
@@ -107,6 +108,7 @@ partial def transformWithCache {m} [Monad m] [MonadLiftT MetaM m] [MonadControlT
let _ : MonadLiftT (ST IO.RealWorld) m := { monadLift := fun x => liftM (m := MetaM) (liftM (m := ST IO.RealWorld) x) }
let rec visit (e : Expr) : MonadCacheT ExprStructEq Expr m Expr :=
checkCache { val := e : ExprStructEq } fun _ => Meta.withIncRecDepth do
(Core.checkSystem "transform" : MetaM Unit)
let rec visitPost (e : Expr) : MonadCacheT ExprStructEq Expr m Expr := do
match ( post e) with
| .done e => pure e

View File

@@ -650,7 +650,7 @@ expand let-expressions, expand assigned meta-variables, unfold aux declarations.
partial def whnfCore (e : Expr) : MetaM Expr :=
go e
where
go (e : Expr) : MetaM Expr :=
go (e : Expr) : MetaM Expr := do
whnfEasyCases e fun e => do
trace[Meta.whnf] e
match e with

View File

@@ -247,6 +247,18 @@ very simple unification and/or non-nested TC. So, if the "app builder" becomes a
we may solve the issue by implementing `isDefEqCheap` that never invokes TC and uses tmp metavars.
-/
structure LevelMetavarDecl where
/-- The nesting depth of this metavariable. We do not want
unification subproblems to influence the results of parent
problems. The depth keeps track of this information and ensures
that unification subproblems cannot leak information out, by unifying
based on depth. -/
depth : Nat
/-- This field tracks how old a metavariable is. It is set using a counter at `MetavarContext`.
We primarily use the index of a level metavariable for pretty printing. -/
index : Nat
deriving Inhabited
/--
`LocalInstance` represents a local typeclass instance registered by and for
the elaborator. It stores the name of the typeclass in `className`, and the
@@ -309,7 +321,8 @@ structure MetavarDecl where
kind : MetavarKind
/-- See comment at `CheckAssignment` `Meta/ExprDefEq.lean` -/
numScopeArgs : Nat := 0
/-- We use this field to track how old a metavariable is. It is set using a counter at `MetavarContext` -/
/-- We use this field to track how old a metavariable is. It is set using a counter at `MetavarContext`.
We also use it for pretty printing anonymous metavariables. -/
index : Nat
deriving Inhabited
@@ -340,9 +353,12 @@ structure MetavarContext where
depth : Nat := 0
/-- At what depth level mvars can be assigned. -/
levelAssignDepth : Nat := 0
/-- Counter for setting the field `index` at `LevelMetavarDecl`. -/
lmvarCounter : Nat := 0
/-- Counter for setting the field `index` at `MetavarDecl` -/
mvarCounter : Nat := 0
lDepth : PersistentHashMap LMVarId Nat := {}
/-- Level metavariable declarations. -/
lDecls : PersistentHashMap LMVarId LevelMetavarDecl := {}
/-- Metavariable declarations. -/
decls : PersistentHashMap MVarId MetavarDecl := {}
/-- Index mapping user-friendly names to ids. -/
@@ -444,11 +460,21 @@ def _root_.Lean.MVarId.isAssignedOrDelayedAssigned [Monad m] [MonadMCtx m] (mvar
let mctx getMCtx
return mctx.eAssignment.contains mvarId || mctx.dAssignment.contains mvarId
def MetavarContext.findLevelDecl? (mctx : MetavarContext) (mvarId : LMVarId) : Option LevelMetavarDecl :=
mctx.lDecls.find? mvarId
def MetavarContext.getLevelDecl (mctx : MetavarContext) (mvarId : LMVarId) : LevelMetavarDecl :=
match mctx.lDecls.find? mvarId with
| some decl => decl
| none => panic! s!"unknown universe metavariable {mvarId.name}"
def isLevelMVarAssignable [Monad m] [MonadMCtx m] (mvarId : LMVarId) : m Bool := do
let mctx getMCtx
match mctx.lDepth.find? mvarId with
| some d => return d >= mctx.levelAssignDepth
| _ => panic! s!"unknown universe metavariable {mvarId.name}"
let decl := mctx.getLevelDecl mvarId
return decl.depth >= mctx.levelAssignDepth
def MetavarContext.findDecl? (mctx : MetavarContext) (mvarId : MVarId) : Option MetavarDecl :=
mctx.decls.find? mvarId
def MetavarContext.getDecl (mctx : MetavarContext) (mvarId : MVarId) : MetavarDecl :=
match mctx.decls.find? mvarId with
@@ -484,30 +510,6 @@ def hasAssignedMVar [Monad m] [MonadMCtx m] : Expr → m Bool
| .proj _ _ e => pure e.hasMVar <&&> hasAssignedMVar e
| .mvar mvarId => mvarId.isAssigned <||> mvarId.isDelayedAssigned
/-- Return true iff the given level contains a metavariable that can be assigned. -/
def hasAssignableLevelMVar [Monad m] [MonadMCtx m] : Level m Bool
| .succ lvl => pure lvl.hasMVar <&&> hasAssignableLevelMVar lvl
| .max lvl₁ lvl₂ => (pure lvl₁.hasMVar <&&> hasAssignableLevelMVar lvl₁) <||> (pure lvl₂.hasMVar <&&> hasAssignableLevelMVar lvl₂)
| .imax lvl₁ lvl₂ => (pure lvl₁.hasMVar <&&> hasAssignableLevelMVar lvl₁) <||> (pure lvl₂.hasMVar <&&> hasAssignableLevelMVar lvl₂)
| .mvar mvarId => isLevelMVarAssignable mvarId
| .zero => return false
| .param _ => return false
/-- Return `true` iff expression contains a metavariable that can be assigned. -/
def hasAssignableMVar [Monad m] [MonadMCtx m] : Expr m Bool
| .const _ lvls => lvls.anyM hasAssignableLevelMVar
| .sort lvl => hasAssignableLevelMVar lvl
| .app f a => (pure f.hasMVar <&&> hasAssignableMVar f) <||> (pure a.hasMVar <&&> hasAssignableMVar a)
| .letE _ t v b _ => (pure t.hasMVar <&&> hasAssignableMVar t) <||> (pure v.hasMVar <&&> hasAssignableMVar v) <||> (pure b.hasMVar <&&> hasAssignableMVar b)
| .forallE _ d b _ => (pure d.hasMVar <&&> hasAssignableMVar d) <||> (pure b.hasMVar <&&> hasAssignableMVar b)
| .lam _ d b _ => (pure d.hasMVar <&&> hasAssignableMVar d) <||> (pure b.hasMVar <&&> hasAssignableMVar b)
| .fvar _ => return false
| .bvar _ => return false
| .lit _ => return false
| .mdata _ e => pure e.hasMVar <&&> hasAssignableMVar e
| .proj _ _ e => pure e.hasMVar <&&> hasAssignableMVar e
| .mvar mvarId => mvarId.isAssignable
/--
Add `mvarId := u` to the universe metavariable assignment.
This method does not check whether `mvarId` is already assigned, nor does it check whether
@@ -826,10 +828,11 @@ def addExprMVarDeclExp (mctx : MetavarContext) (mvarId : MVarId) (userName : Nam
It is used to implement actions in the monads `MetaM`, `ElabM` and `TacticM`.
It should not be used directly since the argument `(mvarId : MVarId)` is assumed to be "unique". -/
def addLevelMVarDecl (mctx : MetavarContext) (mvarId : LMVarId) : MetavarContext :=
{ mctx with lDepth := mctx.lDepth.insert mvarId mctx.depth }
def findDecl? (mctx : MetavarContext) (mvarId : MVarId) : Option MetavarDecl :=
mctx.decls.find? mvarId
{ mctx with
lmvarCounter := mctx.lmvarCounter + 1
lDecls := mctx.lDecls.insert mvarId {
depth := mctx.depth
index := mctx.lmvarCounter } }
def findUserName? (mctx : MetavarContext) (userName : Name) : Option MVarId :=
mctx.userNames.find? userName
@@ -909,12 +912,13 @@ def setFVarBinderInfo (mctx : MetavarContext) (mvarId : MVarId)
mctx.modifyExprMVarLCtx mvarId (·.setBinderInfo fvarId bi)
def findLevelDepth? (mctx : MetavarContext) (mvarId : LMVarId) : Option Nat :=
mctx.lDepth.find? mvarId
(mctx.findLevelDecl? mvarId).map LevelMetavarDecl.depth
def getLevelDepth (mctx : MetavarContext) (mvarId : LMVarId) : Nat :=
match mctx.findLevelDepth? mvarId with
| some d => d
| none => panic! "unknown metavariable"
(mctx.getLevelDecl mvarId).depth
def findLevelIndex? (mctx : MetavarContext) (mvarId : LMVarId) : Option Nat :=
(mctx.findLevelDecl? mvarId).map LevelMetavarDecl.index
def isAnonymousMVar (mctx : MetavarContext) (mvarId : MVarId) : Bool :=
match mctx.findDecl? mvarId with

View File

@@ -622,6 +622,15 @@ declaration signatures.
/-- Debugging command: Prints the result of `Environment.dumpAsyncEnvState`. -/
@[builtin_command_parser] def dumpAsyncEnvState := leading_parser
"#dump_async_env_state"
/--
Mark a syntax kind as deprecated. When this syntax is elaborated, a warning will be emitted.
```
deprecated_syntax Lean.Parser.Term.let_fun "use `have` instead" (since := "2026-03-18")
```
-/
@[builtin_command_parser] def deprecatedSyntax := leading_parser
"deprecated_syntax " >> ident >> optional (ppSpace >> strLit) >> optional (" (" >> nonReservedSymbol "since" >> " := " >> strLit >> ")")
@[builtin_command_parser] def «init_quot» := leading_parser
"init_quot"
/--
@@ -629,6 +638,27 @@ An internal bootstrapping command that reinterprets a Markdown docstring as Vers
-/
@[builtin_command_parser] def «docs_to_verso» := leading_parser
"docs_to_verso " >> sepBy1 ident ", "
/--
`deprecated_module` marks the current module as deprecated.
When another module imports a deprecated module, a warning is emitted during elaboration.
```
deprecated_module "use NewModule instead" (since := "2026-03-19")
```
The warning message is optional but recommended.
The warning can be disabled with `set_option linter.deprecated.module false` or
`-Dlinter.deprecated.module=false`.
-/
@[builtin_command_parser] def «deprecated_module» := leading_parser
"deprecated_module" >> optional (ppSpace >> strLit) >> optional (" (" >> nonReservedSymbol "since" >> " := " >> strLit >> ")")
/--
`#show_deprecated_modules` displays all modules in the current environment that have been
marked with `deprecated_module`.
-/
@[builtin_command_parser] def showDeprecatedModules := leading_parser
"#show_deprecated_modules"
def optionValue := nonReservedSymbol "true" <|> nonReservedSymbol "false" <|> strLit <|> numLit
/--
@@ -648,6 +678,12 @@ only in a single term or tactic.
-/
@[builtin_command_parser] def «set_option» := leading_parser
"set_option " >> identWithPartialTrailingDot >> ppSpace >> optionValue
/--
`unlock_limits` disables all built-in resource limit options (currently `maxRecDepth`,
`maxHeartbeats`, and `synthInstance.maxHeartbeats`) in the current scope by setting them to 0.
-/
@[builtin_command_parser] def «unlock_limits» := leading_parser
"unlock_limits"
def eraseAttr := leading_parser
"-" >> rawIdent
@[builtin_command_parser] def «attribute» := leading_parser

View File

@@ -67,9 +67,9 @@ def notFollowedByRedefinedTermToken :=
"token at 'do' element"
@[builtin_doElem_parser] def doLet := leading_parser
"let " >> optional "mut " >> letDecl
"let " >> optional "mut " >> letConfig >> letDecl
@[builtin_doElem_parser] def doLetElse := leading_parser withPosition <|
"let " >> optional "mut " >> termParser >> " := " >> termParser >>
"let " >> optional "mut " >> letConfig >> termParser >> " := " >> termParser >>
(checkColGe >> " | " >> doSeqIndent) >> optional (checkColGe >> doSeqIndent)
@[builtin_doElem_parser] def doLetExpr := leading_parser withPosition <|
@@ -89,7 +89,7 @@ def doPatDecl := leading_parser
atomic (termParser >> optType >> ppSpace >> leftArrow) >>
doElemParser >> optional ((checkColGe >> " | " >> doSeqIndent) >> optional (checkColGe >> doSeqIndent))
@[builtin_doElem_parser] def doLetArrow := leading_parser withPosition <|
"let " >> optional "mut " >> (doIdDecl <|> doPatDecl)
"let " >> optional "mut " >> letConfig >> (doIdDecl <|> doPatDecl)
/-
We use `letIdDeclNoBinders` to define `doReassign`.
@@ -114,7 +114,7 @@ def letIdDeclNoBinders := leading_parser
@[builtin_doElem_parser] def doReassignArrow := leading_parser
notFollowedByRedefinedTermToken >> (doIdDecl <|> doPatDecl)
@[builtin_doElem_parser] def doHave := leading_parser
"have" >> Term.letDecl
"have" >> Term.letConfig >> Term.letDecl
/-
In `do` blocks, we support `if` without an `else`.
Thus, we use indentation to prevent examples such as

View File

@@ -882,13 +882,19 @@ the available context).
-/
def identProjKind := `Lean.Parser.Term.identProj
@[builtin_term_parser] def dotIdent := leading_parser
"." >> checkNoWsBefore >> rawIdent
def isIdent (stx : Syntax) : Bool :=
-- antiquotations should also be allowed where an identifier is expected
stx.isAntiquot || stx.isIdent
def isIdentOrDotIdent (stx : Syntax) : Bool :=
isIdent stx || stx.isOfKind ``dotIdent
/-- `x.{u, ...}` explicitly specifies the universes `u, ...` of the constant `x`. -/
@[builtin_term_parser] def explicitUniv : TrailingParser := trailing_parser
checkStackTop isIdent "expected preceding identifier" >>
checkStackTop isIdentOrDotIdent "expected preceding identifier" >>
checkNoWsBefore "no space before '.{'" >> ".{" >>
sepBy1 levelParser ", " >> "}"
/-- `x@e` or `x@h:e` matches the pattern `e` and binds its value to the identifier `x`.
@@ -976,9 +982,6 @@ appropriate parameter for the underlying monad's `ST` effects, then passes it to
@[builtin_term_parser] def dynamicQuot := withoutPosition <| leading_parser
"`(" >> ident >> "| " >> incQuotDepth (parserOfStack 1) >> ")"
@[builtin_term_parser] def dotIdent := leading_parser
"." >> checkNoWsBefore >> rawIdent
/--
Implementation of the `show_term` term elaborator.
-/

View File

@@ -469,6 +469,7 @@ def seq : FirstTokens → FirstTokens → FirstTokens
| epsilon, tks => tks
| optTokens s₁, optTokens s₂ => optTokens (s₁ ++ s₂)
| optTokens s₁, tokens s₂ => tokens (s₁ ++ s₂)
| optTokens _, unknown => unknown
| tks, _ => tks
def toOptional : FirstTokens FirstTokens

View File

@@ -70,6 +70,9 @@ def ppExprLegacy (env : Environment) (mctx : MetavarContext) (lctx : LocalContex
{ fileName := "<PrettyPrinter>", fileMap := default }
{ env := env }
def ppLevel (l : Level) : MetaM Format := do
ppCategory `level ( delabLevel l (prec := 0))
def ppTactic (stx : TSyntax `tactic) : CoreM Format := ppCategory `tactic stx
def ppCommand (stx : Syntax.Command) : CoreM Format := ppCategory `command stx
@@ -110,7 +113,7 @@ builtin_initialize
ppExprWithInfos := fun ctx e => ctx.runMetaM <| withoutContext <| ppExprWithInfos e
ppConstNameWithInfos := fun ctx n => ctx.runMetaM <| withoutContext <| ppConstNameWithInfos n
ppTerm := fun ctx stx => ctx.runCoreM <| withoutContext <| ppTerm stx
ppLevel := fun ctx l => return l.format (mvars := getPPMVarsLevels ctx.opts)
ppLevel := fun ctx l => ctx.runMetaM <| withoutContext <| ppLevel l
ppGoal := fun ctx mvarId => ctx.runMetaM <| withoutContext <| Meta.ppGoal mvarId
}

View File

@@ -450,6 +450,10 @@ partial def delab : Delab := do
else
return stx
def delabLevel (l : Level) (prec : Nat) : DelabM Syntax.Level := do
let mvars getPPOption getPPMVarsLevels
return Level.quote l prec (mvars := mvars) (lIndex? := ( getMCtx).findLevelIndex?)
/--
Registers an unexpander for applications of a given constant.
@@ -477,7 +481,11 @@ unsafe builtin_initialize appUnexpanderAttribute : KeyedDeclsAttribute Unexpande
end Delaborator
open SubExpr (Pos PosMap)
open Delaborator (OptionsPerPos topDownAnalyze DelabM)
open Delaborator (OptionsPerPos topDownAnalyze DelabM getPPOption)
def delabLevel (l : Level) (prec : Nat) : MetaM Syntax.Level := do
let mvars := getPPMVarsLevels ( getOptions)
return Level.quote l prec (mvars := mvars) (lIndex? := ( getMCtx).findLevelIndex?)
def delabCore (e : Expr) (optionsPerPos : OptionsPerPos := {}) (delab : DelabM α) :
MetaM (α × PosMap Elab.Info) := do

View File

@@ -91,10 +91,9 @@ def delabSort : Delab := do
| Level.zero => `(Prop)
| Level.succ .zero => `(Type)
| _ =>
let mvars getPPOption getPPMVarsLevels
match l.dec with
| some l' => `(Type $(Level.quote l' (prec := max_prec) (mvars := mvars)))
| none => `(Sort $(Level.quote l (prec := max_prec) (mvars := mvars)))
| some l' => `(Type $( delabLevel l' (prec := max_prec)))
| none => `(Sort $( delabLevel l (prec := max_prec)))
/--
Delaborator for `const` expressions.
@@ -131,8 +130,8 @@ def delabConst : Delab := do
let stx
if !ls.isEmpty && ( getPPOption getPPUniverses) then
let mvars getPPOption getPPMVarsLevels
`($(mkIdent c).{$[$(ls.toArray.map (Level.quote · (prec := 0) (mvars := mvars)))],*})
let ls' ls.toArray.mapM fun l => delabLevel l (prec := 0)
`($(mkIdent c).{$ls',*})
else
pure <| mkIdent c

View File

@@ -52,7 +52,7 @@ structure PPFns where
ppExprWithInfos : PPContext Expr IO FormatWithInfos
ppConstNameWithInfos : PPContext Name IO FormatWithInfos
ppTerm : PPContext Term IO Format
ppLevel : PPContext Level BaseIO Format
ppLevel : PPContext Level IO Format
ppGoal : PPContext MVarId IO Format
deriving Inhabited
@@ -67,7 +67,7 @@ builtin_initialize ppFnsRef : IO.Ref PPFns ←
ppExprWithInfos := fun _ e => return format (toString e)
ppConstNameWithInfos := fun _ n => return format n
ppTerm := fun ctx stx => return formatRawTerm ctx stx
ppLevel := fun _ l => return format l
ppLevel := fun ctx l => return l.format true ctx.mctx.findLevelIndex?
ppGoal := fun _ mvarId => return formatRawGoal mvarId
}
@@ -108,8 +108,14 @@ def ppTerm (ctx : PPContext) (stx : Term) : BaseIO Format := do
else
pure f!"failed to pretty print term (use 'set_option pp.rawOnError true' for raw representation)"
def ppLevel (ctx : PPContext) (l : Level) : BaseIO Format :=
ppExt.getState ctx.env |>.ppLevel ctx l
def ppLevel (ctx : PPContext) (l : Level) : BaseIO Format := do
match ( ppExt.getState ctx.env |>.ppLevel ctx l |>.toBaseIO) with
| .ok fmt => return fmt
| .error ex =>
if pp.rawOnError.get ctx.opts then
pure f!"[Error pretty printing level: {ex}. Falling back to raw printer.]{Format.line}{l.format true ctx.mctx.findLevelIndex?}"
else
pure f!"failed to pretty print level (use 'set_option pp.rawOnError true' for raw representation)"
def ppGoal (ctx : PPContext) (mvarId : MVarId) : BaseIO Format := do
match ( ppExt.getState ctx.env |>.ppGoal ctx mvarId |>.toBaseIO) with

View File

@@ -14,7 +14,7 @@ namespace Lean
register_builtin_option maxRecDepth : Nat := {
defValue := defaultMaxRecDepth
descr := "maximum recursion depth for many Lean procedures"
descr := "maximum recursion depth for many Lean procedures, 0 means no limit"
}
end Lean

View File

@@ -174,19 +174,19 @@ opaque osEnviron : IO (Array (String × String))
Gets the value of an environment variable.
-/
@[extern "lean_uv_os_getenv"]
opaque osGetenv : String IO (Option String)
opaque osGetenv : @& String IO (Option String)
/--
Sets the value of an environment variable.
-/
@[extern "lean_uv_os_setenv"]
opaque osSetenv : String String IO Unit
opaque osSetenv : @& String @& String IO Unit
/--
Unsets an environment variable.
-/
@[extern "lean_uv_os_unsetenv"]
opaque osUnsetenv : String IO Unit
opaque osUnsetenv : @& String IO Unit
/--
Gets the hostname of the machine.

View File

@@ -976,6 +976,13 @@ static inline void lean_sarray_set_size(u_lean_obj_arg o, size_t sz) {
}
static inline uint8_t* lean_sarray_cptr(lean_object * o) { return lean_to_sarray(o)->m_data; }
LEAN_EXPORT bool lean_sarray_eq_cold(b_lean_obj_arg a1, b_lean_obj_arg a2);
static inline bool lean_sarray_eq(b_lean_obj_arg a1, b_lean_obj_arg a2) {
assert(lean_sarray_elem_size(a1) == lean_sarray_elem_size(a2));
return a1 == a2 || (lean_sarray_size(a1) == lean_sarray_size(a2) && lean_sarray_eq_cold(a1, a2));
}
static inline uint8_t lean_sarray_dec_eq(b_lean_obj_arg a1, b_lean_obj_arg a2) { return lean_sarray_eq(a1, a2); }
/* Remark: expand sarray API after we add better support in the compiler */
/* ByteArray (special case of Array of Scalars) */

View File

@@ -77,7 +77,7 @@ globs = ["Lake.*"]
defaultFacets = ["static", "static.export"]
# Load the previous stage's lake native code into lake's build process in order to prevent ABI
# breakages from affecting bootstrapping.
moreLeanArgs = ["--plugin", "${PREV_STAGE}/lib/lean/libLake_shared${CMAKE_SHARED_LIBRARY_SUFFIX}"]
moreLeanArgs = ["--plugin", "${PREV_STAGE}/${CMAKE_RELATIVE_LIBRARY_OUTPUT_DIRECTORY}/libLake_shared${CMAKE_SHARED_LIBRARY_SUFFIX}"]
[[lean_lib]]
name = "LakeMain"

View File

@@ -415,128 +415,17 @@ static void lean_del_core(object * o, object * & todo) {
}
}
// Adaptive deque for lean_dec_ref_cold.
// Uses a small stack-allocated circular buffer processed in FIFO (BFS) order
// for better cache locality when freeing wide structures (arrays, multi-field
// constructors). When the buffer is full, excess objects overflow to the
// existing in-object linked list (DFS order).
#define LEAN_DEC_DEQUE_CAP 32u
#define LEAN_DEC_DEQUE_MASK (LEAN_DEC_DEQUE_CAP - 1u)
struct lean_del_deque {
lean_object * buf[LEAN_DEC_DEQUE_CAP];
unsigned head;
unsigned tail;
lean_object * overflow;
};
static inline void deque_enqueue(lean_del_deque & dq, lean_object * o) {
unsigned next_tail = (dq.tail + 1) & LEAN_DEC_DEQUE_MASK;
if (LEAN_LIKELY(next_tail != dq.head)) {
dq.buf[dq.tail] = o;
dq.tail = next_tail;
} else {
push_back(dq.overflow, o);
}
}
static inline lean_object * deque_pop(lean_del_deque & dq) {
if (LEAN_LIKELY(dq.head != dq.tail)) {
lean_object * r = dq.buf[dq.head];
dq.head = (dq.head + 1) & LEAN_DEC_DEQUE_MASK;
return r;
}
return pop_back(dq.overflow);
}
static inline bool deque_empty(lean_del_deque const & dq) {
return dq.head == dq.tail && dq.overflow == nullptr;
}
static inline void dec_deque(lean_object * o, lean_del_deque & dq) {
if (lean_is_scalar(o))
return;
if (LEAN_LIKELY(o->m_rc > 1)) {
o->m_rc--;
} else if (o->m_rc == 1) {
deque_enqueue(dq, o);
} else if (o->m_rc == 0) {
return;
} else if (std::atomic_fetch_add_explicit(lean_get_rc_mt_addr(o), 1, std::memory_order_acq_rel) == -1) {
deque_enqueue(dq, o);
}
}
static void lean_del_core_deque(object * o, lean_del_deque & dq) {
uint8 tag = lean_ptr_tag(o);
if (tag <= LeanMaxCtorTag) {
object ** it = lean_ctor_obj_cptr(o);
object ** end = it + lean_ctor_num_objs(o);
for (; it != end; ++it) dec_deque(*it, dq);
lean_free_small_object(o);
} else {
switch (tag) {
case LeanClosure: {
object ** it = lean_closure_arg_cptr(o);
object ** end = it + lean_closure_num_fixed(o);
for (; it != end; ++it) dec_deque(*it, dq);
lean_dealloc(o, lean_closure_byte_size(o));
break;
}
case LeanArray: {
object ** it = lean_array_cptr(o);
object ** end = it + lean_array_size(o);
for (; it != end; ++it) dec_deque(*it, dq);
lean_dealloc(o, lean_array_byte_size(o));
break;
}
case LeanScalarArray:
lean_dealloc(o, lean_sarray_byte_size(o));
break;
case LeanString:
lean_dealloc(o, lean_string_byte_size(o));
break;
case LeanMPZ:
to_mpz(o)->m_value.~mpz();
lean_free_small_object(o);
break;
case LeanThunk:
if (object * c = lean_to_thunk(o)->m_closure) dec_deque(c, dq);
if (object * v = lean_to_thunk(o)->m_value) dec_deque(v, dq);
lean_free_small_object(o);
break;
case LeanRef:
if (object * v = lean_to_ref(o)->m_value) dec_deque(v, dq);
lean_free_small_object(o);
break;
case LeanTask:
deactivate_task(lean_to_task(o));
break;
case LeanPromise:
deactivate_promise(lean_to_promise(o));
break;
case LeanExternal:
lean_to_external(o)->m_class->m_finalize(lean_to_external(o)->m_data);
lean_free_small_object(o);
break;
default:
lean_unreachable();
}
}
}
extern "C" LEAN_EXPORT void lean_dec_ref_cold(lean_object * o) {
if (o->m_rc == 1 || std::atomic_fetch_add_explicit(lean_get_rc_mt_addr(o), 1, std::memory_order_acq_rel) == -1) {
#ifdef LEAN_LAZY_RC
push_back(g_to_free, o);
#else
lean_del_deque dq;
dq.head = 0;
dq.tail = 0;
dq.overflow = nullptr;
deque_enqueue(dq, o);
while (!deque_empty(dq)) {
lean_del_core_deque(deque_pop(dq), dq);
object * todo = nullptr;
while (true) {
lean_del_core(o, todo);
if (todo == nullptr)
return;
o = pop_back(todo);
}
#endif
}
@@ -2189,6 +2078,11 @@ extern "C" LEAN_EXPORT bool lean_string_eq_cold(b_lean_obj_arg s1, b_lean_obj_ar
return std::memcmp(lean_string_cstr(s1), lean_string_cstr(s2), lean_string_size(s1)) == 0;
}
extern "C" LEAN_EXPORT bool lean_sarray_eq_cold(b_lean_obj_arg a1, b_lean_obj_arg a2) {
size_t len = lean_sarray_elem_size(a1) * lean_sarray_size(a1);
return std::memcmp(lean_sarray_cptr(a1), lean_sarray_cptr(a2), len) == 0;
}
bool string_eq(object * s1, char const * s2) {
if (lean_string_size(s1) != strlen(s2) + 1)
return false;

View File

@@ -31,7 +31,7 @@ extern "C" LEAN_EXPORT lean_obj_res lean_uv_get_process_title() {
return lean_io_result_mk_ok(lean_title);
}
// Std.Internal.UV.System.setProcessTitle : String → IO Unit
// Std.Internal.UV.System.setProcessTitle : @& String → IO Unit
extern "C" LEAN_EXPORT lean_obj_res lean_uv_set_process_title(b_obj_arg title) {
const char* title_str = lean_string_cstr(title);
if (strlen(title_str) != lean_string_size(title) - 1) {
@@ -124,7 +124,7 @@ extern "C" LEAN_EXPORT lean_obj_res lean_uv_cwd() {
return lean_io_result_mk_ok(lean_cwd);
}
// Std.Internal.UV.System.chdir : String → IO Unit
// Std.Internal.UV.System.chdir : @& String → IO Unit
extern "C" LEAN_EXPORT lean_obj_res lean_uv_chdir(b_obj_arg path) {
const char* path_str = lean_string_cstr(path);
if (strlen(path_str) != lean_string_size(path) - 1) {
@@ -271,7 +271,7 @@ extern "C" LEAN_EXPORT lean_obj_res lean_uv_os_environ() {
return lean_io_result_mk_ok(env_array);
}
// Std.Internal.UV.System.osGetenv : String → IO (Option String)
// Std.Internal.UV.System.osGetenv : @& String → IO (Option String)
extern "C" LEAN_EXPORT lean_obj_res lean_uv_os_getenv(b_obj_arg name) {
const char* name_str = lean_string_cstr(name);
if (strlen(name_str) != lean_string_size(name) - 1) {
@@ -313,7 +313,7 @@ extern "C" LEAN_EXPORT lean_obj_res lean_uv_os_getenv(b_obj_arg name) {
}
// Std.Internal.UV.System.osSetenv : String → String → IO Unit
// Std.Internal.UV.System.osSetenv : @& String → @& String → IO Unit
extern "C" LEAN_EXPORT lean_obj_res lean_uv_os_setenv(b_obj_arg name, b_obj_arg value) {
const char* name_str = lean_string_cstr(name);
const char* value_str = lean_string_cstr(value);
@@ -333,7 +333,7 @@ extern "C" LEAN_EXPORT lean_obj_res lean_uv_os_setenv(b_obj_arg name, b_obj_arg
return lean_io_result_mk_ok(lean_box(0));
}
// Std.Internal.UV.System.osUnsetenv : String → IO Unit
// Std.Internal.UV.System.osUnsetenv : @& String → IO Unit
extern "C" LEAN_EXPORT lean_obj_res lean_uv_os_unsetenv(b_obj_arg name) {
const char* name_str = lean_string_cstr(name);
if (strlen(name_str) != lean_string_size(name) - 1) {
@@ -641,21 +641,21 @@ extern "C" LEAN_EXPORT lean_obj_res lean_uv_os_environ() {
);
}
// Std.Internal.UV.System.osGetenv : String → IO (Option String)
// Std.Internal.UV.System.osGetenv : @& String → IO (Option String)
extern "C" LEAN_EXPORT lean_obj_res lean_uv_os_getenv(b_obj_arg name) {
lean_always_assert(
false && ("Please build a version of Lean4 with libuv to invoke this.")
);
}
// Std.Internal.UV.System.osSetenv : String → String → IO Unit
// Std.Internal.UV.System.osSetenv : @& String → @& String → IO Unit
extern "C" LEAN_EXPORT lean_obj_res lean_uv_os_setenv(b_obj_arg name, b_obj_arg value) {
lean_always_assert(
false && ("Please build a version of Lean4 with libuv to invoke this.")
);
}
// Std.Internal.UV.System.osUnsetenv : String → IO Unit
// Std.Internal.UV.System.osUnsetenv : @& String → IO Unit
extern "C" LEAN_EXPORT lean_obj_res lean_uv_os_unsetenv(b_obj_arg name) {
lean_always_assert(
false && ("Please build a version of Lean4 with libuv to invoke this.")

View File

@@ -162,7 +162,7 @@ else
-Wl,--whole-archive ${LIB}/temp/Lean.*o.export ${LIB}/temp/libleanshell.a -Wl,--no-whole-archive -Wl,--start-group -lInit -lStd -lLean -lleancpp -Wl,--end-group ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS} ${TOOLCHAIN_SHARED_LINKER_FLAGS} ${LEANC_OPTS}
endif
endif
ifeq "${CMAKE_BUILD_TYPE}" "Release"
ifeq "${STRIP_BINARIES}" "ON"
ifeq "${CMAKE_SYSTEM_NAME}" "Linux"
# We only strip like this on Linux for now as our other platforms already seem to exclude the
# unexported symbols by default

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
stage0/stdlib/Lean/Elab/DeprecatedArg.c generated Normal file

Binary file not shown.

BIN
stage0/stdlib/Lean/Elab/DeprecatedSyntax.c generated Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More