Compare commits

...

24 Commits

Author SHA1 Message Date
Kim Morrison
6eb6a364b1 fix: make delta-derived Prop-valued instances theorems
This PR makes the delta-deriving handler create `theorem` declarations
instead of `def` declarations when the instance type is a `Prop`.
Previously, `deriving instance Nonempty for Foo` would always create a
`def`, which is inconsistent with the behavior of a handwritten
`instance` declaration.

Closes #13295

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 22:47:12 +10:00
Sebastian Graf
ec72785927 chore: git ignore .claude/worktrees (#13299)
This PR ignores `.claude/worktrees` after I have had one instance too
many where I nuked all my worktrees due to a brain-dead `git clean
-fxd`.
2026-04-07 09:51:39 +00:00
Keith Seim
ba33c3daa4 fix: match stub signature of lean_uv_dns_get_info to real implementation (#13234)
This PR fixes a build issue when Lean is not linked against libuv.

## Problem

In `src/runtime/uv/dns.cpp`, the non-libuv stub of
`lean_uv_dns_get_info` (in the `#else` branch, compiled when building
without libuv) has a **4-parameter** signature:

```cpp
lean_uv_dns_get_info(b_obj_arg name, b_obj_arg service, uint8_t family, int8_t protocol)
```

But the real implementation above the `#else` has only **3 parameters**:

```cpp
lean_uv_dns_get_info(b_obj_arg name, b_obj_arg service, uint8_t family)
```

The Lean `@[extern]` declaration also expects 3 parameters. The stub has
an extra `int8_t protocol` parameter that the real function and the Lean
FFI caller do not use.

## Fix

Remove the extra `protocol` parameter from the stub so both branches
have the same signature.

## Evidence

Discovered while building Lean4 to WASM via Emscripten for a production
project ([specify-lean](https://github.com/kjsdesigns/specify)) since
v4.27.0. The stub branch is compiled in this configuration, and the
signature mismatch was caught at link time. The fix has been stable in
production across multiple Lean version bumps.

Related: [Zulip thread on WASM build
fixes](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/WASM.20build.20fixes.3A.20libuv.20symbol.20leakage.20.28.236817.29.20and.20unique_lo/with/580836892)
(2026-03-21).

Co-authored-by: Keith Seim <keith@MacBook-Pro.local>
2026-04-07 09:39:53 +00:00
Keith Seim
db1e2ac34c fix: add missing release() and adopt_lock_t to single-threaded unique_lock stub (#13233)
This PR fixes runtime build issues when `LEAN_MULTI_THREAD` is not set.

## Problem

When building with `LEAN_MULTI_THREAD` undefined (required for
Emscripten/WASM targets), the stub `unique_lock<T>` in
`src/runtime/thread.h` is missing two members that the real
`std::unique_lock` provides:

1. **`release()`** — called by runtime code paths, causes a compile
error when the stub is active
2. **`unique_lock(T const &, std::adopt_lock_t)`** — required by code
that acquires a lock before constructing the `unique_lock`

The other stubs in this file (`mutex`, `lock_guard`,
`condition_variable`) are complete; only `unique_lock` is missing API
surface.

## Fix

Add the two missing members to the single-threaded `unique_lock` stub:

```cpp
unique_lock(T const &, std::adopt_lock_t) {}
T * release() { return nullptr; }
```

Both are no-ops, matching the semantics of a single-threaded
environment. `release()` returns `nullptr` (no mutex to release). The
`adopt_lock_t` constructor is a no-op (no lock to adopt).

## Evidence

I've been using this fix in a production project
([specify-lean](https://github.com/kjsdesigns/specify)) since v4.27.0 to
build the Lean4 runtime to WASM via Emscripten. The fix has been stable
across multiple Lean version bumps.

I posted about this on
[Zulip](https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/WASM.20build.20fixes.3A.20libuv.20symbol.20leakage.20.28.236817.29.20and.20unique_lo/with/580836892)
on 2026-03-21.

Co-authored-by: Keith Seim <keith@MacBook-Pro.local>
2026-04-07 09:30:13 +00:00
Jason Yuen
cb06946972 chore: fix typo in annotatedBorrows (#13276)
Fixes a public-facing typo from #13274.
2026-04-07 09:17:26 +00:00
Sebastian Ullrich
4f6bcc5ada chore: avoid segfault in stage2 (#13296)
Lake.Load must not define metaprograms under the current `--plugin`
setup
2026-04-07 09:08:04 +00:00
Jason Yuen
0650cbe0fa chore: fix typo in isInvalidContinuationByte (#13275)
Fixes a public-facing typo from #13274.
2026-04-07 09:01:19 +00:00
Kim Morrison
8bb07f336d chore: add leansqlite to release repos (#13293)
This PR adds `leansqlite` to `release_repos.yml` and lists it as a
dependency of `doc-gen4`, which requires it.

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 08:54:42 +00:00
Mac Malone
c16e88644c feat: Runtime.hold (#13270)
This PR adds `Runtime.hold`, which ensures its argument remains alive
until the callsite by holding a reference to it. This can be useful for
unsafe code (such as an FFI) that relies on a Lean object not being
freed until after some point in the program.

It is implemented via a `static inline` function in `lean.h` so that C
will optimize away the call.

---------

Co-authored-by: Henrik Böving <hargonix@gmail.com>
2026-04-07 08:44:35 +00:00
Mac Malone
96d502bd11 refactor: introduce LakefileConfig & well-formed workspaces (#13282)
This PR introduces `LakefileConfig`, which can be constructed from a
Lake configuration file without all the information required to
construct a full `Package`. Also, workspaces now have a well-formedness
property attached which ensures the workspace indices of its packages
match their index in the workspace. Finally, the facet configuration map
now has its own type: `FacetConfigMap`.

Created by splitting off the major self-contained (but overlapping)
refactors from #11662.
2026-04-06 23:34:48 +00:00
Joachim Breitner
d48863fc2b fix: add checkInterrupted to Core.withIncRecDepth (#13290)
This PR adds a `checkInterrupted` call to `Core.withIncRecDepth`, so
that all recursive CoreM-based operations (inferType, whnf, isDefEq,
simp, …) check for cancellation on each recursion step. Previously,
these operations could run for seconds without responding to IDE
cancellation requests.

This is the single highest-impact change for IDE cancellation
responsiveness. It covers all recursive MetaM/TacticM operations at
once, eliminating the vast majority of multi-second gaps identified by
the `LEAN_CHECK_SYSTEM_INTERVAL_MS` monitoring in #13212.

Gap measurements pending — will be added after CI benchmarks and
instrumented measurement.

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

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 13:45:30 +00:00
Joachim Breitner
c4a664eb5d fix: add checkSystem to LCNF compiler passes (#13231)
This PR adds checkSystem calls to the LCNF compiler to improve IDE
cancellation responsiveness during compilation of large declarations.

Two changes:
1. `Pass.mkPerDeclaration` now calls `checkSystem` before processing
each declaration, allowing interruption between declarations.
2. The LCNF `simp` pass calls `checkSystem` every 128 recursive visits,
allowing interruption within large single-declaration simplifications.

Note: LCNF simp has its own custom `withIncRecDepth` (in `SimpM.lean`)
that does **not** call `checkInterrupted`, unlike
`Core.withIncRecDepth`. So this `checkSystem` call is the only
cancellation check on this code path.

Performance: unconditional `checkSystem` added +0.44% instructions on
`big_do.lean` per CI benchmarks. Amortizing to every 128 visits brings
overhead to noise level while keeping the max simp gap under ~80M
instructions (~14ms at 6 Ginstr/s).

**Before** (measured with `LEAN_CHECK_SYSTEM_INTERVAL_INSN` on the
instrumentation branch):

| Test | Largest LCNF gap | Time at 6 Ginstr/s |
|------|------------------|-------------------|
| `big_do.lean` | 12,623M insn (simp) | 2.1s |
| `riscv-ast.lean` | 1,162M insn (simp) | 194ms |
| `riscv-ast.lean` | 37 gaps total | — |

**After:**

| Test | Largest LCNF gap | Time at 6 Ginstr/s |
|------|------------------|-------------------|
| `big_do.lean` | 869M insn (resetReuse) | 145ms |
| `riscv-ast.lean` | 232M insn (simp) | 39ms |
| `riscv-ast.lean` | reduced gap count | — |

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-06 09:35:58 +00:00
Leonardo de Moura
0cd6dbaad2 feat: add Sym.Arith infrastructure for arithmetic normalization (#13289)
This PR adds the shared infrastructure for arithmetic normalization in
`Sym.Arith/`,
laying the groundwork for both `Sym.simp`'s arith pre-simproc and the
eventual
unification of grind's `CommRing` module.

The key components:

- **`Types.lean`**: Classification structures (`Semiring`, `Ring`,
`CommRing`,
`CommSemiring`) stored in a `SymExtension`. These are the
classification-only
subset of grind's ring types — no solver state. Includes
`withExpThreshold` for
  controlling exponent evaluation limits.

- **`EvalNum.lean`**: `evalNat?`/`evalInt?` for evaluating ground
Nat/Int
expressions in type classes (e.g., `IsCharP`), adapted from grind to
`SymM`.

- **`Classify.lean`**: Algebraic structure detection (CommRing > Ring >
CommSemiring > Semiring) with a single `typeClassify : PHashMap ExprPtr
ClassifyResult` cache.
  Detects `IsCharP`, `NoNatZeroDivisors`, and `Field` instances.

- **Type classes**: `MonadCanon`, `MonadRing`/`MonadCommRing`,
`MonadSemiring`/`MonadCommSemiring`, `MonadGetVar`/`MonadMkVar` —
abstract over
the monad so the same code works in both `SymM` and grind's
`RingM`/`SemiringM`.
Grind's `MonadCanon` is deleted; grind's monads inherit it from `SymM`
via
  `MonadLift`.

- **`Functions.lean`**: Cached function getters (`getAddFn`, `getMulFn`,
etc.)
generic over the type classes. Synthesizes instances, validates via
`isDefEqI`,
  canonicalizes via `canonExpr`.

- **`Reify.lean`**: Converts Lean expressions into
`RingExpr`/`SemiringExpr` for
reflection-based normalization. Variable creation abstracted via
`MonadMkVar`.

- **`DenoteExpr.lean`**: Converts reified expressions back to Lean
`Expr`s.
Roundtrip tests confirm reify→denote produces definitionally equal
results.

- **`ToExpr.lean`**, **`VarRename.lean`**, **`Poly.lean`**: Moved from
`Grind.Arith.CommRing/` — pure utilities on `Grind.CommRing` types with
no
  solver dependencies.

- **Tests**: Unit tests for classification (`Int` → commRing, `Nat` →
commSemiring,
`Rat` → commRing), `evalNat?`/`evalInt?`, exp threshold, and
reify-denote roundtrips.
  
  
**TODO**: use abstractions to implement `grind` ring module, and delete
code duplication.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 05:21:09 +00:00
Joachim Breitner
34d00cb50d feat: add checkSystem calls to bv_decide for cancellation responsiveness (#13284)
This PR adds `checkSystem` calls to the `bv_decide` tactic's main loops
to improve IDE cancellation responsiveness. Previously, `bv_decide` had
zero `checkSystem` calls in its entire codebase, meaning long-running
invocations could not be cancelled promptly.

Insertion points:
- `fixpointPipeline` (normalization fixpoint loop)
- `ReifiedBVExpr.of.go` (recursive BitVec expression reification)
- `ReifiedBVLogical.of.go` (recursive boolean expression reification)
- `reflectBV` (hypothesis loop)

These cover the MetaM-based phases of `bv_decide`. The pure computation
phases (bitblasting via `IO.lazyPure`, AIG→CNF conversion) remain
without checks and would require restructuring to address.

Gap measurements (at 6.0 Ginstr/s, using
`LEAN_CHECK_SYSTEM_INTERVAL_INSN` from #13212):
- `bv_decide_large_aig.lean`: before ~3.3s max gap (19,899M insn),
after: pure bitblast/AIG gaps remain but reification and normalization
phases are now interruptible
- Other bv_decide bench files: normalization and reification loops are
now responsive to cancellation

Note: the largest remaining gaps in bv_decide benchmarks are in the
kernel type checker (proof term checking, up to 10.5s on
`bv_decide_mod.lean`) and in pure bitblasting — both out of scope for
this PR.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 13:11:39 +00:00
Tom Levy
a73be70607 doc: fix typo in doc of Functor.mapConst (#13285)
This PR fixes a typo.

---

Additional comment:

I found the description hard to read (in part because the term "constant
function" is not one I encounter frequently enough, and because there is
no explicit constant function in the signature). Would you consider
changing the first sentence from "Mapping a constant function" to
"Replaces the value in a functor with a constant, retaining the
functor's structure" (based on Functor.discard)?

Also, I would write `(fun _ => a)` rather than `Function.const _ a`.

---------

Co-authored-by: Joachim Breitner <mail@joachim-breitner.de>
2026-04-05 09:16:49 +00:00
Lean stage0 autoupdater
3d49476058 chore: update stage0 2026-04-05 00:39:54 +00:00
Leonardo de Moura
adc45d7c7b feat: mark exposed match auxiliary declarations as implicit_reducible (#13281)
This PR marks any exposed (non-private) auxiliary match declaration as
`[implicit_reducible]`. This is essential when the outer declaration is
marked as `instance_reducible` — without it, reduction is blocked at the
match auxiliary. We do not inherit the attribute from the parent
declaration because match auxiliary declarations are reused across
definitions, and the reducibility setting of the parent can change
independently. This change prepares for implementing the TODO at
`ExprDefEq.lean:465`, which would otherwise cause too many failures
requiring manual `[implicit_reducible]` annotations on match
declarations whose names are not necessarily derived from the outer
function.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 23:55:47 +00:00
Jason Yuen
9efba691e7 chore: fix typo in checkArtifactsExist (#13277)
This PR fixes a public-facing typo in a function name:
`Module.checkArtifactsExsist` -> `Module.checkArtifactsExist`.
2026-04-04 18:56:10 +00:00
Lean stage0 autoupdater
681856324f chore: update stage0 2026-04-04 18:50:11 +00:00
Leonardo de Moura
9f49ea63e2 feat: add backward.isDefEq.respectTransparency.types option (#13280)
This PR adds a new option `backward.isDefEq.respectTransparency.types`
that controls the transparency used when checking whether the type of a
metavariable matches the type of the term being assigned to it during
`checkTypesAndAssign`. Previously, this check always bumped transparency
to `.default` (via `withInferTypeConfig`), which is overly permissive.
The new option uses `.instances` transparency instead (via
`withImplicitConfig`), matching the behavior already used for implicit
arguments.

The option defaults to `false` (preserving old behavior) until stage0 is
updated and breakage is assessed. If
`backward.isDefEq.respectTransparency` (already in v4.29) is set to
`false`, then `backward.isDefEq.respectTransparency.types` is
automatically treated as `false` too.

When `diagnostics` is enabled, a trace message is emitted if the
stricter transparency fails but `.default` would have succeeded, helping
identify affected code. To investigate failures when enabling
`backward.isDefEq.respectTransparency.types`, use:

```
set_option diagnostics true
set_option trace.diagnostics true
```

Also renames `withInstanceConfig` to `withImplicitConfig` since it now
serves implicit argument and type checking, not just instances.
Registers the `diagnostics` trace class in `CoreM`.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 18:05:33 +00:00
Jason Yuen
3770b3dcb8 chore: fix spelling errors (#13274)
This PR fixed typos:

```
pip install codespell --upgrade
codespell --summary --ignore-words-list enew,forin,fro,happend,hge,ihs,iterm,spred --skip stage0 --check-filenames
codespell --summary --ignore-words-list enew,forin,fro,happend,hge,ihs,iterm,spred --skip stage0 --check-filenames --regex '[A-Z][a-z]*'
codespell --summary --ignore-words-list enew,forin,fro,happend,hge,ihs,iterm,spred --skip stage0 --check-filenames --regex "\b[a-z']*"
```
2026-04-04 07:34:34 +00:00
Leonardo de Moura
3c6ea49d0e feat: add mkAppNS, mkAppRevS, betaRevS, betaS, and related Sym functions (#13273)
This PR adds a comprehensive public API for constructing maximally
shared
expression applications and performing beta reduction in the `Sym`
framework.
These functions were previously defined locally in the VC generator and
cbv
tactic, and are needed by downstream `SymM`-based tools.

New functions in `Lean.Meta.Sym.Internal` (generic over
`MonadShareCommon`):
- `mkAppS₆` through `mkAppS₁₁` (higher-arity application builders)
- `mkAppRangeS`, `mkAppNS` (forward application over arrays/ranges)
- `mkAppRevRangeS`, `mkAppRevS` (reversed application over
arrays/ranges)

New public functions in `Lean.Meta.Sym` (`SymM`):
- `betaRevS` and `betaS` (beta reduction with max sharing)
- `mkForallFVarsS` (forall abstraction with max sharing)

The `AlphaShareBuilderM`-specific `mkAppRevRangeS` in
`InstantiateS.lean` is
replaced by the generic version from `Internal`, and the internal
`betaRevS`
is renamed to `betaRevS'`. The `Cbv.mkAppNS` now delegates to
`Internal.mkAppNS`.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 02:31:16 +00:00
Leonardo de Moura
608e0d06a8 fix: extend sym canonicalizer reductions to value positions (#13272)
This PR extends the sym canonicalizer to apply reductions (projection,
match/ite/cond, Nat
arithmetic) in all positions, not just inside types. Previously, a value
`v` appearing in a
type `T(v)` could remain unreduced while `T(v)` was normalized, breaking
the invariant that
definitionally equal types are structurally identical after
canonicalization.

Changes:
- Remove `insideType` guards from `canonApp` and `canonProj`, so
reductions apply unconditionally
(eta reduction remains type-only, to preserve lambda structure for
`grind`)
- Add `canonInstDecCore` to handle `Decidable` instances in
`if-then-else` expressions, dispatching
`Grind.nestedDecidable` to `canonInstDec` and falling back silently for
other instances
- Add `report` parameter to `canonInstCore`/`canonInst'`/`canonInst` to
allow suppressing issue
reporting for propositional and decidable instances that cannot be
resynthesized (common with
`haveI`-provided instances that propagate into types through forward
dependencies)
- Update module documentation to reflect the new reduction scope and the
`haveI` reporting tradeoff

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 01:52:24 +00:00
Leonardo de Moura
5fdeaf0d5a fix: handle propositional and decidable instances in sym canonicalizer (#13271)
This PR refactors instance canonicalization in the sym canonicalizer to
properly handle
\`Grind.nestedProof\` and \`Grind.nestedDecidable\` markers. Previously,
the canonicalizer
would report an issue when it failed to resynthesize propositional
instances that were
provided by \`grind\` itself or by the user via \`haveI\`. Now,
resynthesis failure gracefully
falls back to the original instance in value positions, while remaining
strict inside types.

Changes:
- Extract \`canonInstCore\` as the shared resynthesis + defEq-check
logic
- Add \`canonInstProp\` for \`Grind.nestedProof\`: canonicalize the
proposition, attempt resynthesis, fall back silently (proof irrelevance
means no defEq check needed)
- Add \`canonInstDec\`/\`canonInstDec'\` for \`Grind.nestedDecidable\`:
canonicalize the proposition, attempt resynthesis with defEq guard, fall
back silently
- Remove the separate \`cacheInsts\` cache in favor of the existing
type/value caches via \`withCaching\`
- Update module-level documentation

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 00:40:39 +00:00
319 changed files with 2292 additions and 490 deletions

1
.gitignore vendored
View File

@@ -34,3 +34,4 @@ wdErr.txt
wdIn.txt
wdOut.txt
downstream_releases/
.claude/worktrees/

View File

@@ -28,6 +28,14 @@ repositories:
branch: main
dependencies: []
- name: leansqlite
url: https://github.com/leanprover/leansqlite
toolchain-tag: true
stable-branch: false
branch: main
dependencies:
- plausible
- name: verso
url: https://github.com/leanprover/verso
toolchain-tag: true
@@ -100,7 +108,7 @@ repositories:
toolchain-tag: true
stable-branch: false
branch: main
dependencies: [lean4-cli, BibtexQuery, mathlib4]
dependencies: [lean4-cli, BibtexQuery, mathlib4, leansqlite]
- name: cslib
url: https://github.com/leanprover/cslib

View File

@@ -271,7 +271,7 @@ private def optionPelim' {α : Type u_1} (t : Option α) {β : Sort u_2}
/--
Inserts an `Option` case distinction after the first computation of a call to `MonadAttach.pbind`.
This lemma is useful for simplifying the second computation, which often involes `match` expressions
This lemma is useful for simplifying the second computation, which often involves `match` expressions
that use `pbind`'s proof term.
-/
private theorem pbind_eq_pbind_if_isSome [Monad m] [MonadAttach m] (x : m (Option α)) (f : (_ : _) _ m β) :

View File

@@ -363,7 +363,7 @@ theorem toBitVec_eq_of_parseFirstByte_eq_threeMore {b : UInt8} (h : parseFirstBy
public def isInvalidContinuationByte (b : UInt8) : Bool :=
b &&& 0xc0 != 0x80
theorem isInvalidContinutationByte_eq_false_iff {b : UInt8} :
theorem isInvalidContinuationByte_eq_false_iff {b : UInt8} :
isInvalidContinuationByte b = false b &&& 0xc0 = 0x80 := by
simp [isInvalidContinuationByte]

View File

@@ -31,7 +31,7 @@ namespace Slice
/--
A list of all positions starting at {name}`p`.
This function is not meant to be used in actual progams. Actual programs should use
This function is not meant to be used in actual programs. Actual programs should use
{name}`Slice.positionsFrom` or {name}`Slice.positions`.
-/
protected def Model.positionsFrom {s : Slice} (p : s.Pos) : List { p : s.Pos // p s.endPos } :=
@@ -206,7 +206,7 @@ end Slice
/--
A list of all positions starting at {name}`p`.
This function is not meant to be used in actual progams. Actual programs should use
This function is not meant to be used in actual programs. Actual programs should use
{name}`Slice.positionsFrom` or {name}`Slice.positions`.
-/
protected def Model.positionsFrom {s : String} (p : s.Pos) : List { p : s.Pos // p s.endPos } :=

View File

@@ -23,7 +23,7 @@ Given a {name}`Slice` {name}`s`, the type {lean}`s.Subslice` is the type of half
in {name}`s` delineated by a valid position on both sides.
This type is useful to track regions of interest within some larger slice that is also of interest.
In contrast, {name}`Slice` is used to track regions of interest whithin some larger string that is
In contrast, {name}`Slice` is used to track regions of interest within some larger string that is
not or no longer relevant.
Equality on {name}`Subslice` is somewhat better behaved than on {name}`Slice`, but note that there

View File

@@ -145,7 +145,7 @@ Examples:
The constant function that ignores its argument.
If `a : α`, then `Function.const β a : β → α` is the “constant function with value `a`”. For all
arguments `b : β`, `Function.const β a b = a`.
arguments `b : β`, `Function.const β a b = a`. It is often written directly as `fun _ => a`.
Examples:
* `Function.const Bool 10 true = 10`
@@ -3754,7 +3754,7 @@ class Functor (f : Type u → Type v) : Type (max (u+1) v) where
/--
Mapping a constant function.
Given `a : α` and `v : f α`, `mapConst a v` is equivalent to `Function.const _ a <$> v`. For some
Given `a : α` and `v : f β`, `mapConst a v` is equivalent to `(fun _ => a) <$> v`. For some
functors, this can be implemented more efficiently; for all other functors, the default
implementation may be used.
-/

View File

@@ -1880,3 +1880,12 @@ lead to undefined behavior.
-/
@[extern "lean_runtime_forget"]
def Runtime.forget (a : α) : BaseIO Unit := return
set_option linter.unusedVariables false in
/--
Ensures `a` remains at least alive until the call site by holding a reference to `a`. This can be useful
for unsafe code (such as an FFI) that relies on a Lean object not being freed until after some point
in the program. At runtime, this will be a no-op as the C compiler will optimize away this call.
-/
@[extern "lean_runtime_hold"]
def Runtime.hold (a : @& α) : BaseIO Unit := return

View File

@@ -67,7 +67,7 @@ structure ParamMap where
The set of fvars that were already annotated as borrowed before arriving at this pass. We try to
preserve the annotations here if possible.
-/
annoatedBorrows : Std.HashSet FVarId := {}
annotatedBorrows : Std.HashSet FVarId := {}
namespace ParamMap
@@ -95,7 +95,7 @@ where
modify fun m =>
{ m with
map := m.map.insert (.decl decl.name) (initParamsIfNotExported exported decl.params),
annoatedBorrows := decl.params.foldl (init := m.annoatedBorrows) fun acc p =>
annotatedBorrows := decl.params.foldl (init := m.annotatedBorrows) fun acc p =>
if p.borrow then acc.insert p.fvarId else acc
}
goCode decl.name code
@@ -116,7 +116,7 @@ where
modify fun m =>
{ m with
map := m.map.insert (.jp declName decl.fvarId) (initParams decl.params),
annoatedBorrows := decl.params.foldl (init := m.annoatedBorrows) fun acc p =>
annotatedBorrows := decl.params.foldl (init := m.annotatedBorrows) fun acc p =>
if p.borrow then acc.insert p.fvarId else acc
}
goCode declName decl.value
@@ -286,7 +286,7 @@ where
ownFVar (fvarId : FVarId) (reason : OwnReason) : InferM Unit := do
unless ( get).owned.contains fvarId do
if !reason.isForced && ( get).paramMap.annoatedBorrows.contains fvarId then
if !reason.isForced && ( get).paramMap.annotatedBorrows.contains fvarId then
trace[Compiler.inferBorrow] "user annotation blocked owning {← PP.run <| PP.ppFVar fvarId}: {← reason.toString}"
else
trace[Compiler.inferBorrow] "own {← PP.run <| PP.ppFVar fvarId}: {← reason.toString}"

View File

@@ -121,7 +121,7 @@ def mkPerDeclaration (name : Name) (phase : Phase)
occurrence := occurrence
phase := phase
name := name
run := fun xs => xs.mapM run
run := fun xs => xs.mapM fun decl => do checkSystem "LCNF compiler"; run decl
end Pass

View File

@@ -28,7 +28,7 @@ inserts addition instructions to attempt to reuse the memory right away instead
allocator.
For this the paper defines three functions:
- `R` (called `Decl.insertResetReuse` here) which looks for candidates that might be elligible for
- `R` (called `Decl.insertResetReuse` here) which looks for candidates that might be eligible for
reuse. For these variables it invokes `D`.
- `D` which looks for code regions in which the target variable is dead (i.e. no longer read from),
it then invokes `S`. If `S` succeeds it inserts a `reset` instruction to match the `reuse`

View File

@@ -217,6 +217,8 @@ Simplify `code`
-/
partial def simp (code : Code .pure) : SimpM (Code .pure) := withIncRecDepth do
incVisited
if ( get).visited % 128 == 0 then
checkSystem "LCNF simp"
match code with
| .let decl k =>
let baseDecl := decl

View File

@@ -24,7 +24,7 @@ In particular we perform:
- folding of the most common cases arm into the default arm if possible
Note: Currently the simplifier still contains almost equivalent simplifications to the ones shown
here. We know that this causes unforseen behavior and do plan on changing it eventually.
here. We know that this causes unforeseen behavior and do plan on changing it eventually.
-/
-- TODO: the following functions are duplicated from simp and should be deleted in simp once we

View File

@@ -171,7 +171,7 @@ def toDecl (declName : Name) : CompilerM (Decl .pure) := do
if compiler.ignoreBorrowAnnotation.get ( getOptions) then
decl := { decl with params := decl.params.mapM (·.updateBorrow false) }
if isExport env decl.name && decl.params.any (·.borrow) then
throwError m!" Declaration {decl.name} is marked as `export` but some of its parameters have borrow annotations.\n Consider using `set_option compiler.ignoreBorrowAnnotation true in` to supress the borrow annotations in its type.\n If the declaration is part of an `export`/`extern` pair make sure to also supress the annotations at the `extern` declaration."
throwError m!" Declaration {decl.name} is marked as `export` but some of its parameters have borrow annotations.\n Consider using `set_option compiler.ignoreBorrowAnnotation true in` to suppress the borrow annotations in its type.\n If the declaration is part of an `export`/`extern` pair make sure to also suppress the annotations at the `extern` declaration."
return decl
end Lean.Compiler.LCNF

View File

@@ -20,6 +20,8 @@ register_builtin_option diagnostics : Bool := {
descr := "collect diagnostic information"
}
builtin_initialize registerTraceClass `diagnostics
register_builtin_option diagnostics.threshold : Nat := {
defValue := 20
descr := "only diagnostic counters above this threshold are reported by the definitional equality"
@@ -444,10 +446,6 @@ Note that the value of `ctx.initHeartbeats` is ignored and replaced with `IO.get
@[inline] def CoreM.toIO' (x : CoreM α) (ctx : Context) (s : State) : IO α :=
(·.1) <$> x.toIO ctx s
/-- withIncRecDepth for a monad `m` such that `[MonadControlT CoreM n]`. -/
protected def withIncRecDepth [Monad m] [MonadControlT CoreM m] (x : m α) : m α :=
controlAt CoreM fun runInBase => withIncRecDepth (runInBase x)
/--
Throws an internal interrupt exception if cancellation has been requested. The exception is not
caught by `try catch` but is intended to be caught by `Command.withLoggingExceptions` at the top
@@ -462,6 +460,12 @@ heartbeat tracking (e.g. `SynthInstance`).
if ( tk.isSet) then
throwInterruptException
/-- withIncRecDepth for a monad `m` such that `[MonadControlT CoreM n]`.
Also checks for cancellation, so that recursive elaboration functions
(inferType, whnf, isDefEq, …) respond promptly to interrupt requests. -/
protected def withIncRecDepth [Monad m] [MonadControlT CoreM m] (x : m α) : m α :=
controlAt CoreM fun runInBase => do checkInterrupted; withIncRecDepth (runInBase x)
register_builtin_option debug.moduleNameAtTimeout : Bool := {
defValue := true
descr := "include module name in deterministic timeout error messages.\nRemark: we set this option to false to increase the stability of our test suite"

View File

@@ -233,25 +233,33 @@ def processDefDeriving (view : DerivingClassView) (decl : Expr) (isNoncomputable
finally
Core.setMessageLog (msgLog ++ ( Core.getMessageLog))
let env getEnv
let hints := ReducibilityHints.regular (getMaxHeight env result.value + 1)
let decl mkDefinitionValInferringUnsafe instName result.levelParams.toList result.type result.value hints
-- Pre-check: if the instance value depends on noncomputable definitions and the user didn't write
-- `noncomputable`, give an actionable error with a `Try this:` suggestion.
unless isNoncomputable || ( read).isNoncomputableSection || ( isProp result.type) do
let noncompRef? := preNormValue.foldConsts none fun n acc =>
acc <|> if Lean.isNoncomputable (asyncMode := .local) env n then some n else none
if let some noncompRef := noncompRef? then
if let some cmdRef := cmdRef? then
if let some origText := cmdRef.reprint then
let newText := (origText.replace "deriving instance " "deriving noncomputable instance ").trimAscii
logInfoAt cmdRef m!"Try this: {newText}"
throwError "failed to derive instance because it depends on \
`{.ofConstName noncompRef}`, which is noncomputable"
if isNoncomputable || ( read).isNoncomputableSection then
addDecl <| Declaration.defnDecl decl
modifyEnv (addNoncomputable · instName)
let isPropType isProp result.type
if isPropType then
let decl mkThmOrUnsafeDef {
name := instName, levelParams := result.levelParams.toList,
type := result.type, value := result.value
}
addDecl decl
else
addAndCompile <| Declaration.defnDecl decl
let hints := ReducibilityHints.regular (getMaxHeight env result.value + 1)
let decl mkDefinitionValInferringUnsafe instName result.levelParams.toList result.type result.value hints
-- Pre-check: if the instance value depends on noncomputable definitions and the user didn't write
-- `noncomputable`, give an actionable error with a `Try this:` suggestion.
unless isNoncomputable || ( read).isNoncomputableSection do
let noncompRef? := preNormValue.foldConsts none fun n acc =>
acc <|> if Lean.isNoncomputable (asyncMode := .local) env n then some n else none
if let some noncompRef := noncompRef? then
if let some cmdRef := cmdRef? then
if let some origText := cmdRef.reprint then
let newText := (origText.replace "deriving instance " "deriving noncomputable instance ").trimAscii
logInfoAt cmdRef m!"Try this: {newText}"
throwError "failed to derive instance because it depends on \
`{.ofConstName noncompRef}`, which is noncomputable"
if isNoncomputable || ( read).isNoncomputableSection then
addDecl <| Declaration.defnDecl decl
modifyEnv (addNoncomputable · instName)
else
addAndCompile <| Declaration.defnDecl decl
trace[Elab.Deriving] "Derived instance `{.ofConstName instName}`"
registerInstance instName AttributeKind.global (eval_prio default)
addDeclarationRangesFromSyntax instName ( getRef)

View File

@@ -111,7 +111,7 @@ def mkMatchNew (ctx : Context) (header : Header) (indVal : InductiveVal) : TermE
let x1 := mkIdent header.targetNames[0]!
let x2 := mkIdent header.targetNames[1]!
let ctorIdxName := mkCtorIdxName indVal.name
-- NB: the getMatcherInfo? assumes all mathcers are called `match_`
-- NB: the getMatcherInfo? assumes all matchers are called `match_`
let casesOnSameCtorName mkFreshUserName (indVal.name ++ `match_on_same_ctor)
mkCasesOnSameCtor casesOnSameCtorName indVal.name
let alts Array.ofFnM (n := indVal.numCtors) fun ctorIdx, _ => do

View File

@@ -36,7 +36,7 @@ def mkContext (lratPath : System.FilePath) (cfg : BVDecideConfig) : TermElabM Ta
TacticContext.new lratPath cfg
/--
Prepare an `Expr` that proves `bvExpr.unsat` using native evalution.
Prepare an `Expr` that proves `bvExpr.unsat` using native evaluation.
-/
def lratChecker (ctx : TacticContext) (reflectionResult : ReflectionResult) : MetaM Expr := do
let cert LratCert.ofFile ctx.lratPath ctx.config.trimProofs

View File

@@ -357,6 +357,7 @@ def reflectBV (g : MVarId) : M ReflectionResult := g.withContext do
let mut sats := #[]
let mut unusedHypotheses := {}
for hyp in hyps do
checkSystem "bv_decide"
if let (some reflected, lemmas) (SatAtBVLogical.of (mkFVar hyp)).run then
sats := (sats ++ lemmas).push reflected
else

View File

@@ -33,6 +33,7 @@ where
Reify `x`, returns `none` if the reification procedure failed.
-/
go (origExpr : Expr) : LemmaM (Option ReifiedBVExpr) := do
checkSystem "bv_decide"
match_expr origExpr with
| BitVec.ofNat _ _ => goBvLit origExpr
| HAnd.hAnd _ _ _ _ lhsExpr rhsExpr =>
@@ -340,6 +341,7 @@ where
Reify `t`, returns `none` if the reification procedure failed.
-/
go (origExpr : Expr) : LemmaM (Option ReifiedBVLogical) := do
checkSystem "bv_decide"
match_expr origExpr with
| Bool.true => ReifiedBVLogical.mkBoolConst true
| Bool.false => ReifiedBVLogical.mkBoolConst false

View File

@@ -159,6 +159,7 @@ Repeatedly run a list of `Pass` until they either close the goal or an iteration
the goal anymore.
-/
partial def fixpointPipeline (passes : List Pass) (goal : MVarId) : PreProcessM (Option MVarId) := do
checkSystem "bv_decide"
let mut newGoal := goal
for pass in passes do
if let some nextGoal pass.run newGoal then

View File

@@ -48,6 +48,16 @@ register_builtin_option backward.isDefEq.respectTransparency : Bool := {
when checking whether implicit arguments are definitionally equal"
}
/--
Controls the transparency used to check whether the type of metavariable matches the type of the
term being assigned to it.
-/
register_builtin_option backward.isDefEq.respectTransparency.types : Bool := {
defValue := false -- TODO: replace with `true` after we fix stage0
descr := "if true, do not bump transparency to `.default` \
when checking whether the type of a metavariable matches the type of the term being assigned to it."
}
/--
Controls whether *all* implicit arguments (not just instance-implicit `[..]`) get their
transparency bumped to `TransparencyMode.instances` during `isDefEq`.
@@ -335,10 +345,10 @@ private def isDefEqArgsFirstPass
/--
Ensure `MetaM` configuration is strong enough for checking definitional equality of
instances. For example, we must be able to unfold instances, `beta := true`, `proj := .yesWithDelta`
are essential.
implicit arguments (e.g., instances) and types.
For example, we must be able to unfold instances, `beta := true`, `proj := .yesWithDelta` are essential.
-/
@[inline] def withInstanceConfig (x : MetaM α) : MetaM α :=
@[inline] def withImplicitConfig (x : MetaM α) : MetaM α :=
withAtLeastTransparency .instances do
let cfg getConfig
if cfg.beta && cfg.iota && cfg.zeta && cfg.zetaHave && cfg.zetaDelta && cfg.proj == .yesWithDelta then
@@ -382,7 +392,7 @@ private partial def isDefEqArgs (f : Expr) (args₁ args₂ : Array Expr) : Meta
-- Bump to `.instances` so that `[implicit_reducible]` definitions (instances, `Nat.add`,
-- `Array.size`, etc.) are unfolded. The user doesn't choose implicit arguments directly,
-- so Lean should try harder than the caller's transparency to make them match.
unless ( withInstanceConfig <| Meta.isExprDefEqAux a₁ a₂) do return false
unless ( withImplicitConfig <| Meta.isExprDefEqAux a₁ a₂) do return false
else if respectTransparency then
unless ( Meta.isExprDefEqAux a₁ a₂) do return false
else
@@ -392,7 +402,7 @@ private partial def isDefEqArgs (f : Expr) (args₁ args₂ : Array Expr) : Meta
let a₁ := args₁[i]!
let a₂ := args₂[i]!
if respectTransparency && (implicitBump || finfo.paramInfo[i]!.isInstance) then
unless ( withInstanceConfig <| Meta.isExprDefEqAux a₁ a₂) do return false
unless ( withImplicitConfig <| Meta.isExprDefEqAux a₁ a₂) do return false
else if !respectTransparency && finfo.paramInfo[i]!.isInstance then
-- Old behavior
unless ( withInferTypeConfig <| Meta.isExprDefEqAux a₁ a₂) do return false
@@ -454,6 +464,19 @@ private partial def isDefEqBindingAux (lctx : LocalContext) (fvars : Array Expr)
let lctx getLCtx
isDefEqBindingAux lctx #[] a b #[]
/--
Returns `true` if both `backward.isDefEq.respectTransparency` and `backward.isDefEq.respectTransparency.types` is true.
The option `backward.isDefEq.respectTransparency.types` is newer than ``backward.isDefEq.respectTransparency`,
and is used to enable the transparency bump when checking metavariable assignments.
If `backward.isDefEq.respectTransparency` is `false`, then we automatically disable
`backward.isDefEq.respectTransparency.types` too.
-/
abbrev respectTransparencyAtTypes : CoreM Bool := do
let opts getOptions
return backward.isDefEq.respectTransparency.types.get opts && backward.isDefEq.respectTransparency.get opts
private def checkTypesAndAssign (mvar : Expr) (v : Expr) : MetaM Bool :=
withTraceNodeBefore `Meta.isDefEq.assign.checkTypes (fun _ => return m!"({mvar} : {← inferType mvar}) := ({v} : {← inferType v})") do
if !mvar.isMVar then
@@ -462,14 +485,24 @@ private def checkTypesAndAssign (mvar : Expr) (v : Expr) : MetaM Bool :=
else
-- must check whether types are definitionally equal or not, before assigning and returning true
let mvarType inferType mvar
-- **TODO**: avoid transparency bump. Let's fix other issues first
withInferTypeConfig do
let vType inferType v
if ( Meta.isExprDefEqAux mvarType vType) then
mvar.mvarId!.assign v
pure true
else
pure false
let vType inferType v
if ( respectTransparencyAtTypes) then
withImplicitConfig do
if ( Meta.isExprDefEqAux mvarType vType) then
mvar.mvarId!.assign v
return true
else
if ( isDiagnosticsEnabled) then withInferTypeConfig do
if ( Meta.isExprDefEqAux mvarType vType) then
trace[diagnostics] "failure when assigning metavariable with type{indentExpr mvarType}\nwhich is not definitionally equal to{indentExpr vType}\nwhen using `.instances` transparency, but it is with `.default`.\nWorkaround: `set_option backward.isDefEq.respectTransparency.types false`"
return false
else
withInferTypeConfig do
if ( Meta.isExprDefEqAux mvarType vType) then
mvar.mvarId!.assign v
return true
else
return false
/--
Auxiliary method for solving constraints of the form `?m xs := v`.
@@ -2062,7 +2095,7 @@ private def isDefEqProj : Expr → Expr → MetaM Bool
for instance-implicit parameters. -/
let fromClass := isClass ( getEnv) m
let isDefEqStructArgs (x : MetaM Bool) : MetaM Bool :=
if fromClass then withInstanceConfig x else x
if fromClass then withImplicitConfig x else x
if ( read).inTypeClassResolution then
-- See comment at `inTypeClassResolution`
pure (i == j && m == n) <&&> isDefEqStructArgs (Meta.isExprDefEqAux t s)

View File

@@ -33,12 +33,12 @@ The high-level overview of moves are
* If there is an alternative, solve its constraints
* Else use `contradiction` to prove completeness of the match
* Process “independent prefixes” of patterns. These are patterns that can be processed without
affecting the aother alternatives, and without side effects in the sense of updating the `mvarId`.
affecting the other alternatives, and without side effects in the sense of updating the `mvarId`.
These are
- variable patterns; substitute
- inaccessible patterns; add equality constraints
- as-patterns: substitute value and equality
After thes have been processed, we use `.inaccessible x` where `x` is the variable being matched
After these have been processed, we use `.inaccessible x` where `x` is the variable being matched
to mark them as “done”.
* If all patterns start with “done”, drop the first variable
* The first alt has only “done” patterns, drop remaining alts (they're overlapped)
@@ -1108,6 +1108,9 @@ def mkMatcherAuxDefinition (name : Name) (type : Expr) (value : Expr) (isSplitte
-- matcher bodies should always be exported, if not private anyway
withExporting do
addDecl decl
-- if `matcher` is not private, we mark it as `implicit_reducible` too
unless isPrivateName name do
setReducibilityStatus name .implicitReducible
unless isSplitter do
modifyEnv fun env => matcherExt.modifyState env fun s => s.insert key name
addMatcherInfo name mi

View File

@@ -17,7 +17,7 @@ namespace Lean.Meta
/--
Tries to rewrite the `ite`, `dite` or `cond` expression `e` with the hypothesis `hc`.
If it fails, it returns a rewrite with `proof? := none` and unchaged expression.
If it fails, it returns a rewrite with `proof? := none` and unchanged expression.
-/
def rwIfWith (hc : Expr) (e : Expr) : MetaM Simp.Result := do
match_expr e with

View File

@@ -22,9 +22,9 @@ of that computation as an axiom towards the logic.
-/
public inductive NativeEqTrueResult where
/-- The given expression `e` evalutes to true. `prf` is a proof of `e = true`. -/
/-- The given expression `e` evaluates to true. `prf` is a proof of `e = true`. -/
| success (prf : Expr)
/-- The given expression `e` evalutes to false. -/
/-- The given expression `e` evaluates to false. -/
| notTrue
/--

View File

@@ -14,7 +14,7 @@ This module contains utilities for dealing with equalities between constructor a
in particular about which fields must be the same a-priori for the equality to type check.
Users include (or will include) the injectivity theorems, the per-constructor no-confusion
construction and deriving type classes lik `BEq`, `DecidableEq` or `Ord`.
construction and deriving type classes like `BEq`, `DecidableEq` or `Ord`.
-/
namespace Lean.Meta

View File

@@ -25,6 +25,7 @@ public import Lean.Meta.Sym.Simp
public import Lean.Meta.Sym.Util
public import Lean.Meta.Sym.Eta
public import Lean.Meta.Sym.Canon
public import Lean.Meta.Sym.Arith
public import Lean.Meta.Sym.Grind
public import Lean.Meta.Sym.SynthInstance

View File

@@ -97,4 +97,16 @@ public def mkLambdaFVarsS (xs : Array Expr) (e : Expr) : SymM Expr := do
let type abstractFVarsRange decl.type i xs
mkLambdaS decl.userName decl.binderInfo type b
/--
Similar to `mkForallFVars`, but uses the more efficient `abstractFVars` and `abstractFVarsRange`,
and makes the same assumption made by these functions.
-/
public def mkForallFVarsS (xs : Array Expr) (e : Expr) : SymM Expr := do
let b abstractFVars e xs
xs.size.foldRevM (init := b) fun i _ b => do
let x := xs[i]
let decl x.fvarId!.getDecl
let type abstractFVarsRange decl.type i xs
mkForallS decl.userName decl.binderInfo type b
end Lean.Meta.Sym

View File

@@ -189,4 +189,48 @@ def mkAppS₄ (f a₁ a₂ a₃ a₄ : Expr) : m Expr := do
def mkAppS₅ (f a₁ a₂ a₃ a₄ a₅ : Expr) : m Expr := do
mkAppS ( mkAppS₄ f a₁ a₂ a₃ a₄) a₅
def mkAppS₆ (f a₁ a₂ a₃ a₄ a₅ a₆ : Expr) : m Expr := do
mkAppS ( mkAppS₅ f a₁ a₂ a₃ a₄ a₅) a₆
def mkAppS₇ (f a₁ a₂ a₃ a₄ a₅ a₆ a₇ : Expr) : m Expr := do
mkAppS ( mkAppS₆ f a₁ a₂ a₃ a₄ a₅ a₆) a₇
def mkAppS₈ (f a₁ a₂ a₃ a₄ a₅ a₆ a₇ a₈ : Expr) : m Expr := do
mkAppS ( mkAppS₇ f a₁ a₂ a₃ a₄ a₅ a₆ a₇) a₈
def mkAppS₉ (f a₁ a₂ a₃ a₄ a₅ a₆ a₇ a₈ a₉ : Expr) : m Expr := do
mkAppS ( mkAppS₈ f a₁ a₂ a₃ a₄ a₅ a₆ a₇ a₈) a₉
def mkAppS₁₀ (f a₁ a₂ a₃ a₄ a₅ a₆ a₇ a₈ a₉ a₁₀ : Expr) : m Expr := do
mkAppS ( mkAppS₉ f a₁ a₂ a₃ a₄ a₅ a₆ a₇ a₈ a₉) a₁₀
def mkAppS₁₁ (f a₁ a₂ a₃ a₄ a₅ a₆ a₇ a₈ a₉ a₁₀ a₁₁ : Expr) : m Expr := do
mkAppS ( mkAppS₁₀ f a₁ a₂ a₃ a₄ a₅ a₆ a₇ a₈ a₉ a₁₀) a₁₁
/-- `mkAppRangeS f i j #[a₀, ..., aᵢ, ..., aⱼ, ...]` ==> `f aᵢ ... aⱼ₋₁` with max sharing. -/
partial def mkAppRangeS (f : Expr) (beginIdx endIdx : Nat) (args : Array Expr) : m Expr :=
go endIdx f beginIdx
where
go (endIdx : Nat) (b : Expr) (i : Nat) : m Expr := do
if endIdx i then return b
else go endIdx ( mkAppS b args[i]!) (i + 1)
/-- `mkAppNS f #[a₀, ..., aₙ]` constructs `f a₀ ... aₙ` with max sharing. -/
def mkAppNS (f : Expr) (args : Array Expr) : m Expr :=
mkAppRangeS f 0 args.size args
/-- `mkAppRevRangeS f b e revArgs` ==> `mkAppRev f (revArgs.extract b e)` with max sharing. -/
partial def mkAppRevRangeS (f : Expr) (beginIdx endIdx : Nat) (revArgs : Array Expr) : m Expr :=
go revArgs beginIdx f endIdx
where
go (revArgs : Array Expr) (start : Nat) (b : Expr) (i : Nat) : m Expr := do
if i start then return b
else
let i := i - 1
go revArgs start ( mkAppS b revArgs[i]!) i
/-- Same as `mkAppS f args` but reversing `args`, with max sharing. -/
def mkAppRevS (f : Expr) (revArgs : Array Expr) : m Expr :=
mkAppRevRangeS f 0 revArgs.size revArgs
end Lean.Meta.Sym.Internal

View File

@@ -0,0 +1,20 @@
/-
Copyright (c) 2026 Amazon.com, Inc. or its affiliates. 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.Sym.Arith.Types
public import Lean.Meta.Sym.Arith.EvalNum
public import Lean.Meta.Sym.Arith.Classify
public import Lean.Meta.Sym.Arith.MonadCanon
public import Lean.Meta.Sym.Arith.MonadRing
public import Lean.Meta.Sym.Arith.MonadSemiring
public import Lean.Meta.Sym.Arith.MonadVar
public import Lean.Meta.Sym.Arith.Functions
public import Lean.Meta.Sym.Arith.Reify
public import Lean.Meta.Sym.Arith.DenoteExpr
public import Lean.Meta.Sym.Arith.ToExpr
public import Lean.Meta.Sym.Arith.VarRename
public import Lean.Meta.Sym.Arith.Poly

View File

@@ -0,0 +1,143 @@
/-
Copyright (c) 2026 Amazon.com, Inc. or its affiliates. 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.Sym.Arith.EvalNum
import Lean.Meta.Sym.SynthInstance
import Lean.Meta.Sym.Canon
import Lean.Meta.DecLevel
import Init.Grind.Ring
public section
namespace Lean.Meta.Sym.Arith
/-!
# Algebraic structure classification
Detects the strongest algebraic structure available for a type and caches
the classification in `Arith.State.typeClassify`. The detection order is:
1. `Grind.CommRing` (includes `Field` check)
2. `Grind.Ring` (non-commutative)
3. `Grind.CommSemiring` (via `OfSemiring.Q` envelope)
4. `Grind.Semiring` (non-commutative)
Results (including failures) are cached in a single `PHashMap ExprPtr ClassifyResult`
to avoid repeated synthesis attempts.
-/
private def getIsCharInst? (u : Level) (type : Expr) (semiringInst : Expr) : SymM (Option (Expr × Nat)) := do
withNewMCtxDepth do
let n mkFreshExprMVar (mkConst ``Nat)
let charType := mkApp3 (mkConst ``Grind.IsCharP [u]) type semiringInst n
let some charInst Sym.synthInstance? charType | return none
let n instantiateMVars n
let some n evalNat? n | return none
return some (charInst, n)
private def getNoZeroDivInst? (u : Level) (type : Expr) : SymM (Option Expr) := do
let natModuleType := mkApp (mkConst ``Grind.NatModule [u]) type
let some natModuleInst Sym.synthInstance? natModuleType | return none
let noZeroDivType := mkApp2 (mkConst ``Grind.NoNatZeroDivisors [u]) type natModuleInst
Sym.synthInstance? noZeroDivType
/-- Try to classify `type` as a `CommRing`. Returns the ring id on success. -/
private def tryCommRing? (type : Expr) : SymM (Option Nat) := do
let u getDecLevel type
let commRing := mkApp (mkConst ``Grind.CommRing [u]) type
let some commRingInst Sym.synthInstance? commRing | return none
let ringInst := mkApp2 (mkConst ``Grind.CommRing.toRing [u]) type commRingInst
let semiringInst := mkApp2 (mkConst ``Grind.Ring.toSemiring [u]) type ringInst
let commSemiringInst := mkApp2 (mkConst ``Grind.CommRing.toCommSemiring [u]) type semiringInst
let charInst? getIsCharInst? u type semiringInst
let noZeroDivInst? getNoZeroDivInst? u type
let fieldInst? Sym.synthInstance? <| mkApp (mkConst ``Grind.Field [u]) type
let semiringId? := none
let id := ( getArithState).rings.size
let ring : CommRing := {
id, semiringId?, type, u, semiringInst, ringInst, commSemiringInst,
commRingInst, charInst?, noZeroDivInst?, fieldInst?,
}
modifyArithState fun s => { s with rings := s.rings.push ring }
return some id
/-- Try to classify `type` as a non-commutative `Ring`. -/
private def tryNonCommRing? (type : Expr) : SymM (Option Nat) := do
let u getDecLevel type
let ring := mkApp (mkConst ``Grind.Ring [u]) type
let some ringInst Sym.synthInstance? ring | return none
let semiringInst := mkApp2 (mkConst ``Grind.Ring.toSemiring [u]) type ringInst
let charInst? getIsCharInst? u type semiringInst
let id := ( getArithState).ncRings.size
let ring : Ring := {
id, type, u, semiringInst, ringInst, charInst?
}
modifyArithState fun s => { s with ncRings := s.ncRings.push ring }
return some id
/-- Helper function for `tryCommSemiring? -/
private def tryCacheAndCommRing? (type : Expr) : SymM (Option Nat) := do
if let some result := ( getArithState).typeClassify.find? { expr := type } then
let .commRing id := result | return none
return id
let id? tryCommRing? type
let result := match id? with
| none => .none
| some id => .commRing id
modifyArithState fun s => { s with typeClassify := s.typeClassify.insert { expr := type } result }
return id?
/-- Try to classify `type` as a `CommSemiring`. Creates the `OfSemiring.Q` envelope ring. -/
private def tryCommSemiring? (type : Expr) : SymM (Option Nat) := do
let u getDecLevel type
let commSemiring := mkApp (mkConst ``Grind.CommSemiring [u]) type
let some commSemiringInst Sym.synthInstance? commSemiring | return none
let semiringInst := mkApp2 (mkConst ``Grind.CommSemiring.toSemiring [u]) type commSemiringInst
let q shareCommon ( Sym.canon (mkApp2 (mkConst ``Grind.Ring.OfSemiring.Q [u]) type semiringInst))
-- The envelope `Q` type must be classifiable as a CommRing.
let some ringId tryCacheAndCommRing? q
| reportIssue! "unexpected failure initializing ring{indentExpr q}"; return none
let id := ( getArithState).semirings.size
let semiring : CommSemiring := {
id, type, ringId, u, semiringInst, commSemiringInst
}
modifyArithState fun s => { s with semirings := s.semirings.push semiring }
-- Link the envelope ring back to this semiring
modifyArithState fun s =>
let rings := s.rings.modify ringId fun r => { r with semiringId? := some id }
{ s with rings }
return some id
/-- Try to classify `type` as a non-commutative `Semiring`. -/
private def tryNonCommSemiring? (type : Expr) : SymM (Option Nat) := do
let u getDecLevel type
let semiring := mkApp (mkConst ``Grind.Semiring [u]) type
let some semiringInst Sym.synthInstance? semiring | return none
let id := ( getArithState).ncSemirings.size
let semiring : Semiring := { id, type, u, semiringInst }
modifyArithState fun s => { s with ncSemirings := s.ncSemirings.push semiring }
return some id
/--
Classify the algebraic structure of `type`, trying the strongest first:
CommRing > Ring > CommSemiring > Semiring.
Results are cached in `Arith.State.typeClassify`.
-/
def classify? (type : Expr) : SymM ClassifyResult := do
if let some result := ( getArithState).typeClassify.find? { expr := type } then
return result
let result go
modifyArithState fun s => { s with typeClassify := s.typeClassify.insert { expr := type } result }
return result
where
go : SymM ClassifyResult := do
if let some id tryCommRing? type then return .commRing id
if let some id tryNonCommRing? type then return .nonCommRing id
if let some id tryCommSemiring? type then return .commSemiring id
if let some id tryNonCommSemiring? type then return .nonCommSemiring id
return .none
end Lean.Meta.Sym.Arith

View File

@@ -0,0 +1,93 @@
/-
Copyright (c) 2026 Amazon.com, Inc. or its affiliates. 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.Sym.Arith.Functions
public import Lean.Meta.Sym.Arith.MonadVar
public section
namespace Lean.Meta.Sym.Arith
/-!
# Denotation of reified expressions
Converts reified `RingExpr`, `Poly`, `Mon`, `Power` back into Lean `Expr`s using
the ring's cached operator functions and variable array.
-/
variable [Monad m] [MonadError m] [MonadLiftT MetaM m] [MonadCanon m] [MonadRing m]
/-- Convert an integer to a numeral expression in the ring. Negative values use `getNegFn`. -/
def denoteNum (k : Int) : m Expr := do
let ring getRing
let n := mkRawNatLit k.natAbs
let ofNatInst if let some inst MonadCanon.synthInstance? (mkApp2 (mkConst ``OfNat [ring.u]) ring.type n) then
pure inst
else
pure <| mkApp3 (mkConst ``Grind.Semiring.ofNat [ring.u]) ring.type ring.semiringInst n
let e := mkApp3 (mkConst ``OfNat.ofNat [ring.u]) ring.type n ofNatInst
if k < 0 then
return mkApp ( getNegFn) e
else
return e
/-- Denote a `Power` (variable raised to a power). -/
def denotePower [MonadGetVar m] (pw : Power) : m Expr := do
let x getVar pw.x
if pw.k == 1 then
return x
else
return mkApp2 ( getPowFn) x (toExpr pw.k)
/-- Denote a `Mon` (product of powers). -/
def denoteMon [MonadGetVar m] (mn : Mon) : m Expr := do
match mn with
| .unit => denoteNum 1
| .mult pw mn => go mn ( denotePower pw)
where
go (mn : Mon) (acc : Expr) : m Expr := do
match mn with
| .unit => return acc
| .mult pw mn => go mn (mkApp2 ( getMulFn) acc ( denotePower pw))
/-- Denote a `Poly` (sum of coefficient × monomial terms). -/
def denotePoly [MonadGetVar m] (p : Poly) : m Expr := do
match p with
| .num k => denoteNum k
| .add k mn p => go p ( denoteTerm k mn)
where
denoteTerm (k : Int) (mn : Mon) : m Expr := do
if k == 1 then
denoteMon mn
else
return mkApp2 ( getMulFn) ( denoteNum k) ( denoteMon mn)
go (p : Poly) (acc : Expr) : m Expr := do
match p with
| .num 0 => return acc
| .num k => return mkApp2 ( getAddFn) acc ( denoteNum k)
| .add k mn p => go p (mkApp2 ( getAddFn) acc ( denoteTerm k mn))
/-- Denote a `RingExpr` using a variable lookup function. -/
@[specialize]
private def denoteRingExprCore (getVarExpr : Nat Expr) (e : RingExpr) : m Expr := do
go e
where
go : RingExpr m Expr
| .num k => denoteNum k
| .natCast k => return mkApp ( getNatCastFn) (mkNatLit k)
| .intCast k => return mkApp ( getIntCastFn) (mkIntLit k)
| .var x => return getVarExpr x
| .add a b => return mkApp2 ( getAddFn) ( go a) ( go b)
| .sub a b => return mkApp2 ( getSubFn) ( go a) ( go b)
| .mul a b => return mkApp2 ( getMulFn) ( go a) ( go b)
| .pow a k => return mkApp2 ( getPowFn) ( go a) (toExpr k)
| .neg a => return mkApp ( getNegFn) ( go a)
/-- Denote a `RingExpr` using an explicit variable array. -/
def denoteRingExpr (vars : Array Expr) (e : RingExpr) : m Expr := do
denoteRingExprCore (fun x => vars[x]!) e
end Lean.Meta.Sym.Arith

View File

@@ -0,0 +1,90 @@
/-
Copyright (c) 2026 Amazon.com, Inc. or its affiliates. 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.Sym.Arith.Types
import Lean.Meta.Sym.LitValues
import Lean.Meta.IntInstTesters
import Lean.Meta.NatInstTesters
public section
namespace Lean.Meta.Sym.Arith
/-!
Functions for evaluating simple `Nat` and `Int` expressions that appear in type classes
(e.g., `ToInt` and `IsCharP`). Using `whnf` for this purpose is too expensive and can
exhaust the stack. We considered `evalExpr` as an alternative, but it introduces
considerable overhead. We may use `evalExpr` as a fallback in the future.
-/
def checkExp (k : Nat) : OptionT SymM Unit := do
let exp getExpThreshold
if k > exp then
reportIssue! "exponent {k} exceeds threshold for exponentiation `(exp := {exp})`"
failure
/-
**Note**: It is safe to use (the more efficient) structural instance tests here because
`Sym.Canon` has already run.
-/
open Structural in
mutual
private partial def evalNatCore (e : Expr) : OptionT SymM Nat := do
match_expr e with
| Nat.zero => return 0
| Nat.succ a => return ( evalNatCore a) + 1
| Int.toNat a => return ( evalIntCore a).toNat
| Int.natAbs a => return ( evalIntCore a).natAbs
| HAdd.hAdd _ _ _ inst a b => guard ( isInstHAddNat inst); return ( evalNatCore a) + ( evalNatCore b)
| HMul.hMul _ _ _ inst a b => guard ( isInstHMulNat inst); return ( evalNatCore a) * ( evalNatCore b)
| HSub.hSub _ _ _ inst a b => guard ( isInstHSubNat inst); return ( evalNatCore a) - ( evalNatCore b)
| HDiv.hDiv _ _ _ inst a b => guard ( isInstHDivNat inst); return ( evalNatCore a) / ( evalNatCore b)
| HMod.hMod _ _ _ inst a b => guard ( isInstHModNat inst); return ( evalNatCore a) % ( evalNatCore b)
| OfNat.ofNat _ _ _ =>
let some n := Sym.getNatValue? e |>.run | failure
return n
| HPow.hPow _ _ _ inst a k =>
guard ( isInstHPowNat inst)
let k evalNatCore k
checkExp k
let a evalNatCore a
return a ^ k
| _ => failure
private partial def evalIntCore (e : Expr) : OptionT SymM Int := do
match_expr e with
| Neg.neg _ i a => guard ( isInstNegInt i); return - ( evalIntCore a)
| HAdd.hAdd _ _ _ i a b => guard ( isInstHAddInt i); return ( evalIntCore a) + ( evalIntCore b)
| HSub.hSub _ _ _ i a b => guard ( isInstHSubInt i); return ( evalIntCore a) - ( evalIntCore b)
| HMul.hMul _ _ _ i a b => guard ( isInstHMulInt i); return ( evalIntCore a) * ( evalIntCore b)
| HDiv.hDiv _ _ _ i a b => guard ( isInstHDivInt i); return ( evalIntCore a) / ( evalIntCore b)
| HMod.hMod _ _ _ i a b => guard ( isInstHModInt i); return ( evalIntCore a) % ( evalIntCore b)
| HPow.hPow _ _ _ i a k =>
guard ( isInstHPowInt i)
let a evalIntCore a
let k evalNatCore k
checkExp k
return a ^ k
| OfNat.ofNat _ _ _ =>
let some n := Sym.getIntValue? e |>.run | failure
return n
| NatCast.natCast _ i a =>
let_expr instNatCastInt i | failure
return ( evalNatCore a)
| Nat.cast _ i a =>
let_expr instNatCastInt i | failure
return ( evalNatCore a)
| _ => failure
end
def evalNat? (e : Expr) : SymM (Option Nat) :=
evalNatCore e |>.run
def evalInt? (e : Expr) : SymM (Option Int) :=
evalIntCore e |>.run
end Lean.Meta.Sym.Arith

View File

@@ -0,0 +1,171 @@
/-
Copyright (c) 2026 Amazon.com, Inc. or its affiliates. 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.Sym.Arith.MonadRing
public import Lean.Meta.Sym.Arith.MonadSemiring
public section
namespace Lean.Meta.Sym.Arith
/-!
# Cached function expressions for arithmetic operators
Synthesizes and caches the canonical Lean expressions for arithmetic operators
(`+`, `*`, `-`, `^`, `intCast`, `natCast`, etc.). These cached expressions are used
during reification to validate instances via pointer equality (`isSameExpr`).
Each getter checks the cache field first. On a miss, it synthesizes the instance,
verifies it against the expected instance from the ring structure using `isDefEqI`,
canonicalizes the result via `canonExpr`, and stores it.
-/
variable [MonadLiftT MetaM m] [MonadError m] [Monad m] [MonadCanon m]
private def checkInst (declName : Name) (inst inst' : Expr) : MetaM Unit := do
unless ( withReducibleAndInstances <| isDefEq inst inst') do
throwError "error while initializing arithmetic operators:\ninstance for `{declName}` {indentExpr inst}\nis not definitionally equal to the expected one {indentExpr inst'}\nwhen only reducible definitions and instances are reduced"
private def mkUnaryFn (type : Expr) (u : Level) (instDeclName : Name) (declName : Name) (expectedInst : Expr) : m Expr := do
let inst MonadCanon.synthInstance <| mkApp (mkConst instDeclName [u]) type
checkInst declName inst expectedInst
canonExpr <| mkApp2 (mkConst declName [u]) type inst
private def mkBinHomoFn (type : Expr) (u : Level) (instDeclName : Name) (declName : Name) (expectedInst : Expr) : m Expr := do
let inst MonadCanon.synthInstance <| mkApp3 (mkConst instDeclName [u, u, u]) type type type
checkInst declName inst expectedInst
canonExpr <| mkApp4 (mkConst declName [u, u, u]) type type type inst
private def mkPowFn (u : Level) (type : Expr) (semiringInst : Expr) : m Expr := do
let inst MonadCanon.synthInstance <| mkApp3 (mkConst ``HPow [u, 0, u]) type Nat.mkType type
let inst' := mkApp2 (mkConst ``Grind.Semiring.npow [u]) type semiringInst
checkInst ``HPow.hPow inst inst'
canonExpr <| mkApp4 (mkConst ``HPow.hPow [u, 0, u]) type Nat.mkType type inst
private def mkNatCastFn (u : Level) (type : Expr) (semiringInst : Expr) : m Expr := do
let inst' := mkApp2 (mkConst ``Grind.Semiring.natCast [u]) type semiringInst
let instType := mkApp (mkConst ``NatCast [u]) type
-- Note: `Semiring.natCast` is not a global instance, so `NatCast α` may not be available.
-- When present, verify defeq; otherwise fall back to the semiring field.
let inst match ( MonadCanon.synthInstance? instType) with
| none => pure inst'
| some inst => checkInst ``NatCast.natCast inst inst'; pure inst
canonExpr <| mkApp2 (mkConst ``NatCast.natCast [u]) type inst
section RingFns
variable [MonadRing m]
def getAddFn : m Expr := do
let ring getRing
if let some addFn := ring.addFn? then return addFn
let expectedInst := mkApp2 (mkConst ``instHAdd [ring.u]) ring.type <| mkApp2 (mkConst ``Grind.Semiring.toAdd [ring.u]) ring.type ring.semiringInst
let addFn mkBinHomoFn ring.type ring.u ``HAdd ``HAdd.hAdd expectedInst
modifyRing fun s => { s with addFn? := some addFn }
return addFn
def getMulFn : m Expr := do
let ring getRing
if let some mulFn := ring.mulFn? then return mulFn
let expectedInst := mkApp2 (mkConst ``instHMul [ring.u]) ring.type <| mkApp2 (mkConst ``Grind.Semiring.toMul [ring.u]) ring.type ring.semiringInst
let mulFn mkBinHomoFn ring.type ring.u ``HMul ``HMul.hMul expectedInst
modifyRing fun s => { s with mulFn? := some mulFn }
return mulFn
def getSubFn : m Expr := do
let ring getRing
if let some subFn := ring.subFn? then return subFn
let expectedInst := mkApp2 (mkConst ``instHSub [ring.u]) ring.type <| mkApp2 (mkConst ``Grind.Ring.toSub [ring.u]) ring.type ring.ringInst
let subFn mkBinHomoFn ring.type ring.u ``HSub ``HSub.hSub expectedInst
modifyRing fun s => { s with subFn? := some subFn }
return subFn
def getNegFn : m Expr := do
let ring getRing
if let some negFn := ring.negFn? then return negFn
let expectedInst := mkApp2 (mkConst ``Grind.Ring.toNeg [ring.u]) ring.type ring.ringInst
let negFn mkUnaryFn ring.type ring.u ``Neg ``Neg.neg expectedInst
modifyRing fun s => { s with negFn? := some negFn }
return negFn
def getPowFn : m Expr := do
let ring getRing
if let some powFn := ring.powFn? then return powFn
let powFn mkPowFn ring.u ring.type ring.semiringInst
modifyRing fun s => { s with powFn? := some powFn }
return powFn
def getIntCastFn : m Expr := do
let ring getRing
if let some intCastFn := ring.intCastFn? then return intCastFn
let inst' := mkApp2 (mkConst ``Grind.Ring.intCast [ring.u]) ring.type ring.ringInst
let instType := mkApp (mkConst ``IntCast [ring.u]) ring.type
-- Note: `Ring.intCast` is not a global instance. Same pattern as `mkNatCastFn`.
let inst match ( MonadCanon.synthInstance? instType) with
| none => pure inst'
| some inst => checkInst ``Int.cast inst inst'; pure inst
let intCastFn canonExpr <| mkApp2 (mkConst ``IntCast.intCast [ring.u]) ring.type inst
modifyRing fun s => { s with intCastFn? := some intCastFn }
return intCastFn
def getNatCastFn : m Expr := do
let ring getRing
if let some natCastFn := ring.natCastFn? then return natCastFn
let natCastFn mkNatCastFn ring.u ring.type ring.semiringInst
modifyRing fun s => { s with natCastFn? := some natCastFn }
return natCastFn
end RingFns
section CommRingFns
variable [MonadCommRing m]
def getInvFn : m Expr := do
let ring getCommRing
let some fieldInst := ring.fieldInst?
| throwError "internal error: type is not a field{indentExpr ring.type}"
if let some invFn := ring.invFn? then return invFn
let expectedInst := mkApp2 (mkConst ``Grind.Field.toInv [ring.u]) ring.type fieldInst
let invFn mkUnaryFn ring.type ring.u ``Inv ``Inv.inv expectedInst
modifyCommRing fun s => { s with invFn? := some invFn }
return invFn
end CommRingFns
section SemiringFns
variable [MonadSemiring m]
def getAddFn' : m Expr := do
let sr getSemiring
if let some addFn := sr.addFn? then return addFn
let expectedInst := mkApp2 (mkConst ``instHAdd [sr.u]) sr.type <| mkApp2 (mkConst ``Grind.Semiring.toAdd [sr.u]) sr.type sr.semiringInst
let addFn mkBinHomoFn sr.type sr.u ``HAdd ``HAdd.hAdd expectedInst
modifySemiring fun s => { s with addFn? := some addFn }
return addFn
def getMulFn' : m Expr := do
let sr getSemiring
if let some mulFn := sr.mulFn? then return mulFn
let expectedInst := mkApp2 (mkConst ``instHMul [sr.u]) sr.type <| mkApp2 (mkConst ``Grind.Semiring.toMul [sr.u]) sr.type sr.semiringInst
let mulFn mkBinHomoFn sr.type sr.u ``HMul ``HMul.hMul expectedInst
modifySemiring fun s => { s with mulFn? := some mulFn }
return mulFn
def getPowFn' : m Expr := do
let sr getSemiring
if let some powFn := sr.powFn? then return powFn
let powFn mkPowFn sr.u sr.type sr.semiringInst
modifySemiring fun s => { s with powFn? := some powFn }
return powFn
def getNatCastFn' : m Expr := do
let sr getSemiring
if let some natCastFn := sr.natCastFn? then return natCastFn
let natCastFn mkNatCastFn sr.u sr.type sr.semiringInst
modifySemiring fun s => { s with natCastFn? := some natCastFn }
return natCastFn
end SemiringFns
end Lean.Meta.Sym.Arith

View File

@@ -1,24 +1,23 @@
/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Copyright (c) 2026 Amazon.com, Inc. or its affiliates. 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.Tactic.Grind.Arith.CommRing.Types
public import Lean.Meta.Sym.Arith.Types
public section
namespace Lean.Meta.Grind.Arith.CommRing
namespace Lean.Meta.Sym.Arith
class MonadCanon (m : Type Type) where
/--
Helper function for removing dependency on `GoalM`.
In `RingM` and `SemiringM`, this is just `sharedCommon ( canon e)`
When printing counterexamples, we are at `MetaM`, and this is just the identity function.
Canonicalize an expression (types, instances, support arguments).
In `SymM`, this is `Sym.canon`. In `PP.M` (diagnostics), this is the identity.
-/
canonExpr : Expr m Expr
/--
Helper function for removing dependency on `GoalM`. During search we
want to track the instances synthesized by `grind`, and this is `Grind.synthInstance`.
Synthesize an instance, returning `none` on failure.
In `SymM`, this is `Sym.synthInstance?`. In `PP.M`, this is `Meta.synthInstance?`.
-/
synthInstance? : Expr m (Option Expr)
@@ -31,7 +30,7 @@ instance (m n) [MonadLift m n] [MonadCanon m] : MonadCanon n where
def MonadCanon.synthInstance [Monad m] [MonadError m] [MonadCanon m] (type : Expr) : m Expr := do
let some inst synthInstance? type
| throwError "`grind` failed to find instance{indentExpr type}"
| throwError "failed to find instance{indentExpr type}"
return inst
end Lean.Meta.Grind.Arith.CommRing
end Lean.Meta.Sym.Arith

View File

@@ -0,0 +1,39 @@
/-
Copyright (c) 2026 Amazon.com, Inc. or its affiliates. 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.Sym.Arith.MonadCanon
public section
namespace Lean.Meta.Sym.Arith
class MonadRing (m : Type Type) where
getRing : m Ring
modifyRing : (Ring Ring) m Unit
export MonadRing (getRing modifyRing)
@[always_inline]
instance (m n) [MonadLift m n] [MonadRing m] : MonadRing n where
getRing := liftM (getRing : m Ring)
modifyRing f := liftM (modifyRing f : m Unit)
class MonadCommRing (m : Type Type) where
getCommRing : m CommRing
modifyCommRing : (CommRing CommRing) m Unit
export MonadCommRing (getCommRing modifyCommRing)
@[always_inline]
instance (m n) [MonadLift m n] [MonadCommRing m] : MonadCommRing n where
getCommRing := liftM (getCommRing : m CommRing)
modifyCommRing f := liftM (modifyCommRing f : m Unit)
@[always_inline]
instance (m) [Monad m] [MonadCommRing m] : MonadRing m where
getRing := return ( getCommRing).toRing
modifyRing f := modifyCommRing fun s => { s with toRing := f s.toRing }
end Lean.Meta.Sym.Arith

View File

@@ -0,0 +1,39 @@
/-
Copyright (c) 2026 Amazon.com, Inc. or its affiliates. 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.Sym.Arith.MonadCanon
public section
namespace Lean.Meta.Sym.Arith
class MonadSemiring (m : Type Type) where
getSemiring : m Semiring
modifySemiring : (Semiring Semiring) m Unit
export MonadSemiring (getSemiring modifySemiring)
@[always_inline]
instance (m n) [MonadLift m n] [MonadSemiring m] : MonadSemiring n where
getSemiring := liftM (getSemiring : m Semiring)
modifySemiring f := liftM (modifySemiring f : m Unit)
class MonadCommSemiring (m : Type Type) where
getCommSemiring : m CommSemiring
modifyCommSemiring : (CommSemiring CommSemiring) m Unit
export MonadCommSemiring (getCommSemiring modifyCommSemiring)
@[always_inline]
instance (m n) [MonadLift m n] [MonadCommSemiring m] : MonadCommSemiring n where
getCommSemiring := liftM (getCommSemiring : m CommSemiring)
modifyCommSemiring f := liftM (modifyCommSemiring f : m Unit)
@[always_inline]
instance (m) [Monad m] [MonadCommSemiring m] : MonadSemiring m where
getSemiring := return ( getCommSemiring).toSemiring
modifySemiring f := modifyCommSemiring fun s => { s with toSemiring := f s.toSemiring }
end Lean.Meta.Sym.Arith

View File

@@ -0,0 +1,32 @@
/-
Copyright (c) 2026 Amazon.com, Inc. or its affiliates. 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.Sym.Arith.Types
public section
namespace Lean.Meta.Sym.Arith
/-- Read a variable's Lean expression by index. Used by `DenoteExpr` and diagnostics (PP). -/
class MonadGetVar (m : Type Type) where
getVar : Var m Expr
export MonadGetVar (getVar)
@[always_inline]
instance (m n) [MonadLift m n] [MonadGetVar m] : MonadGetVar n where
getVar x := liftM (getVar x : m Expr)
/-- Create or lookup a variable for a Lean expression. Used by reification. -/
class MonadMkVar (m : Type Type) where
mkVar : Expr m Var
export MonadMkVar (mkVar)
@[always_inline]
instance (m n) [MonadLift m n] [MonadMkVar m] : MonadMkVar n where
mkVar e := liftM (mkVar e : m Var)
end Lean.Meta.Sym.Arith

View File

@@ -0,0 +1,205 @@
/-
Copyright (c) 2026 Amazon.com, Inc. or its affiliates. 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.Sym.Arith.Functions
public import Lean.Meta.Sym.Arith.MonadVar
public import Lean.Meta.Sym.LitValues
public section
namespace Lean.Meta.Sym.Arith
open Sym.Arith (MonadCanon)
/-!
# Reification of arithmetic expressions
Converts Lean expressions into `CommRing.Expr` (ring) or `CommSemiring.Expr`
(semiring) for reflection-based normalization.
Instance validation uses pointer equality (`isSameExpr`) against cached function
expressions from `Functions.lean`.
## Differences from grind's `Reify.lean`
- Uses `MonadMkVar` for variable creation instead of grind's `internalize` + `mkVarCore`
- Uses `Sym.getNatValue?`/`Sym.getIntValue?` (pure) instead of `MetaM` versions
- No `MonadSetTermId` — term-to-ring-id tracking is grind-specific
-/
section RingReify
variable [MonadLiftT SymM m] [MonadLiftT MetaM m] [MonadError m] [Monad m] [MonadCanon m] [MonadRing m] [MonadMkVar m]
def isAddInst (inst : Expr) : m Bool :=
return isSameExpr ( getAddFn).appArg! inst
def isMulInst (inst : Expr) : m Bool :=
return isSameExpr ( getMulFn).appArg! inst
def isSubInst (inst : Expr) : m Bool :=
return isSameExpr ( getSubFn).appArg! inst
def isNegInst (inst : Expr) : m Bool :=
return isSameExpr ( getNegFn).appArg! inst
def isPowInst (inst : Expr) : m Bool :=
return isSameExpr ( getPowFn).appArg! inst
def isIntCastInst (inst : Expr) : m Bool :=
return isSameExpr ( getIntCastFn).appArg! inst
def isNatCastInst (inst : Expr) : m Bool :=
return isSameExpr ( getNatCastFn).appArg! inst
private def reportRingAppIssue [MonadLiftT SymM m] (e : Expr) : m Unit := do
reportIssue! "ring term with unexpected instance{indentExpr e}"
/--
Converts a Lean expression `e` into a `RingExpr`.
If `skipVar` is `true`, returns `none` if `e` is not an interpreted ring term
(used for equalities/disequalities). If `false`, treats non-interpreted terms
as variables (used for inequalities).
-/
partial def reifyRing? (e : Expr) (skipVar : Bool := true) : m (Option RingExpr) := do
let toVar (e : Expr) : m RingExpr := do
return .var ( mkVar e)
let asVar (e : Expr) : m RingExpr := do
reportRingAppIssue e
return .var ( mkVar e)
let rec go (e : Expr) : m RingExpr := do
match_expr e with
| HAdd.hAdd _ _ _ i a b =>
if ( isAddInst i) then return .add ( go a) ( go b) else asVar e
| HMul.hMul _ _ _ i a b =>
if ( isMulInst i) then return .mul ( go a) ( go b) else asVar e
| HSub.hSub _ _ _ i a b =>
if ( isSubInst i) then return .sub ( go a) ( go b) else asVar e
| HPow.hPow _ _ _ i a b =>
let some k := Sym.getNatValue? b |>.run | toVar e
if ( isPowInst i) then return .pow ( go a) k else asVar e
| Neg.neg _ i a =>
if ( isNegInst i) then return .neg ( go a) else asVar e
| IntCast.intCast _ i a =>
if ( isIntCastInst i) then
let some k := Sym.getIntValue? a |>.run | toVar e
return .intCast k
else
asVar e
| NatCast.natCast _ i a =>
if ( isNatCastInst i) then
let some k := Sym.getNatValue? a |>.run | toVar e
return .natCast k
else
asVar e
| OfNat.ofNat _ n _ =>
/-
**Note**: We extract `n` directly as a raw nat literal. The grind version uses `MetaM`'s
`getNatValue?` which handles multiple encodings (raw literals, nested `OfNat`, etc.).
In `SymM`, we assume terms have been canonicalized by `Sym.canon` before reification,
so `OfNat.ofNat _ n _` always has a raw nat literal at position 1.
-/
let .lit (.natVal k) := n | toVar e
return .num k
| BitVec.ofNat _ n =>
let .lit (.natVal k) := n | toVar e
return .num k
| _ => toVar e
let toTopVar (e : Expr) : m (Option RingExpr) := do
if skipVar then
return none
else
return some ( toVar e)
let asTopVar (e : Expr) : m (Option RingExpr) := do
reportRingAppIssue e
toTopVar e
match_expr e with
| HAdd.hAdd _ _ _ i a b =>
if ( isAddInst i) then return some (.add ( go a) ( go b)) else asTopVar e
| HMul.hMul _ _ _ i a b =>
if ( isMulInst i) then return some (.mul ( go a) ( go b)) else asTopVar e
| HSub.hSub _ _ _ i a b =>
if ( isSubInst i) then return some (.sub ( go a) ( go b)) else asTopVar e
| HPow.hPow _ _ _ i a b =>
let some k := Sym.getNatValue? b |>.run | asTopVar e
if ( isPowInst i) then return some (.pow ( go a) k) else asTopVar e
| Neg.neg _ i a =>
if ( isNegInst i) then return some (.neg ( go a)) else asTopVar e
| IntCast.intCast _ i a =>
if ( isIntCastInst i) then
let some k := Sym.getIntValue? a |>.run | toTopVar e
return some (.intCast k)
else
asTopVar e
| NatCast.natCast _ i a =>
if ( isNatCastInst i) then
let some k := Sym.getNatValue? a |>.run | toTopVar e
return some (.natCast k)
else
asTopVar e
| OfNat.ofNat _ n _ =>
let .lit (.natVal k) := n | asTopVar e
return some (.num k)
| _ => toTopVar e
end RingReify
section SemiringReify
variable [MonadLiftT SymM m] [MonadLiftT MetaM m] [MonadError m] [Monad m] [MonadCanon m] [MonadSemiring m] [MonadMkVar m]
private def reportSemiringAppIssue [MonadLiftT SymM m] (e : Expr) : m Unit := do
reportIssue! "semiring term with unexpected instance{indentExpr e}"
/--
Converts a Lean expression `e` into a `SemiringExpr`.
Only recognizes `add`, `mul`, `pow`, `natCast`, and numerals (no `sub`, `neg`, `intCast`).
-/
partial def reifySemiring? (e : Expr) : m (Option SemiringExpr) := do
let toVar (e : Expr) : m SemiringExpr := do
return .var ( mkVar e)
let asVar (e : Expr) : m SemiringExpr := do
reportSemiringAppIssue e
return .var ( mkVar e)
let rec go (e : Expr) : m SemiringExpr := do
match_expr e with
| HAdd.hAdd _ _ _ i a b =>
if isSameExpr ( getAddFn').appArg! i then return .add ( go a) ( go b) else asVar e
| HMul.hMul _ _ _ i a b =>
if isSameExpr ( getMulFn').appArg! i then return .mul ( go a) ( go b) else asVar e
| HPow.hPow _ _ _ i a b =>
let some k := Sym.getNatValue? b |>.run | toVar e
if isSameExpr ( getPowFn').appArg! i then return .pow ( go a) k else asVar e
| NatCast.natCast _ i a =>
if isSameExpr ( getNatCastFn').appArg! i then
let some k := Sym.getNatValue? a |>.run | toVar e
return .num k
else
asVar e
| OfNat.ofNat _ n _ =>
let .lit (.natVal k) := n | toVar e
return .num k
| _ => toVar e
let toTopVar (e : Expr) : m (Option SemiringExpr) := do
return some ( toVar e)
let asTopVar (e : Expr) : m (Option SemiringExpr) := do
reportSemiringAppIssue e
toTopVar e
match_expr e with
| HAdd.hAdd _ _ _ i a b =>
if isSameExpr ( getAddFn').appArg! i then return some (.add ( go a) ( go b)) else asTopVar e
| HMul.hMul _ _ _ i a b =>
if isSameExpr ( getMulFn').appArg! i then return some (.mul ( go a) ( go b)) else asTopVar e
| HPow.hPow _ _ _ i a b =>
let some k := Sym.getNatValue? b |>.run | return none
if isSameExpr ( getPowFn').appArg! i then return some (.pow ( go a) k) else asTopVar e
| NatCast.natCast _ i a =>
if isSameExpr ( getNatCastFn').appArg! i then
let some k := Sym.getNatValue? a |>.run | toTopVar e
return some (.num k)
else
asTopVar e
| OfNat.ofNat _ n _ =>
let .lit (.natVal k) := n | asTopVar e
return some (.num k)
| _ => toTopVar e
end SemiringReify
end Lean.Meta.Sym.Arith

View File

@@ -8,7 +8,7 @@ prelude
public import Init.Grind.Ring.CommSemiringAdapter
public import Lean.ToExpr
public section
namespace Lean.Meta.Grind.Arith.CommRing
namespace Lean.Meta.Sym.Arith
open Grind.CommRing
/-!
`ToExpr` instances for `CommRing.Poly` types.
@@ -57,4 +57,4 @@ instance : ToExpr CommRing.Expr where
toExpr := ofRingExpr
toTypeExpr := mkConst ``CommRing.Expr
end Lean.Meta.Grind.Arith.CommRing
end Lean.Meta.Sym.Arith

View File

@@ -0,0 +1,137 @@
/-
Copyright (c) 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Init.Grind.Ring.CommSemiringAdapter
public import Lean.Meta.Sym.SymM
public section
namespace Lean.Meta.Sym.Arith
export Lean.Grind.CommRing (Var Power Mon Poly)
abbrev RingExpr := Grind.CommRing.Expr
/-
**Note**: recall that we use ring expressions to represent semiring expressions,
and ignore non-applicable constructors.
-/
abbrev SemiringExpr := Grind.CommRing.Expr
/-- Classification state for a type with a `Semiring` instance. -/
structure Semiring where
id : Nat
type : Expr
/-- Cached `getDecLevel type` -/
u : Level
/-- `Semiring` instance for `type` -/
semiringInst : Expr
addFn? : Option Expr := none
mulFn? : Option Expr := none
powFn? : Option Expr := none
natCastFn? : Option Expr := none
deriving Inhabited
/-- Classification state for a type with a `Ring` instance. -/
structure Ring where
id : Nat
type : Expr
/-- Cached `getDecLevel type` -/
u : Level
/-- `Ring` instance for `type` -/
ringInst : Expr
/-- `Semiring` instance for `type` -/
semiringInst : Expr
/-- `IsCharP` instance for `type` if available. -/
charInst? : Option (Expr × Nat)
addFn? : Option Expr := none
mulFn? : Option Expr := none
subFn? : Option Expr := none
negFn? : Option Expr := none
powFn? : Option Expr := none
intCastFn? : Option Expr := none
natCastFn? : Option Expr := none
one? : Option Expr := none
deriving Inhabited
/-- Classification state for a type with a `CommRing` instance. -/
structure CommRing extends Ring where
/-- Inverse function if `fieldInst?` is `some inst` -/
invFn? : Option Expr := none
/--
If this is a `OfSemiring.Q α` ring, this field contains the
`semiringId` for `α`.
-/
semiringId? : Option Nat
/-- `CommSemiring` instance for `type` -/
commSemiringInst : Expr
/-- `CommRing` instance for `type` -/
commRingInst : Expr
/-- `NoNatZeroDivisors` instance for `type` if available. -/
noZeroDivInst? : Option Expr
/-- `Field` instance for `type` if available. -/
fieldInst? : Option Expr
deriving Inhabited
/--
Classification state for a type with a `CommSemiring` instance.
Recall that `CommSemiring` types are normalized using the `OfSemiring.Q` envelope.
-/
structure CommSemiring extends Semiring where
/-- Id of the envelope ring `OfSemiring.Q type` -/
ringId : Nat
/-- `CommSemiring` instance for `type` -/
commSemiringInst : Expr
/-- `AddRightCancel` instance for `type` if available. -/
addRightCancelInst? : Option (Option Expr) := none
toQFn? : Option Expr := none
deriving Inhabited
/-- Result of classifying a type's algebraic structure. -/
inductive ClassifyResult where
| commRing (id : Nat)
| nonCommRing (id : Nat)
| commSemiring (id : Nat)
| nonCommSemiring (id : Nat)
| /-- No algebraic structure found. -/ none
deriving Inhabited
/-- Arith type classification state, stored as a `SymExtension`. -/
structure State where
/-- Exponent threshold for `HPow` evaluation. -/
exp : Nat := 8
/-- Commutative rings. -/
rings : Array CommRing := {}
/-- Commutative semirings. -/
semirings : Array CommSemiring := {}
/-- Non-commutative rings. -/
ncRings : Array Ring := {}
/-- Non-commutative semirings. -/
ncSemirings : Array Semiring := {}
/-- Mapping from types to their classification result. Caches failures as `.none`. -/
typeClassify : PHashMap ExprPtr ClassifyResult := {}
deriving Inhabited
builtin_initialize arithExt : SymExtension State registerSymExtension (return {})
def getArithState : SymM State :=
arithExt.getState
@[inline] def modifyArithState (f : State State) : SymM Unit :=
arithExt.modifyState f
/-- Get the exponent threshold. -/
def getExpThreshold : SymM Nat :=
return ( getArithState).exp
/-- Set the exponent threshold. -/
def setExpThreshold (exp : Nat) : SymM Unit :=
modifyArithState fun s => { s with exp }
/-- Run `k` with a temporary exponent threshold. -/
def withExpThreshold (exp : Nat) (k : SymM α) : SymM α := do
let oldExp := ( getArithState).exp
setExpThreshold exp
try k finally setExpThreshold oldExp
end Lean.Meta.Sym.Arith

View File

@@ -24,10 +24,19 @@ builtin_initialize registerTraceClass `sym.debug.canon
Canonicalizes expressions by normalizing types and instances. At the top level, it traverses
applications, foralls, lambdas, and let-bindings, classifying each argument as a type, instance,
implicit, or value using `shouldCanon`. Values are recursively visited but not normalized.
Types and instances receive targeted reductions.
implicit, or value using `shouldCanon`. Targeted reductions (projection, match/ite/cond, Nat
arithmetic) are applied to all positions; instances are re-synthesized.
## Reductions (applied only in type positions)
**Note about types:** `grind` is not built for reasoning about types that are not propositions.
We assume that definitionally equal types will be structurally identical after we apply the
canonicalizer. We also erase most of the subsingleton markers occurring inside types.
## Reductions
It also normalizes expressions using the following reductions. We can view it as a cheap/custom `dsimp`.
We used to reduce only terms inside types, but it restricted important normalizations that were important
when, for example, a term had a forward dependency. That is, the term is not directly in a type, but
there is a type that depends on it.
- **Eta**: `fun x => f x` → `f`
- **Projection**: `⟨a, b⟩.1` → `a` (structure projections, not class projections)
@@ -35,11 +44,30 @@ Types and instances receive targeted reductions.
- **Nat arithmetic**: ground evaluation (`2 + 1` → `3`) and offset normalization
(`n.succ + 1` → `n + 2`)
**Note**: Eta is applied only if the lambda is occurring inside of a type. For lambdas occurring
in non type positions, we want to leverage the support in `grind` for lambda-expressions.
## Instance canonicalization
Instances are re-synthesized via `synthInstance`. The instance type is first normalized
using the type-level reductions above, so that `OfNat (Fin (2+1)) 0` and `OfNat (Fin 3) 0`
produce the same canonical instance.
produce the same canonical instance. Two special cases:
- **`Decidable` instances** (`Grind.nestedDecidable`): the proposition is recursively
canonicalized, then the `Decidable` instance is re-synthesized. If resynthesis fails,
the original instance is kept (users often provide these via `haveI`).
A `checkDefEqInst` guard is required because structurally different `Decidable` instances
are not necessarily definitionally equal.
- **Propositional instances** (`Grind.nestedProof`): the proposition is recursively
canonicalized, then the proof is re-synthesized. If resynthesis fails, the original
proof is kept. No definitional-equality check is needed thanks to proof irrelevance.
In both cases, resynthesis failure is silent — the original instance or proof is kept.
Ideally we would report an issue when resynthesis fails inside a type (where structural
agreement matters most), but in practice users provide non-synthesizable instances via `haveI`,
and these instances propagate into types through forward dependencies. Reporting failures
for such instances produces noise that obscures real issues.
## Two caches
@@ -213,7 +241,7 @@ def checkDefEqInst (e : Expr) (inst : Expr) : SymM Expr := do
return e
return inst
/-- Canonicalize `e`. Applies targeted reductions in type positions; recursively visits value positions. -/
/-- Canonicalize `e`. Applies targeted reductions and re-synthesizes instances. -/
partial def canon (e : Expr) : CanonM Expr := do
match e with
| .forallE .. => withCaching e <| canonForall #[] e
@@ -246,23 +274,91 @@ where
else
withReader (fun ctx => { ctx with insideType := true }) <| canon e
canonInst (e : Expr) : CanonM Expr := do
if let some inst := ( get).canon.cacheInsts.get? e then
checkDefEqInst e inst
/--
Canonicalize `e : type` where `e` is an instance by trying to resynthesize `type`.
If `report` is `true`, we report an issue when the instance cannot be resynthesized.
-/
canonInstCore (e : Expr) (type : Expr) (report := true) : CanonM Expr := do
let some inst Sym.synthInstance? type |
if report then
reportIssue! "failed to canonicalize instance{indentExpr e}\nfailed to synthesize{indentExpr type}"
return e
checkDefEqInst e inst
/--
Canonicalize an instance by trying to resynthesize it without caching.
Recall that we have special support for `Decidable` and propositional instances.
-/
canonInst' (e : Expr) (report := true) : CanonM Expr := do
/-
We normalize the type to make sure `OfNat (Fin (2+1)) 1` and `OfNat (Fin 3) 1` will produce
the same instances.
-/
let type inferType e
let type' canonInsideType' type
canonInstCore e type' report
/-- `withCaching` + `canonInst'` -/
canonInst (e : Expr) (report := true) : CanonM Expr := withCaching e do
canonInst' e report
/--
Canonicalize a proposition that is also a term instance.
Given a term `e` of the form `@Grind.nestedProof prop h`, where `g` is the constant `Grind.nestedProof`,
we canonicalize it as follows:
1- We recursively canonicalize the proposition `prop`.
2- Try to resynthesize the instance, but keep the original one in case of failure since users often
provide them using `haveI`.
-/
canonInstProp (g : Expr) (prop : Expr) (h : Expr) (e : Expr) : CanonM Expr := withCaching e do
let prop' canon prop
if ( read).insideType then
/- We suppress reporting here because `haveI`-provided instances propagate into types
through forward dependencies, and reporting them produces noise. See module doc. -/
canonInstCore h prop' (report := false)
else
/-
We normalize the type to make sure `OfNat (Fin (2+1)) 1` and `OfNat (Fin 3) 1` will produce
the same instances.
**Note**: We try to resynthesize the proposition, but if it fails we keep the current one.
This may happen because propositional instances are often provided manually using `haveI`.
-/
let type inferType e
let type' canonInsideType' type
let some inst Sym.synthInstance? type' |
reportIssue! "failed to canonicalize instance{indentExpr e}\nfailed to synthesize{indentExpr type'}"
return e
let inst checkDefEqInst e inst
-- Remark: we cache result using the type **before** canonicalization.
modify fun s => { s with canon.cacheInsts := s.canon.cacheInsts.insert e inst }
return inst
let h' := ( Sym.synthInstance? prop').getD h
/- **Note**: We don't need to check whether `h` and `h'` are definitionally equal because of proof irrelevance. -/
return if isSameExpr prop prop' && isSameExpr h h' then e else mkApp2 g prop' h'
/--
Canonicalize a decidable instance without checking the cache.
Given a term `e` of the form `@Grind.nestedDecidable prop inst`, where `g` is the constant `Grind.nestedDecidable`,
we canonicalize it as follows:
1- We recursively canonicalize the proposition `prop`.
2- Try to resynthesize the instance, but keep the original one in case of failure since users often
provide them using `haveI`.
-/
canonInstDec' (g : Expr) (prop : Expr) (inst : Expr) (e : Expr) : CanonM Expr := do
let prop' canon prop
let type := mkApp (mkConst ``Decidable) prop'
if ( read).insideType then
/- See comment in `canonInstProp` for why we suppress reporting here. -/
canonInstCore inst type (report := false)
else
/-
**Note**: We try to resynthesize the instance, but if it fails we keep the current one.
We use `checkDefEqInst` here because two structurally different decidable instances are not necessarily
definitionally equal.
This may happen because propositional instances are often provided manually using `haveI`.
-/
let inst' := ( Sym.synthInstance? type).getD inst
let inst' checkDefEqInst inst inst'
return if isSameExpr prop prop' && isSameExpr inst inst' then e else mkApp2 g prop' inst'
/-- `withCaching` + `canonInstDec'` -/
canonInstDec (g : Expr) (prop : Expr) (h : Expr) (e : Expr) : CanonM Expr := withCaching e do
canonInstDec' g prop h e
/-- `canonInstDec` variant for normalizing `if-then-else` expressions. -/
canonInstDecCore (e : Expr) : CanonM Expr := do
match_expr e with
| g@Grind.nestedDecidable prop inst => canonInstDec g prop inst e
| _ => canonInst e (report := false)
canonLambda (e : Expr) : CanonM Expr := do
if ( read).insideType then
@@ -295,60 +391,56 @@ where
mkLetFVars (generalizeNondepLet := false) fvars ( canon (e.instantiateRev fvars))
canonAppDefault (e : Expr) : CanonM Expr := e.withApp fun f args => do
if f.isConstOf ``Grind.nestedProof && args.size == 2 then
let prop := args[0]!
let prop' canon prop
let e' := if isSameExpr prop prop' then e else mkAppN f (args.set! 0 prop')
return e'
else if f.isConstOf ``Grind.nestedDecidable && args.size == 2 then
let prop := args[0]!
let prop' canon prop
let e' := if isSameExpr prop prop' then e else mkAppN f (args.set! 0 prop')
return e'
if args.size == 2 then
if f.isConstOf ``Grind.nestedProof then
/- **Note**: We don't have special treatment if `e` inside a type. -/
let prop := args[0]!
let prop' canon prop
let e' := if isSameExpr prop prop' then e else mkApp2 f prop' args[1]!
return e'
else if f.isConstOf ``Grind.nestedDecidable then
return ( canonInstDec' f args[0]! args[1]! e)
let mut modified := false
let args if f.isConstOf ``OfNat.ofNat then
let some args normOfNatArgs? args | pure args
modified := true
pure args
else
let mut modified := false
let args if f.isConstOf ``OfNat.ofNat then
let some args normOfNatArgs? args | pure args
pure args
let mut f := f
let f' canon f
unless isSameExpr f f' do
f := f'
modified := true
let pinfos := ( getFunInfo f).paramInfo
let mut args := args.toVector
for h : i in *...args.size do
let arg := args[i]
trace[sym.debug.canon] "[{repr (← shouldCanon pinfos i arg)}]: {arg} : {← inferType arg}"
let arg' match ( shouldCanon pinfos i arg) with
| .canonType =>
/-
The type may have nested propositions and terms that may need to be canonicalized too.
So, we must recurse over it. See issue #10232
-/
canonInsideType' arg
| .canonImplicit => canon arg
| .visit => canon arg
| .canonInst =>
match_expr arg with
| g@Grind.nestedDecidable prop h => canonInstDec g prop h arg
| g@Grind.nestedProof prop h => canonInstProp g prop h arg
| _ => canonInst arg
unless isSameExpr arg arg' do
args := args.set i arg'
modified := true
pure args
else
pure args
let mut f := f
let f' canon f
unless isSameExpr f f' do
f := f'
modified := true
let pinfos := ( getFunInfo f).paramInfo
let mut args := args.toVector
for h : i in *...args.size do
let arg := args[i]
trace[sym.debug.canon] "[{repr (← shouldCanon pinfos i arg)}]: {arg} : {← inferType arg}"
let arg' match ( shouldCanon pinfos i arg) with
| .canonType =>
/-
The type may have nested propositions and terms that may need to be canonicalized too.
So, we must recurse over it. See issue #10232
-/
canonInsideType' arg
| .canonImplicit => canon arg
| .visit => canon arg
| .canonInst =>
if arg.isAppOfArity ``Grind.nestedDecidable 2 then
let prop := arg.appFn!.appArg!
let prop' canon prop
if isSameExpr prop prop' then pure arg else pure (mkApp2 arg.appFn!.appFn! prop' arg.appArg!)
else
canonInst arg
unless isSameExpr arg arg' do
args := args.set i arg'
modified := true
return if modified then mkAppN f args.toArray else e
return if modified then mkAppN f args.toArray else e
canonIte (f : Expr) (α c inst a b : Expr) : CanonM Expr := do
let c canon c
if isTrueCond c then canon a
else if isFalseCond c then canon b
else return mkApp5 f ( canonInsideType α) c ( canonInst inst) ( canon a) ( canon b)
else return mkApp5 f ( canonInsideType α) c ( canonInstDecCore inst) ( canon a) ( canon b)
canonCond (f : Expr) (α c a b : Expr) : CanonM Expr := do
let c canon c
@@ -389,30 +481,24 @@ where
return e
canonApp (e : Expr) : CanonM Expr := do
if ( read).insideType then
match_expr e with
| f@ite α c i a b => canonIte f α c i a b
| f@cond α c a b => canonCond f α c a b
-- Remark: We currently don't normalize dependent-if-then-else occurring in types.
| _ =>
let f := e.getAppFn
let .const declName _ := f | canonAppAndPost e
if ( isMatcher declName) then
canonMatch e
else
canonAppAndPost e
else
canonAppDefault e
match_expr e with
| f@ite α c i a b => canonIte f α c i a b
| f@cond α c a b => canonCond f α c a b
-- Remark: We currently don't normalize dependent-if-then-else occurring in types.
| _ =>
let f := e.getAppFn
let .const declName _ := f | canonAppAndPost e
if ( isMatcher declName) then
canonMatch e
else
canonAppAndPost e
canonProj (e : Expr) : CanonM Expr := do
let e := e.updateProj! ( canon e.projExpr!)
if ( read).insideType then
return ( reduceProj? e).getD e
else
return e
return ( reduceProj? e).getD e
/--
Returns `true` if `shouldCannon pinfos i arg` is not `.visit`.
Returns `true` if `shouldCanon pinfos i arg` is not `.visit`.
This is a helper function used to implement mbtc.
-/
public def isSupport (pinfos : Array ParamInfo) (i : Nat) (arg : Expr) : MetaM Bool := do
@@ -423,8 +509,8 @@ end Canon
/--
Canonicalize `e` by normalizing types, instances, and support arguments.
Types receive targeted reductions (eta, projection, match/ite, Nat arithmetic).
Instances are re-synthesized. Values are traversed but not reduced.
Applies targeted reductions (projection, match/ite/cond, Nat arithmetic) in all positions;
eta reduction is applied only inside types. Instances are re-synthesized.
Runs at reducible transparency.
-/
public def canon (e : Expr) : SymM Expr := do profileitM Exception "sym canon" ( getOptions) do

View File

@@ -86,22 +86,8 @@ It assumes the input is maximally shared, and ensures the output is too.
public def instantiateS (e : Expr) (subst : Array Expr) : SymM Expr :=
liftBuilderM <| instantiateS' e subst
/-- `mkAppRevRangeS f b e args == mkAppRev f (revArgs.extract b e)` -/
def mkAppRevRangeS (f : Expr) (beginIdx endIdx : Nat) (revArgs : Array Expr) : AlphaShareBuilderM Expr :=
loop revArgs beginIdx f endIdx
where
loop (revArgs : Array Expr) (start : Nat) (b : Expr) (i : Nat) : AlphaShareBuilderM Expr := do
if i start then
return b
else
let i := i - 1
loop revArgs start ( mkAppS b revArgs[i]!) i
/--
Beta-reduces `f` applied to reversed arguments `revArgs`, ensuring maximally shared terms.
`betaRevS f #[a₃, a₂, a₁]` computes the beta-normal form of `f a₁ a₂ a₃`.
-/
partial def betaRevS (f : Expr) (revArgs : Array Expr) : AlphaShareBuilderM Expr :=
/-- Internal variant of `betaRevS` that runs in `AlphaShareBuilderM`. -/
private partial def betaRevS' (f : Expr) (revArgs : Array Expr) : AlphaShareBuilderM Expr :=
if revArgs.size == 0 then
return f
else
@@ -173,7 +159,7 @@ where
| .bvar bidx =>
let f' visitBVar f bidx offset
if modified || !isSameExpr f f' then
betaRevS f' argsRev
betaRevS' f' argsRev
else
return e
| _ => unreachable!
@@ -215,4 +201,18 @@ public def instantiateRevBetaS (e : Expr) (subst : Array Expr) : SymM Expr := do
if !e.hasLooseBVars || subst.isEmpty then return e
else liftBuilderM <| instantiateRevBetaS' e subst
/--
Beta-reduces `f` applied to reversed arguments `revArgs`, ensuring maximally shared terms.
`betaRevS f #[a₃, a₂, a₁]` computes the beta-normal form of `f a₁ a₂ a₃`.
-/
public def betaRevS (f : Expr) (revArgs : Array Expr) : SymM Expr :=
liftBuilderM <| betaRevS' f revArgs
/--
Apply the given arguments to `f`, beta-reducing if `f` is a lambda expression,
ensuring maximally shared terms. See `betaRevS` for details.
-/
public def betaS (f : Expr) (args : Array Expr) : SymM Expr :=
betaRevS f args.reverse
end Lean.Meta.Sym

View File

@@ -152,8 +152,6 @@ structure Canon.State where
cache : Std.HashMap Expr Expr := {}
/-- Cache for type-level canonicalization (reductions applied). -/
cacheInType : Std.HashMap Expr Expr := {}
/-- Cache mapping instances to their canonical synthesized instances. -/
cacheInsts : Std.HashMap Expr Expr := {}
/-- Mutable state for the symbolic computation framework. -/
structure State where

View File

@@ -44,7 +44,7 @@ def isCbvNoncomputable (p : Name) : CoreM Bool := do
return evalLemmas.isNone && Lean.isNoncomputable ( getEnv) p
/--
Attemps to synthesize `Decidable p` instance and guards against picking up a `noncomputable` instance
Attempts to synthesize `Decidable p` instance and guards against picking up a `noncomputable` instance
-/
def trySynthComputableInstance (p : Expr) : SymM <| Option Expr := do
let .some inst' trySynthInstance (mkApp (mkConst ``Decidable) p) | return .none
@@ -112,7 +112,7 @@ builtin_cbv_simproc ↓ simpIteCbv (@ite _ _ _ _ _) := fun e => do
else if ( isFalseExpr c') then
return .step b (mkApp (e.replaceFn ``ite_cond_eq_false) h) (contextDependent := cd)
else
-- If we got stuck with simplifying `p` then let's try evaluating the original isntance
-- If we got stuck with simplifying `p` then let's try evaluating the original instance
simpAndMatchIteDecidable f α c inst a b do
-- If we get stuck here, maybe the problem is that we need to look at `Decidable c'`
let inst' := mkApp4 (mkConst ``decidable_of_decidable_of_eq) c c' inst h
@@ -317,7 +317,7 @@ public def reduceRecMatcher : Simproc := fun e => do
else
return .rfl
builtin_cbv_simproc simpDecidableRec (@Decidable.rec _ _ _ _ _) :=
builtin_cbv_simproc simpDecidableRec (@Decidable.rec _ _ _ _ _) :=
(simpInterlaced · #[false,false,false,false,true]) >> reduceRecMatcher
def tryMatchEquations (appFn : Name) : Simproc := fun e => do

View File

@@ -272,7 +272,7 @@ def handleProj : Simproc := fun e => do
let reduced Sym.share reduced
return .step reduced ( Sym.mkEqRefl reduced)
| .none =>
-- If we failed to reduce it, we turn to a last resort; we try use heterogenous congruence lemma that we then try to turn into an equality.
-- If we failed to reduce it, we turn to a last resort; we try use heterogeneous congruence lemma that we then try to turn into an equality.
unless ( isDefEq struct e') do
-- If we rewrote the projection body using something that holds up to propositional equality, then there is nothing we can do.
-- TODO: Check if there is a need to report this to a user, or shall we fail silently.
@@ -283,6 +283,7 @@ def handleProj : Simproc := fun e => do
let newProof mkEqOfHEq newProof (check := false)
return .step ( Lean.Expr.updateProjS! e e') newProof
open Sym.Internal in
/--
For an application whose head is neither a constant nor a lambda (e.g. a projection
like `p.1 x`), simplify the function head and lift the proof via `congrArg`.

View File

@@ -24,9 +24,6 @@ namespace Lean.Meta.Tactic.Cbv
open Lean.Meta.Sym.Simp
public def mkAppNS (f : Expr) (args : Array Expr) : Sym.SymM Expr := do
args.foldlM Sym.Internal.mkAppS f
abbrev isNatValue (e : Expr) : Bool := (Sym.getNatValue? e).isSome
abbrev isStringValue (e : Expr) : Bool := (Sym.getStringValue? e).isSome
abbrev isIntValue (e : Expr) : Bool := (Sym.getIntValue? e).isSome

View File

@@ -5,11 +5,9 @@ Authors: Leonardo de Moura
-/
module
prelude
public import Lean.Meta.Tactic.Grind.Arith.CommRing.Poly
public import Lean.Meta.Tactic.Grind.Arith.CommRing.Types
public import Lean.Meta.Tactic.Grind.Arith.CommRing.RingId
public import Lean.Meta.Tactic.Grind.Arith.CommRing.Internalize
public import Lean.Meta.Tactic.Grind.Arith.CommRing.ToExpr
public import Lean.Meta.Tactic.Grind.Arith.CommRing.RingM
public import Lean.Meta.Tactic.Grind.Arith.CommRing.SemiringM
public import Lean.Meta.Tactic.Grind.Arith.CommRing.NonCommRingM
@@ -21,8 +19,6 @@ public import Lean.Meta.Tactic.Grind.Arith.CommRing.Proof
public import Lean.Meta.Tactic.Grind.Arith.CommRing.DenoteExpr
public import Lean.Meta.Tactic.Grind.Arith.CommRing.Inv
public import Lean.Meta.Tactic.Grind.Arith.CommRing.PP
public import Lean.Meta.Tactic.Grind.Arith.CommRing.VarRename
public import Lean.Meta.Tactic.Grind.Arith.CommRing.MonadCanon
public import Lean.Meta.Tactic.Grind.Arith.CommRing.MonadRing
public import Lean.Meta.Tactic.Grind.Arith.CommRing.MonadSemiring
public import Lean.Meta.Tactic.Grind.Arith.CommRing.Action

View File

@@ -8,6 +8,7 @@ prelude
public import Lean.Meta.Tactic.Grind.Arith.CommRing.Functions
public section
namespace Lean.Meta.Grind.Arith.CommRing
open Sym.Arith
/-!
Helper functions for converting reified terms back into their denotations.
-/

View File

@@ -8,6 +8,7 @@ prelude
public import Lean.Meta.Tactic.Grind.Arith.CommRing.MonadRing
public section
namespace Lean.Meta.Grind.Arith.CommRing
open Sym.Arith
variable [MonadLiftT MetaM m] [MonadError m] [Monad m] [MonadCanon m]
section

View File

@@ -6,7 +6,7 @@ Authors: Leonardo de Moura
module
prelude
public import Lean.Meta.Tactic.Grind.Arith.CommRing.RingM
import Lean.Meta.Tactic.Grind.Arith.CommRing.Poly
import Lean.Meta.Sym.Arith.Poly
public section
namespace Lean.Meta.Grind.Arith.CommRing

View File

@@ -5,7 +5,8 @@ Authors: Leonardo de Moura
-/
module
prelude
public import Lean.Meta.Tactic.Grind.Arith.CommRing.MonadCanon
public import Lean.Meta.Sym.Arith.MonadCanon
public import Lean.Meta.Tactic.Grind.Arith.CommRing.Types
public section
namespace Lean.Meta.Grind.Arith.CommRing

View File

@@ -5,7 +5,8 @@ Authors: Leonardo de Moura
-/
module
prelude
public import Lean.Meta.Tactic.Grind.Arith.CommRing.MonadCanon
public import Lean.Meta.Sym.Arith.MonadCanon
public import Lean.Meta.Tactic.Grind.Arith.CommRing.Types
public section
namespace Lean.Meta.Grind.Arith.CommRing

View File

@@ -8,7 +8,7 @@ prelude
public import Lean.Meta.Tactic.Grind.Arith.CommRing.RingM
public section
namespace Lean.Meta.Grind.Arith.CommRing
open Sym.Arith
structure NonCommRingM.Context where
ringId : Nat

View File

@@ -8,6 +8,7 @@ prelude
public import Lean.Meta.Tactic.Grind.Arith.CommRing.SemiringM
public section
namespace Lean.Meta.Grind.Arith.CommRing
open Sym.Arith (MonadCanon)
structure NonCommSemiringM.Context where
semiringId : Nat

View File

@@ -10,6 +10,7 @@ import Lean.Meta.Tactic.Grind.Arith.CommRing.DenoteExpr
import Init.Omega
public section
namespace Lean.Meta.Grind.Arith.CommRing
open Sym.Arith
private abbrev M := StateT CommRing MetaM

View File

@@ -12,12 +12,14 @@ import Lean.Data.RArray
import Lean.Meta.Tactic.Grind.Diseq
import Lean.Meta.Tactic.Grind.ProofUtil
import Lean.Meta.Tactic.Grind.Arith.CommRing.DenoteExpr
import Lean.Meta.Tactic.Grind.Arith.CommRing.ToExpr
import Lean.Meta.Tactic.Grind.Arith.CommRing.VarRename
import Lean.Meta.Sym.Arith.ToExpr
import Lean.Meta.Sym.Arith.VarRename
import Init.Data.Nat.Order
import Init.Data.Order.Lemmas
public section
namespace Lean.Meta.Grind.Arith.CommRing
open Sym.Arith (MonadCanon)
/--
Returns a context of type `RArray α` containing the variables `vars` where
`α` is the type of the ring.

View File

@@ -9,6 +9,7 @@ public import Lean.Meta.Tactic.Grind.Arith.CommRing.NonCommRingM
public import Lean.Meta.Tactic.Grind.Arith.CommRing.NonCommSemiringM
public section
namespace Lean.Meta.Grind.Arith.CommRing
open Sym.Arith (MonadCanon)
variable [MonadLiftT MetaM m] [MonadError m] [Monad m] [MonadCanon m] [MonadRing m]

View File

@@ -9,6 +9,7 @@ public import Lean.Meta.Tactic.Grind.SynthInstance
public import Lean.Meta.Tactic.Grind.Arith.CommRing.MonadRing
public section
namespace Lean.Meta.Grind.Arith.CommRing
open Sym.Arith
def checkMaxSteps : GoalM Bool := do
return ( get').steps >= ( getConfig).ringSteps

View File

@@ -6,7 +6,7 @@ Authors: Leonardo de Moura
module
prelude
public import Lean.Meta.Tactic.Grind.Arith.CommRing.RingM
public import Lean.Meta.Tactic.Grind.Arith.CommRing.Poly
public import Lean.Meta.Sym.Arith.Poly
import Lean.Meta.Tactic.Grind.Arith.EvalNum
import Init.Data.Nat.Linear
public section

View File

@@ -11,6 +11,7 @@ import Lean.Meta.Tactic.Grind.Arith.CommRing.DenoteExpr
public import Lean.Meta.Tactic.Grind.Arith.CommRing.Functions
public section
namespace Lean.Meta.Grind.Arith.CommRing
open Sym.Arith
structure SemiringM.Context where
semiringId : Nat

View File

@@ -7,7 +7,7 @@ module
prelude
public import Init.Grind.Ring.CommSemiringAdapter
public import Lean.Meta.Tactic.Grind.Types
import Lean.Meta.Tactic.Grind.Arith.CommRing.Poly
import Lean.Meta.Sym.Arith.Poly
public section
namespace Lean.Meta.Grind.Arith.CommRing

View File

@@ -14,8 +14,8 @@ import Lean.Meta.Tactic.Grind.Arith.Cutsat.CommRing
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Util
import Lean.Meta.Tactic.Grind.Arith.Cutsat.Nat
import Lean.Meta.Tactic.Grind.Arith.Cutsat.VarRename
import Lean.Meta.Tactic.Grind.Arith.CommRing.VarRename
import Lean.Meta.Tactic.Grind.Arith.CommRing.ToExpr
import Lean.Meta.Sym.Arith.VarRename
import Lean.Meta.Sym.Arith.ToExpr
import Init.Data.Nat.Order
import Init.Data.Order.Lemmas
public section

View File

@@ -9,6 +9,7 @@ public import Lean.Meta.Tactic.Grind.Arith.Linear.Types
public import Lean.Meta.Tactic.Grind.Arith.CommRing.RingM
public section
namespace Lean.Meta.Grind.Arith.Linear
open Sym.Arith (MonadCanon)
def get' : GoalM State := do
linearExt.getState

View File

@@ -11,8 +11,8 @@ import Lean.Data.RArray
import Lean.Meta.Tactic.Grind.Arith.Linear.ToExpr
import Lean.Meta.Tactic.Grind.Diseq
import Lean.Meta.Tactic.Grind.ProofUtil
import Lean.Meta.Tactic.Grind.Arith.CommRing.VarRename
import Lean.Meta.Tactic.Grind.Arith.CommRing.ToExpr
import Lean.Meta.Sym.Arith.VarRename
import Lean.Meta.Sym.Arith.ToExpr
import Lean.Meta.Tactic.Grind.Arith.Linear.VarRename
public import Lean.Meta.Tactic.Grind.Arith.Linear.DenoteExpr
public import Lean.Meta.Tactic.Grind.Arith.Linear.OfNatModule

View File

@@ -172,7 +172,7 @@ private partial def addEqStep (lhs rhs proof : Expr) (isHEq : Bool) : GoalM Unit
trueEqFalse := true
else
let hasHEq := isHEq || lhsRoot.heqProofs || rhsRoot.heqProofs
-- **Note**: We only have to check the types if there are heterogenous equalities.
-- **Note**: We only have to check the types if there are heterogeneous equalities.
if ( pure !hasHEq <||> hasSameType lhsRoot.self rhsRoot.self) then
valueInconsistency := true
if (lhsRoot.interpreted && !rhsRoot.interpreted)

View File

@@ -97,6 +97,8 @@ def mkCnstrNorm0 (s : Struct) (ringInst : Expr) (kind : CnstrKind) (lhs rhs : Ex
| .le => mkLeNorm0 s ringInst lhs rhs
| .lt => mkLtNorm0 s ringInst lhs rhs
open Sym.Arith (MonadCanon)
/--
Returns `rel lhs (rhs + 0)`
-/

View File

@@ -1973,7 +1973,7 @@ def SolverExtension.markTerm (ext : SolverExtension σ) (e : Expr) : GoalM Unit
| .next id' e' sTerms' =>
if id == id' then
-- Skip if `e` and `e'` have different types (e.g., they were merged via `HEq` from `cast`).
-- This can happen when we have heterogenous equalities in an equivalence class containing types such as `Fin n` and `Fin m`
-- This can happen when we have heterogeneous equalities in an equivalence class containing types such as `Fin n` and `Fin m`
if ( pure !root.heqProofs <||> hasSameType e e') then
( solverExtensionsRef.get)[id]!.newEq e e'
return sTerms
@@ -2056,7 +2056,7 @@ where
| .nil => return ()
| .eq solverId lhs rhs rest =>
-- Skip if `lhs` and `rhs` have different types (e.g., they were merged via `HEq` from `cast`).
-- This can happen when we have heterogenous equalities in an equivalence class containing types such as `Fin n` and `Fin m`
-- This can happen when we have heterogeneous equalities in an equivalence class containing types such as `Fin n` and `Fin m`
let root getRootENode lhs
if ( pure !root.heqProofs <||> hasSameType lhs rhs) then
( solverExtensionsRef.get)[solverId]!.newEq lhs rhs

View File

@@ -192,7 +192,7 @@ private def matchEndPos (query : String) (startPos : String.Pos.Raw) : String.Po
startPos + query
@[specialize]
private def hightlightStringMatches? (query text : String) (matchPositions : Array String.Pos.Raw)
private def highlightStringMatches? (query text : String) (matchPositions : Array String.Pos.Raw)
(highlight : α) (offset : String.Pos.Raw := 0) (mapIdx : Nat Nat := id) :
Option (TaggedText α) := Id.run do
if query.isEmpty || text.isEmpty || matchPositions.isEmpty then
@@ -245,7 +245,7 @@ private def advanceTaggedTextHighlightState (text : String) (highlighted : α) :
let query := ( get).query
let p := ( get).p
let ms := ( get).ms
let highlighted? := hightlightStringMatches? query text ms highlighted (offset := p)
let highlighted? := highlightStringMatches? query text ms highlighted (offset := p)
(mapIdx := fun i => ms.size - i - 1)
updateState text highlighted?.isSome
return highlighted?.getD (.text text)

View File

@@ -53,7 +53,7 @@ structure BVDecideConfig where
/--
Split hypotheses of the form `h : (x && y) = true` into `h1 : x = true` and `h2 : y = true`.
This has synergy potential with embedded constraint substitution. Because embedded constraint
subsitution is the only use case for this feature it is automatically disabled whenever embedded
substitution is the only use case for this feature it is automatically disabled whenever embedded
constraint substitution is disabled.
-/
andFlattening : Bool := true

View File

@@ -3168,6 +3168,10 @@ static inline lean_obj_res lean_manual_get_root(lean_obj_arg _unit) {
return lean_mk_string(LEAN_MANUAL_ROOT);
}
static inline lean_obj_res lean_runtime_hold(b_lean_obj_arg a) {
return lean_box(0);
}
#ifdef LEAN_EMSCRIPTEN
#define LEAN_SCALAR_PTR_LITERAL(b1, b2, b3, b4, b5, b6, b7, b8) (lean_object*)((uint32_t)b1 | ((uint32_t)b2 << 8) | ((uint32_t)b3 << 16) | ((uint32_t)b4 << 24)), (lean_object*)((uint32_t)b5 | ((uint32_t)b6 << 8) | ((uint32_t)b7 << 16) | ((uint32_t)b8 << 24))
#else

View File

@@ -226,7 +226,7 @@ public:
bool is_unsafe() const;
/** \brief Only definitions have values for the purpose of reduction and
type checking. Theorems used to be like that; now they are treated like
opaque declations. */
opaque declarations. */
bool has_value() const { return is_definition(); }
axiom_val const & to_axiom_val() const { lean_assert(is_axiom()); return static_cast<axiom_val const &>(cnstr_get_ref(raw(), 0)); }

View File

@@ -271,7 +271,7 @@ Returns `true` if the saved trace exists and its hash matches `inputHash`.
If up-to-date, replays the saved log from the trace and sets the current
build action to `replay`. Otherwise, if the log is empty and trace is synthetic,
or if the trace is not up-to-date, the build action will be set ot `fetch`.
or if the trace is not up-to-date, the build action will be set to `fetch`.
-/
public def SavedTrace.replayOrFetchIfUpToDate (inputHash : Hash) (self : SavedTrace) : JobM Bool := do
if let .ok data := self then

View File

@@ -19,7 +19,7 @@ import Lake.Build.InputFile
namespace Lake
/-- The initial set of Lake facets. -/
public def initFacetConfigs : DNameMap FacetConfig :=
public def initFacetConfigs : FacetConfigMap :=
DNameMap.empty
|> insert Module.initFacetConfigs
|> insert Package.initFacetConfigs
@@ -30,4 +30,4 @@ public def initFacetConfigs : DNameMap FacetConfig :=
|> insert InputDir.initFacetConfigs
where
insert {k} (group : DNameMap (KFacetConfig k)) map :=
group.foldl (init := map) fun m k v => m.insert k v.toFacetConfig
group.foldl (init := map) fun m _ v => m.insert v.toFacetConfig

View File

@@ -705,7 +705,7 @@ private def Module.restoreAllArtifacts (mod : Module) (cached : ModuleOutputArti
where
@[inline] restoreSome file art? := art?.mapM (restoreArtifact file ·)
public def Module.checkArtifactsExsist (self : Module) (isModule : Bool) : BaseIO Bool := do
public def Module.checkArtifactsExist (self : Module) (isModule : Bool) : BaseIO Bool := do
unless ( self.oleanFile.pathExists) do return false
unless ( self.ileanFile.pathExists) do return false
unless ( self.cFile.pathExists) do return false
@@ -718,7 +718,7 @@ public def Module.checkArtifactsExsist (self : Module) (isModule : Bool) : BaseI
return true
public protected def Module.checkExists (self : Module) (isModule : Bool) : BaseIO Bool := do
self.ltarFile.pathExists <||> self.checkArtifactsExsist isModule
self.ltarFile.pathExists <||> self.checkArtifactsExist isModule
@[deprecated Module.checkExists (since := "2025-03-04")]
public instance : CheckExists Module := Module.checkExists (isModule := false)
@@ -788,7 +788,7 @@ instance : ToOutputJson ModuleOutputArtifacts := ⟨(toJson ·.descrs)⟩
def Module.packLtar (self : Module) (arts : ModuleOutputArtifacts) : JobM Artifact := do
let arts id do
if ( self.checkArtifactsExsist arts.isModule) then
if ( self.checkArtifactsExist arts.isModule) then
return arts
else self.restoreAllArtifacts arts
let args id do
@@ -941,7 +941,7 @@ where
| .inr savedTrace =>
let status savedTrace.replayIfUpToDate' (oldTrace := srcTrace.mtime) mod depTrace
if status.isUpToDate then
unless ( mod.checkArtifactsExsist setup.isModule) do
unless ( mod.checkArtifactsExist setup.isModule) do
mod.unpackLtar mod.ltarFile
else
discard <| mod.buildLean depTrace srcFile setup
@@ -953,7 +953,7 @@ where
mod.computeArtifacts setup.isModule
else
if ( savedTrace.replayIfUpToDate (oldTrace := srcTrace.mtime) mod depTrace) then
unless ( mod.checkArtifactsExsist setup.isModule) do
unless ( mod.checkArtifactsExist setup.isModule) do
mod.unpackLtar mod.ltarFile
mod.computeArtifacts setup.isModule
else

View File

@@ -151,7 +151,7 @@ public def ofDecimal? (s : String) : Option Hash :=
@[inline] public def ofString? (s : String) : Option Hash :=
ofHex? s
/-- Laod a hash from a `.hash` file. -/
/-- Load a hash from a `.hash` file. -/
public def load? (hashFile : FilePath) : BaseIO (Option Hash) :=
ofString? <$> IO.FS.readFile hashFile |>.catchExceptions fun _ => pure none

View File

@@ -356,9 +356,9 @@ USAGE:
COMMANDS:
get [<mappings>] download build outputs into the local Lake cache
put <mappings> upload build ouptuts to a remote cache
put <mappings> upload build outputs to a remote cache
add <mappings> add input-to-output mappings to the Lake cache
clean removes ALL froms the local Lake cache
clean removes ALL from the local Lake cache
services print configured remote cache services
STAGING COMMANDS:
@@ -415,7 +415,7 @@ will search the repository's entire history (or as far as Git will allow).
By default, Lake will download both the input-to-output mappings and the
output artifacts for a package. By using `--mappings-onlys`, Lake will only
download the mappings abd delay downloading artifacts until they are needed.
download the mappings and delay downloading artifacts until they are needed.
If a download for an artifact fails or the download process for a whole
package fails, Lake will report this and continue on to the next. Once done,
@@ -469,7 +469,7 @@ OPTIONS:
--scope=<remote-scope> the prefix of artifacts within the service
--repo=<github-repo> for Reservoir, a GitHub repository scope
Reads a list of input-to-output mapppings from the provided file and adds
Reads a list of input-to-output mappings from the provided file and adds
them to the local Lake cache. If `--service` is provided, the output artifacts
can then be fetched lazily from that service during a Lake build. The service
must either be `reservoir` or be configured through the Lake system

View File

@@ -58,7 +58,7 @@ public structure Env where
If `none`, no suitable system directory for the cache exists.
-/
lakeSystemCache? : Option Cache := none
/-- The path to the sytem Lake configuration (i.e., `LAKE_CONFIG`). -/
/-- The path to the system Lake configuration (i.e., `LAKE_CONFIG`). -/
lakeConfig? : Option FilePath
/-- The authentication key for cache uploads (i.e., `LAKE_CACHE_KEY`). -/
cacheKey? : Option String

View File

@@ -27,6 +27,20 @@ public structure FacetConfig (name : Name) : Type where
memoize : Bool := true
deriving Inhabited
/-- A mapping of facet names to the configuration for that name. -/
public abbrev FacetConfigMap := DNameMap FacetConfig
/--
Tries to retrieve the facet configuration for the given {lean}`name`,
returning {lean}`none` if no such mapping is present.
-/
public nonrec def FacetConfigMap.get? (name : Name) (self : FacetConfigMap) : Option (FacetConfig name) :=
self.get? name -- specializes `get?`
/-- Inserts the facet configuration {lean}`cfg` into the map (overwriting any existing configuration). -/
public nonrec def FacetConfigMap.insert {name} (cfg : FacetConfig name) (self : FacetConfigMap) : FacetConfigMap :=
self.insert name cfg -- specializes `insert`
public protected abbrev FacetConfig.name (_ : FacetConfig name) := name
public structure KFacetConfig (k : Name) (name : Name) extends FacetConfig name where
@@ -70,6 +84,9 @@ public structure NamedConfigDecl (β : Name → Type u) where
name : Name
config : β name
/-- A facet declaration from a configuration file. -/
public abbrev FacetDecl := NamedConfigDecl FacetConfig
/-- A module facet's declarative configuration. -/
public abbrev ModuleFacetConfig := KFacetConfig Module.facetKind

View File

@@ -0,0 +1,53 @@
/-
Copyright (c) 2025 Mac Malone. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mac Malone
-/
module
prelude
public import Lake.Config.Script
public import Lake.Config.Dependency
public import Lake.Config.PackageConfig
public import Lake.Config.FacetConfig
public import Lake.Config.TargetConfig
set_option doc.verso true
open Lean
namespace Lake
/-- The configuration defined by a Lake configuration file (e.g., {lit}`lakefile.(lean|toml)`). -/
public structure LakefileConfig where
/-- The package deceleration of the configuration file. -/
pkgDecl : PackageDecl
/-- Depdency configurations in the order declared by the configuration file. -/
depConfigs : Array Dependency := #[]
/-- Facet configurations in the order declared by the configuration file. -/
facetDecls : Array FacetDecl := {}
/-- Target configurations in the order declared by the configuration file. -/
targetDecls : Array (PConfigDecl pkgDecl.keyName) := #[]
/-- Name-declaration map of target configurations declared in the configuration file. -/
targetDeclMap : DNameMap (NConfigDecl pkgDecl.keyName) :=
targetDecls.foldl (fun m d => m.insert d.name (.mk d rfl)) {}
/-- The names of targets declared via {lit}`@[default_target]`. -/
defaultTargets : Array Name := #[]
/-- Scripts declared in the configuration file. -/
scripts : NameMap Script := {}
/-- The names of the scripts declared via {lit}`@[default_script]`. -/
defaultScripts : Array Script := #[]
/-- {lit}`post_update` hooks in the order declared by the configuration file.. -/
postUpdateHooks : Array (OpaquePostUpdateHook pkgDecl.keyName) := #[]
/--
The name of the configuration file's test driver.
It is either declared via {lit}`@[test_driver]` or derived from {lean}`PackageConfig.testDriver`.
-/
testDriver : String := pkgDecl.config.testDriver
/--
The name of the configuration file's test driver.
It is either declared via {lit}`@[lint_driver]` or derived from {lean}`PackageConfig.lintDriver`.
-/
lintDriver : String := pkgDecl.config.lintDriver

View File

@@ -45,7 +45,7 @@ public structure Package where
/-- The path to the package's configuration file (relative to `dir`). -/
relConfigFile : FilePath
/-- The path to the package's JSON manifest of remote dependencies (relative to `dir`). -/
relManifestFile : FilePath := defaultManifestFile
relManifestFile : FilePath
/-- The package's scope (e.g., in Reservoir). -/
scope : String
/-- The URL to this package's Git remote. -/

View File

@@ -336,11 +336,16 @@ public configuration PackageConfig (p : Name) (n : Name) extends WorkspaceConfig
deriving Inhabited
/-- The package's name as specified by the author. -/
@[deprecated "Deprecated without replacement" (since := "2025-12-10")]
public abbrev PackageConfig.origName (_ : PackageConfig p n) := n
/-- A package declaration from a configuration written in Lean. -/
public structure PackageDecl where
name : Name
baseName : Name
keyName : Name
origName : Name
config : PackageConfig name origName
config : PackageConfig keyName origName
deriving TypeName
@[deprecated PackageDecl.keyName (since := "2025-12-10")]
public abbrev PackageDecl.name := @keyName

View File

@@ -22,15 +22,17 @@ open Lean (Name LeanOptions)
namespace Lake
/-- **For internal use only.** Computes the cache to use for the package based on the environment. -/
/--
**For internal use only.**
Computes the cache to use for the package based on the environment.
-/
public def computeLakeCache (pkg : Package) (lakeEnv : Lake.Env) : Cache :=
if pkg.bootstrap then
lakeEnv.lakeSystemCache?.getD pkg.lakeDir / "cache"
else
lakeEnv.lakeCache?.getD pkg.lakeDir / "cache"
/-- A Lake workspace -- the top-level package directory. -/
public structure Workspace : Type where
public structure Workspace.Raw : Type where
/-- The root package of the workspace. -/
root : Package
/-- The detected {lean}`Lake.Env` of the workspace. -/
@@ -49,14 +51,25 @@ public structure Workspace : Type where
The packages within the workspace
(in {lit}`require` declaration order with the root coming first).
-/
packages : Array Package := {}
packages : Array Package := #[]
/-- Name-package map of packages within the workspace. -/
packageMap : DNameMap NPackage := {}
/-- Configuration map of facets defined in the workspace. -/
facetConfigs : DNameMap FacetConfig := {}
facetConfigs : FacetConfigMap := {}
deriving Nonempty
public instance : Nonempty Workspace :=
by constructor <;> exact Classical.ofNonempty
public structure Workspace.Raw.WF (ws : Workspace.Raw) : Prop where
packages_wsIdx : (h : i < ws.packages.size), (ws.packages[i]'h).wsIdx = i
/-- A Lake workspace -- the top-level package directory. -/
public structure Workspace extends raw : Workspace.Raw, wf : raw.WF
public instance : Nonempty Workspace := .intro {
lakeEnv := default
lakeConfig := Classical.ofNonempty
root := Classical.ofNonempty
packages_wsIdx h := by simp at h
}
public hydrate_opaque_type OpaqueWorkspace Workspace
@@ -175,9 +188,20 @@ This is configured through {lit}`cache.service` entries in the system Lake confi
@[inline] public def packageOverridesFile (self : Workspace) : FilePath :=
self.lakeDir / "package-overrides.json"
/-- **For internal use only.** Add a well-formed package to the workspace. -/
@[inline] public def addPackage' (pkg : Package) (self : Workspace) (h : pkg.wsIdx = self.packages.size) : Workspace :=
{self with
packages := self.packages.push pkg
packageMap := self.packageMap.insert pkg.keyName pkg
packages_wsIdx {i} i_lt := by
cases Nat.lt_add_one_iff_lt_or_eq.mp <| Array.size_push .. i_lt with
| inl i_lt => simpa [Array.getElem_push_lt i_lt] using self.packages_wsIdx i_lt
| inr i_eq => simpa [i_eq] using h
}
/-- Add a package to the workspace. -/
public def addPackage (pkg : Package) (self : Workspace) : Workspace :=
{self with packages := self.packages.push pkg, packageMap := self.packageMap.insert pkg.keyName pkg}
@[inline] public def addPackage (pkg : Package) (self : Workspace) : Workspace :=
self.addPackage' {pkg with wsIdx := self.packages.size} rfl
/-- Returns the unique package in the workspace (if any) that is identified by {lean}`keyName`. -/
@[inline] public protected def findPackageByKey? (keyName : Name) (self : Workspace) : Option (NPackage keyName) :=
@@ -251,15 +275,15 @@ public def findTargetDecl? (name : Name) (self : Workspace) : Option ((pkg : Pac
self.packages.findSome? fun pkg => pkg.findTargetDecl? name <&> (pkg, ·)
/-- Add a facet to the workspace. -/
public def addFacetConfig {name} (cfg : FacetConfig name) (self : Workspace) : Workspace :=
{self with facetConfigs := self.facetConfigs.insert name cfg}
@[inline] public def addFacetConfig {name} (cfg : FacetConfig name) (self : Workspace) : Workspace :=
{self with facetConfigs := self.facetConfigs.insert cfg}
/-- Try to find a facet configuration in the workspace with the given name. -/
public def findFacetConfig? (name : Name) (self : Workspace) : Option (FacetConfig name) :=
@[inline] public def findFacetConfig? (name : Name) (self : Workspace) : Option (FacetConfig name) :=
self.facetConfigs.get? name
/-- Add a module facet to the workspace. -/
public def addModuleFacetConfig (cfg : ModuleFacetConfig name) (self : Workspace) : Workspace :=
@[inline] public def addModuleFacetConfig (cfg : ModuleFacetConfig name) (self : Workspace) : Workspace :=
self.addFacetConfig cfg.toFacetConfig
/-- Try to find a module facet configuration in the workspace with the given name. -/
@@ -267,7 +291,7 @@ public def findModuleFacetConfig? (name : Name) (self : Workspace) : Option (Mod
self.findFacetConfig? name |>.bind (·.toKind? Module.facetKind)
/-- Add a package facet to the workspace. -/
public def addPackageFacetConfig (cfg : PackageFacetConfig name) (self : Workspace) : Workspace :=
@[inline] public def addPackageFacetConfig (cfg : PackageFacetConfig name) (self : Workspace) : Workspace :=
self.addFacetConfig cfg.toFacetConfig
/-- Try to find a package facet configuration in the workspace with the given name. -/
@@ -275,7 +299,7 @@ public def findPackageFacetConfig? (name : Name) (self : Workspace) : Option (Pa
self.findFacetConfig? name |>.bind (·.toKind? Package.facetKind)
/-- Add a library facet to the workspace. -/
public def addLibraryFacetConfig (cfg : LibraryFacetConfig name) (self : Workspace) : Workspace :=
@[inline] public def addLibraryFacetConfig (cfg : LibraryFacetConfig name) (self : Workspace) : Workspace :=
self.addFacetConfig cfg.toFacetConfig
/-- Try to find a library facet configuration in the workspace with the given name. -/

View File

@@ -26,17 +26,18 @@ def elabPackageCommand : CommandElab := fun stx => do
let configId : Ident `(pkgConfig)
let id mkConfigDeclIdent nameStx?
let origName := Name.quoteFrom id id.getId
let idx, name := nameExt.getState ( getEnv)
let name := match name with
let wsIdx, name := nameExt.getState ( getEnv)
let baseName := match name with
| .anonymous => origName
| name => Name.quoteFrom id name
let name := Syntax.mkCApp ``Name.num #[name, quote idx]
let ty := Syntax.mkCApp ``PackageConfig #[name, origName]
let wsIdx := quote wsIdx
let keyName := Syntax.mkCApp ``Name.num #[baseName, wsIdx]
let ty := Syntax.mkCApp ``PackageConfig #[keyName, origName]
elabConfig ``PackageConfig configId ty cfg
let attr `(Term.attrInstance| «package»)
let attrs := #[attr] ++ expandAttrs attrs?
let id := mkIdentFrom id packageDeclName
let decl `({name := $name, origName := $origName, config := $configId})
let decl `({baseName := $baseName, origName := $origName, keyName := $keyName, config := $configId})
let cmd `($[$doc?]? @[$attrs,*] abbrev $id : PackageDecl := $decl)
withMacroExpansion stx cmd <| elabCommand cmd
let nameId := mkIdentFrom id <| packageDeclName.str "name"

View File

@@ -7,6 +7,7 @@ module
prelude
public import Lake.Config.Env
public import Lake.Config.Lang
public import Lake.Config.LakeConfig
public import Lake.Load.Manifest
@@ -41,8 +42,16 @@ public structure LoadConfig where
pkgDir : FilePath := wsDir / relPkgDir
/-- The package's Lake configuration file (relative to its directory). -/
relConfigFile : FilePath := defaultConfigFile
/-- The full path to the loaded package's Lake configuration file. -/
/-- The full path to the loaded package's Lake configuration file. -/
configFile : FilePath := pkgDir / relConfigFile
/-
The format of the package's configuration file.
If {lean}`none`, the format will be determined by the file's extension.
-/
configLang? : Option ConfigLang := none
/-- The package's Lake manifest file (relative to its directory). -/
relManifestFile : FilePath := defaultManifestFile
/-- Additional package overrides for this workspace load. -/
packageOverrides : Array PackageEntry := #[]
/-- A set of key-value Lake configuration options (i.e., {lit}`-K` settings). -/
@@ -55,6 +64,7 @@ public structure LoadConfig where
updateDeps : Bool := false
/--
Whether to update the workspace's {lit}`lean-toolchain` when dependencies are updated.
If {lean}`true` and a toolchain update occurs, Lake will need to be restarted.
-/
updateToolchain : Bool := true

View File

@@ -7,6 +7,7 @@ module
prelude
public import Lake.Config.Package
public import Lake.Config.LakefileConfig
public import Lake.Load.Config
import Lake.Load.Lean.Elab
import Lake.Load.Lean.Eval
@@ -21,22 +22,7 @@ open Lean
namespace Lake
/--
Elaborate a Lean configuration file into a `Package`.
The resulting package does not yet include any dependencies.
-/
public def loadLeanConfig (cfg : LoadConfig) : LogIO (Package × Environment) := do
/-- Elaborate a Lake configuration file written in Lean and extract the Lake configuration. -/
public def loadLeanConfig (cfg : LoadConfig) : LogIO LakefileConfig := do
let configEnv importConfigFile cfg
let keyName, origName, config IO.ofExcept <| PackageDecl.loadFromEnv configEnv cfg.leanOpts
let baseName := if cfg.pkgName.isAnonymous then origName else cfg.pkgName
let pkg : Package := {
wsIdx := cfg.pkgIdx
baseName, keyName, origName, config
dir := cfg.pkgDir
relDir := cfg.relPkgDir
configFile := cfg.configFile
relConfigFile := cfg.relConfigFile
scope := cfg.scope
remoteUrl := cfg.remoteUrl
}
return ( pkg.loadFromEnv configEnv cfg.leanOpts, configEnv)
LakefileConfig.loadFromEnv configEnv cfg.leanOpts

View File

@@ -7,6 +7,7 @@ module
prelude
public import Lake.Config.Workspace
public import Lake.Config.LakefileConfig
import Lean.DocString
import Lake.DSL.AttributesCore
@@ -75,39 +76,38 @@ private def mkOrdTagMap
return map.insert declName <| f declName
/-- Load a `PackageDecl` from a configuration environment. -/
public def PackageDecl.loadFromEnv
private def PackageDecl.loadFromEnv
(env : Environment) (opts := Options.empty)
: Except String PackageDecl := do
let declName
match packageAttr.getAllEntries env |>.toList with
| [] => error s!"configuration file is missing a `package` declaration"
| [name] => pure name
| [keyName] => pure keyName
| _ => error s!"configuration file has multiple `package` declarations"
evalConstCheck env opts _ declName
/--
Load the optional elements of a `Package` from the Lean environment.
This is done after loading its core configuration but before resolving
its dependencies.
-/
public def Package.loadFromEnv
(self : Package) (env : Environment) (opts : Options)
: LogIO Package := do
/-- Load a Lake configuration from a configuration file's environment. -/
public def LakefileConfig.loadFromEnv
(env : Environment) (opts : Options)
: LogIO LakefileConfig := do
-- load package declaration
let pkgDecl IO.ofExcept <| PackageDecl.loadFromEnv env opts
let prettyName := pkgDecl.baseName.toString (escape := false)
let keyName := pkgDecl.keyName
-- load target, script, hook, and driver configurations
let constTargetMap IO.ofExcept <| mkOrdTagMap env targetAttr fun name => do
evalConstCheck env opts ConfigDecl name
let targetDecls constTargetMap.toArray.mapM fun decl => do
if h : decl.pkg = self.keyName then
if h : decl.pkg = keyName then
return .mk decl h
else
error s!"\
target '{decl.name}' was defined in package '{decl.pkg}', \
but registered under '{self.keyName}'"
error s!"target '{decl.name}' was defined in package '{decl.pkg}', \
but registered under '{keyName}'"
let targetDeclMap targetDecls.foldlM (init := {}) fun m decl => do
if let some orig := m.get? decl.name then
error s!"\
{self.prettyName}: target '{decl.name}' was already defined as a '{orig.kind}', \
error s!"{prettyName}: target '{decl.name}' was already defined as a '{orig.kind}', \
but then redefined as a '{decl.kind}'"
else
return m.insert decl.name (.mk decl rfl)
@@ -116,8 +116,7 @@ public def Package.loadFromEnv
if let some exeConfig := decl.config? LeanExe.configKind then
let root := exeConfig.root
if let some origExe := exeRoots.get? root then
error s!"\
{self.prettyName}: executable '{decl.name}' has the same root module '{root}' \
error s!"{prettyName}: executable '{decl.name}' has the same root module '{root}' \
as executable '{origExe}'"
else
return exeRoots.insert root decl.name
@@ -125,80 +124,78 @@ public def Package.loadFromEnv
return exeRoots
let defaultTargets defaultTargetAttr.getAllEntries env |>.mapM fun name =>
if let some decl := constTargetMap.find? name then pure decl.name else
error s!"{self.prettyName}: package is missing target '{name}' marked as a default"
error s!"{prettyName}: package is missing target '{name}' marked as a default"
let scripts mkTagMap env scriptAttr fun scriptName => do
let name := self.prettyName ++ "/" ++ scriptName.toString (escape := false)
let name := prettyName ++ "/" ++ scriptName.toString (escape := false)
let fn IO.ofExcept <| evalConstCheck env opts ScriptFn scriptName
return {name, fn, doc? := findDocString? env scriptName : Script}
let defaultScripts defaultScriptAttr.getAllEntries env |>.mapM fun name =>
if let some script := scripts.get? name then pure script else
error s!"{self.prettyName}: package is missing script '{name}' marked as a default"
error s!"{prettyName}: package is missing script '{name}' marked as a default"
let postUpdateHooks postUpdateAttr.getAllEntries env |>.mapM fun name =>
match evalConstCheck env opts PostUpdateHookDecl name with
| .ok decl =>
if h : decl.pkg = self.keyName then
return OpaquePostUpdateHook.mk cast (by rw [h, keyName]) decl.fn
if h : decl.pkg = keyName then
return OpaquePostUpdateHook.mk cast (by rw [h]) decl.fn
else
error s!"post-update hook was defined in '{decl.pkg}', but was registered in '{self.keyName}'"
error s!"post-update hook was defined in '{decl.pkg}', but was registered in '{keyName}'"
| .error e => error e
let depConfigs IO.ofExcept <| packageDepAttr.getAllEntries env |>.mapM fun name =>
evalConstCheck env opts Dependency name
let testDrivers testDriverAttr.getAllEntries env |>.mapM fun name =>
if let some decl := constTargetMap.find? name then
pure decl.name
return decl.name
else if scripts.contains name then
pure name
return name
else
error s!"{self.prettyName}: package is missing script or target '{name}' marked as a test driver"
let testDriver
error s!"{prettyName}: package is missing script or target '{name}' marked as a test driver"
let testDriver id do
if testDrivers.size > 1 then
error s!"{self.prettyName}: only one script, executable, or library can be tagged @[test_driver]"
error s!"{prettyName}: only one script, executable, or library can be tagged @[test_driver]"
else if h : testDrivers.size > 0 then
if self.config.testDriver.isEmpty then
pure (testDrivers[0]'h |>.toString)
if pkgDecl.config.testDriver.isEmpty then
return (testDrivers[0]'h |>.toString)
else
error s!"{self.prettyName}: cannot both set testDriver and use @[test_driver]"
error s!"{prettyName}: cannot both set testDriver and use @[test_driver]"
else
pure self.config.testDriver
return pkgDecl.config.testDriver
let lintDrivers lintDriverAttr.getAllEntries env |>.mapM fun name =>
if let some decl := constTargetMap.find? name then
pure decl.name
return decl.name
else if scripts.contains name then
pure name
return name
else
error s!"{self.prettyName}: package is missing script or target '{name}' marked as a lint driver"
let lintDriver
error s!"{prettyName}: package is missing script or target '{name}' marked as a lint driver"
let lintDriver id do
if lintDrivers.size > 1 then
error s!"{self.prettyName}: only one script or executable can be tagged @[lint_driver]"
error s!"{prettyName}: only one script or executable can be tagged @[lint_driver]"
else if h : lintDrivers.size > 0 then
if self.config.lintDriver.isEmpty then
pure (lintDrivers[0]'h |>.toString)
if pkgDecl.config.lintDriver.isEmpty then
return (lintDrivers[0]'h |>.toString)
else
error s!"{self.prettyName}: cannot both set lintDriver and use @[lint_driver]"
error s!"{prettyName}: cannot both set lintDriver and use @[lint_driver]"
else
pure self.config.lintDriver
return pkgDecl.config.lintDriver
-- load facets
let facetDecls IO.ofExcept do
let mut decls : Array FacetDecl := #[]
for name in moduleFacetAttr.getAllEntries env do
let decl evalConstCheck env opts ModuleFacetDecl name
decls := decls.push {decl with config := decl.config.toFacetConfig}
for name in packageFacetAttr.getAllEntries env do
let decl evalConstCheck env opts PackageFacetDecl name
decls := decls.push {decl with config := decl.config.toFacetConfig}
for name in libraryFacetAttr.getAllEntries env do
let decl evalConstCheck env opts LibraryFacetDecl name
decls := decls.push {decl with config := decl.config.toFacetConfig}
return decls
-- Fill in the Package
return {self with
depConfigs, targetDecls, targetDeclMap
defaultTargets, scripts, defaultScripts
testDriver, lintDriver, postUpdateHooks
return {
pkgDecl, depConfigs, facetDecls,
targetDecls, targetDeclMap, defaultTargets,
scripts, defaultScripts,
testDriver, lintDriver,
postUpdateHooks,
}
/--
Load module/package facets into a `Workspace` from a configuration environment.
-/
public def Workspace.addFacetsFromEnv
(env : Environment) (opts : Options) (self : Workspace)
: Except String Workspace := do
let mut ws := self
for name in moduleFacetAttr.getAllEntries env do
let decl evalConstCheck env opts ModuleFacetDecl name
ws := ws.addModuleFacetConfig decl.config
for name in packageFacetAttr.getAllEntries env do
let decl evalConstCheck env opts PackageFacetDecl name
ws := ws.addPackageFacetConfig decl.config
for name in libraryFacetAttr.getAllEntries env do
let decl evalConstCheck env opts LibraryFacetDecl name
ws := ws.addLibraryFacetConfig decl.config
return ws

View File

@@ -94,6 +94,9 @@ public structure PackageEntry where
namespace PackageEntry
@[inline] public def prettyName (entry : PackageEntry) : String :=
entry.name.toString (escape := false)
public protected def toJson (entry : PackageEntry) : Json :=
let fields := [
("name", toJson entry.name),

View File

@@ -10,6 +10,7 @@ public import Lake.Config.Env
public import Lake.Load.Manifest
public import Lake.Config.Package
import Lake.Util.Git
import Lake.Util.IO
import Lake.Reservoir
open System Lean
@@ -87,6 +88,8 @@ def materializeGitRepo
cloneGitPkg name repo url rev?
public structure MaterializedDep where
/-- Absolute path to the materialized package. -/
pkgDir : FilePath
/-- Path to the materialized package relative to the workspace's root directory. -/
relPkgDir : FilePath
/--
@@ -105,17 +108,32 @@ namespace MaterializedDep
@[inline] public def name (self : MaterializedDep) : Name :=
self.manifestEntry.name
@[inline] public def prettyName (self : MaterializedDep) : String :=
self.manifestEntry.name.toString (escape := false)
@[inline] public def scope (self : MaterializedDep) : String :=
self.manifestEntry.scope
/-- Path to the dependency's configuration file (relative to `relPkgDir`). -/
@[inline] public def manifestFile? (self : MaterializedDep) : Option FilePath :=
/-- Path to the dependency's manfiest file (relative to `relPkgDir`). -/
@[inline] public def relManifestFile? (self : MaterializedDep) : Option FilePath :=
self.manifestEntry.manifestFile?
/-- Path to the dependency's manfiest file (relative to `relPkgDir`). -/
@[inline] public def relManifestFile (self : MaterializedDep) : FilePath :=
self.relManifestFile?.getD defaultManifestFile
/-- Absolute path to the dependency's manfiest file. -/
@[inline] public def manifestFile (self : MaterializedDep) : FilePath :=
self.pkgDir / self.relManifestFile
/-- Path to the dependency's configuration file (relative to `relPkgDir`). -/
@[inline] public def configFile (self : MaterializedDep) : FilePath :=
@[inline] public def relConfigFile (self : MaterializedDep) : FilePath :=
self.manifestEntry.configFile
/-- Absolute path to the dependency's configuration file. -/
@[inline] public def configFile (self : MaterializedDep) : FilePath :=
self.pkgDir / self.relConfigFile
public def fixedToolchain (self : MaterializedDep) : Bool :=
match self.manifest? with
| .ok manifest => manifest.fixedToolchain
@@ -157,10 +175,11 @@ public def Dependency.materialize
(lakeEnv : Env) (wsDir relPkgsDir relParentDir : FilePath)
: LoggerIO MaterializedDep := do
if let some src := dep.src? then
let sname := dep.name.toString (escape := false)
match src with
| .path dir =>
let relPkgDir := relParentDir / dir
mkDep (wsDir / relPkgDir) relPkgDir "" (.path relPkgDir)
mkDep sname relPkgDir "" (.path relPkgDir)
| .git url inputRev? subDir? => do
let sname := dep.name.toString (escape := false)
let repoUrl := Git.filterUrl? url |>.getD ""
@@ -208,16 +227,19 @@ public def Dependency.materialize
| _ => error s!"{pkg.fullName}: Git source not found on Reservoir"
where
materializeGit name relPkgDir gitUrl remoteUrl inputRev? subDir? : LoggerIO MaterializedDep := do
let pkgDir := wsDir / relPkgDir
let repo := GitRepo.mk pkgDir
let gitDir := wsDir / relPkgDir
let repo := GitRepo.mk gitDir
let gitUrl := lakeEnv.pkgUrlMap.find? dep.name |>.getD gitUrl
materializeGitRepo name repo gitUrl inputRev?
let rev repo.getHeadRevision
let relPkgDir := if let some subDir := subDir? then relPkgDir / subDir else relPkgDir
mkDep pkgDir relPkgDir remoteUrl <| .git gitUrl rev inputRev? subDir?
@[inline] mkDep pkgDir relPkgDir remoteUrl src : LoggerIO MaterializedDep := do
mkDep name relPkgDir remoteUrl <| .git gitUrl rev inputRev? subDir?
@[inline] mkDep name relPkgDir remoteUrl src : LoggerIO MaterializedDep := do
let pkgDir := wsDir / relPkgDir
let some pkgDir resolvePath? pkgDir
| error s!"{name}: package directory not found: {pkgDir}"
return {
relPkgDir, remoteUrl
pkgDir, relPkgDir, remoteUrl,
manifest? := Manifest.load (pkgDir / defaultManifestFile) |>.toBaseIO
manifestEntry := {name := dep.name, scope := dep.scope, inherited, src}
}
@@ -231,9 +253,9 @@ public def PackageEntry.materialize
: LoggerIO MaterializedDep :=
match manifestEntry.src with
| .path (dir := relPkgDir) .. =>
mkDep (wsDir / relPkgDir) relPkgDir ""
mkDep relPkgDir ""
| .git (url := url) (rev := rev) (subDir? := subDir?) .. => do
let sname := manifestEntry.name.toString (escape := false)
let sname := manifestEntry.prettyName
let relGitDir := relPkgsDir / sname
let gitDir := wsDir / relGitDir
let repo := GitRepo.mk gitDir
@@ -254,12 +276,15 @@ public def PackageEntry.materialize
let url := lakeEnv.pkgUrlMap.find? manifestEntry.name |>.getD url
cloneGitPkg sname repo url rev
let relPkgDir := match subDir? with | .some subDir => relGitDir / subDir | .none => relGitDir
mkDep gitDir relPkgDir (Git.filterUrl? url |>.getD "")
mkDep relPkgDir (Git.filterUrl? url |>.getD "")
where
@[inline] mkDep pkgDir relPkgDir remoteUrl : LoggerIO MaterializedDep := do
@[inline] mkDep relPkgDir remoteUrl : LoggerIO MaterializedDep := do
let pkgDir := wsDir / relPkgDir
let some pkgDir resolvePath? pkgDir
| error s!"{manifestEntry.prettyName}: package directory not found: {pkgDir}"
let manifest? id do
if let some manifestFile := manifestEntry.manifestFile? then
Manifest.load (pkgDir / manifestFile) |>.toBaseIO
else
return .error (.noFileOrDirectory "" 0 "")
return {relPkgDir, remoteUrl, manifest?, manifestEntry}
return {pkgDir, relPkgDir, remoteUrl, manifest?, manifestEntry}

View File

@@ -8,10 +8,13 @@ module
prelude
public import Lake.Load.Config
public import Lake.Config.Package
public import Lake.Config.LakefileConfig
import Lake.Util.IO
import Lake.Load.Lean
import Lake.Load.Toml
set_option doc.verso true
/-! # Package Loader
This module contains the main definitions to load a package
@@ -23,6 +26,38 @@ open System (FilePath)
namespace Lake
/--
**For interal use only.**
Constructs a package from the configuration defined in its Lake configuration file
and the load configuaration.
-/
public def mkPackage
(loadCfg : LoadConfig) (fileCfg : LakefileConfig) (wsIdx := loadCfg.pkgIdx)
: Package :=
let {
pkgDir := dir, relPkgDir := relDir,
configFile, relConfigFile, relManifestFile,
scope, remoteUrl, ..} := loadCfg
let {
depConfigs, defaultTargets, scripts, defaultScripts, testDriver, lintDriver
-- destructing needed for type-correctness
pkgDecl, targetDecls, targetDeclMap, postUpdateHooks,
..} := fileCfg
let {baseName, keyName, origName, config} := pkgDecl
{
wsIdx, baseName, keyName, origName, config
dir, relDir, configFile, relConfigFile, relManifestFile
scope, remoteUrl
depConfigs
targetDecls, targetDeclMap, defaultTargets
scripts, defaultScripts
testDriver, lintDriver
postUpdateHooks
}
public theorem wsIdx_mkPackage : (mkPackage l f i).wsIdx = i := by rfl
/--
Return whether a configuration file with the given name
and/or a supported extension exists.
@@ -49,21 +84,25 @@ public def realConfigFile (cfgFile : FilePath) : BaseIO FilePath := do
resolvePath (cfgFile.addExtension "toml")
/--
Loads a Lake package configuration (either Lean or TOML).
The resulting package does not yet include any dependencies.
**For internal use only.**
Resolves a relative configuration file path in {lean}`cfg` and
detects its configuration language (if necessary).
-/
public def loadPackageCore
public def resolveConfigFile
(name : String) (cfg : LoadConfig)
: LogIO (Package × Option Environment) := do
if let some ext := cfg.relConfigFile.extension then
let cfg
if let some configFile resolvePath? cfg.configFile then
pure {cfg with configFile}
else error s!"{name}: configuration file not found: {cfg.configFile}"
: LogIO {cfg : LoadConfig // cfg.configLang?.isSome} := do
if h : cfg.configLang?.isSome then
let some configFile resolvePath? cfg.configFile
| error s!"{name}: configuration file not found: {cfg.configFile}"
return {cfg with configFile}, h
else if let some ext := cfg.relConfigFile.extension then
let some configFile resolvePath? cfg.configFile
| error s!"{name}: configuration file not found: {cfg.configFile}"
match ext with
| "lean" => (·.map id some) <$> loadLeanConfig cfg
| "toml" => ((·,none)) <$> loadTomlConfig cfg
| _ => error s!"{name}: configuration has unsupported file extension: {cfg.configFile}"
| "lean" => return {cfg with configFile, configLang? := some .lean}, rfl
| "toml" => return {cfg with configFile, configLang? := some .toml}, rfl
| _ => error s!"{name}: configuration has unsupported file extension: {configFile}"
else
let relLeanFile := cfg.relConfigFile.addExtension "lean"
let relTomlFile := cfg.relConfigFile.addExtension "toml"
@@ -72,18 +111,28 @@ public def loadPackageCore
if let some configFile resolvePath? leanFile then
if ( tomlFile.pathExists) then
logInfo s!"{name}: {relLeanFile} and {relTomlFile} are both present; using {relLeanFile}"
let cfg := {cfg with configFile, relConfigFile := relLeanFile}
let (pkg, env) loadLeanConfig cfg
return (pkg, some env)
return {cfg with configFile, relConfigFile := relLeanFile, configLang? := some .lean}, rfl
else if let some configFile resolvePath? tomlFile then
return {cfg with configFile, relConfigFile := relTomlFile, configLang? := some .toml}, rfl
else
if let some configFile resolvePath? tomlFile then
let cfg := {cfg with configFile, relConfigFile := relTomlFile}
let pkg loadTomlConfig cfg
return (pkg, none)
else
error s!"{name}: no configuration file with a supported extension:\n{leanFile}\n{tomlFile}"
error s!"{name}: no configuration file with a supported extension:\n{leanFile}\n{tomlFile}"
/--
**For internal use only.**
Reads the configuration of a Lake configuration file.
The load configuration {lean}`cfg` is assumed to already have an absolute path in
{lean}`cfg.configFile` and that the proper configuratin langauge for it is in
{lean}`cfg.configLang?`. This can be ensured through {lean}`resolveConfigFile`.
-/
public def loadConfigFile (cfg : LoadConfig) (h : cfg.configLang?.isSome) : LogIO LakefileConfig := do
match cfg.configLang?.get h with
| .lean => loadLeanConfig cfg
| .toml => loadTomlConfig cfg
/-- Loads a Lake package as a single independent object (without dependencies). -/
public def loadPackage (config : LoadConfig) : LogIO Package := do
Lean.searchPathRef.set config.lakeEnv.leanSearchPath
(·.1) <$> loadPackageCore "[root]" config
public def loadPackage (cfg : LoadConfig) : LogIO Package := do
Lean.searchPathRef.set cfg.lakeEnv.leanSearchPath
let cfg, h resolveConfigFile "[root]" cfg
let fileCfg loadConfigFile cfg h
return mkPackage cfg fileCfg

View File

@@ -25,37 +25,43 @@ This module contains definitions for resolving the dependencies of a package.
namespace Lake
/-- Returns the load configuration of a materialized dependency. -/
@[inline] def mkDepLoadConfig
(ws : Workspace) (dep : MaterializedDep)
(lakeOpts : NameMap String) (leanOpts : Options) (reconfigure : Bool)
: LoadConfig where
lakeEnv := ws.lakeEnv
wsDir := ws.dir
pkgIdx := ws.packages.size
pkgName := dep.name
pkgDir := dep.pkgDir
relPkgDir := dep.relPkgDir
relConfigFile := dep.relConfigFile
relManifestFile := dep.relManifestFile
lakeOpts; leanOpts; reconfigure
scope := dep.scope
remoteUrl := dep.remoteUrl
def Workspace.addFacetDecls (decls : Array FacetDecl) (self : Workspace) : Workspace :=
decls.foldl (·.addFacetConfig ·.config) self
/--
Loads the package configuration of a materialized dependency.
Adds the facets defined in the package to the `Workspace`.
Adds the package and the facets defined within it to the `Workspace`.
-/
def loadDepPackage
(wsIdx : Nat)
def addDepPackage
(dep : MaterializedDep)
(lakeOpts : NameMap String)
(leanOpts : Options) (reconfigure : Bool)
: StateT Workspace LogIO Package := fun ws => do
let name := dep.name.toString (escape := false)
let pkgDir := ws.dir / dep.relPkgDir
let some pkgDir resolvePath? pkgDir
| error s!"{name}: package directory not found: {pkgDir}"
let (pkg, env?) loadPackageCore name {
lakeEnv := ws.lakeEnv
wsDir := ws.dir
pkgIdx := wsIdx
pkgName := dep.name
pkgDir
relPkgDir := dep.relPkgDir
relConfigFile := dep.configFile
lakeOpts, leanOpts, reconfigure
scope := dep.scope
remoteUrl := dep.remoteUrl
}
if let some env := env? then
let ws IO.ofExcept <| ws.addFacetsFromEnv env leanOpts
return (pkg, ws)
else
return (pkg, ws)
let wsIdx := ws.packages.size
let loadCfg := mkDepLoadConfig ws dep lakeOpts leanOpts reconfigure
let loadCfg, h resolveConfigFile dep.prettyName loadCfg
let fileCfg loadConfigFile loadCfg h
let pkg := mkPackage loadCfg fileCfg wsIdx
let ws := ws.addPackage' pkg wsIdx_mkPackage
let ws := ws.addFacetDecls fileCfg.facetDecls
return (pkg, ws)
/--
The resolver's call stack of dependencies.
@@ -100,7 +106,7 @@ abbrev ResolveT m := DepStackT <| StateT Workspace m
/-
Recursively visits each node in a package's dependency graph, starting from
the workspace package `root`. Each dependency missing from the workspace is
resolved using the `load` function and added into the workspace.
added to the workspace using the `resolve` function.
Recursion occurs breadth-first. Each direct dependency of a package is
resolved in reverse order before recursing to the dependencies' dependencies.
@@ -108,9 +114,10 @@ resolved in reverse order before recursing to the dependencies' dependencies.
See `Workspace.updateAndMaterializeCore` for more details.
-/
@[inline] private def Workspace.resolveDepsCore
[Monad m] [MonadError m] (ws : Workspace)
(load : Package Dependency Nat StateT Workspace m Package)
[Monad m] [MonadError m] [MonadLiftT LogIO m] (ws : Workspace)
(resolve : Package Dependency Workspace m MaterializedDep)
(root : Package := ws.root) (stack : DepStack := {})
(leanOpts : Options := {}) (reconfigure := true)
: m Workspace := do
ws.runResolveT go root stack
where
@@ -123,8 +130,8 @@ where
return -- already handled in another branch
if pkg.baseName = dep.name then
error s!"{pkg.prettyName}: package requires itself (or a package with the same name)"
let depPkg load pkg dep ws.packages.size
modifyThe Workspace (·.addPackage depPkg)
let matDep resolve pkg dep ( getWorkspace)
discard <| addDepPackage matDep dep.opts leanOpts reconfigure
-- Recursively load the dependencies' dependencies
( getWorkspace).packages.forM recurse start
@@ -171,17 +178,17 @@ private def reuseManifest
logWarning s!"{rootName}: ignoring previous manifest because it failed to load: {e}"
/-- Add a package dependency's manifest entries to the update state. -/
private def addDependencyEntries (pkg : Package) (matDep : MaterializedDep) : UpdateT LoggerIO PUnit := do
match matDep.manifest? with
private def addDependencyEntries (dep : MaterializedDep) : UpdateT LoggerIO PUnit := do
match ( Manifest.load dep.manifestFile |>.toBaseIO) with
| .ok manifest =>
manifest.packages.forM fun entry => do
unless ( getThe (NameMap PackageEntry)).contains entry.name do
let entry := entry.setInherited.inDirectory pkg.relDir
let entry := entry.setInherited.inDirectory dep.relPkgDir
store entry.name entry
| .error (.noFileOrDirectory ..) =>
logWarning s!"{pkg.prettyName}: ignoring missing manifest '{pkg.manifestFile}'"
logWarning s!"{dep.prettyName}: ignoring missing manifest:\n {dep.manifestFile}"
| .error e =>
logWarning s!"{pkg.prettyName}: ignoring manifest because it failed to load: {e}"
logWarning s!"{dep.prettyName}: ignoring manifest because it failed to load: {e}"
/-- Materialize a single dependency, updating it if desired. -/
private def updateAndMaterializeDep
@@ -220,7 +227,7 @@ private structure ToolchainCandidate where
fixed : Bool := false
private structure ToolchainState where
/-- The name of depedency which provided the current candidate toolchain. -/
/-- The name of dependency which provided the current candidate toolchain. -/
src : Name
/-- The current candidate toolchain version (if any). -/
tc? : Option ToolchainVer
@@ -356,7 +363,6 @@ def Workspace.updateAndMaterializeCore
(updateToolchain := true)
: LoggerIO (Workspace × NameMap PackageEntry) := UpdateT.run do
reuseManifest ws toUpdate
let ws := ws.addPackage ws.root
if updateToolchain then
let deps := ws.root.depConfigs.reverse
let matDeps deps.mapM fun dep => do
@@ -365,21 +371,18 @@ def Workspace.updateAndMaterializeCore
ws.updateToolchain matDeps
let start := ws.packages.size
let ws (deps.zip matDeps).foldlM (init := ws) fun ws (dep, matDep) => do
let (depPkg, ws) loadUpdatedDep ws.packages.size dep matDep ws
let ws := ws.addPackage depPkg
addDependencyEntries matDep
let (_, ws) addDepPackage matDep dep.opts leanOpts true ws
return ws
ws.packages.foldlM (init := ws) (start := start) fun ws pkg =>
ws.resolveDepsCore (stack := [ws.root.baseName]) updateAndLoadDep pkg
ws.resolveDepsCore updateAndAddDep pkg [ws.root.baseName] leanOpts true
else
ws.resolveDepsCore updateAndLoadDep
ws.resolveDepsCore updateAndAddDep (leanOpts := leanOpts) (reconfigure := true)
where
@[inline] updateAndLoadDep pkg dep wsIdx := do
let matDep updateAndMaterializeDep ( getWorkspace) pkg dep
loadUpdatedDep wsIdx dep matDep
@[inline] loadUpdatedDep wsIdx dep matDep : StateT Workspace (UpdateT LoggerIO) Package := do
let depPkg loadDepPackage wsIdx matDep dep.opts leanOpts true
addDependencyEntries depPkg matDep
return depPkg
@[inline] updateAndAddDep pkg dep ws := do
let matDep updateAndMaterializeDep ws pkg dep
addDependencyEntries matDep
return matDep
/-- Write package entries to the workspace manifest. -/
def Workspace.writeManifest
@@ -471,12 +474,9 @@ public def Workspace.materializeDeps
if pkgEntries.isEmpty && !ws.root.depConfigs.isEmpty then
error "missing manifest; use `lake update` to generate one"
-- Materialize all dependencies
let ws := ws.addPackage ws.root
ws.resolveDepsCore fun pkg dep wsIdx => do
let ws getWorkspace
ws.resolveDepsCore (leanOpts := leanOpts) (reconfigure := reconfigure) fun pkg dep ws => do
if let some entry := pkgEntries.find? dep.name then
let result entry.materialize ws.lakeEnv ws.dir relPkgsDir
loadDepPackage wsIdx result dep.opts leanOpts reconfigure
entry.materialize ws.lakeEnv ws.dir relPkgsDir
else
if pkg.isRoot then
error <|

View File

@@ -7,10 +7,12 @@ module
prelude
public import Lake.Config.Package
public import Lake.Config.LakefileConfig
public import Lake.Load.Config
public import Lake.Toml.Decode
import Lake.Toml.Load
import Lean.Parser.Extension
import Lake.Build.Infos
import Init.Omega
meta import Lake.Config.LakeConfig
meta import Lake.Config.InputFileConfig
@@ -424,14 +426,15 @@ private def decodeTargetDecls
(pkg : Name) (prettyName : String) (t : Table)
: DecodeM (Array (PConfigDecl pkg) × DNameMap (NConfigDecl pkg)) := do
let r : DecodeTargetState pkg := {}
let r go r LeanLib.keyword LeanLib.configKind LeanLibConfig.decodeToml
let r go r LeanExe.keyword LeanExe.configKind LeanExeConfig.decodeToml
let r go r InputFile.keyword InputFile.configKind InputFileConfig.decodeToml
let r go r InputDir.keyword InputDir.configKind InputDirConfig.decodeToml
let r go r LeanLib.keyword LeanLib.configKind LeanLibConfig.decodeToml (by simp)
let r go r LeanExe.keyword LeanExe.configKind LeanExeConfig.decodeToml (by simp)
let r go r InputFile.keyword InputFile.configKind InputFileConfig.decodeToml (by simp)
let r go r InputDir.keyword InputDir.configKind InputDirConfig.decodeToml (by simp)
return (r.decls, r.map)
where
go (r : DecodeTargetState pkg) kw kind
(decode : {n : Name} Table DecodeM (ConfigType kind pkg n)) := do
(decode : {n : Name} Table DecodeM (ConfigType kind pkg n))
(h : DataType kind = OpaqueConfigTarget kind) := do
let some tableArrayVal := t.find? kw | return r
let some vals tryDecode? tableArrayVal.decodeValueArray | return r
vals.foldlM (init := r) fun r val => do
@@ -446,8 +449,10 @@ where
else
let config @decode name t
let decl : NConfigDecl pkg name :=
-- Safety: By definition, config kind = facet kind for declarative configurations.
unsafe {pkg, name, kind, config, wf_data := lcProof}
-- Safety: By definition, for declarative configurations, the type of a package target
-- is its configuration's data kind (i.e., `CustomData pkg name = DataType kind`).
-- In the equivalent Lean configuration, this would hold by type family axiom.
unsafe {pkg, name, kind, config, wf_data := fun _ => lcProof, h}
-- Check that executables have distinct root module names
let exeRoots id do
if h : kind = LeanExe.configKind then
@@ -469,8 +474,8 @@ where
/-! ## Root Loader -/
/-- Load a `Package` from a Lake configuration file written in TOML. -/
public def loadTomlConfig (cfg: LoadConfig) : LogIO Package := do
/-- Load a Lake configuration from a file written in TOML. -/
public def loadTomlConfig (cfg : LoadConfig) : LogIO LakefileConfig := do
let input IO.FS.readFile cfg.configFile
let ictx := mkInputContext input cfg.relConfigFile.toString
match ( loadToml ictx |>.toBaseIO) with
@@ -482,21 +487,12 @@ public def loadTomlConfig (cfg: LoadConfig) : LogIO Package := do
let keyName := baseName.num wsIdx
let prettyName := baseName.toString (escape := false)
let config @PackageConfig.decodeToml keyName origName table
let pkgDecl := {baseName, keyName, origName, config : PackageDecl}
let (targetDecls, targetDeclMap) decodeTargetDecls keyName prettyName table
let defaultTargets table.tryDecodeD `defaultTargets #[]
let defaultTargets := defaultTargets.map stringToLegalOrSimpleName
let depConfigs table.tryDecodeD `require #[]
return {
wsIdx, baseName, keyName, origName
dir := cfg.pkgDir
relDir := cfg.relPkgDir
configFile := cfg.configFile
relConfigFile := cfg.relConfigFile
scope := cfg.scope
remoteUrl := cfg.remoteUrl
config, depConfigs, targetDecls, targetDeclMap
defaultTargets
}
return {pkgDecl, depConfigs, targetDecls, targetDeclMap, defaultTargets}
if errs.isEmpty then
return pkg
else

View File

@@ -31,18 +31,21 @@ Does not resolve dependencies.
public def loadWorkspaceRoot (config : LoadConfig) : LogIO Workspace := do
Lean.searchPathRef.set config.lakeEnv.leanSearchPath
let lakeConfig loadLakeConfig config.lakeEnv
let (root, env?) loadPackageCore "[root]" {config with pkgIdx := 0}
let config := {config with pkgIdx := 0}
let config, h resolveConfigFile "[root]" config
let fileCfg loadConfigFile config h
let root := mkPackage config fileCfg 0
let facetConfigs := fileCfg.facetDecls.foldl (·.insert ·.config) initFacetConfigs
let ws : Workspace := {
root
lakeEnv := config.lakeEnv
lakeConfig
lakeArgs? := config.lakeArgs?
facetConfigs := initFacetConfigs
facetConfigs
packages_wsIdx h := by simp at h
}
if let some env := env? then
IO.ofExcept <| ws.addFacetsFromEnv env config.leanOpts
else
return ws
let ws := ws.addPackage' root wsIdx_mkPackage
return ws
/--
Load a `Workspace` for a Lake package by

View File

@@ -181,9 +181,11 @@ public:
template<typename T> class unique_lock {
public:
unique_lock(T const &) {}
unique_lock(T const &, std::adopt_lock_t) {}
~unique_lock() {}
void lock() {}
void unlock() {}
T * release() { return nullptr; }
};
inline unsigned hardware_concurrency() { return 1; }
}

View File

@@ -173,7 +173,7 @@ extern "C" LEAN_EXPORT lean_obj_res lean_uv_dns_get_name(b_obj_arg addr) {
#else
// Std.Internal.IO.Async.DNS.getAddrInfo (host service : @& String) (family : UInt8) : IO (IO.Promise (Except IO.Error (Array IPAddr)))
extern "C" LEAN_EXPORT lean_obj_res lean_uv_dns_get_info(b_obj_arg name, b_obj_arg service, uint8_t family, int8_t protocol) {
extern "C" LEAN_EXPORT lean_obj_res lean_uv_dns_get_info(b_obj_arg name, b_obj_arg service, uint8_t family) {
lean_always_assert(
false && ("Please build a version of Lean4 with libuv to invoke this.")
);

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