Compare commits

...

391 Commits

Author SHA1 Message Date
Sofia Rodrigues
e006edd7a1 feat: make tests smaller, add functions 2026-01-21 19:41:01 -03:00
Sofia Rodrigues
017c99b581 feat: onError 2026-01-21 15:16:01 -03:00
Sofia Rodrigues
b2cdef129f fix: small fixes 2026-01-21 00:08:57 -03:00
Sofia Rodrigues
1caabef943 fix: make url behavior srtict 2026-01-19 23:07:29 -03:00
Sofia Rodrigues
0617cc2cac fix: test 2026-01-18 14:39:31 -03:00
Sofia Rodrigues
9686680591 fix: small comment changes 2026-01-18 14:05:23 -03:00
Sofia Rodrigues
e12db84dc0 fix: small issues 2026-01-18 13:51:09 -03:00
Sofia Rodrigues
185d2be818 fix: remove useless files 2026-01-18 13:43:08 -03:00
Sofia Rodrigues
21cdf34d6c fix: typos 2026-01-18 13:42:49 -03:00
Sofia Rodrigues
d92122c8c4 fix: URI parsing was wrong for authorityForm
localhost:8080 now is parsed as a scheme and a path, to work with the URI RFC and HTTP1.1 RFC
2026-01-18 13:36:52 -03:00
Sofia Rodrigues
14129a736f fix: docs 2026-01-16 18:25:58 -03:00
Sofia Rodrigues
a1c1995076 fix: comments 2026-01-16 12:11:33 -03:00
Sofia Rodrigues
00f41fb152 feat: headers 2026-01-14 15:37:23 -03:00
Sofia Rodrigues
1458a61f7f feat: chunked as last encoding 2026-01-12 16:36:18 -03:00
Sofia Rodrigues
8cb626c8db Merge branch 'sofia/async-http' of https://github.com/leanprover/lean4 into sofia/async-http 2026-01-12 15:22:08 -03:00
Sofia Rodrigues
8fc12a44eb feat: headers normal form 2026-01-12 15:20:51 -03:00
Sofia Rodrigues
abae28d28b feat: more uri helpers and theorems 2026-01-09 20:02:07 -03:00
Sofia Rodrigues
aafc5f5f1f feat: uri decidable 2026-01-09 20:02:07 -03:00
Sofia Rodrigues
c94865d221 feat: request smuggling check 2026-01-09 20:02:07 -03:00
Sofia Rodrigues
71d7c96e82 fix: test with wrong content-length order 2026-01-09 20:02:06 -03:00
Sofia Rodrigues
64c58c5b2b feat: add limit to bodycollect 2026-01-09 20:02:06 -03:00
Sofia Rodrigues
4f0fa598c2 feat: add property in headers 2026-01-09 20:02:06 -03:00
Sofia Rodrigues
1e08ec5e8d feat: add more tests for uri, specialize the userinfo type 2026-01-09 20:02:06 -03:00
Sofia Rodrigues
522f08d212 feat: change parsing of uri 2026-01-09 20:02:06 -03:00
Sofia Rodrigues
7f3178941c fix: recv selector of bytestream 2026-01-09 20:02:06 -03:00
Sofia Rodrigues
dcb58be1b7 feat: HasAll in headers 2026-01-09 20:02:06 -03:00
Sofia Rodrigues
d2706fd156 feat: keep alive timeout 2026-01-09 20:02:06 -03:00
Sofia Rodrigues
02c736eb4d reverse: files 2026-01-09 20:02:06 -03:00
Sofia Rodrigues
f2d280160f fix: server timeout 2026-01-09 20:02:06 -03:00
Sofia Rodrigues
4af4420c64 feat: change build functions 2026-01-09 20:02:06 -03:00
Sofia Rodrigues
81f3a88511 reverse: signal 2026-01-09 20:02:06 -03:00
Sofia Rodrigues
865c3953a4 remove: useless files 2026-01-09 20:02:06 -03:00
Sofia Rodrigues
26d5bc7a74 feat: future 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
c5577d6d3b fix: tests 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
9b59503854 fix: tests 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
5cd8b6fce4 feat: doc 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
762c328ec3 fix: connection fork 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
aa693c18fa feat: more tests 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
0a71777aee test: add test 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
79343b87c0 fix: context 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
29f1d178ab fix: cancellationctx 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
397d67a0b4 fix: keep alive behavior 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
0f7390582d feat: cancellation backwards 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
1fd8d038c6 fix: comments 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
e7efa58e6e fix: docs 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
aa4f133c74 feat: add more tests 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
a0a0f45f38 feat: contextual fixes 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
13c6c4994c fix: context 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
59f4c09c21 fix: ext chunk 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
146419bd63 fix: gramatic issues 2026-01-09 20:02:05 -03:00
Sofia Rodrigues
36bcbb093a fix: grammatical issues 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
3a26a540ca fix: parser 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
39c035cad7 fix: trailer 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
f87c952296 feat: parse chunk 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
21ac5a6c80 fix: trailers parsing 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
a726a11ed6 fix: string changes 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
a2b322387b fix: multiple small problems 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
92fe0967b0 fix: weird behaviro when message as not sent :) 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
3d39609878 fix: test 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
83c51ead75 fix: details of connection: close 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
fc231fc42d fix: remove client related 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
25d950b50b fix: tests 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
98602cd8ce fix: bug fixes and improvement in performance 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
c1007723a7 fix: test 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
cfd5ca67aa feat: header changes 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
b974d4ca4d feat: small improvements in tests and client 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
4ef5e0f8e5 fix: import structure 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
34f848225c fix: test 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
5ec62e008c fix: copyright header 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
3f9cfbce83 feat: limits and tests 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
8992b70fb9 feat: header detection and duplication 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
f1f2d98fc7 fix: loop 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
04c5d3ae47 fix: remove small useless things 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
a1f8a5fddf feat: remove useless variable 2026-01-09 20:02:04 -03:00
Sofia Rodrigues
83cfe853fe fix: small issues with chunks and comments 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
97c8a3bf1a fix: protocol 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
818f915362 fix: headers 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
b7a3496999 refactor: part of the connection API 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
a542f73cdf feat: big refactor 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
e7247e1312 fix: remove log 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
470b49f573 feat: improve chunk extensions and handling of shutdown 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
d6ee4c539f fix: comment 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
0ff829a10b fix: remove macro 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
f1a4f3ee60 fix: copyright header 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
a0bcb4f1c7 fix: behavior of the client 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
65d5ef42c1 feat: closePeerConnection 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
2673fe3575 feat: small changes 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
9036480e07 fix: selectors 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
ee126cb28b fix: remove orphaned modules 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
f62fb4ff38 fix: http 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
33bd907d4f fix: update to master 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
6537fdc559 feat: small changes 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
6d209bcfa6 fix: style changes 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
7a35339254 fix: comments 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
a27778b261 fix: comments 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
3590ac0a8a fix: comment and bytebuffer 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
a813e216f9 feat: small changes 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
aab4877222 feat: add header value validation 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
b2adbc96ff fix: bytestream 2026-01-09 20:02:03 -03:00
Sofia Rodrigues
1930fd26ff feat: tests and small changes 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
08daa78cc3 fix: funnel imports 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
99e8939d9b fix: remove orphaned module 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
62e76bc384 feat: http 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
1a8aeca374 fix: async 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
9c983fa7ca fix: streammap 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
0274659e18 feat: cancellation 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
7c2f56f66c fix: simplify 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
e1ebd0e5a5 fix: async 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
271ff7ae0a fix: small comments 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
e146c0e61f fix: async 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
de669dbcb7 fix: streammap 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
9363e42c21 fix: async stream 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
7d81615fc5 fix: channel 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
f074c51af9 fix: stream map 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
bec683ca35 feat: basics stream map 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
69681b56a5 fix: channel 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
ef9df58907 fix: wrong function 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
7ba79053b3 fix: small fixes 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
3a253893d6 feat: async traits 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
4222c6cebd fix: stream can only return one type 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
449112b3da fix: remove useless function 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
8f22819d68 feat: remove outparams 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
b430c5456e feat: async type classes 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
801548be14 feat: http client 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
6cb476d464 refactor: http 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
d850276573 feat: rename 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
1c18ca11b5 fix: test 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
9bc9b49261 fix: timeout 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
fba7b5ad8f fix: update to master 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
75dbf7df97 feat: small changes 2026-01-09 20:02:02 -03:00
Sofia Rodrigues
c7595e1aba fix: small changes 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
965be635a2 feat: improve comment of serveConnection 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
1bc51eb0dd fix: style changes 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
24091da04f fix: comments 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
559885014e fix: bytestream comments 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
94b5ee5836 fix: comments 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
d86fc27254 fix: comment and bytebuffer 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
c09a0f70ea fix: url parser 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
7705e951a6 feat: small changes 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
51b67327de fix: test and small comments 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
c512fa1b49 fix: merge 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
ea2305b174 feat: add header value validation 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
e623949488 fix: remove useless coe 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
aab7d16cf4 fix: bytestream 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
e9ed6a9204 feat: tests and small changes 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
cd63928177 fix: imports 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
6383021cec fix: funnel imports 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
514f1bb20d fix: remove orphaned module 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
7d8bf08fd9 fix: imports 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
7b5a9d662c fix: coe option 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
63365564b0 fix: copyright notice 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
1cf73d1e66 feat: http 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
546b79e481 fix: async 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
c876d64c55 fix: streammap 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
39606ece6e feat: cancellation 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
7df95d7e01 fix: simplify 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
089a9eb254 fix: async 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
1bd391b450 fix: small comments 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
ebb4d1f2c3 fix: async 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
ffc9b3cb14 fix: streammap 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
29bdadea39 fix: async stream 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
d6ff24fdf3 fix: channel 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
dd79fda420 fix: stream map 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
4ff1146b19 feat: basics stream map 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
55692a55ae fix: channel 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
acb0af7da9 fix: wrong function 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
068fdb4842 fix: small fixes 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
2dd2d6cfc5 feat: async traits 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
8f0e8ffd77 fix: stream can only return one type 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
fe99ae3a37 fix: remove useless function 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
5952dc115a feat: remove outparams 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
48ea7def07 feat: async type classes 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
1800a079b6 fix: function names 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
08c14ffafd fix: notes and concurrently 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
2f03e145d4 feat: countAliveTokens and background 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
695cf3c9e9 fix: name and remove backgroudn 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
a934bc0acd test: async context test 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
46fd70864b feat: add selector.cancelled function 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
58a4d61e99 fix: comments 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
0f429d4ed2 feat: add contextual monad 2026-01-09 20:02:01 -03:00
Sofia Rodrigues
392e15075f fix: comment 2026-01-09 20:02:00 -03:00
Sofia Rodrigues
c6f80c2a11 fix: comments 2026-01-09 20:02:00 -03:00
Sofia Rodrigues
8cf20cdeb4 feat: context 2026-01-09 20:02:00 -03:00
Sofia Rodrigues
6313f22b1e feat: future 2026-01-09 20:01:19 -03:00
Sofia Rodrigues
d429561512 feat: more uri helpers and theorems 2026-01-09 19:58:14 -03:00
Kim Morrison
5bb7f37645 feat: add first_par combinator for try? with grind +locals (#11949)
This PR adds a new `first_par` tactic combinator that runs multiple
tactics in parallel and returns the first successful result (cancelling
the others).

The `try?` tactic's `atomicSuggestions` step now uses `first_par` to try
three grind variants in parallel:
- `grind? +suggestions` - uses library suggestion engine  
- `grind? +locals` - unfolds local definitions from current file
- `grind? +locals +suggestions` - combines both

This leverages `TacticM.parFirst` which already provides the "first
success wins" parallel execution with cancellation.

### Depends on
- [x] depends on: #11946

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 21:09:41 +00:00
Sebastian Graf
15a719cb36 chore: extract shared match splitting impl from FunInd and mvcgen (#11953)
This was an indepedently viable refactoring proposed by Joachim. It
fixes a bug in `mvcgen` exposed by the now reverted #11696.
2026-01-09 15:53:26 +00:00
Sebastian Graf
0f1eb1d0e5 chore: remove superfluous (generalizing := false) after revert of #11696 (#11952) 2026-01-09 10:14:18 +00:00
Lean stage0 autoupdater
e766839345 chore: update stage0 2026-01-09 09:33:31 +00:00
Sebastian Graf
22bef1c45a chore: revert "feat: abstract metavariables when generalizing match motives (#8099)" (#11941)
This PR reverts #11696.

Reopens #8099.
2026-01-09 08:24:03 +00:00
George Rennie
b771d12072 feat: Decidable instance for Nat.isPowerOfTwo (#11905)
This PR provides a `Decidable` instance for `Nat.isPowerOfTwo` based on
the formula `(n ≠ 0) ∧ (n &&& (n - 1)) = 0`.

To do this it includes theorems about `Nat.testBit` to show that the
`n.log2`th bit is set in `n` and `n - 1` for non powers of two.

Bitwise lemmas are needed to reason about the `&&&` so the file
`Init.Data.Nat.Power2` is renamed to `Init.Data.Nat.Power2.Basic` and
`Init.Data.Nat.Power2.Lemmas` introduced that depends on
`Init.Data.Nat.Bitwise.Lemmas` to prevent circular includes.

---------

Co-authored-by: Kim Morrison <477956+kim-em@users.noreply.github.com>
2026-01-09 07:51:41 +00:00
dependabot[bot]
214abb7eb2 chore: CI: bump actions/checkout from 5 to 6 (#11459)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to
6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/releases">actions/checkout's
releases</a>.</em></p>
<blockquote>
<h2>v6.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update README to include Node.js 24 support details and requirements
by <a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li>
<li>Persist creds to a separate file by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li>
<li>v6-beta by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2298">actions/checkout#2298</a></li>
<li>update readme/changelog for v6 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2311">actions/checkout#2311</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v5.0.0...v6.0.0">https://github.com/actions/checkout/compare/v5.0.0...v6.0.0</a></p>
<h2>v6-beta</h2>
<h2>What's Changed</h2>
<p>Updated persist-credentials to store the credentials under
<code>$RUNNER_TEMP</code> instead of directly in the local git
config.</p>
<p>This requires a minimum Actions Runner version of <a
href="https://github.com/actions/runner/releases/tag/v2.329.0">v2.329.0</a>
to access the persisted credentials for <a
href="https://docs.github.com/en/actions/tutorials/use-containerized-services/create-a-docker-container-action">Docker
container action</a> scenarios.</p>
<h2>v5.0.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Port v6 cleanup to v5 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2301">actions/checkout#2301</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/actions/checkout/compare/v5...v5.0.1">https://github.com/actions/checkout/compare/v5...v5.0.1</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>V6.0.0</h2>
<ul>
<li>Persist creds to a separate file by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li>
<li>Update README to include Node.js 24 support details and requirements
by <a href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li>
</ul>
<h2>V5.0.1</h2>
<ul>
<li>Port v6 cleanup to v5 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2301">actions/checkout#2301</a></li>
</ul>
<h2>V5.0.0</h2>
<ul>
<li>Update actions checkout to use node 24 by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li>
</ul>
<h2>V4.3.1</h2>
<ul>
<li>Port v6 cleanup to v4 by <a
href="https://github.com/ericsciple"><code>@​ericsciple</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2305">actions/checkout#2305</a></li>
</ul>
<h2>V4.3.0</h2>
<ul>
<li>docs: update README.md by <a
href="https://github.com/motss"><code>@​motss</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li>
<li>Add internal repos for checking out multiple repositories by <a
href="https://github.com/mouismail"><code>@​mouismail</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li>
<li>Documentation update - add recommended permissions to Readme by <a
href="https://github.com/benwells"><code>@​benwells</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li>
<li>Adjust positioning of user email note and permissions heading by <a
href="https://github.com/joshmgross"><code>@​joshmgross</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li>
<li>Update README.md by <a
href="https://github.com/nebuk89"><code>@​nebuk89</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li>
<li>Update CODEOWNERS for actions by <a
href="https://github.com/TingluoHuang"><code>@​TingluoHuang</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li>
<li>Update package dependencies by <a
href="https://github.com/salmanmkc"><code>@​salmanmkc</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li>
</ul>
<h2>v4.2.2</h2>
<ul>
<li><code>url-helper.ts</code> now leverages well-known environment
variables by <a href="https://github.com/jww3"><code>@​jww3</code></a>
in <a
href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li>
<li>Expand unit test coverage for <code>isGhes</code> by <a
href="https://github.com/jww3"><code>@​jww3</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li>
</ul>
<h2>v4.2.1</h2>
<ul>
<li>Check out other refs/* by commit if provided, fall back to ref by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li>
</ul>
<h2>v4.2.0</h2>
<ul>
<li>Add Ref and Commit outputs by <a
href="https://github.com/lucacome"><code>@​lucacome</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1180">actions/checkout#1180</a></li>
<li>Dependency updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>- <a
href="https://redirect.github.com/actions/checkout/pull/1777">actions/checkout#1777</a>,
<a
href="https://redirect.github.com/actions/checkout/pull/1872">actions/checkout#1872</a></li>
</ul>
<h2>v4.1.7</h2>
<ul>
<li>Bump the minor-npm-dependencies group across 1 directory with 4
updates by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1739">actions/checkout#1739</a></li>
<li>Bump actions/checkout from 3 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1697">actions/checkout#1697</a></li>
<li>Check out other refs/* by commit by <a
href="https://github.com/orhantoy"><code>@​orhantoy</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1774">actions/checkout#1774</a></li>
<li>Pin actions/checkout's own workflows to a known, good, stable
version. by <a href="https://github.com/jww3"><code>@​jww3</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1776">actions/checkout#1776</a></li>
</ul>
<h2>v4.1.6</h2>
<ul>
<li>Check platform to set archive extension appropriately by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1732">actions/checkout#1732</a></li>
</ul>
<h2>v4.1.5</h2>
<ul>
<li>Update NPM dependencies by <a
href="https://github.com/cory-miller"><code>@​cory-miller</code></a> in
<a
href="https://redirect.github.com/actions/checkout/pull/1703">actions/checkout#1703</a></li>
<li>Bump github/codeql-action from 2 to 3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1694">actions/checkout#1694</a></li>
<li>Bump actions/setup-node from 1 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1696">actions/checkout#1696</a></li>
<li>Bump actions/upload-artifact from 2 to 4 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/actions/checkout/pull/1695">actions/checkout#1695</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="1af3b93b68"><code>1af3b93</code></a>
update readme/changelog for v6 (<a
href="https://redirect.github.com/actions/checkout/issues/2311">#2311</a>)</li>
<li><a
href="71cf2267d8"><code>71cf226</code></a>
v6-beta (<a
href="https://redirect.github.com/actions/checkout/issues/2298">#2298</a>)</li>
<li><a
href="069c695914"><code>069c695</code></a>
Persist creds to a separate file (<a
href="https://redirect.github.com/actions/checkout/issues/2286">#2286</a>)</li>
<li><a
href="ff7abcd0c3"><code>ff7abcd</code></a>
Update README to include Node.js 24 support details and requirements (<a
href="https://redirect.github.com/actions/checkout/issues/2248">#2248</a>)</li>
<li>See full diff in <a
href="https://github.com/actions/checkout/compare/v5...v6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=5&new-version=6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-09 07:43:13 +00:00
dependabot[bot]
76a734c907 chore: CI: bump softprops/action-gh-release from 2.4.1 to 2.5.0 (#11458)
Bumps
[softprops/action-gh-release](https://github.com/softprops/action-gh-release)
from 2.4.1 to 2.5.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/softprops/action-gh-release/releases">softprops/action-gh-release's
releases</a>.</em></p>
<blockquote>
<h2>v2.5.0</h2>
<!-- raw HTML omitted -->
<h2>What's Changed</h2>
<h3>Exciting New Features 🎉</h3>
<ul>
<li>feat: mark release as draft until all artifacts are uploaded by <a
href="https://github.com/dumbmoron"><code>@​dumbmoron</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/692">softprops/action-gh-release#692</a></li>
</ul>
<h3>Other Changes 🔄</h3>
<ul>
<li>chore(deps): bump the npm group across 1 directory with 5 updates by
<a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/697">softprops/action-gh-release#697</a></li>
<li>chore(deps): bump actions/checkout from 5.0.0 to 5.0.1 in the
github-actions group by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/689">softprops/action-gh-release#689</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/dumbmoron"><code>@​dumbmoron</code></a>
made their first contribution in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/692">softprops/action-gh-release#692</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/softprops/action-gh-release/compare/v2.4.2...v2.5.0">https://github.com/softprops/action-gh-release/compare/v2.4.2...v2.5.0</a></p>
<h2>v2.4.2</h2>
<h2>What's Changed</h2>
<h3>Exciting New Features 🎉</h3>
<ul>
<li>feat: Ensure generated release notes cannot be over 125000
characters by <a
href="https://github.com/BeryJu"><code>@​BeryJu</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/684">softprops/action-gh-release#684</a></li>
</ul>
<h3>Other Changes 🔄</h3>
<ul>
<li>dependency updates</li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/BeryJu"><code>@​BeryJu</code></a> made
their first contribution in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/684">softprops/action-gh-release#684</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/softprops/action-gh-release/compare/v2.4.1...v2.4.2">https://github.com/softprops/action-gh-release/compare/v2.4.1...v2.4.2</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md">softprops/action-gh-release's
changelog</a>.</em></p>
<blockquote>
<h2>2.5.0</h2>
<h2>What's Changed</h2>
<h3>Exciting New Features 🎉</h3>
<ul>
<li>feat: mark release as draft until all artifacts are uploaded by <a
href="https://github.com/dumbmoron"><code>@​dumbmoron</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/692">softprops/action-gh-release#692</a></li>
</ul>
<h3>Other Changes 🔄</h3>
<ul>
<li>dependency updates</li>
</ul>
<h2>2.4.2</h2>
<h2>What's Changed</h2>
<h3>Exciting New Features 🎉</h3>
<ul>
<li>feat: Ensure generated release notes cannot be over 125000
characters by <a
href="https://github.com/BeryJu"><code>@​BeryJu</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/684">softprops/action-gh-release#684</a></li>
</ul>
<h3>Other Changes 🔄</h3>
<ul>
<li>dependency updates</li>
</ul>
<h2>2.4.1</h2>
<h2>What's Changed</h2>
<h3>Other Changes 🔄</h3>
<ul>
<li>fix(util): support brace expansion globs containing commas in
parseInputFiles by <a
href="https://github.com/Copilot"><code>@​Copilot</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/672">softprops/action-gh-release#672</a></li>
<li>fix: gracefully fallback to body when body_path cannot be read by <a
href="https://github.com/Copilot"><code>@​Copilot</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/671">softprops/action-gh-release#671</a></li>
</ul>
<h2>2.4.0</h2>
<h2>What's Changed</h2>
<h3>Exciting New Features 🎉</h3>
<ul>
<li>feat(action): respect working_directory for files globs by <a
href="https://github.com/stephenway"><code>@​stephenway</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/667">softprops/action-gh-release#667</a></li>
</ul>
<h2>2.3.4</h2>
<h2>What's Changed</h2>
<h3>Bug fixes 🐛</h3>
<ul>
<li>fix(action): handle 422 already_exists race condition by <a
href="https://github.com/stephenway"><code>@​stephenway</code></a> in <a
href="https://redirect.github.com/softprops/action-gh-release/pull/665">softprops/action-gh-release#665</a></li>
</ul>
<h3>Other Changes 🔄</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a06a81a03e"><code>a06a81a</code></a>
release 2.5.0</li>
<li><a
href="7da8983734"><code>7da8983</code></a>
feat: mark release as draft until all artifacts are uploaded (<a
href="https://redirect.github.com/softprops/action-gh-release/issues/692">#692</a>)</li>
<li><a
href="87973286a4"><code>8797328</code></a>
chore(deps): bump actions/checkout in the github-actions group (<a
href="https://redirect.github.com/softprops/action-gh-release/issues/689">#689</a>)</li>
<li><a
href="1bfc62a71b"><code>1bfc62a</code></a>
chore(deps): bump the npm group across 1 directory with 5 updates (<a
href="https://redirect.github.com/softprops/action-gh-release/issues/697">#697</a>)</li>
<li><a
href="5be0e66d93"><code>5be0e66</code></a>
release 2.4.2</li>
<li><a
href="af658b4d5d"><code>af658b4</code></a>
feat: Ensure generated release notes cannot be over 125000 characters
(<a
href="https://redirect.github.com/softprops/action-gh-release/issues/684">#684</a>)</li>
<li><a
href="237aaccf71"><code>237aacc</code></a>
chore: bump node to 24.11.0</li>
<li><a
href="00362bea6f"><code>00362be</code></a>
chore(deps): bump the npm group with 5 updates (<a
href="https://redirect.github.com/softprops/action-gh-release/issues/687">#687</a>)</li>
<li><a
href="0adea5aa98"><code>0adea5a</code></a>
chore(deps): bump the npm group with 3 updates (<a
href="https://redirect.github.com/softprops/action-gh-release/issues/686">#686</a>)</li>
<li><a
href="aa05f9d779"><code>aa05f9d</code></a>
chore(deps): bump actions/setup-node from 5.0.0 to 6.0.0 in the
github-action...</li>
<li>Additional commits viewable in <a
href="6da8fa9354...a06a81a03e">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=softprops/action-gh-release&package-manager=github_actions&previous-version=2.4.1&new-version=2.5.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-09 07:42:17 +00:00
dependabot[bot]
f65fe51630 chore: CI: bump namespacelabs/nscloud-checkout-action from 7 to 8 (#11457)
Bumps
[namespacelabs/nscloud-checkout-action](https://github.com/namespacelabs/nscloud-checkout-action)
from 7 to 8.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/namespacelabs/nscloud-checkout-action/releases">namespacelabs/nscloud-checkout-action's
releases</a>.</em></p>
<blockquote>
<h2>v8.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Rework checkout flow to precisely control what is fetched and
checked out. by <a
href="https://github.com/nichtverstehen"><code>@​nichtverstehen</code></a>
in <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/pull/22">namespacelabs/nscloud-checkout-action#22</a></li>
<li>Log all mirrored refs in debug mode. by <a
href="https://github.com/nichtverstehen"><code>@​nichtverstehen</code></a>
in <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/pull/23">namespacelabs/nscloud-checkout-action#23</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/namespacelabs/nscloud-checkout-action/compare/v7.1.0...v8.0.0">https://github.com/namespacelabs/nscloud-checkout-action/compare/v7.1.0...v8.0.0</a></p>
<h2>v7.1.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Correctly check out merge branches for PRs. by <a
href="https://github.com/nichtverstehen"><code>@​nichtverstehen</code></a>
in <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/pull/21">namespacelabs/nscloud-checkout-action#21</a></li>
<li>Set up remote.origin.fetch to allow fetching any branch. by <a
href="https://github.com/nichtverstehen"><code>@​nichtverstehen</code></a>
in <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/pull/21">namespacelabs/nscloud-checkout-action#21</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/nichtverstehen"><code>@​nichtverstehen</code></a>
made their first contribution in <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/pull/21">namespacelabs/nscloud-checkout-action#21</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/namespacelabs/nscloud-checkout-action/compare/v7...v7.1.0">https://github.com/namespacelabs/nscloud-checkout-action/compare/v7...v7.1.0</a></p>
<h2>V7.0.1</h2>
<p>No release notes provided.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="0c5e6ce59a"><code>0c5e6ce</code></a>
Merge pull request <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/issues/25">#25</a>
from namespacelabs/fix-doing-lfs-checkouts-on-macos</li>
<li><a
href="135cb5f92a"><code>135cb5f</code></a>
Fix doing lfs checkouts on macos</li>
<li><a
href="2716e107fb"><code>2716e10</code></a>
Merge pull request <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/issues/24">#24</a>
from namespacelabs/kirill/prune</li>
<li><a
href="700dc55af6"><code>700dc55</code></a>
Prune references in the mirror when fetching.</li>
<li><a
href="28e5665c3c"><code>28e5665</code></a>
Merge pull request <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/issues/23">#23</a>
from namespacelabs/kirill/all</li>
<li><a
href="e03456f7c8"><code>e03456f</code></a>
Log all mirrored refs in debug mode.</li>
<li><a
href="253d47cfc0"><code>253d47c</code></a>
Merge pull request <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/issues/22">#22</a>
from namespacelabs/kirill/init</li>
<li><a
href="f2df08a5f7"><code>f2df08a</code></a>
Rework checkout flow to precisely control what is fetched and checked
out.</li>
<li><a
href="65866b8ec2"><code>65866b8</code></a>
Merge pull request <a
href="https://redirect.github.com/namespacelabs/nscloud-checkout-action/issues/21">#21</a>
from namespacelabs/NSL-6774</li>
<li><a
href="ef7d6fcaeb"><code>ef7d6fc</code></a>
Correctly check out merge branches for PRs.</li>
<li>See full diff in <a
href="https://github.com/namespacelabs/nscloud-checkout-action/compare/v7...v8">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=namespacelabs/nscloud-checkout-action&package-manager=github_actions&previous-version=7&new-version=8)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-09 07:41:50 +00:00
Rob23oba
e6d021967e fix: ensure that decide (xs = #[]) gets compiled to efficient code (#11945)
This PR changes the runtime implementation of the `Decidable (xs = #[])`
and `Decidable (#[] = xs)` instances to use `Array.isEmpty`. Previously,
`decide (xs = #[])` would first convert `xs` into a list and then
compare it against `List.nil`.
2026-01-09 07:30:37 +00:00
Alok Singh
4c360d50fa style: fix typos in Init/ and Std/ docstrings (#11864)
Typos in `Init/` and `Std/`.

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

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 07:24:07 +00:00
Alok Singh
821218aabd style: fix typos in Lake docstrings (#11867)
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 07:21:35 +00:00
Kim Morrison
7b1fb7ac9e feat: add simp +locals to include local definitions (#11947)
This PR adds a `+locals` configuration option to the `simp`, `simp_all`,
and `dsimp` tactics that automatically adds all definitions from the
current file to unfold.

Example usage:
```lean
def foo (n : Nat) : Nat := n + 1

-- Without +locals, simp doesn't know about foo
example (n : Nat) : foo n = n + 1 := by simp  -- fails

-- With +locals, simp can unfold foo
example (n : Nat) : foo n = n + 1 := by simp +locals  -- succeeds
```

The implementation iterates over `env.constants.map₂` (which contains
constants defined in the current module) and adds definitions to unfold.
Instance definitions and internal details are filtered out.

**Note:** For local theorems, use `+suggestions` instead, which will
include relevant local theorems via the library suggestion engine.

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

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 07:19:40 +00:00
Kim Morrison
cd632b033d feat: add grind +locals to include local definitions (#11946)
This PR adds a `+locals` configuration option to the `grind` tactic that
automatically adds all definitions from the current file as e-match
theorems. This provides a convenient alternative to manually adding
`[local grind]` attributes to each definition. In the form `grind?
+locals`, it is also helpful for discovering which local declarations it
may be useful to add `[local grind]` attributes to.

Example usage:
```lean
def foo (n : Nat) : Nat := n + 1

-- Without +locals, grind doesn't know about foo
example (n : Nat) : foo n = n + 1 := by grind  -- fails

-- With +locals, grind can use the equation
example (n : Nat) : foo n = n + 1 := by grind +locals  -- succeeds
```

Instance definitions and internal details are filtered out.

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

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 07:19:32 +00:00
Leonardo de Moura
d92cdae8e9 feat: simpForall and simpArrow in Sym.simp (#11950)
This PR implements `simpForall` and `simpArrow` in `Sym.simp`.
2026-01-09 06:20:04 +00:00
David Thrane Christiansen
7d5a96941e doc: add missing docstrings to iterator library (#11912)
This PR adds missing docstrings for parts of the iterator library, which
removes warnings and empty content in the manual.

---------

Co-authored-by: Rob23oba <152706811+Rob23oba@users.noreply.github.com>
Co-authored-by: Paul Reichert <6992158+datokrat@users.noreply.github.com>
2026-01-08 19:25:39 +00:00
Sebastian Ullrich
b4cf6b02b9 chore: shake: explain indirect uses as well (#11944) 2026-01-08 16:42:13 +00:00
Sebastian Ullrich
ea7c740ad4 fix: ctor visibility in mutual public inductives (#11940)
This PR fixes module system visibiltity issues when trying to declare a
public inductive inside a mutual block.

Fixes #11115
2026-01-08 14:25:25 +00:00
Sebastian Ullrich
aa8fa47321 chore: attributes do not have to be tracked as public uses (#11939) 2026-01-08 13:39:10 +00:00
Henrik Böving
7e6365567f refactor: preparatory change from structure to inductive on LCNF (#11934) 2026-01-08 09:56:41 +00:00
Sebastian Ullrich
1361d733a6 feat: re-integrate lean4checker as leanchecker (#11887)
This PR makes the external checker lean4checker available as the
existing `leanchecker` binary already known to elan, allowing for
out-of-the-box access to it.

---------

Co-authored-by: Kim Morrison <kim@tqft.net>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 09:41:33 +00:00
Kim Morrison
0ad15fe982 refactor: add message log capture helpers for tactic evaluation (#11933)
This PR adds utility functions for managing the message log during
tactic
evaluation, and refactors existing code to use them.

**New helpers in `Lean.Elab.Tactic`:**
- `withSuppressedMessages`: executes an action while suppressing new
messages
- `withCapturedMessages`: executes an action and returns any new
messages
- `hasErrorMessages`: checks if a message list contains errors

**Refactored to use these helpers:**
- `LibrarySearch.tryDischarger`: now uses `withSuppressedMessages`
- `Try.evalAndSuggest`: now uses `withSuppressedMessages`
- `Try.evalAndSuggestWithBy`: now uses `withSuppressedMessages`

These helpers provide a standard pattern for tactic validation that
needs to
inspect error messages (e.g., filtering out suggestions that produce
errors).

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 05:25:36 +00:00
Leonardo de Moura
531dbf0e1b perf: mkFunExtFor (#11932)
This PR eliminates super-linear kernel type checking overhead when
simplifying lambda expressions. I improved the proof term produced by
`mkFunext`. This function is used in `Sym.simp` when simplifying lambda
expressions.

 ### Lambda benchmark: before vs after optimization

| Lambda | Before simp (ms) | After simp (ms) | Simp speedup | Before
kernel (ms) | After kernel (ms) | Kernel speedup | Before proof | After
proof | Proof reduction |

|--------|------------------|-----------------|--------------|--------------------|-------------------|----------------|--------------|-------------|-----------------|
| 10 | 0.269 | 0.208 | 1.29× | 0.521 | 0.390 | 1.34× | 583 | 498 | 1.17×
|
| 20 | 0.457 | 0.382 | 1.20× | 1.126 | 0.651 | 1.73× | 1323 | 918 |
1.44× |
| 30 | 0.747 | 0.536 | 1.39× | 1.733 | 0.789 | 2.20× | 2263 | 1338 |
1.69× |
| 40 | 0.819 | 0.697 | 1.18× | 2.696 | 1.065 | 2.53× | 3403 | 1758 |
1.94× |
| 50 | 1.035 | 0.901 | 1.15× | 3.918 | 1.304 | 3.01× | 4743 | 2178 |
2.18× |
| 100 | 2.351 | 1.823 | 1.29× | 20.073 | 2.927 | 6.86× | 14443 | 4278 |
3.38× |
| 150 | 3.920 | 2.873 | 1.36× | 60.266 | 5.290 | 11.39× | 29143 | 6378 |
4.57× |
| 200 | 5.869 | 3.819 | 1.54× | 148.681 | 6.903 | 21.54× | 48843 | 8478
| 5.76× |

We can now handle much larger lambda expressions. For example:

lambda_1000: 20.869250ms, kernel: 98.637875ms, proof_size=42078

This new approach will be implemented in `Meta.simp` in the future. Here
is the table with the `Meta.simp` numbers.

 ### Old `Meta.simp` lambda benchmark

| Lambda | Simp time (ms) | Kernel time (ms) | Proof size |
|--------|----------------|------------------|------------|
| 10  | 2.308 | 0.667 | 1273 |
| 20  | 5.739 | 1.817 | 3323 |
| 30  | 10.687 | 3.320 | 6173 |
| 40  | 17.607 | 6.326 | 9823 |
| 50  | 28.336 | 9.024 | 14273 |
| 100 | 137.878 | 34.344 | 48523 |
| 150 | 395.429 | 77.329 | 102773 |
| 200 | 866.097 | 143.020 | 177023 |
2026-01-08 04:28:58 +00:00
Michael Rothgang
2e649e16f0 fix: pretty-printing of unification hints (#11780)
This PR ensures that pretty-printing of unification hints inserts a
space after |- resp. ⊢.

All uses in Lean core and mathlib add a space after the |- or ⊢ symbol;
this makes the output match usage in practice.
This was discovered in leanprover-community/mathlib4#30658, adding a
formatting linter using pretty-printing as initial guide.
2026-01-08 01:46:25 +00:00
Leonardo de Moura
0e4794a1a9 test: benchmarks for lambda-telescopes (#11929) 2026-01-08 00:20:03 +00:00
Kim Morrison
975a81cdb8 feat: filter out deprecated lemmas from suggestions in exact?/rw? (#11918)
This PR filters deprecated lemmas from `exact?` and `rw?` suggestions.

Previously, both tactics would suggest deprecated lemmas, which could be
confusing for users since using the suggestion would trigger a
deprecation warning.

Now, lemmas marked with `@[deprecated]` are filtered out in the
`addImport` functions that populate the discrimination trees used by
these tactics.

**Example (before this PR):**
```lean
import Mathlib.Logic.Basic

example (h : ∃ n : Nat, n > 0) : True := by
  choose (n : Nat) (hn : n > 0 + 0) using h
  guard_hyp hn : n > 0  -- `rw?` would suggest `Eq.rec_eq_cast` which is deprecated
```

Zulip discussion:
https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/deprecated.20lemma.20from.20rw.3F/near/554106870

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 23:45:04 +00:00
Kim Morrison
f7de0c408f fix: improve error message for initialize with missing Nonempty instance (#11919)
This PR improves the error message when `initialize` (or `opaque`) fails
to find an `Inhabited` or `Nonempty` instance.

**Before:**
```
failed to synthesize
  Inhabited Foo
```

**After:**
```
failed to synthesize 'Inhabited' or 'Nonempty' instance for
  Foo

If this type is defined using the 'structure' or 'inductive' command, you can try adding a 'deriving Nonempty' clause to it.
```

Prompted by
https://leanprover.zulipchat.com/#narrow/channel/270676-lean4/topic/initialize.20structure.20with.20IO.2ERef/near/564936030

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 23:44:33 +00:00
Kim Morrison
60cdda3c1e refactor: move simp/grind attributes for leftpad/rightpad to definition (#11928)
This PR moves the `@[simp, grind =]` attributes for `List.leftpad` and
`List.rightpad` from `Init.Data.List.Lemmas` to the point of definition
in `Init.Data.List.Basic`.

This makes the simp behavior discoverable at the definition site,
addressing the discoverability issue raised in [this Zulip
discussion](https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/Finding.20the.20.60.40.5Bsimp.5D.60.20attribute/near/566714920).

🤖 Prepared with Claude Code

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 23:43:05 +00:00
Leonardo de Moura
8484dbad5d test: benchmarks for have-telescopes (#11927) 2026-01-07 23:24:46 +00:00
Kim Morrison
b0ebfaa812 fix: ensure use of unsafeEIO is marked as unsafe (#11926)
This PR adds an `unsafe` modifier to an existing helper function user
`unsafeEIO`, and also leaves the function private.
2026-01-07 21:38:37 +00:00
Henrik Böving
c3cc61cdb4 feat: add a symbol gadget for non linear Array copies (#11916)
This PR adds a symbol to the runtime for marking `Array`
non-linearities. This should allow users to
spot them more easily in profiles or hunt them down using a debugger.
2026-01-07 13:08:45 +00:00
Leonardo de Moura
ff87bcb8e5 feat: add option for simplifying have decls in two passes (#11923)
This PR adds a new option to the function `simpHaveTelescope` in which
the `have` telescope is simplified in two passes:

* In the first pass, only the values and the body are simplified.
* In the second pass, unused declarations are eliminated.

This new mode eliminates **superlinear** behavior in the benchmark
`simp_3.lean`. Note that the kernel type checker still **exhibits**
quadratic behavior in this example, because it **does not have support**
for expanding a `have`/`let` telescope in a single step.
2026-01-07 01:58:36 +00:00
Kim Morrison
a6ed0d640d feat: add #guard_panic command and substring option for #guard_msgs (#11908)
This PR adds two features to the message testing commands:

## `#guard_panic` command

A new `#guard_panic` command that succeeds if the nested command
produces a panic message. Unlike `#guard_msgs`, it does not check the
exact message content, only that a panic occurred.

This is useful for testing commands that are expected to panic, where
the exact panic message text may be volatile. It is particularly useful
when minimizing a panic discovered "in the wild", while ensuring the
panic behaviour is preserved.

## `substring := true` option for `#guard_msgs`

Adds a `substring := true` option to `#guard_msgs` that checks if the
docstring appears as a substring of the output (after whitespace
normalization), rather than requiring an exact match. This is useful
when you only care about part of the message.

Example:
```lean
/-- Unknown identifier -/
#guard_msgs (substring := true) in
example : α := x
```

## Refactoring

Also refactors `runAndCollectMessages` as a shared helper function used
by both `#guard_msgs` and `#guard_panic`.

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 01:34:49 +00:00
Leonardo de Moura
8154453bb5 feat: simplify have blocks in Sym.simp (#11920)
This PR implements support for simplifying `have` telescopes in
`Sym.simp`.
2026-01-07 00:10:47 +00:00
Lean stage0 autoupdater
11e4e44be0 chore: update stage0 2026-01-06 21:42:26 +00:00
Leonardo de Moura
c871f66cfa refactor: have telescope support (#11914)
This PR factors out the `have`-telescope support used in `simp`, and
implements it using the `MonadSimp` interface. The goal is to
use this nice infrastructure for both `Meta.simp` and `Sym.simp`.
2026-01-06 19:20:25 +00:00
Anne Baanen
0f866236c7 doc: write a guideline for tactic docstrings (#11406)
This PR covers tactic docstrings in the documentation style guide.

At the Mathlib Initiative we want to ensure that tactics have good
documentation. Since this will involve adding documentation to tactics
built into core Lean, I discussed with David that we should write a
shared set of documentation guidelines that allow me to do my work both
on the Lean and on the Mathlib repositories.

I have already shown an earlier version of this guideline to David who
made some helpful suggestions but would be away for a few days. So to
make sure the discussion doesn't get lost, I've made a PR with the
version I ended up with after the first round of comments.

---------

Co-authored-by: Robert J. Simmons <442315+robsimmons@users.noreply.github.com>
2026-01-06 04:40:20 +00:00
Leonardo de Moura
f6c8b8d974 perf: replaceS and instantiateRevBetaS (#11911)
This PR minimizes the number of expression allocations performed by
`replaceS` and `instantiateRevBetaS`.
2026-01-06 03:03:01 +00:00
Sofia Rodrigues
00e569fe62 Merge branch 'master' of https://github.com/leanprover/lean4 into sofia/async-http 2025-12-29 13:21:09 -03:00
Sofia Rodrigues
59031e2908 feat: uri decidable 2025-12-26 19:58:47 -03:00
Sofia Rodrigues
207c7776a6 feat: request smuggling check 2025-12-26 17:20:54 -03:00
Sofia Rodrigues
c6b0245929 fix: test with wrong content-length order 2025-12-26 17:11:24 -03:00
Sofia Rodrigues
d2ec777a4e feat: add limit to bodycollect 2025-12-26 17:01:00 -03:00
Sofia Rodrigues
1fb0ab22c9 feat: add property in headers 2025-12-26 16:33:34 -03:00
Sofia Rodrigues
e1ece4838e feat: add more tests for uri, specialize the userinfo type 2025-12-26 14:48:36 -03:00
Sofia Rodrigues
e024d60260 feat: change parsing of uri 2025-12-24 17:51:50 -03:00
Sofia Rodrigues
ebd0056bfa fix: recv selector of bytestream 2025-12-23 21:40:30 -03:00
Sofia Rodrigues
998b7a98ad feat: HasAll in headers 2025-12-19 21:14:21 -03:00
Sofia Rodrigues
be1b96fa27 feat: keep alive timeout 2025-12-18 03:08:16 -03:00
Sofia Rodrigues
d4a378b50f reverse: files 2025-12-18 02:39:30 -03:00
Sofia Rodrigues
e8e5902f9a fix: server timeout 2025-12-18 02:39:04 -03:00
Sofia Rodrigues
eae30712cf feat: change build functions 2025-12-17 22:53:41 -03:00
Sofia Rodrigues
2eeb73bfae reverse: signal 2025-12-17 21:07:44 -03:00
Sofia Rodrigues
4493731335 remove: useless files 2025-12-17 21:00:54 -03:00
Sofia Rodrigues
5103fd944b feat: future 2025-12-17 20:59:20 -03:00
Sofia Rodrigues
c2ea05793f fix: tests 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
9756d5946f fix: tests 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
6a889eda3a feat: doc 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
9e9688d021 fix: connection fork 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
876547d404 feat: more tests 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
4f1795248f test: add test 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
3efb149d85 fix: context 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
b4bee04324 fix: cancellationctx 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
c5930d8284 fix: keep alive behavior 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
09287a574c feat: cancellation backwards 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
24a776e8a0 fix: comments 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
778289c5e6 fix: docs 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
597beb0a48 feat: add more tests 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
7f492f75a5 feat: contextual fixes 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
f26bf10cf1 fix: context 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
f135a4830a fix: ext chunk 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
31b8ed5157 fix: gramatic issues 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
b9f9518d15 fix: grammatical issues 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
2bb0503dc0 fix: parser 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
e11e1b9937 fix: trailer 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
7cca5e070b feat: parse chunk 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
2a3ec888ee fix: trailers parsing 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
a3a970e553 fix: string changes 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
e5bcdfe020 fix: multiple small problems 2025-12-17 20:57:20 -03:00
Sofia Rodrigues
12f33eebd4 fix: weird behaviro when message as not sent :) 2025-12-17 20:57:18 -03:00
Sofia Rodrigues
5fd23f0878 fix: test 2025-12-17 20:57:09 -03:00
Sofia Rodrigues
7afd19513b fix: details of connection: close 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
342e614f0f fix: remove client related 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
55645fa51b fix: tests 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
3aeb1cce3c fix: bug fixes and improvement in performance 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
c1683ec5bc fix: test 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
52e6863ad0 feat: header changes 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
bfc7cb1b27 feat: small improvements in tests and client 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
8d6baae17b fix: import structure 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
d169f2606d fix: test 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
25634a78f1 fix: copyright header 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
3e969253b3 feat: limits and tests 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
60f16b7532 feat: header detection and duplication 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
3b7381d326 fix: loop 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
b4f919468b fix: remove small useless things 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
0d90d45418 feat: remove useless variable 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
6326c7d3b3 fix: small issues with chunks and comments 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
2c08b50839 fix: protocol 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
595db1cf64 fix: headers 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
2f86695614 refactor: part of the connection API 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
7b269375f6 feat: big refactor 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
83b17191d2 fix: remove log 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
123c5209d1 feat: improve chunk extensions and handling of shutdown 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
ee98ed7535 fix: comment 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
ad3b3b6fd8 fix: remove macro 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
0577251413 fix: copyright header 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
2abcad5e6e fix: behavior of the client 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
78ec06d3cc feat: closePeerConnection 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
36c1416ac9 feat: small changes 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
9042768059 fix: selectors 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
183086a5bd fix: remove orphaned modules 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
4b68a44976 fix: http 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
2d29158568 fix: update to master 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
0a4823baab feat: small changes 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
f431c97297 fix: style changes 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
ce0a6a99a6 fix: comments 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
94d17e0ca3 fix: comments 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
f054f7d679 fix: comment and bytebuffer 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
dd701fd809 feat: small changes 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
5244656a14 feat: add header value validation 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
183ce44d55 fix: bytestream 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
9b58239b4b feat: tests and small changes 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
60eca9a897 fix: funnel imports 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
c89e865a41 fix: remove orphaned module 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
cbfa6ed78c feat: http 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
426b21b9ab fix: async 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
7ab3b6fed8 fix: streammap 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
15066ffb64 feat: cancellation 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
3b70b4e74a fix: simplify 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
2e48a50fb6 fix: async 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
78de8de671 fix: small comments 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
021a56f4a7 fix: async 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
4586e4acbf fix: streammap 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
b6b2fd7a87 fix: async stream 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
ef7f651f2d fix: channel 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
9bbe37b656 fix: stream map 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
26b208abe7 feat: basics stream map 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
97fb044377 fix: channel 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
8f302823b7 fix: wrong function 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
448988cdb4 fix: small fixes 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
9f1f701cef feat: async traits 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
a2c5207f9d fix: stream can only return one type 2025-12-17 20:57:08 -03:00
Sofia Rodrigues
b499386bc1 fix: remove useless function 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
4f6bfcca78 feat: remove outparams 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
ff27df47cb feat: async type classes 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
5b3ce9d804 feat: http client 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
e83ecd5e1b refactor: http 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
2b130c09ff feat: rename 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
a33134efa9 fix: test 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
6aefaeea6a fix: timeout 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
b7a209a10e fix: update to master 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
abe970846f feat: small changes 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
8dcfd41ea2 fix: small changes 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
5632fc881c feat: improve comment of serveConnection 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
a5d327fa44 fix: style changes 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
16c660ffe1 fix: comments 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
9a1417add3 fix: bytestream comments 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
97945791e6 fix: comments 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
93a14968ee fix: comment and bytebuffer 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
4cee3d3eaf fix: url parser 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
ca246c5923 feat: small changes 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
dfd0ed400e fix: test and small comments 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
850be6c8e8 fix: merge 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
8a586a8b8d feat: add header value validation 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
491be56c1f fix: remove useless coe 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
95152c8ca0 fix: bytestream 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
942c4ba3a0 feat: tests and small changes 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
537105018d fix: imports 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
4244af7ddc fix: funnel imports 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
ddb0e62764 fix: remove orphaned module 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
97b22ecf92 fix: imports 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
3b1cfca2d1 fix: coe option 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
1e0d86ebcc fix: copyright notice 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
7944ec0160 feat: http 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
278b46398e fix: async 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
7b052b02ab fix: streammap 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
96668994d4 feat: cancellation 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
bb74e1bccf fix: simplify 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
a7cd25f1ff fix: async 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
f88c758b0e fix: small comments 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
e2c331ab7e fix: async 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
1ed788ab9e fix: streammap 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
2ac72964e2 fix: async stream 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
969bd9242f fix: channel 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
19d88b8a92 fix: stream map 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
1729756bcc feat: basics stream map 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
c9a753b55d fix: channel 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
3ec6c66cec fix: wrong function 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
0936da3b78 fix: small fixes 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
31adf6837f feat: async traits 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
6efb2f8e59 fix: stream can only return one type 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
b98472e911 fix: remove useless function 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
3d4f30c232 feat: remove outparams 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
179d51f13d feat: async type classes 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
a59e0b48ba fix: function names 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
5e0ede4f8d fix: notes and concurrently 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
718b9741b7 feat: countAliveTokens and background 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
514e3ba0e8 fix: name and remove backgroudn 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
45d1fbc5ae test: async context test 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
dfa8bf7a81 feat: add selector.cancelled function 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
0495667e6f fix: comments 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
5a1aa2ea06 feat: add contextual monad 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
71abb2a3e8 fix: comment 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
fcfe7e98b9 fix: comments 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
a6e7210793 feat: context 2025-12-17 20:57:07 -03:00
Sofia Rodrigues
40f26ec349 feat: future 2025-12-17 19:28:01 -03:00
693 changed files with 15049 additions and 1028 deletions

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: actionlint
uses: raven-actions/actionlint@v2
with:

View File

@@ -67,13 +67,13 @@ jobs:
if: runner.os == 'macOS'
- name: Checkout
if: (!endsWith(matrix.os, '-with-cache'))
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}
- name: Namespace Checkout
if: endsWith(matrix.os, '-with-cache')
uses: namespacelabs/nscloud-checkout-action@v7
uses: namespacelabs/nscloud-checkout-action@v8
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Open Nix shell once

View File

@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}

View File

@@ -8,7 +8,7 @@ jobs:
check-stage0-on-queue:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0

View File

@@ -50,7 +50,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
# don't schedule nightlies on forks
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly' || (startsWith(github.ref, 'refs/tags/') && github.repository == 'leanprover/lean4')
- name: Set Nightly
@@ -434,7 +434,7 @@ jobs:
with:
path: artifacts
- name: Release
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b
with:
files: artifacts/*/*
fail_on_unmatched_files: true
@@ -455,7 +455,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
# needed for tagging
fetch-depth: 0
@@ -480,7 +480,7 @@ jobs:
echo -e "\n*Full commit log*\n" >> diff.md
git log --oneline "$last_tag"..HEAD | sed 's/^/* /' >> diff.md
- name: Release Nightly
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b
with:
body_path: diff.md
prerelease: true

View File

@@ -6,7 +6,7 @@ jobs:
check-lean-files:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Verify .lean files start with a copyright header.
run: |

View File

@@ -71,7 +71,7 @@ jobs:
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Release (short format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b
with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }}
# There are coredumps files here as well, but all in deeper subdirectories.
@@ -86,7 +86,7 @@ jobs:
- name: Release (SHA-suffixed format)
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@6da8fa9354ddfdc4aeace5fc48d7f679b5214090
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b
with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }} (${{ steps.workflow-info.outputs.sourceHeadSha }})
# There are coredumps files here as well, but all in deeper subdirectories.
@@ -387,7 +387,7 @@ jobs:
# Checkout the Batteries repository with all branches
- name: Checkout Batteries repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
repository: leanprover-community/batteries
token: ${{ secrets.MATHLIB4_BOT }}
@@ -447,7 +447,7 @@ jobs:
# Checkout the mathlib4 repository with all branches
- name: Checkout mathlib4 repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
repository: leanprover-community/mathlib4-nightly-testing
token: ${{ secrets.MATHLIB4_BOT }}
@@ -530,7 +530,7 @@ jobs:
# Checkout the reference manual repository with all branches
- name: Checkout mathlib4 repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.reference-manual-ready.outputs.manual_ready == 'true'
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
repository: leanprover/reference-manual
token: ${{ secrets.MANUAL_PR_BOT }}

View File

@@ -27,7 +27,7 @@ jobs:
# This action should push to an otherwise protected branch, so it
# uses a deploy key with write permissions, as suggested at
# https://stackoverflow.com/a/76135647/946226
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
ssh-key: ${{secrets.STAGE0_SSH_KEY}}
- run: echo "should_update_stage0=yes" >> "$GITHUB_ENV"

View File

@@ -810,7 +810,7 @@ Docstrings for constants should have the following structure:
The **short summary** should be 13 sentences (ideally 1) and provide
enough information for most readers to quickly decide whether the
docstring is relevant to their task. The first (or only) sentence of
constant is relevant to their task. The first (or only) sentence of
the short summary should be a *sentence fragment* in which the subject
is implied to be the documented item, written in present tense
indicative, or a *noun phrase* that characterizes the documented
@@ -1123,6 +1123,110 @@ infix:50 " ⇔ " => Bijection
recommended_spelling "bij" for "⇔" in [Bijection, «term_⇔_»]
```
#### Tactics
Docstrings for tactics should have the following structure:
* Short summary
* Details
* Variants
* Examples
Sometimes more than one declaration is needed to implement what the user
sees as a single tactic. In that case, only one declaration should have
the associated docstring, and the others should have the `tactic_alt`
attribute to mark them as an implementation detail.
The **short summary** should be 13 sentences (ideally 1) and provide
enough information for most readers to quickly decide whether the
tactic is relevant to their task. The first (or only) sentence of
the short summary should be a full sentence in which the subject
is an example invocation of the tactic, written in present tense
indicative. If the example tactic invocation names parameters, then the
short summary may refer to them. For the example invocation, prefer the
simplest or most typical example. Explain more complicated forms in the
variants section. If needed, abbreviate the invocation by naming part of
the syntax and expanding it in the next sentence. The summary should be
written as a single paragraph.
**Details**, if needed, may be 1-3 paragraphs that describe further
relevant information. They may insert links as needed. This section
should fully explain the scope of the tactic: its syntax format,
on which goals it works and what the resulting goal(s) look like. It
should be clear whether the tactic fails if it does not close the main
goal and whether it creates any side goals. The details may include
explanatory examples that cant necessarily be machine checked and
dont fit the format.
If the tactic is extensible using `macro_rules`, mention this in the
details, with a link to `lean-manual://section/tactic-macro-extension`
and give a one-line example. If the tactic provides an attribute or a
command that allows the user to extend its behavior, the documentation
on how to extend the tactic belongs to that attribute or command. In the
tactic docstring, use a single sentence to refer the reader to this
further documentation.
**Variants**, if needed, should be a bulleted list describing different
options and forms of the same tactic. The reader should be able to parse
and understand the parts of a tactic invocation they are hovering over,
using this list. Each list item should describe an individual variant
and take one of two formats: the **short summary** as above, or a
**named list item**. A named list item consists of a title in bold
followed by an indented short paragraph.
Variants should be explained from the perspective of the tactic's users, not
their implementers. A tactic that is implemented as a single Lean parser may
have multiple variants from the perspective of users, while a tactic that is
implemented as multiple parsers may have no variants, but merely an optional
part of the syntax.
**Examples** should start with the line `Examples:` (or `Example:` if
theres exactly one). The section should consist of a sequence of code
blocks, each showing a Lean declaration (usually with the `example`
keyword) that invokes the tactic. When the effect of the tactic is not
clear from the code, you can use code comments to describe this. Do
not include text between examples, because it can be unclear whether
the text refers to the code before or after the example.
##### Example
````
`rw [e]` uses the expression `e` as a rewrite rule on the main goal,
then tries to close the goal by "cheap" (reducible) `rfl`.
If `e` is a defined constant, then the equational theorems associated with `e`
are used. This provides a convenient way to unfold `e`. If `e` has parameters,
the tactic will try to fill these in by unification with the matching part of
the target. Parameters are only filled in once per rule, restricting which
later rewrites can be found. Parameters that are not filled in after
unification will create side goals. If the `rfl` fails to close the main goal,
no error is raised.
`rw` may fail to rewrite terms "under binders", such as `∀ x, ...` or `∃ x,
...`. `rw` can also fail with a "motive is type incorrect" error in the context
of dependent types. In these cases, consider using `simp only`.
* `rw [e₁, ... eₙ]` applies the given rules sequentially.
* `rw [← e]` or `rw [<- e]` applies the rewrite in the reverse direction.
* `rw [e] at l` rewrites with `e` at location(s) `l`.
* `rw (occs := .pos L) [e]`, where `L` is a literal list of natural numbers,
only rewrites the given occurrences in the target. Occurrences count from 1.
* `rw (occs := .neg L) [e]`, where `L` is a literal list of natural numbers,
skips rewriting the given occurrences in the target. Occurrences count from 1.
Examples:
```lean
example {a b : Nat} (h : a + a = b) : (a + a) + (a + a) = b + b := by rw [h]
```
```lean
example {f : Nat -> Nat} (h : ∀ x, f x = 1) (a b : Nat) : f a = f b := by
rw [h] -- `rw` instantiates `h` only once, so this is equivalent to: `rw [h a]`
-- goal: ⊢ 1 = f b
rw [h] -- equivalent to: `rw [h b]`
```
````
## Dictionary

View File

@@ -338,12 +338,14 @@ where
deps := deps.union k {indMod}
return deps
abbrev Explanations := Std.HashMap (ModuleIdx × NeedsKind) (Option (Name × Name))
/--
Calculates the same as `calcNeeds` but tracing each module to a use-def declaration pair or
`none` if merely a recorded extra use.
-/
def getExplanations (env : Environment) (i : ModuleIdx) :
Std.HashMap (ModuleIdx × NeedsKind) (Option (Name × Name)) := Id.run do
def getExplanations (s : State) (i : ModuleIdx) : Explanations := Id.run do
let env := s.env
let mut deps := default
for ci in env.header.moduleData[i]!.constants do
-- Added guard for cases like `structure` that are still exported even if private
@@ -364,18 +366,25 @@ def getExplanations (env : Environment) (i : ModuleIdx) :
where
/-- Accumulate the results from expression `e` into `deps`. -/
visitExpr (k : NeedsKind) name e deps :=
let env := s.env
Lean.Expr.foldConsts e deps fun c deps => Id.run do
let mut deps := deps
if let some c := getDepConstName? env c then
if let some j := env.getModuleIdxFor? c then
let k := { k with isMeta := k.isMeta && !isDeclMeta' env c }
if
if let some (some (name', _)) := deps[(j, k)]? then
decide (name.toString.length < name'.toString.length)
else true
then
deps := deps.insert (j, k) (name, c)
deps := addExplanation j k name c deps
for indMod in (indirectModUseExt.getState env)[c]?.getD #[] do
if s.transDeps[i]!.has k indMod then
deps := addExplanation indMod k name (`_indirect ++ c) deps
return deps
addExplanation (j : ModuleIdx) (k : NeedsKind) (use def_ : Name) (deps : Explanations) : Explanations :=
if
if let some (some (name', _)) := deps[(j, k)]? then
decide (use.toString.length < name'.toString.length)
else true
then
deps.insert (j, k) (use, def_)
else deps
partial def initStateFromEnv (env : Environment) : State := Id.run do
let mut s := { env }
@@ -542,7 +551,7 @@ def visitModule (pkg : Name) (srcSearchPath : SearchPath)
let mut imp : Import := { k with module := s.modNames[j]! }
let mut j := j
if args.trace then
IO.eprintln s!"`{imp}` is needed"
IO.eprintln s!"`{imp}` is needed{if needs.has k j then " (calculated)" else ""}"
if args.addPublic && !k.isExported &&
-- also add as public if previously `public meta`, which could be from automatic porting
(s.transDepsOrig[i]!.has { k with isExported := true } j || s.transDepsOrig[i]!.has { k with isExported := true, isMeta := true } j) then
@@ -660,7 +669,7 @@ def visitModule (pkg : Name) (srcSearchPath : SearchPath)
modify fun s => { s with transDeps := s.transDeps.set! i newTransDepsI }
if args.explain then
let explanation := getExplanations s.env i
let explanation := getExplanations s i
let sanitize n := if n.hasMacroScopes then (sanitizeName n).run' { options := {} } else n
let run (imp : Import) := do
let j := s.env.getModuleIdx? imp.module |>.get!

View File

@@ -695,7 +695,7 @@ endif()
set(STDLIBS Init Std Lean Leanc)
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
list(APPEND STDLIBS Lake)
list(APPEND STDLIBS Lake LeanChecker)
endif()
add_custom_target(make_stdlib ALL
@@ -758,6 +758,12 @@ if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
DEPENDS lake_shared
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make lake
VERBATIM)
add_custom_target(leanchecker ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS lake_shared
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanchecker
VERBATIM)
endif()
if(PREV_STAGE)

View File

@@ -102,7 +102,7 @@ noncomputable def strongIndefiniteDescription {α : Sort u} (p : α → Prop) (h
xp.val, fun _ => xp.property)
(fun hp => choice h, fun h => absurd h hp)
/-- the Hilbert epsilon Function -/
/-- The Hilbert epsilon function. -/
noncomputable def epsilon {α : Sort u} [h : Nonempty α] (p : α Prop) : α :=
(strongIndefiniteDescription p h).val

View File

@@ -144,7 +144,7 @@ instance : ToBool Bool where
Converts the result of the monadic action `x` to a `Bool`. If it is `true`, returns it and ignores
`y`; otherwise, runs `y` and returns its result.
This a monadic counterpart to the short-circuiting `||` operator, usually accessed via the `<||>`
This is a monadic counterpart to the short-circuiting `||` operator, usually accessed via the `<||>`
operator.
-/
@[macro_inline] def orM {m : Type u Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do
@@ -161,7 +161,7 @@ recommended_spelling "orM" for "<||>" in [orM, «term_<||>_»]
Converts the result of the monadic action `x` to a `Bool`. If it is `true`, returns `y`; otherwise,
returns the original result of `x`.
This a monadic counterpart to the short-circuiting `&&` operator, usually accessed via the `<&&>`
This is a monadic counterpart to the short-circuiting `&&` operator, usually accessed via the `<&&>`
operator.
-/
@[macro_inline] def andM {m : Type u Type v} {β : Type u} [Monad m] [ToBool β] (x y : m β) : m β := do

View File

@@ -337,7 +337,7 @@ inductive Exists {α : Sort u} (p : α → Prop) : Prop where
An indication of whether a loop's body terminated early that's used to compile the `for x in xs`
notation.
A collection's `ForIn` or `ForIn'` instance describe's how to iterate over its elements. The monadic
A collection's `ForIn` or `ForIn'` instance describes how to iterate over its elements. The monadic
action that represents the body of the loop returns a `ForInStep α`, where `α` is the local state
used to implement features such as `let mut`.
-/
@@ -510,12 +510,12 @@ abbrev SSuperset [HasSSubset α] (a b : α) := SSubset b a
/-- Notation type class for the union operation ``. -/
class Union (α : Type u) where
/-- `a b` is the union of`a` and `b`. -/
/-- `a b` is the union of `a` and `b`. -/
union : α α α
/-- Notation type class for the intersection operation `∩`. -/
class Inter (α : Type u) where
/-- `a ∩ b` is the intersection of`a` and `b`. -/
/-- `a ∩ b` is the intersection of `a` and `b`. -/
inter : α α α
/-- Notation type class for the set difference `\`. -/
@@ -538,10 +538,10 @@ infix:50 " ⊇ " => Superset
/-- Strict superset relation: `a ⊃ b` -/
infix:50 "" => SSuperset
/-- `a b` is the union of`a` and `b`. -/
/-- `a b` is the union of `a` and `b`. -/
infixl:65 " " => Union.union
/-- `a ∩ b` is the intersection of`a` and `b`. -/
/-- `a ∩ b` is the intersection of `a` and `b`. -/
infixl:70 "" => Inter.inter
/--

View File

@@ -125,6 +125,22 @@ instance instDecidableEmpEq (ys : Array α) : Decidable (#[] = ys) :=
| [] => isTrue rfl
| _ :: _ => isFalse (fun h => Array.noConfusion rfl (heq_of_eq h) (fun h => List.noConfusion rfl h))
@[inline]
def instDecidableEqEmpImpl (xs : Array α) : Decidable (xs = #[]) :=
decidable_of_iff xs.isEmpty <| by rcases xs with <;> simp [Array.isEmpty]
@[inline]
def instDecidableEmpEqImpl (xs : Array α) : Decidable (#[] = xs) :=
decidable_of_iff xs.isEmpty <| by rcases xs with <;> simp [Array.isEmpty]
@[csimp]
theorem instDecidableEqEmp_csimp : @instDecidableEqEmp = @instDecidableEqEmpImpl :=
Subsingleton.allEq _ _
@[csimp]
theorem instDecidableEmpEq_csimp : @instDecidableEmpEq = @instDecidableEmpEqImpl :=
Subsingleton.allEq _ _
theorem beq_eq_decide [BEq α] (xs ys : Array α) :
(xs == ys) = if h : xs.size = ys.size then
decide ( (i : Nat) (h' : i < xs.size), xs[i] == ys[i]'(h h')) else false := by

View File

@@ -10,6 +10,7 @@ public import Init.Classical
public import Init.Ext
set_option doc.verso true
set_option linter.missingDocs true
public section
@@ -349,14 +350,24 @@ abbrev PlausibleIterStep.casesOn {IsPlausibleStep : IterStep α β → Prop}
end IterStep
/--
The typeclass providing the step function of an iterator in `Iter (α := α) β` or
`IterM (α := α) m β`.
The step function of an iterator in `Iter (α := α) β` or `IterM (α := α) m β`.
In order to allow intrinsic termination proofs when iterating with the `step` function, the
step object is bundled with a proof that it is a "plausible" step for the given current iterator.
-/
class Iterator (α : Type w) (m : Type w Type w') (β : outParam (Type w)) where
/--
A relation that governs the allowed steps from a given iterator.
The "plausible" steps are those which make sense for a given state; plausibility can ensure
properties such as the successor iterator being drawn from the same collection, that an iterator
resulting from a skip will return the same next value, or that the next item yielded is next one
in the original collection.
-/
IsPlausibleStep : IterM (α := α) m β IterStep (IterM (α := α) m β) β Prop
/--
Carries out a step of iteration.
-/
step : (it : IterM (α := α) m β) m (Shrink <| PlausibleIterStep <| IsPlausibleStep it)
section Monadic
@@ -369,7 +380,7 @@ def IterM.mk {α : Type w} (it : α) (m : Type w → Type w') (β : Type w) :
IterM (α := α) m β :=
it
@[deprecated IterM.mk (since := "2025-12-01"), inline, expose]
@[deprecated IterM.mk (since := "2025-12-01"), inline, expose, inherit_doc IterM.mk]
def Iterators.toIterM := @IterM.mk
@[simp]
@@ -377,6 +388,7 @@ theorem IterM.mk_internalState {α m β} (it : IterM (α := α) m β) :
.mk it.internalState m β = it :=
rfl
set_option linter.missingDocs false in
@[deprecated IterM.mk_internalState (since := "2025-12-01")]
def Iterators.toIterM_internalState := @IterM.mk_internalState
@@ -459,8 +471,10 @@ number of steps.
-/
inductive IterM.IsPlausibleIndirectOutput {α β : Type w} {m : Type w Type w'} [Iterator α m β]
: IterM (α := α) m β β Prop where
/-- The output value could plausibly be emitted in the next step. -/
| direct {it : IterM (α := α) m β} {out : β} : it.IsPlausibleOutput out
it.IsPlausibleIndirectOutput out
/-- The output value could plausibly be emitted in a step after the next step. -/
| indirect {it it' : IterM (α := α) m β} {out : β} : it'.IsPlausibleSuccessorOf it
it'.IsPlausibleIndirectOutput out it.IsPlausibleIndirectOutput out
@@ -470,7 +484,9 @@ finitely many steps. This relation is reflexive.
-/
inductive IterM.IsPlausibleIndirectSuccessorOf {α β : Type w} {m : Type w Type w'}
[Iterator α m β] : IterM (α := α) m β IterM (α := α) m β Prop where
/-- Every iterator is a plausible indirect successor of itself. -/
| refl (it : IterM (α := α) m β) : it.IsPlausibleIndirectSuccessorOf it
/-- The iterator is a plausible successor of one of the current iterator's successors. -/
| cons_right {it'' it' it : IterM (α := α) m β} (h' : it''.IsPlausibleIndirectSuccessorOf it')
(h : it'.IsPlausibleSuccessorOf it) : it''.IsPlausibleIndirectSuccessorOf it
@@ -595,8 +611,10 @@ number of steps.
-/
inductive Iter.IsPlausibleIndirectOutput {α β : Type w} [Iterator α Id β] :
Iter (α := α) β β Prop where
/-- The output value could plausibly be emitted in the next step. -/
| direct {it : Iter (α := α) β} {out : β} : it.IsPlausibleOutput out
it.IsPlausibleIndirectOutput out
/-- The output value could plausibly be emitted in a step after the next step. -/
| indirect {it it' : Iter (α := α) β} {out : β} : it'.IsPlausibleSuccessorOf it
it'.IsPlausibleIndirectOutput out it.IsPlausibleIndirectOutput out
@@ -627,7 +645,9 @@ finitely many steps. This relation is reflexive.
-/
inductive Iter.IsPlausibleIndirectSuccessorOf {α : Type w} {β : Type w} [Iterator α Id β] :
Iter (α := α) β Iter (α := α) β Prop where
/-- Every iterator is a plausible indirect successor of itself. -/
| refl (it : Iter (α := α) β) : IsPlausibleIndirectSuccessorOf it it
/-- The iterator is a plausible indirect successor of one of the current iterator's successors. -/
| cons_right {it'' it' it : Iter (α := α) β} (h' : it''.IsPlausibleIndirectSuccessorOf it')
(h : it'.IsPlausibleSuccessorOf it) : it''.IsPlausibleIndirectSuccessorOf it
@@ -701,6 +721,11 @@ recursion over finite iterators. See also `IterM.finitelyManySteps` and `Iter.fi
-/
structure IterM.TerminationMeasures.Finite
(α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
/--
The wrapped iterator.
In the wrapper, its finiteness is used as a termination measure.
-/
it : IterM (α := α) m β
/--
@@ -827,6 +852,11 @@ recursion over productive iterators. See also `IterM.finitelyManySkips` and `Ite
-/
structure IterM.TerminationMeasures.Productive
(α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
/--
The wrapped iterator.
In the wrapper, its productivity is used as a termination measure.
-/
it : IterM (α := α) m β
/--
@@ -930,6 +960,9 @@ library.
-/
class LawfulDeterministicIterator (α : Type w) (m : Type w Type w') [Iterator α m β]
where
/--
Every iterator with state `α` in monad `m` has exactly one plausible step.
-/
isPlausibleStep_eq_eq : it : IterM (α := α) m β, step, it.IsPlausibleStep = (· = step)
namespace Iterators
@@ -940,14 +973,13 @@ This structure provides a more convenient way to define `Finite α m` instances
-/
structure FinitenessRelation (α : Type w) (m : Type w Type w') {β : Type w}
[Iterator α m β] where
/-
A well-founded relation such that if `it'` is a successor iterator of `it`, then
`Rel it' it`.
/--
A well-founded relation such that if `it'` is a successor iterator of `it`, then `Rel it' it`.
-/
Rel (it' it : IterM (α := α) m β) : Prop
/- A proof that `Rel` is well-founded. -/
/-- `Rel` is well-founded. -/
wf : WellFounded Rel
/- A proof that if `it'` is a successor iterator of `it`, then `Rel it' it`. -/
/-- If `it'` is a successor iterator of `it`, then `Rel it' it`. -/
subrelation : {it it'}, it'.IsPlausibleSuccessorOf it Rel it' it
theorem Finite.of_finitenessRelation
@@ -967,14 +999,13 @@ This structure provides a more convenient way to define `Productive α m` instan
-/
structure ProductivenessRelation (α : Type w) (m : Type w Type w') {β : Type w}
[Iterator α m β] where
/-
A well-founded relation such that if `it'` is obtained from `it` by skipping, then
`Rel it' it`.
/--
A well-founded relation such that if `it'` is obtained from `it` by skipping, then `Rel it' it`.
-/
Rel : (IterM (α := α) m β) (IterM (α := α) m β) Prop
/- A proof that `Rel` is well-founded. -/
/-- `Rel` is well-founded. -/
wf : WellFounded Rel
/- A proof that if `it'` is obtained from `it` by skipping, then `Rel it' it`. -/
/-- If `it'` is obtained from `it` by skipping, then `Rel it' it`. -/
subrelation : {it it'}, it'.IsPlausibleSkipSuccessorOf it Rel it' it
theorem Productive.of_productivenessRelation

View File

@@ -9,6 +9,8 @@ prelude
public import Init.Data.Iterators.Consumers.Loop
public import Init.Data.Iterators.Consumers.Monadic.Access
set_option linter.missingDocs true
@[expose] public section
namespace Std

View File

@@ -8,6 +8,8 @@ module
prelude
public import Init.Data.Iterators.Basic
set_option linter.missingDocs true
public section
namespace Std
@@ -57,8 +59,8 @@ theorem IterM.not_isPlausibleNthOutputStep_yield {α β : Type w} {m : Type w
/--
`IteratorAccess α m` provides efficient implementations for random access or iterators that support
it. `it.nextAtIdx? n` either returns the step in which the `n`-th value of `it` is emitted
(necessarily of the form `.yield _ _`) or `.done` if `it` terminates before emitting the `n`-th
it. `it.nextAtIdx? n` either returns the step in which the `n`th value of `it` is emitted
(necessarily of the form `.yield _ _`) or `.done` if `it` terminates before emitting the `n`th
value.
For monadic iterators, the monadic effects of this operation may differ from manually iterating
@@ -68,6 +70,11 @@ is guaranteed to plausible in the sense of `IterM.IsPlausibleNthOutputStep`.
This class is experimental and users of the iterator API should not explicitly depend on it.
-/
class IteratorAccess (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β] where
/--
`nextAtIdx? it n` either returns the step in which the `n`th value of `it` is emitted
(necessarily of the form `.yield _ _`) or `.done` if `it` terminates before emitting the `n`th
value.
-/
nextAtIdx? (it : IterM (α := α) m β) (n : Nat) :
m (PlausibleIterStep (it.IsPlausibleNthOutputStep n))

View File

@@ -11,6 +11,8 @@ public import Init.Data.Iterators.Consumers.Monadic.Total
public import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
public import Init.WFExtrinsicFix
set_option linter.missingDocs true
@[expose] public section
/-!

View File

@@ -11,6 +11,8 @@ public import Init.Data.Iterators.Internal.LawfulMonadLiftFunction
public import Init.WFExtrinsicFix
public import Init.Data.Iterators.Consumers.Monadic.Total
set_option linter.missingDocs true
public section
/-!
@@ -70,6 +72,9 @@ provided by the standard library.
@[ext]
class IteratorLoop (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β]
(n : Type x Type x') where
/--
Iteration over the iterator `it` in the manner expected by `for` loops.
-/
forIn : (_liftBind : (γ : Type w) (δ : Type x) (γ n δ) m γ n δ) (γ : Type x),
(plausible_forInStep : β γ ForInStep γ Prop)
(it : IterM (α := α) m β) γ
@@ -82,7 +87,9 @@ end Typeclasses
structure IteratorLoop.WithWF (α : Type w) (m : Type w Type w') {β : Type w} [Iterator α m β]
{γ : Type x} (PlausibleForInStep : β γ ForInStep γ Prop)
(hwf : IteratorLoop.WellFounded α m PlausibleForInStep) where
/-- Internal implementation detail of the iterator library. -/
it : IterM (α := α) m β
/-- Internal implementation detail of the iterator library. -/
acc : γ
instance IteratorLoop.WithWF.instWellFoundedRelation
@@ -163,6 +170,7 @@ Asserts that a given `IteratorLoop` instance is equal to `IteratorLoop.defaultIm
-/
class LawfulIteratorLoop (α : Type w) (m : Type w Type w') (n : Type x Type x')
[Monad m] [Monad n] [Iterator α m β] [i : IteratorLoop α m n] where
/-- The implementation of `IteratorLoop.forIn` in `i` is equal to the default implementation. -/
lawful lift [LawfulMonadLiftBindFunction lift] γ it init
(Pl : β γ ForInStep γ Prop) (wf : IteratorLoop.WellFounded α m Pl)
(f : (b : β) it.IsPlausibleIndirectOutput b (c : γ) n (Subtype (Pl b c))) :
@@ -219,6 +227,7 @@ instance IterM.instForInOfIteratorLoop {m : Type w → Type w'} {n : Type w →
haveI : ForIn' n (IterM (α := α) m β) β _ := IterM.instForIn'
instForInOfForIn'
/-- Internal implementation detail of the iterator library. -/
@[always_inline, inline]
def IterM.Partial.instForIn' {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoop α m n] [MonadLiftT m n] [Monad n] :
@@ -226,6 +235,7 @@ def IterM.Partial.instForIn' {m : Type w → Type w'} {n : Type w → Type w''}
forIn' it init f :=
haveI := @IterM.instForIn'; forIn' it.it init f
/-- Internal implementation detail of the iterator library. -/
@[always_inline, inline]
def IterM.Total.instForIn' {m : Type w Type w'} {n : Type w Type w''}
{α : Type w} {β : Type w} [Iterator α m β] [IteratorLoop α m n] [MonadLiftT m n] [Monad n]

View File

@@ -8,6 +8,8 @@ module
prelude
public import Init.Data.Iterators.Basic
set_option linter.missingDocs true
public section
namespace Std
@@ -16,6 +18,9 @@ namespace Std
A wrapper around an iterator that provides partial consumers. See `IterM.allowNontermination`.
-/
structure IterM.Partial {α : Type w} (m : Type w Type w') (β : Type w) where
/--
The wrapped iterator, which was wrapped by `IterM.allowNontermination`.
-/
it : IterM (α := α) m β
/--

View File

@@ -9,12 +9,19 @@ prelude
public import Init.Data.Iterators.Basic
set_option doc.verso true
set_option linter.missingDocs true
public section
namespace Std
/--
A wrapper around an iterator that provides total consumers. See `IterM.ensureTermination`.
-/
structure IterM.Total {α : Type w} (m : Type w Type w') (β : Type w) where
/--
The wrapped iterator, which was wrapped by `IterM.ensureTermination`.
-/
it : IterM (α := α) m β
/--

View File

@@ -8,6 +8,8 @@ module
prelude
public import Init.Data.Iterators.Basic
set_option linter.missingDocs true
public section
namespace Std
@@ -16,6 +18,9 @@ namespace Std
A wrapper around an iterator that provides partial consumers. See `Iter.allowNontermination`.
-/
structure Iter.Partial {α : Type w} (β : Type w) where
/--
The wrapped iterator, which was wrapped by `Iter.allowNontermination`.
-/
it : Iter (α := α) β
/--

View File

@@ -9,6 +9,8 @@ prelude
public import Init.Data.Stream
public import Init.Data.Iterators.Consumers.Access
set_option linter.missingDocs true
public section
namespace Std

View File

@@ -9,12 +9,19 @@ prelude
public import Init.Data.Iterators.Basic
set_option doc.verso true
set_option linter.missingDocs true
public section
namespace Std
/--
A wrapper around an iterator that provides total consumers. See `Iter.ensureTermination`.
-/
structure Iter.Total {α : Type w} (β : Type w) where
/--
The wrapped iterator, which was wrapped by `Iter.ensureTermination`.
-/
it : Iter (α := α) β
/--

View File

@@ -72,7 +72,7 @@ def PostconditionT.liftWithProperty {α : Type w} {m : Type w → Type w'} {P :
P, x
/--
Given a function `f : α → β`, returns a a function `PostconditionT m α → PostconditionT m β`,
Given a function `f : α → β`, returns a function `PostconditionT m α → PostconditionT m β`,
turning `PostconditionT m` into a functor.
The postcondition of the `x.map f` states that the return value is the image under `f` of some
@@ -85,7 +85,7 @@ protected def PostconditionT.map {m : Type w → Type w'} [Functor m] {α : Type
(fun a => f a.val, _, rfl) <$> x.operation
/--
Given a function `α → PostconditionT m β`, returns a a function
Given a function `α → PostconditionT m β`, returns a function
`PostconditionT m α → PostconditionT m β`, turning `PostconditionT m` into a monad.
-/
@[always_inline, inline, expose]

View File

@@ -11,7 +11,7 @@ public import Init.Core
public section
/--
The `BEq α` and `Hashable α` instances on `α` are compatible. This means that that `a == b` implies
The `BEq α` and `Hashable α` instances on `α` are compatible. This means that `a == b` implies
`hash a = hash b`.
This is automatic if the `BEq` instance is lawful.

View File

@@ -717,6 +717,7 @@ Examples:
* `["red", "green", "blue"].leftpad 3 "blank" = ["red", "green", "blue"]`
* `["red", "green", "blue"].leftpad 1 "blank" = ["red", "green", "blue"]`
-/
@[simp, grind =]
def leftpad (n : Nat) (a : α) (l : List α) : List α := replicate (n - length l) a ++ l
@@ -730,6 +731,7 @@ Examples:
* `["red", "green", "blue"].rightpad 3 "blank" = ["red", "green", "blue"]`
* `["red", "green", "blue"].rightpad 1 "blank" = ["red", "green", "blue"]`
-/
@[simp, grind =]
def rightpad (n : Nat) (a : α) (l : List α) : List α := l ++ replicate (n - length l) a
/-! ### reduceOption -/

View File

@@ -50,7 +50,7 @@ Users that want to use `mapM` with `Applicative` should use `mapA` instead.
Applies the monadic action `f` to every element in the list, left-to-right, and returns the list of
results.
This implementation is tail recursive. `List.mapM'` is a a non-tail-recursive variant that may be
This implementation is tail recursive. `List.mapM'` is a non-tail-recursive variant that may be
more convenient to reason about. `List.forM` is the variant that discards the results and
`List.mapA` is the variant that works with `Applicative`.
-/
@@ -107,7 +107,7 @@ Applies the monadic action `f` to the corresponding elements of two lists, left-
at the end of the shorter list. `zipWithM f as bs` is equivalent to `mapM id (zipWith f as bs)`
for lawful `Monad` instances.
This implementation is tail recursive. `List.zipWithM'` is a a non-tail-recursive variant that may
This implementation is tail recursive. `List.zipWithM'` is a non-tail-recursive variant that may
be more convenient to reason about.
-/
@[inline, expose]

View File

@@ -2941,9 +2941,6 @@ theorem getLast?_replicate {a : α} {n : Nat} : (replicate n a).getLast? = if n
/-! ### leftpad -/
-- We unfold `leftpad` and `rightpad` for verification purposes.
attribute [simp, grind =] leftpad rightpad
-- `length_leftpad` and `length_rightpad` are in `Init.Data.List.Nat.Basic`.
theorem leftpad_prefix {n : Nat} {a : α} {l : List α} :

View File

@@ -223,6 +223,16 @@ theorem testBit_lt_two_pow {x i : Nat} (lt : x < 2^i) : x.testBit i = false := b
exfalso
exact Nat.not_le_of_gt lt (ge_two_pow_of_testBit p)
theorem testBit_of_two_pow_le_and_two_pow_add_one_gt {n i : Nat}
(hle : 2^i n) (hgt : n < 2^(i + 1)) : n.testBit i = true := by
rcases exists_ge_and_testBit_of_ge_two_pow hle with i', _, _
have : i = i' := by
false_or_by_contra
have : 2 ^ (i + 1) 2 ^ i' := Nat.pow_le_pow_of_le (by decide) (by omega)
have : n.testBit i' = false := testBit_lt_two_pow (by omega)
simp_all only [Bool.false_eq_true]
rwa [this]
theorem lt_pow_two_of_testBit (x : Nat) (p : i, i n testBit x i = false) : x < 2^n := by
apply Decidable.by_contra
intro not_lt
@@ -231,6 +241,10 @@ theorem lt_pow_two_of_testBit (x : Nat) (p : ∀i, i ≥ n → testBit x i = fal
have test_false := p _ i_ge_n
simp [test_true] at test_false
theorem testBit_log2 {n : Nat} (h : n 0) : n.testBit n.log2 = true := by
have := log2_eq_iff (n := n) (k := n.log2) (by omega)
apply testBit_of_two_pow_le_and_two_pow_add_one_gt <;> omega
private theorem succ_mod_two : succ x % 2 = 1 - x % 2 := by
induction x with
| zero =>

View File

@@ -10,7 +10,7 @@ import all Init.Data.Nat.Bitwise.Basic
public import Init.Data.Nat.MinMax
public import Init.Data.Nat.Log2
import all Init.Data.Nat.Log2
public import Init.Data.Nat.Power2
public import Init.Data.Nat.Power2.Basic
public import Init.Data.Nat.Mod
import Init.TacticsExtra
import Init.BinderPredicates

View File

@@ -6,66 +6,5 @@ Authors: Leonardo de Moura
module
prelude
public import Init.Data.Nat.Linear
public section
namespace Nat
theorem nextPowerOfTwo_dec {n power : Nat} (h₁ : power > 0) (h₂ : power < n) : n - power * 2 < n - power := by
have : power * 2 = power + power := by simp +arith
rw [this, Nat.sub_add_eq]
exact Nat.sub_lt (Nat.zero_lt_sub_of_lt h₂) h₁
/--
Returns the least power of two that's greater than or equal to `n`.
Examples:
* `Nat.nextPowerOfTwo 0 = 1`
* `Nat.nextPowerOfTwo 1 = 1`
* `Nat.nextPowerOfTwo 2 = 2`
* `Nat.nextPowerOfTwo 3 = 4`
* `Nat.nextPowerOfTwo 5 = 8`
-/
def nextPowerOfTwo (n : Nat) : Nat :=
go 1 (by decide)
where
go (power : Nat) (h : power > 0) : Nat :=
if power < n then
go (power * 2) (Nat.mul_pos h (by decide))
else
power
termination_by n - power
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
/--
A natural number `n` is a power of two if there exists some `k : Nat` such that `n = 2 ^ k`.
-/
def isPowerOfTwo (n : Nat) := k, n = 2 ^ k
theorem isPowerOfTwo_one : isPowerOfTwo 1 :=
0, by decide
theorem isPowerOfTwo_mul_two_of_isPowerOfTwo (h : isPowerOfTwo n) : isPowerOfTwo (n * 2) :=
have k, h := h
k+1, by simp [h, Nat.pow_succ]
theorem pos_of_isPowerOfTwo (h : isPowerOfTwo n) : n > 0 := by
have k, h := h
rw [h]
apply Nat.pow_pos
decide
theorem isPowerOfTwo_nextPowerOfTwo (n : Nat) : n.nextPowerOfTwo.isPowerOfTwo := by
apply isPowerOfTwo_go
apply isPowerOfTwo_one
where
isPowerOfTwo_go (power : Nat) (h₁ : power > 0) (h₂ : power.isPowerOfTwo) : (nextPowerOfTwo.go n power h₁).isPowerOfTwo := by
unfold nextPowerOfTwo.go
split
. exact isPowerOfTwo_go (power*2) (Nat.mul_pos h₁ (by decide)) (Nat.isPowerOfTwo_mul_two_of_isPowerOfTwo h₂)
. assumption
termination_by n - power
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
end Nat
public import Init.Data.Nat.Power2.Basic
public import Init.Data.Nat.Power2.Lemmas

View File

@@ -0,0 +1,71 @@
/-
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Init.Data.Nat.Linear
public section
namespace Nat
theorem nextPowerOfTwo_dec {n power : Nat} (h₁ : power > 0) (h₂ : power < n) : n - power * 2 < n - power := by
have : power * 2 = power + power := by simp +arith
rw [this, Nat.sub_add_eq]
exact Nat.sub_lt (Nat.zero_lt_sub_of_lt h₂) h₁
/--
Returns the least power of two that's greater than or equal to `n`.
Examples:
* `Nat.nextPowerOfTwo 0 = 1`
* `Nat.nextPowerOfTwo 1 = 1`
* `Nat.nextPowerOfTwo 2 = 2`
* `Nat.nextPowerOfTwo 3 = 4`
* `Nat.nextPowerOfTwo 5 = 8`
-/
def nextPowerOfTwo (n : Nat) : Nat :=
go 1 (by decide)
where
go (power : Nat) (h : power > 0) : Nat :=
if power < n then
go (power * 2) (Nat.mul_pos h (by decide))
else
power
termination_by n - power
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
/--
A natural number `n` is a power of two if there exists some `k : Nat` such that `n = 2 ^ k`.
-/
def isPowerOfTwo (n : Nat) := k, n = 2 ^ k
theorem isPowerOfTwo_one : isPowerOfTwo 1 :=
0, by decide
theorem isPowerOfTwo_mul_two_of_isPowerOfTwo (h : isPowerOfTwo n) : isPowerOfTwo (n * 2) :=
have k, h := h
k+1, by simp [h, Nat.pow_succ]
theorem pos_of_isPowerOfTwo (h : isPowerOfTwo n) : n > 0 := by
have k, h := h
rw [h]
apply Nat.pow_pos
decide
theorem isPowerOfTwo_nextPowerOfTwo (n : Nat) : n.nextPowerOfTwo.isPowerOfTwo := by
apply isPowerOfTwo_go
apply isPowerOfTwo_one
where
isPowerOfTwo_go (power : Nat) (h₁ : power > 0) (h₂ : power.isPowerOfTwo) : (nextPowerOfTwo.go n power h₁).isPowerOfTwo := by
unfold nextPowerOfTwo.go
split
. exact isPowerOfTwo_go (power*2) (Nat.mul_pos h₁ (by decide)) (Nat.isPowerOfTwo_mul_two_of_isPowerOfTwo h₂)
. assumption
termination_by n - power
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
end Nat

View File

@@ -0,0 +1,62 @@
/-
Copyright (c) George Rennie. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: George Rennie
-/
module
prelude
import all Init.Data.Nat.Power2.Basic
public import Init.Data.Nat.Bitwise.Lemmas
public section
/-!
# Further lemmas about `Nat.isPowerOfTwo`, with the convenience of having bitwise lemmas available.
-/
namespace Nat
theorem not_isPowerOfTwo_zero : ¬isPowerOfTwo 0 := by
rw [isPowerOfTwo, not_exists]
intro x
have := one_le_pow x 2 (by decide)
omega
theorem and_sub_one_testBit_log2 {n : Nat} (h : n 0) (hpow2 : ¬n.isPowerOfTwo) :
(n &&& (n - 1)).testBit n.log2 := by
rw [testBit_and, Bool.and_eq_true]
constructor
· exact testBit_log2 (by omega)
· by_cases n = 2^n.log2
· rw [isPowerOfTwo, not_exists] at hpow2
have := hpow2 n.log2
trivial
· have := log2_eq_iff (n := n) (k := n.log2) (by omega)
have : (n - 1).log2 = n.log2 := by rw [log2_eq_iff] <;> omega
rw [this]
exact testBit_log2 (by omega)
theorem and_sub_one_eq_zero_iff_isPowerOfTwo {n : Nat} (h : n 0) :
(n &&& (n - 1)) = 0 n.isPowerOfTwo := by
constructor
· intro hbitwise
false_or_by_contra
rename_i hpow2
have := and_sub_one_testBit_log2 h hpow2
rwa [hbitwise, zero_testBit n.log2, Bool.false_eq_true] at this
· intro hpow2
rcases hpow2 with _, hpow2
rw [hpow2, and_two_pow_sub_one_eq_mod, mod_self]
theorem ne_zero_and_sub_one_eq_zero_iff_isPowerOfTwo {n : Nat} :
((n 0) (n &&& (n - 1)) = 0) n.isPowerOfTwo := by
match h : n with
| 0 => simp [not_isPowerOfTwo_zero]
| n + 1 => simp; exact and_sub_one_eq_zero_iff_isPowerOfTwo (by omega)
@[inline]
instance {n : Nat} : Decidable n.isPowerOfTwo :=
decidable_of_iff _ ne_zero_and_sub_one_eq_zero_iff_isPowerOfTwo
end Nat

View File

@@ -46,7 +46,7 @@ theorem ne_of_cmp_ne_eq {α : Type u} {cmp : αα → Ordering} [Std.ReflCm
end ReflCmp
/-- A typeclasses for ordered types for which `compare a a = .eq` for all `a`. -/
/-- A typeclass for ordered types for which `compare a a = .eq` for all `a`. -/
abbrev ReflOrd (α : Type u) [Ord α] := ReflCmp (compare : α α Ordering)
@[simp]

View File

@@ -246,8 +246,12 @@ class InfinitelyUpwardEnumerable (α : Type u) [UpwardEnumerable α] where
This propositional typeclass ensures that `UpwardEnumerable.succ?` is injective.
-/
class LinearlyUpwardEnumerable (α : Type u) [UpwardEnumerable α] where
/-- The implementation of `UpwardEnumerable.succ?` for `α` is injective. -/
eq_of_succ?_eq : a b : α, UpwardEnumerable.succ? a = UpwardEnumerable.succ? b a = b
/--
If a type is infinitely upwardly enumerable, then every element has a successor.
-/
theorem UpwardEnumerable.isSome_succ? {α : Type u} [UpwardEnumerable α]
[InfinitelyUpwardEnumerable α] {a : α} : (succ? a).isSome :=
InfinitelyUpwardEnumerable.isSome_succ? a

View File

@@ -40,7 +40,7 @@ class Rcc.Sliceable (α : Type u) (β : outParam (Type v)) (γ : outParam (Type
This typeclass indicates how to obtain slices of elements of {lit}`α` over ranges in the index type
{lit}`β`, the ranges being left-closed right-open.
The type of resulting the slices is {lit}`γ`.
The type of the resulting slices is {lit}`γ`.
-/
class Rco.Sliceable (α : Type u) (β : outParam (Type v)) (γ : outParam (Type w)) where
/--

View File

@@ -57,4 +57,10 @@ theorem length_map {f : Char → Char} {s : String} : (s.map f).length = s.lengt
theorem map_eq_empty {f : Char Char} {s : String} : s.map f = "" s = "" := by
simp [ toList_eq_nil_iff]
@[simp]
theorem map_idempotent {s : String} (h : (c : Char) f (f c) = f c) : (s.map f |>.map f) = s.map f := by
apply String.ext
simp [String.toList_map, List.map_map]
exact fun c _ => h c
end String

View File

@@ -230,7 +230,7 @@ Examples:
* `"Orange".toLower = "orange"`
* `"ABc123".toLower = "abc123"`
-/
@[inline] def toLower (s : String) : String :=
@[inline, expose] def toLower (s : String) : String :=
s.map Char.toLower
/--

View File

@@ -119,7 +119,7 @@ instance (s : Slice) : Std.Iterator (ForwardSliceSearcher s) Id (SearchStep s) w
-- **Invariant 1:** we have already covered everything up until `stackPos - needlePos` (exclusive),
-- with matches and rejections.
-- **Invariant 2:** `stackPos - needlePos` is a valid position
-- **Invariant 3:** the range from from `stackPos - needlePos` to `stackPos` (exclusive) is a
-- **Invariant 3:** the range from `stackPos - needlePos` to `stackPos` (exclusive) is a
-- prefix of the pattern.
if h₁ : stackPos < s.rawEndPos then
let stackByte := s.getUTF8Byte stackPos h₁

View File

@@ -20,7 +20,7 @@ functionality for searching for various kinds of pattern matches in slices to it
provide subslices according to matches etc. The key design principles behind this module are:
- Instead of providing one function per kind of pattern the API is generic over various kinds of
patterns. Thus it only provides e.g. one kind of function for looking for the position of the
first occurence of a pattern. Currently the supported patterns are:
first occurrence of a pattern. Currently the supported patterns are:
- {name}`Char`
- {lean}`Char → Bool`
- {name}`String` and {name}`String.Slice` (partially: doing non trivial searches backwards is not

View File

@@ -21,6 +21,8 @@ structure Config where
/-- If `suggestions` is `true`, `grind` will invoke the currently configured library suggestion engine on the current goal,
and add attempt to use the resulting suggestions as additional parameters to the `grind` tactic. -/
suggestions : Bool := false
/-- If `locals` is `true`, `grind` will add all definitions from the current file. -/
locals : Bool := false
/-- Maximum number of case-splits in a proof search branch. It does not include splits performed during normalization. -/
splits : Nat := 9
/-- Maximum number of E-matching (aka heuristic theorem instantiation) rounds before each case split. -/

View File

@@ -766,7 +766,7 @@ def Poly.cancelVar (c : Int) (x : Var) (p : Poly) : Poly :=
(fun _ _ _ _ => a.toPoly_k.pow k)
(fun _ _ _ _ => a.toPoly_k.pow k)
(fun _ _ _ => a.toPoly_k.pow k)
a) = match (generalizing := false) a with
a) = match a with
| num n => Poly.num (n ^ k)
| .intCast n => .num (n^k)
| .natCast n => .num (n^k)

View File

@@ -132,6 +132,11 @@ structure Config where
Unused `have`s are still removed if `zeta` or `zetaUnused` are true.
-/
zetaHave : Bool := true
/--
If `locals` is `true`, `dsimp` will unfold all definitions from the current file.
For local theorems, use `+suggestions` instead.
-/
locals : Bool := false
deriving Inhabited, BEq
end DSimp
@@ -297,6 +302,11 @@ structure Config where
and attempt to use the resulting suggestions as parameters to the `simp` tactic.
-/
suggestions : Bool := false
/--
If `locals` is `true`, `simp` will unfold all definitions from the current file.
For local theorems, use `+suggestions` instead.
-/
locals : Bool := false
deriving Inhabited, BEq
-- Configuration object for `simp_all`

View File

@@ -523,7 +523,7 @@ macro_rules
| `(bif $c then $t else $e) => `(cond $c $t $e)
/--
Haskell-like pipe operator `<|`. `f <| x` means the same as the same as `f x`,
Haskell-like pipe operator `<|`. `f <| x` means the same as `f x`,
except that it parses `x` with lower precedence, which means that `f <| g <| x`
is interpreted as `f (g x)` rather than `(f g) x`.
-/
@@ -557,7 +557,7 @@ macro_rules
| `($a |> $f) => `($f $a)
/--
Alternative syntax for `<|`. `f $ x` means the same as the same as `f x`,
Alternative syntax for `<|`. `f $ x` means the same as `f x`,
except that it parses `x` with lower precedence, which means that `f $ g $ x`
is interpreted as `f (g x)` rather than `(f g) x`.
-/
@@ -782,9 +782,16 @@ Position reporting for `#guard_msgs`:
-/
syntax guardMsgsPositions := &"positions" " := " guardMsgsPositionsArg
/--
Substring matching for `#guard_msgs`:
- `substring := true` checks that the docstring appears as a substring of the output.
- `substring := false` (the default) requires exact matching (modulo whitespace normalization).
-/
syntax guardMsgsSubstring := &"substring" " := " (&"true" <|> &"false")
set_option linter.missingDocs false in
syntax guardMsgsSpecElt :=
guardMsgsFilter <|> guardMsgsWhitespace <|> guardMsgsOrdering <|> guardMsgsPositions
guardMsgsFilter <|> guardMsgsWhitespace <|> guardMsgsOrdering <|> guardMsgsPositions <|> guardMsgsSubstring
set_option linter.missingDocs false in
syntax guardMsgsSpec := "(" guardMsgsSpecElt,* ")"
@@ -860,6 +867,11 @@ Position reporting:
`#guard_msgs` appears.
- `positions := false` does not report position info.
Substring matching:
- `substring := true` checks that the docstring appears as a substring of the output
(after whitespace normalization). This is useful when you only care about part of the message.
- `substring := false` (the default) requires exact matching (modulo whitespace normalization).
For example, `#guard_msgs (error, drop all) in cmd` means to check errors and drop
everything else.
@@ -873,6 +885,13 @@ The top-level command elaborator only runs the linters if `#guard_msgs` is not p
syntax (name := guardMsgsCmd)
(plainDocComment)? "#guard_msgs" (ppSpace guardMsgsSpec)? " in" ppLine command : command
/--
`#guard_panic in cmd` runs `cmd` and succeeds if the command produces a panic message.
This is useful for testing that a command panics without matching the exact (volatile) panic text.
-/
syntax (name := guardPanicCmd)
"#guard_panic" " in" ppLine command : command
/--
Format and print the info trees for a given command.
This is mostly useful for debugging info trees.

View File

@@ -67,7 +67,7 @@ syntax unifConstraint := term patternIgnore(" =?= " <|> " ≟ ") term
syntax unifConstraintElem := colGe unifConstraint ", "?
syntax (docComment)? attrKind "unif_hint" (ppSpace ident)? (ppSpace bracketedBinder)*
" where " withPosition(unifConstraintElem*) patternIgnore(atomic("|" noWs "-") <|> "") unifConstraint : command
" where " withPosition(unifConstraintElem*) patternIgnore(atomic("|" noWs "-") <|> "") ppSpace unifConstraint : command
macro_rules
| `($[$doc?:docComment]? $kind:attrKind unif_hint $(n)? $bs* where $[$cs₁ $cs₂]* |- $t₁ $t₂) => do
@@ -120,7 +120,7 @@ calc
_ = z := pyz
```
It is also possible to write the *first* relation as `<lhs>\n _ = <rhs> :=
<proof>`. This is useful for aligning relation symbols, especially on longer:
<proof>`. This is useful for aligning relation symbols, especially on longer
identifiers:
```
calc abc

View File

@@ -907,7 +907,7 @@ instance [Inhabited α] : Inhabited (ULift α) where
Lifts a type or proposition to a higher universe level.
`PULift α` wraps a value of type `α`. It is a generalization of
`PLift` that allows lifting values who's type may live in `Sort s`.
`PLift` that allows lifting values whose type may live in `Sort s`.
It also subsumes `PLift`.
-/
-- The universe variable `r` is written first so that `ULift.{r} α` can be used
@@ -3525,7 +3525,7 @@ instance : DecidableEq String.Pos.Raw :=
/--
A region or slice of some underlying string.
A substring contains an string together with the start and end byte positions of a region of
A substring contains a string together with the start and end byte positions of a region of
interest. Actually extracting a substring requires copying and memory allocation, while many
substrings of the same underlying string may exist with very little overhead, and they are more
convenient than tracking the bounds by hand.

View File

@@ -38,6 +38,12 @@ theorem eq_false_of_decide {p : Prop} {_ : Decidable p} (h : decide p = false) :
theorem implies_congr {p₁ p₂ : Sort u} {q₁ q₂ : Sort v} (h₁ : p₁ = p₂) (h₂ : q₁ = q₂) : (p₁ q₁) = (p₂ q₂) :=
h₁ h₂ rfl
theorem implies_congr_left {p₁ p₂ : Sort u} {q : Sort v} (h : p₁ = p₂) : (p₁ q) = (p₂ q) :=
h rfl
theorem implies_congr_right {p : Sort u} {q₁ q₂ : Sort v} (h : q₁ = q₂) : (p q₁) = (p q₂) :=
h rfl
theorem iff_congr {p₁ p₂ q₁ q₂ : Prop} (h₁ : p₁ p₂) (h₂ : q₁ q₂) : (p₁ q₁) (p₂ q₂) :=
Iff.of_eq (propext h₁ propext h₂ rfl)

View File

@@ -150,7 +150,7 @@ def parent (p : FilePath) : Option FilePath :=
/--
Extracts the last element of a path if it is a file or directory name.
Returns `none ` if the last entry is a special name (such as `.` or `..`) or if the path is the root
Returns `none` if the last entry is a special name (such as `.` or `..`) or if the path is the root
directory.
-/
def fileName (p : FilePath) : Option String :=

View File

@@ -561,7 +561,7 @@ Waits for the task to finish, then returns its result.
return t.get
/--
Waits until any of the tasks in the list has finished, then return its result.
Waits until any of the tasks in the list has finished, then returns its result.
-/
@[extern "lean_io_wait_any"] opaque waitAny (tasks : @& List (Task α))
(h : tasks.length > 0 := by exact Nat.zero_lt_succ _) : BaseIO α :=
@@ -679,7 +679,7 @@ File handles wrap the underlying operating system's file descriptors. There is n
to close a file: when the last reference to a file handle is dropped, the file is closed
automatically.
Handles have an associated read/write cursor that determines the where reads and writes occur in the
Handles have an associated read/write cursor that determines where reads and writes occur in the
file.
-/
opaque FS.Handle : Type := Unit
@@ -790,7 +790,7 @@ An exception is thrown if the file cannot be opened.
/--
Acquires an exclusive or shared lock on the handle. Blocks to wait for the lock if necessary.
Acquiring a exclusive lock while already possessing a shared lock will **not** reliably succeed: it
Acquiring an exclusive lock while already possessing a shared lock will **not** reliably succeed: it
works on Unix-like systems but not on Windows.
-/
@[extern "lean_io_prim_handle_lock"] opaque lock (h : @& Handle) (exclusive := true) : IO Unit
@@ -798,7 +798,7 @@ works on Unix-like systems but not on Windows.
Tries to acquire an exclusive or shared lock on the handle and returns `true` if successful. Will
not block if the lock cannot be acquired, but instead returns `false`.
Acquiring a exclusive lock while already possessing a shared lock will **not** reliably succeed: it
Acquiring an exclusive lock while already possessing a shared lock will **not** reliably succeed: it
works on Unix-like systems but not on Windows.
-/
@[extern "lean_io_prim_handle_try_lock"] opaque tryLock (h : @& Handle) (exclusive := true) : IO Bool
@@ -1350,7 +1350,7 @@ def withTempFile [Monad m] [MonadFinally m] [MonadLiftT IO m] (f : Handle → Fi
removeFile path
/--
Creates a temporary directory in the most secure manner possible, providing a its path to an `IO`
Creates a temporary directory in the most secure manner possible, providing its path to an `IO`
action. Afterwards, all files in the temporary directory are recursively deleted, regardless of how
or when they were created.
@@ -1480,7 +1480,7 @@ possible to close the child's standard input before the process terminates, whic
@[extern "lean_io_process_spawn"] opaque spawn (args : SpawnArgs) : IO (Child args.toStdioConfig)
/--
Blocks until the child process has exited and return its exit code.
Blocks until the child process has exited and returns its exit code.
-/
@[extern "lean_io_process_child_wait"] opaque Child.wait {cfg : @& StdioConfig} : @& Child cfg IO UInt32
@@ -1586,7 +1586,7 @@ end Process
/--
POSIX-style file permissions.
The `FileRight` structure describes these permissions for a file's owner, members of it's designated
The `FileRight` structure describes these permissions for a file's owner, members of its designated
group, and all others.
-/
structure AccessRight where
@@ -1863,7 +1863,7 @@ unsafe def Runtime.markPersistent (a : α) : BaseIO α := return a
set_option linter.unusedVariables false in
/--
Discards the passed owned reference. This leads to `a` any any object reachable from it never being
Discards the passed owned reference. This leads to `a` and any object reachable from it never being
freed. This can be a useful optimization for eliding deallocation time of big object graphs that are
kept alive close to the end of the process anyway (in which case calling `Runtime.markPersistent`
would be similarly costly to deallocation). It is still considered a safe operation as it cannot

View File

@@ -58,6 +58,9 @@ syntax (name := attemptAll) "attempt_all " withPosition((ppDedent(ppLine) colGe
/-- Helper internal tactic for implementing the tactic `try?` with parallel execution. -/
syntax (name := attemptAllPar) "attempt_all_par " withPosition((ppDedent(ppLine) colGe "| " tacticSeq)+) : tactic
/-- Helper internal tactic for implementing the tactic `try?` with parallel execution, returning first success. -/
syntax (name := firstPar) "first_par " withPosition((ppDedent(ppLine) colGe "| " tacticSeq)+) : tactic
/-- Helper internal tactic used to implement `evalSuggest` in `try?` -/
syntax (name := tryResult) "try_suggestions " tactic* : tactic

View File

@@ -463,7 +463,7 @@ variable {motive : α → Sort v}
variable (h : α Nat)
variable (F : (x : α) ((y : α) InvImage (· < ·) h y x motive y) motive x)
/-- Helper gadget that prevents reduction of `Nat.eager n` unless `n` evalutes to a ground term. -/
/-- Helper gadget that prevents reduction of `Nat.eager n` unless `n` evaluates to a ground term. -/
def Nat.eager (n : Nat) : Nat :=
if Nat.beq n n = true then n else n
@@ -474,8 +474,8 @@ A well-founded fixpoint operator specialized for `Nat`-valued measures. Given a
its higher order function argument `F` to invoke its argument only on values `y` that are smaller
than `x` with regard to `h`.
In contrast to to `WellFounded.fix`, this fixpoint operator reduces on closed terms. (More precisely:
when `h x` evalutes to a ground value)
In contrast to `WellFounded.fix`, this fixpoint operator reduces on closed terms. (More precisely:
when `h x` evaluates to a ground value)
-/
def Nat.fix : (x : α) motive x :=

View File

@@ -147,18 +147,11 @@ inductive Alt where
| alt (ctorName : Name) (params : Array Param) (code : Code)
| default (code : Code)
structure FunDecl where
fvarId : FVarId
binderName : Name
params : Array Param
type : Expr
value : Code
inductive FunDecl where
| mk (fvarId : FVarId) (binderName : Name) (params : Array Param) (type : Expr) (value : Code)
structure Cases where
typeName : Name
resultType : Expr
discr : FVarId
alts : Array Alt
inductive Cases where
| mk (typeName : Name) (resultType : Expr) (discr : FVarId) (alts : Array Alt)
deriving Inhabited
inductive Code where
@@ -173,6 +166,57 @@ inductive Code where
end
@[inline]
def FunDecl.fvarId : FunDecl FVarId
| .mk (fvarId := fvarId) .. => fvarId
@[inline]
def FunDecl.binderName : FunDecl Name
| .mk (binderName := binderName) .. => binderName
@[inline]
def FunDecl.params : FunDecl Array Param
| .mk (params := params) .. => params
@[inline]
def FunDecl.type : FunDecl Expr
| .mk (type := type) .. => type
@[inline]
def FunDecl.value : FunDecl Code
| .mk (value := value) .. => value
@[inline]
def FunDecl.updateBinderName : FunDecl Name FunDecl
| .mk fvarId _ params type value, new =>
.mk fvarId new params type value
@[inline]
def FunDecl.toParam (decl : FunDecl) (borrow : Bool) : Param :=
match decl with
| .mk fvarId binderName _ type .. => fvarId, binderName, type, borrow
@[inline]
def Cases.typeName : Cases Name
| .mk (typeName := typeName) .. => typeName
@[inline]
def Cases.resultType : Cases Expr
| .mk (resultType := resultType) .. => resultType
@[inline]
def Cases.discr : Cases FVarId
| .mk (discr := discr) .. => discr
@[inline]
def Cases.alts : Cases Array Alt
| .mk (alts := alts) .. => alts
@[inline]
def Cases.updateAlts : Cases Array Alt Cases
| .mk typeName resultType discr _, new =>
.mk typeName resultType discr new
deriving instance Inhabited for Alt
deriving instance Inhabited for FunDecl
@@ -281,14 +325,18 @@ private unsafe def updateAltImp (alt : Alt) (ps' : Array Param) (k' : Code) : Al
@[inline] private unsafe def updateAltsImp (c : Code) (alts : Array Alt) : Code :=
match c with
| .cases cs => if ptrEq cs.alts alts then c else .cases { cs with alts }
| .cases cs => if ptrEq cs.alts alts then c else .cases <| cs.updateAlts alts
| _ => unreachable!
@[implemented_by updateAltsImp] opaque Code.updateAlts! (c : Code) (alts : Array Alt) : Code
@[inline] private unsafe def updateCasesImp (c : Code) (resultType : Expr) (discr : FVarId) (alts : Array Alt) : Code :=
match c with
| .cases cs => if ptrEq cs.alts alts && ptrEq cs.resultType resultType && cs.discr == discr then c else .cases { cs with discr, resultType, alts }
| .cases cs =>
if ptrEq cs.alts alts && ptrEq cs.resultType resultType && cs.discr == discr then
c
else
.cases <| cs.typeName, resultType, discr, alts
| _ => unreachable!
@[implemented_by updateCasesImp] opaque Code.updateCases! (c : Code) (resultType : Expr) (discr : FVarId) (alts : Array Alt) : Code
@@ -368,7 +416,7 @@ private unsafe def updateFunDeclCoreImp (decl: FunDecl) (type : Expr) (params :
if ptrEq type decl.type && ptrEq params decl.params && ptrEq value decl.value then
decl
else
{ decl with type, params, value }
decl.fvarId, decl.binderName, params, type, value
/--
Low-level update `FunDecl` function. It does not update the local context.
@@ -378,7 +426,7 @@ to be updated.
@[implemented_by updateFunDeclCoreImp] opaque FunDecl.updateCore (decl : FunDecl) (type : Expr) (params : Array Param) (value : Code) : FunDecl
def Cases.extractAlt! (cases : Cases) (ctorName : Name) : Alt × Cases :=
let found i := (cases.alts[i], { cases with alts := cases.alts.eraseIdx i })
let found i := (cases.alts[i]!, cases.updateAlts (cases.alts.eraseIdx! i))
if let some i := cases.alts.findFinIdx? fun | .alt ctorName' .. => ctorName == ctorName' | _ => false then
found i
else if let some i := cases.alts.findFinIdx? fun | .default _ => true | _ => false then

View File

@@ -48,7 +48,7 @@ where
if alts.isEmpty then
throwError "`Code.bind` failed, empty `cases` found"
let resultType mkCasesResultType alts
return .cases { c with alts, resultType }
return .cases c.typeName, resultType, c.discr, alts
| .return fvarId => f fvarId
| .jmp fvarId .. =>
unless ( read).contains fvarId do

View File

@@ -137,7 +137,7 @@ mutual
/- We only collect the variables in the scope of the function application being specialized. -/
if let some funDecl findFunDecl? fvarId then
if ctx.abstract funDecl.fvarId then
modify fun s => { s with params := s.params.push <| { funDecl with borrow := false } }
modify fun s => { s with params := s.params.push <| funDecl.toParam false }
else
collectFunDecl funDecl
modify fun s => { s with decls := s.decls.push <| .fun funDecl }

View File

@@ -359,7 +359,7 @@ def mkLetDecl (binderName : Name) (type : Expr) (value : LetValue) : CompilerM L
def mkFunDecl (binderName : Name) (type : Expr) (params : Array Param) (value : Code) : CompilerM FunDecl := do
let fvarId mkFreshFVarId
let binderName ensureNotAnonymous binderName `_f
let funDecl := { fvarId, binderName, type, params, value }
let funDecl := fvarId, binderName, params, type, value
modifyLCtx fun lctx => lctx.addFunDecl funDecl
return funDecl
@@ -397,7 +397,7 @@ private unsafe def updateFunDeclImp (decl : FunDecl) (type : Expr) (params : Arr
if ptrEq type decl.type && ptrEq params decl.params && ptrEq value decl.value then
return decl
else
let decl := { decl with type, params, value }
let decl := decl.fvarId, decl.binderName, params, type, value
modifyLCtx fun lctx => lctx.addFunDecl decl
return decl

View File

@@ -119,7 +119,7 @@ partial def internalizeFunDecl (decl : FunDecl) : InternalizeM FunDecl := do
let params decl.params.mapM internalizeParam
let value internalizeCode decl.value
let fvarId mkNewFVarId decl.fvarId
let decl := { decl with binderName, fvarId, params, type, value }
let decl := fvarId, binderName, params, type, value
modifyLCtx fun lctx => lctx.addFunDecl decl
return decl
@@ -139,7 +139,7 @@ partial def internalizeCode (code : Code) : InternalizeM Code := do
let alts c.alts.mapM fun
| .alt ctorName params k => return .alt ctorName ( params.mapM internalizeParam) ( internalizeAltCode k)
| .default k => return .default ( internalizeAltCode k)
return .cases { c with discr, alts, resultType }
return .cases c.typeName, resultType, discr, alts
end

View File

@@ -229,10 +229,8 @@ where
| _, _ => return Code.updateLet! code decl ( go k)
| .fun decl k =>
if let some replacement := ( read)[decl.fvarId]? then
let newDecl := { decl with
binderName := replacement,
value := ( go decl.value)
}
let newValue go decl.value
let newDecl := decl.fvarId, replacement, decl.params, decl.type, newValue
modifyLCtx fun lctx => lctx.addFunDecl newDecl
return .jp newDecl ( go k)
else

View File

@@ -35,7 +35,7 @@ def LetDecl.applyRenaming (decl : LetDecl) (r : Renaming) : CompilerM LetDecl :=
mutual
partial def FunDecl.applyRenaming (decl : FunDecl) (r : Renaming) : CompilerM FunDecl := do
if let some binderName := r.get? decl.fvarId then
let decl := { decl with binderName }
let decl := decl.updateBinderName binderName
modifyLCtx fun lctx => lctx.addFunDecl decl
decl.updateValue ( decl.value.applyRenaming r)
else

View File

@@ -268,7 +268,7 @@ where
else
altsNew := altsNew.push (alt.updateCode k)
modify fun s => s.insert decl.fvarId jpAltMap
let value := LCNF.attachCodeDecls decls (.cases { cases with alts := altsNew })
let value := LCNF.attachCodeDecls decls (.cases <| cases.updateAlts altsNew)
let decl decl.updateValue value
let code := .jp decl ( visit k)
return LCNF.attachCodeDecls jpAltDecls code

View File

@@ -115,7 +115,7 @@ def isGround [TraverseFVar α] (e : α) : SpecializeM Bool := do
match findFunDecl? fnFVarId with
-- This ascription to `Bool` is required to avoid this being inferred as `Prop`,
-- even with a type specified on the `let` binding.
| some { params, .. } => pure ((args.size < params.size) : Bool)
| some (.mk (params := params) ..) => pure ((args.size < params.size) : Bool)
| none => pure false
| _ => pure false
let fvarId := decl.fvarId

View File

@@ -72,7 +72,7 @@ partial def visitCode (code : Code) : M Code := do
modify fun s => { s with projMap := s.projMap.erase base }
let resultType toMonoType ( k.inferType)
let alts := #[.alt ctorInfo.name params k]
return .cases { typeName, resultType, discr := base, alts }
return .cases typeName, resultType, base, alts
| _ => return code.updateLet! ( decl.updateValue ( visitLetValue decl.value)) ( visitCode k)
| .fun decl k =>
let decl decl.updateValue ( visitCode decl.value)

View File

@@ -63,7 +63,7 @@ That is, our goal is to try to promote the pre join points `_alt.<idx>` into a p
partial def bindCases (jpDecl : FunDecl) (cases : Cases) : CompilerM Code := do
let (alts, s) visitAlts cases.alts |>.run {}
let resultType mkCasesResultType alts
let result := .cases { cases with alts, resultType }
let result := .cases cases.typeName, resultType, cases.discr, alts
let result := s.foldl (init := result) fun result _ altJp => .jp altJp result
return .jp jpDecl result
where
@@ -147,7 +147,7 @@ where
if alts.isEmpty then
throwError "`Code.bind` failed, empty `cases` found"
let resultType mkCasesResultType alts
return .cases { c with alts, resultType }
return .cases c.typeName, resultType, c.discr, alts
| .return fvarId => return .jmp jpDecl.fvarId #[.fvar fvarId]
| .jmp .. | .unreach .. => return code
@@ -183,7 +183,7 @@ where
result instead of a join point that takes a closure.
-/
eraseParam auxParam
let auxFunDecl := { auxParam with params := #[], value := .cases cases : FunDecl }
let auxFunDecl := auxParam.fvarId, auxParam.binderName, #[], auxParam.type, .cases cases
modifyLCtx fun lctx => lctx.addFunDecl auxFunDecl
let auxFunDecl auxFunDecl.etaExpand
go seq (i - 1) (.fun auxFunDecl c)
@@ -597,7 +597,7 @@ where
let (altType, alt) visitAlt numParams args[i]!
resultType := joinTypes altType resultType
alts := alts.push alt
let cases : Cases := { typeName, discr := discrFVarId, resultType, alts }
let cases := typeName, resultType, discrFVarId, alts
let auxDecl mkAuxParam resultType
pushElement (.cases auxDecl cases)
let result := .fvar auxDecl.fvarId

View File

@@ -205,7 +205,7 @@ partial def decToMono (c : Cases) (_ : c.typeName == ``Decidable) : ToMonoM Code
eraseParams ps
let ctorName := if ctorName == ``Decidable.isTrue then ``Bool.true else ``Bool.false
return .alt ctorName #[] ( k.toMono)
return .cases { c with resultType, alts, typeName := ``Bool }
return .cases ``Bool, resultType, c.discr, alts
/-- Eliminate `cases` for `Nat`. -/
partial def casesNatToMono (c: Cases) (_ : c.typeName == ``Nat) : ToMonoM Code := do
@@ -226,7 +226,7 @@ partial def casesNatToMono (c: Cases) (_ : c.typeName == ``Nat) : ToMonoM Code :
return .alt ``Bool.false #[] (.let oneDecl (.let subOneDecl ( k.toMono)))
else
return .alt ``Bool.true #[] ( k.toMono)
return .let zeroDecl (.let isZeroDecl (.cases { discr := isZeroDecl.fvarId, resultType, alts, typeName := ``Bool }))
return .let zeroDecl (.let isZeroDecl (.cases ``Bool, resultType, isZeroDecl.fvarId, alts))
/-- Eliminate `cases` for `Int`. -/
partial def casesIntToMono (c: Cases) (_ : c.typeName == ``Int) : ToMonoM Code := do
@@ -251,7 +251,7 @@ partial def casesIntToMono (c: Cases) (_ : c.typeName == ``Int) : ToMonoM Code :
let absDecl := { fvarId := p.fvarId, binderName := p.binderName, type := natType, value := .const ``Int.natAbs [] #[.fvar c.discr] }
modifyLCtx fun lctx => lctx.addLetDecl absDecl
return .alt ``Bool.false #[] (.let absDecl ( k.toMono))
return .let zeroNatDecl (.let zeroIntDecl (.let isNegDecl (.cases { discr := isNegDecl.fvarId, resultType, alts, typeName := ``Bool })))
return .let zeroNatDecl (.let zeroIntDecl (.let isNegDecl (.cases ``Bool, resultType, isNegDecl.fvarId, alts)))
/-- Eliminate `cases` for `UInt` types. -/
partial def casesUIntToMono (c : Cases) (uintName : Name) (_ : c.typeName == uintName) : ToMonoM Code := do
@@ -317,13 +317,13 @@ partial def casesThunkToMono (c : Cases) (_ : c.typeName == ``Thunk) : ToMonoM C
let letValue := .const ``Thunk.get [] #[.erased, .fvar c.discr]
let letDecl mkLetDecl ( mkFreshBinderName `_x) anyExpr letValue
let paramType := .const `PUnit []
let decl := {
fvarId := p.fvarId
binderName := p.binderName
type := ( mkArrow paramType anyExpr)
params := #[ mkAuxParam paramType]
value := .let letDecl (.return letDecl.fvarId)
}
let decl :=
p.fvarId,
p.binderName,
#[ mkAuxParam paramType],
( mkArrow paramType anyExpr),
.let letDecl (.return letDecl.fvarId)
modifyLCtx fun lctx => lctx.addFunDecl decl
let k k.toMono
return .fun decl k
@@ -418,7 +418,7 @@ partial def Code.toMono (code : Code) : ToMonoM Code := do
let ps mkFieldParamsForComputedFields ctorInfo.type ctorInfo.numParams numNewFields ps
let k k.toMono
return .alt implCtorName ps k
return .cases { discr := c.discr, resultType, typeName, alts }
return .cases typeName, resultType, c.discr, alts
else
let alts c.alts.mapM fun alt =>
match alt with

View File

@@ -45,26 +45,29 @@ def toAttributeKind (attrKindStx : Syntax) : MacroM AttributeKind := do
def mkAttrKindGlobal : Syntax :=
mkNode ``Lean.Parser.Term.attrKind #[mkNullNode]
def elabAttr [Monad m] [MonadEnv m] [MonadResolveName m] [MonadError m] [MonadMacroAdapter m] [MonadRecDepth m] [MonadTrace m] [MonadOptions m] [AddMessageContext m] [MonadLiftT IO m] (attrInstance : Syntax) : m Attribute := do
/- attrInstance := ppGroup $ leading_parser attrKind >> attrParser -/
let attrKind liftMacroM <| toAttributeKind attrInstance[0]
let attr := attrInstance[1]
let attr liftMacroM <| expandMacros attr
let attrName if attr.getKind == ``Parser.Attr.simple then
pure attr[0].getId.eraseMacroScopes
else match attr.getKind with
| .str _ s => pure <| Name.mkSimple s
| _ => throwErrorAt attr "Unknown attribute"
let .ok _impl := getAttributeImpl ( getEnv) attrName
| throwError "Unknown attribute `[{attrName}]`"
if let .ok impl := getAttributeImpl ( getEnv) attrName then
if regularInitAttr.getParam? ( getEnv) impl.ref |>.isSome then -- skip `builtin_initialize` attributes
recordExtraModUseFromDecl (isMeta := true) impl.ref
/- The `AttrM` does not have sufficient information for expanding macros in `args`.
So, we expand them before here before we invoke the attributer handlers implemented using `AttrM`. -/
return { kind := attrKind, name := attrName, stx := attr }
def elabAttr [Monad m] [MonadEnv m] [MonadResolveName m] [MonadError m] [MonadMacroAdapter m] [MonadRecDepth m] [MonadTrace m] [MonadOptions m] [AddMessageContext m] [MonadLiftT IO m] [MonadFinally m] (attrInstance : Syntax) : m Attribute := do
-- Resolving the attribute itself can be done in the private scope; running the attribute handler
-- will later be done in a scope determined by `applyAttributesCore`.
withoutExporting do
/- attrInstance := ppGroup $ leading_parser attrKind >> attrParser -/
let attrKind liftMacroM <| toAttributeKind attrInstance[0]
let attr := attrInstance[1]
let attr liftMacroM <| expandMacros attr
let attrName if attr.getKind == ``Parser.Attr.simple then
pure attr[0].getId.eraseMacroScopes
else match attr.getKind with
| .str _ s => pure <| Name.mkSimple s
| _ => throwErrorAt attr "Unknown attribute"
let .ok _impl := getAttributeImpl ( getEnv) attrName
| throwError "Unknown attribute `[{attrName}]`"
if let .ok impl := getAttributeImpl ( getEnv) attrName then
if regularInitAttr.getParam? ( getEnv) impl.ref |>.isSome then -- skip `builtin_initialize` attributes
recordExtraModUseFromDecl (isMeta := true) impl.ref
/- The `AttrM` does not have sufficient information for expanding macros in `args`.
So, we expand them before here before we invoke the attributer handlers implemented using `AttrM`. -/
return { kind := attrKind, name := attrName, stx := attr }
def elabAttrs [Monad m] [MonadEnv m] [MonadResolveName m] [MonadError m] [MonadMacroAdapter m] [MonadRecDepth m] [MonadTrace m] [MonadOptions m] [AddMessageContext m] [MonadLog m] [MonadLiftT IO m] (attrInstances : Array Syntax) : m (Array Attribute) := do
def elabAttrs [Monad m] [MonadEnv m] [MonadResolveName m] [MonadError m] [MonadMacroAdapter m] [MonadRecDepth m] [MonadTrace m] [MonadOptions m] [AddMessageContext m] [MonadLog m] [MonadLiftT IO m] [MonadFinally m] (attrInstances : Array Syntax) : m (Array Attribute) := do
let mut attrs := #[]
for attr in attrInstances do
try
@@ -74,7 +77,7 @@ def elabAttrs [Monad m] [MonadEnv m] [MonadResolveName m] [MonadError m] [MonadM
return attrs
-- leading_parser "@[" >> sepBy1 attrInstance ", " >> "]"
def elabDeclAttrs [Monad m] [MonadEnv m] [MonadResolveName m] [MonadError m] [MonadMacroAdapter m] [MonadRecDepth m] [MonadTrace m] [MonadOptions m] [AddMessageContext m] [MonadLog m] [MonadLiftT IO m] (stx : Syntax) : m (Array Attribute) :=
def elabDeclAttrs [Monad m] [MonadEnv m] [MonadResolveName m] [MonadError m] [MonadMacroAdapter m] [MonadRecDepth m] [MonadTrace m] [MonadOptions m] [AddMessageContext m] [MonadLog m] [MonadLiftT IO m] [MonadFinally m] (stx : Syntax) : m (Array Attribute) :=
elabAttrs stx[1].getSepArgs
end Lean.Elab

View File

@@ -573,11 +573,16 @@ def elabDefaultOrNonempty : TermElab := fun stx expectedType? => do
| some expectedType =>
try
mkDefault expectedType
catch ex => try
catch _ => try
mkOfNonempty expectedType
catch _ =>
if stx[1].isNone then
throw ex
throwError "\
failed to synthesize '{.ofConstName ``Inhabited}' or '{.ofConstName ``Nonempty}' instance for\
{indentExpr expectedType}\n\
\n\
If this type is defined using the 'structure' or 'inductive' command, \
you can try adding a 'deriving Nonempty' clause to it."
else
-- It is in the context of an `unsafe` constant. We can use sorry instead.
-- Another option is to make a recursive application since it is unsafe.

View File

@@ -11,10 +11,13 @@ public import Lean.Server.CodeActions.Attr
public section
/-! `#guard_msgs` command for testing commands
/-! `#guard_msgs` and `#guard_panic` commands for testing commands
This module defines a command to test that another command produces the expected messages.
See the docstring on the `#guard_msgs` command.
This module defines commands to test that other commands produce expected messages:
- `#guard_msgs`: tests that a command produces exactly the expected messages
- `#guard_panic`: tests that a command produces a panic message (without checking the exact text)
See the docstrings on the individual commands.
-/
open Lean Parser.Tactic Elab Command
@@ -88,6 +91,8 @@ structure GuardMsgsSpec where
ordering : MessageOrdering := .exact
/-- Whether to report position information. -/
reportPositions : Bool := false
/-- Whether to check for substring containment instead of exact match. -/
substring : Bool := false
def parseGuardMsgsFilterAction (action? : Option (TSyntax ``guardMsgsFilterAction)) :
CommandElabM FilterSpec := do
@@ -118,7 +123,7 @@ def parseGuardMsgsSpec (spec? : Option (TSyntax ``guardMsgsSpec)) : CommandElabM
| `(guardMsgsSpec| ($[$elts:guardMsgsSpecElt],*)) => pure elts
| _ => throwUnsupportedSyntax
let defaultFilterFn := cfg.filterFn
let mut { whitespace, ordering, reportPositions .. } := cfg
let mut { whitespace, ordering, reportPositions, substring .. } := cfg
let mut p? : Option (Message FilterSpec) := none
let pushP (action : FilterSpec) (msgP : Message Bool) (p? : Option (Message FilterSpec))
(msg : Message) : FilterSpec :=
@@ -136,9 +141,11 @@ def parseGuardMsgsSpec (spec? : Option (TSyntax ``guardMsgsSpec)) : CommandElabM
| `(guardMsgsSpecElt| ordering := sorted) => ordering := .sorted
| `(guardMsgsSpecElt| positions := true) => reportPositions := true
| `(guardMsgsSpecElt| positions := false) => reportPositions := false
| `(guardMsgsSpecElt| substring := true) => substring := true
| `(guardMsgsSpecElt| substring := false) => substring := false
| _ => throwUnsupportedSyntax
let filterFn := p?.getD defaultFilterFn
return { filterFn, whitespace, ordering, reportPositions }
return { filterFn, whitespace, ordering, reportPositions, substring }
/-- An info tree node corresponding to a failed `#guard_msgs` invocation,
used for code action support. -/
@@ -176,22 +183,31 @@ def MessageOrdering.apply (mode : MessageOrdering) (msgs : List String) : List S
| .exact => msgs
| .sorted => msgs |>.toArray.qsort (· < ·) |>.toList
/--
Runs a command and collects all messages (sync and async) it produces.
Clears the snapshot tasks after collection.
Returns the collected messages.
-/
def runAndCollectMessages (cmd : Syntax) : CommandElabM MessageLog := do
-- do not forward snapshot as we don't want messages assigned to it to leak outside
withReader ({ · with snap? := none }) do
-- The `#guard_msgs` command is special-cased in `elabCommandTopLevel` to ensure linters only run once.
elabCommandTopLevel cmd
-- collect sync and async messages
let msgs := ( get).messages ++
( get).snapshotTasks.foldl (· ++ ·.get.getAll.foldl (· ++ ·.diagnostics.msgLog) .empty) .empty
-- clear async messages as we don't want them to leak outside
modify ({ · with snapshotTasks := #[] })
return msgs
@[builtin_command_elab Lean.guardMsgsCmd] def elabGuardMsgs : CommandElab
| `(command| $[$dc?:docComment]? #guard_msgs%$tk $(spec?)? in $cmd) => do
let expected : String := ( dc?.mapM (getDocStringText ·)).getD ""
|>.trimAscii |>.copy |> removeTrailingWhitespaceMarker
let { whitespace, ordering, filterFn, reportPositions } parseGuardMsgsSpec spec?
-- do not forward snapshot as we don't want messages assigned to it to leak outside
withReader ({ · with snap? := none }) do
-- The `#guard_msgs` command is special-cased in `elabCommandTopLevel` to ensure linters only run once.
elabCommandTopLevel cmd
-- collect sync and async messages
let msgs := ( get).messages ++
( get).snapshotTasks.foldl (· ++ ·.get.getAll.foldl (· ++ ·.diagnostics.msgLog) {}) {}
-- clear async messages as we don't want them to leak outside
modify ({ · with snapshotTasks := #[] })
let mut toCheck : MessageLog := .empty
let mut toPassthrough : MessageLog := .empty
let { whitespace, ordering, filterFn, reportPositions, substring } parseGuardMsgsSpec spec?
let msgs runAndCollectMessages cmd
let mut toCheck : MessageLog := MessageLog.empty
let mut toPassthrough : MessageLog := MessageLog.empty
for msg in msgs.toList do
if msg.isSilent then
continue
@@ -207,7 +223,13 @@ def MessageOrdering.apply (mode : MessageOrdering) (msgs : List String) : List S
let strings toCheck.toList.mapM (messageToString · reportPos?)
let strings := ordering.apply strings
let res := "---\n".intercalate strings |>.trimAscii |>.copy
if whitespace.apply expected == whitespace.apply res then
let passed := if substring then
-- Substring mode: check that expected appears within res (after whitespace normalization)
(whitespace.apply res).contains (whitespace.apply expected)
else
-- Exact mode: check equality (after whitespace normalization)
whitespace.apply expected == whitespace.apply res
if passed then
-- Passed. Only put toPassthrough messages back on the message log
modify fun st => { st with messages := toPassthrough }
else
@@ -257,4 +279,24 @@ def guardMsgsCodeAction : CommandCodeAction := fun _ _ _ node => do
}
}]
@[builtin_command_elab Lean.guardPanicCmd] def elabGuardPanic : CommandElab
| `(command| #guard_panic in $cmd) => do
let msgs runAndCollectMessages cmd
-- Check if any message contains "PANIC"
let mut foundPanic := false
for msg in msgs.toList do
if msg.isSilent then continue
let msgStr msg.data.toString
if msgStr.contains "PANIC" then
foundPanic := true
break
if foundPanic then
-- Success - clear the messages so they don't appear
modify fun st => { st with messages := MessageLog.empty }
else
-- Failed - put the messages back and add our error
modify fun st => { st with messages := msgs }
logError "Expected a PANIC but none was found"
| _ => throwUnsupportedSyntax
end Lean.Elab.Tactic.GuardMsgs

View File

@@ -29,72 +29,76 @@ private def inductiveSyntaxToView (modifiers : Modifiers) (decl : Syntax) (isCoi
let (binders, type?) := expandOptDeclSig decl[2]
let declId := decl[1]
let name, declName, levelNames, docString? Term.expandDeclId ( getCurrNamespace) ( Term.getLevelNames) declId modifiers
if modifiers.isMeta then
modifyEnv (markMeta · declName)
addDeclarationRangesForBuiltin declName modifiers.stx decl
/-
Relates to issue
https://github.com/leanprover/lean4/issues/10503
-/
if declName.hasMacroScopes && isCoinductive then
throwError "Coinductive predicates are not allowed inside of macro scopes"
let ctors decl[4].getArgs.mapM fun ctor => withRef ctor do
/-
```
def ctor := leading_parser optional docComment >> "\n| " >> declModifiers >> rawIdent >> optDeclSig
```
-/
let modifiersStx := ctor[2]
let mut ctorModifiers elabModifiers modifiersStx
if let some leadingDocComment := ctor[0].getOptional? then
if ctorModifiers.docString?.isSome then
logErrorAt leadingDocComment "Duplicate doc string"
ctorModifiers := { ctorModifiers with
docString? := some (leadingDocComment, doc.verso.get ( getOptions)) }
if ctorModifiers.isPrivate && modifiers.isPrivate then
let hint do
let .original .. := modifiersStx.getHeadInfo | pure .nil
let some range := modifiersStx[2].getRangeWithTrailing? | pure .nil
-- Drop the doc comment from both the `declModifiers` and outer `ctor`, as well as
-- everything after the constructor name (yielding invalid syntax with the desired range)
let previewSpan? := ctor.modifyArgs (·[2...4].toArray.modify 0 (·.modifyArgs (·[1...*])))
MessageData.hint "Remove `private` modifier from constructor" #[{
suggestion := ""
span? := Syntax.ofRange range
previewSpan?
toCodeActionTitle? := some fun _ => "Delete `private` modifier"
}]
throwError m!"Constructor cannot be marked `private` because it is already in a `private` inductive datatype" ++ hint
if ctorModifiers.isProtected && modifiers.isPrivate then
throwError "Constructor cannot be `protected` because it is in a `private` inductive datatype"
checkValidCtorModifier ctorModifiers
let ctorName := ctor.getIdAt 3
let ctorName := declName ++ ctorName
let ctorName withRef ctor[3] <| applyVisibility ctorModifiers ctorName
let (binders, type?) := expandOptDeclSig ctor[4]
addDeclarationRangesFromSyntax ctorName ctor ctor[3]
-- In the case of mutual inductives, this is the earliests point where we can establish the
-- correct scope for each individual inductive declaration (used e.g. to infer ctor visibility
-- below), so let's do that now.
withExporting (isExporting := !isPrivateName declName) do
if modifiers.isMeta then
modifyEnv (markMeta · ctorName)
return { ref := ctor, declId := ctor[3], modifiers := ctorModifiers, declName := ctorName, binders := binders, type? := type? : CtorView }
let computedFields (decl[5].getOptional?.map (·[1].getArgs) |>.getD #[]).mapM fun cf => withRef cf do
return { ref := cf, modifiers := cf[0], fieldId := cf[1].getId, type := cf[3], matchAlts := cf[4] }
let classes getOptDerivingClasses decl[6]
if decl[3][0].isToken ":=" then
-- https://github.com/leanprover/lean4/issues/5236
withRef decl[0] <| Linter.logLintIf Linter.linter.deprecated decl[3]
"`inductive ... :=` has been deprecated in favor of `inductive ... where`"
return {
ref := decl
shortDeclName := name
derivingClasses := classes
allowIndices := true
allowSortPolymorphism := true
declId, modifiers, isClass, declName, levelNames
binders, type?, ctors
computedFields
docString?
isCoinductive := isCoinductive
}
modifyEnv (markMeta · declName)
addDeclarationRangesForBuiltin declName modifiers.stx decl
/-
Relates to issue
https://github.com/leanprover/lean4/issues/10503
-/
if declName.hasMacroScopes && isCoinductive then
throwError "Coinductive predicates are not allowed inside of macro scopes"
let ctors decl[4].getArgs.mapM fun ctor => withRef ctor do
/-
```
def ctor := leading_parser optional docComment >> "\n| " >> declModifiers >> rawIdent >> optDeclSig
```
-/
let modifiersStx := ctor[2]
let mut ctorModifiers elabModifiers modifiersStx
if let some leadingDocComment := ctor[0].getOptional? then
if ctorModifiers.docString?.isSome then
logErrorAt leadingDocComment "Duplicate doc string"
ctorModifiers := { ctorModifiers with
docString? := some (leadingDocComment, doc.verso.get ( getOptions)) }
if ctorModifiers.isPrivate && modifiers.isPrivate then
let hint do
let .original .. := modifiersStx.getHeadInfo | pure .nil
let some range := modifiersStx[2].getRangeWithTrailing? | pure .nil
-- Drop the doc comment from both the `declModifiers` and outer `ctor`, as well as
-- everything after the constructor name (yielding invalid syntax with the desired range)
let previewSpan? := ctor.modifyArgs (·[2...4].toArray.modify 0 (·.modifyArgs (·[1...*])))
MessageData.hint "Remove `private` modifier from constructor" #[{
suggestion := ""
span? := Syntax.ofRange range
previewSpan?
toCodeActionTitle? := some fun _ => "Delete `private` modifier"
}]
throwError m!"Constructor cannot be marked `private` because it is already in a `private` inductive datatype" ++ hint
if ctorModifiers.isProtected && modifiers.isPrivate then
throwError "Constructor cannot be `protected` because it is in a `private` inductive datatype"
checkValidCtorModifier ctorModifiers
let ctorName := ctor.getIdAt 3
let ctorName := declName ++ ctorName
let ctorName withRef ctor[3] <| applyVisibility ctorModifiers ctorName
let (binders, type?) := expandOptDeclSig ctor[4]
addDeclarationRangesFromSyntax ctorName ctor ctor[3]
if modifiers.isMeta then
modifyEnv (markMeta · ctorName)
return { ref := ctor, declId := ctor[3], modifiers := ctorModifiers, declName := ctorName, binders := binders, type? := type? : CtorView }
let computedFields (decl[5].getOptional?.map (·[1].getArgs) |>.getD #[]).mapM fun cf => withRef cf do
return { ref := cf, modifiers := cf[0], fieldId := cf[1].getId, type := cf[3], matchAlts := cf[4] }
let classes getOptDerivingClasses decl[6]
if decl[3][0].isToken ":=" then
-- https://github.com/leanprover/lean4/issues/5236
withRef decl[0] <| Linter.logLintIf Linter.linter.deprecated decl[3]
"`inductive ... :=` has been deprecated in favor of `inductive ... where`"
return {
ref := decl
shortDeclName := name
derivingClasses := classes
allowIndices := true
allowSortPolymorphism := true
declId, modifiers, isClass, declName, levelNames
binders, type?, ctors
computedFields
docString?
isCoinductive := isCoinductive
}
private def isInductiveFamily (numParams : Nat) (indFVar : Expr) : TermElabM Bool := do
let indFVarType inferType indFVar

View File

@@ -886,7 +886,7 @@ private def generalize (discrs : Array Discr) (matchType : Expr) (altViews : Arr
let matchType' forallBoundedTelescope matchType discrs.size fun ds type => do
let type mkForallFVars ys type
let (discrs', ds') := Array.unzip <| Array.zip discrExprs ds |>.filter fun (di, _) => di.isFVar
let type type.replaceFVarsM discrs' ds'
let type := type.replaceFVars discrs' ds'
mkForallFVars ds type
if ( isTypeCorrect matchType') then
let discrs := discrs ++ ys.map fun y => { expr := y : Discr }
@@ -1119,11 +1119,11 @@ private def elabMatchAux (generalizing? : Option Bool) (discrStxs : Array Syntax
withRef altLHS.ref do
for d in altLHS.fvarDecls do
if d.hasExprMVar then
-- This code path is a vestige prior to fixing #8099, but it is still appears to be
-- important for testcase 1300.lean.
tryPostpone
withExistingLocalDecls altLHS.fvarDecls do
runPendingTacticsAt d.type
if ( instantiateMVars d.type).hasExprMVar then
throwMVarError m!"Invalid match expression: The type of pattern variable '{d.toExpr}' contains metavariables:{indentExpr d.type}"
for p in altLHS.patterns do
if ( Match.instantiatePatternMVars p).hasExprMVar then
tryPostpone

View File

@@ -369,6 +369,33 @@ def withoutRecover (x : TacticM α) : TacticM α :=
def withRecover (recover : Bool) (x : TacticM α) : TacticM α :=
withReader (fun ctx => { ctx with recover }) x
/-! ## Message log utilities -/
/-- Execute an action while suppressing any new messages it generates.
Restores the original message log after the action completes.
Useful for trying tactics without polluting the message log with errors from failed attempts. -/
def withSuppressedMessages (action : TacticM α) : TacticM α := do
let initialLog Core.getMessageLog
try
action
finally
Core.setMessageLog initialLog
/-- Execute an action and return any new messages it generates.
Restores the original message log afterward.
Useful for inspecting messages produced by a tactic without committing them. -/
def withCapturedMessages (action : TacticM α) : TacticM (α × List Message) := do
let initialLog Core.getMessageLog
let initialMsgCount := initialLog.toList.length
let result action
let newMsgs := ( Core.getMessageLog).toList.drop initialMsgCount
Core.setMessageLog initialLog
return (result, newMsgs)
/-- Check if any messages in the list are errors. -/
def hasErrorMessages (msgs : List Message) : Bool :=
msgs.any (·.severity == .error)
/--
Like `throwErrorAt`, but, if recovery is enabled, logs the error instead.
-/

View File

@@ -182,6 +182,7 @@ open LibrarySuggestions in
def elabGrindSuggestions
(params : Grind.Params) (suggestions : Array Suggestion := #[]) : MetaM Grind.Params := do
let mut params := params
let mut added : Array Name := #[]
for p in suggestions do
let attr match p.flag with
| some flag => parseModifier flag
@@ -190,6 +191,7 @@ def elabGrindSuggestions
| .ematch kind =>
try
params addEMatchTheorem params (mkIdent p.name) p.name kind false (warn := false)
added := added.push p.name
catch _ => pure () -- Don't worry if library suggestions gave bad theorems.
| _ =>
-- We could actually support arbitrary grind modifiers,
@@ -197,26 +199,40 @@ def elabGrindSuggestions
-- but this would require a larger refactor.
-- Let's only do this if there is a prospect of a library suggestion engine supporting this.
throwError "unexpected modifier {p.flag}"
unless added.isEmpty do
trace[grind.debug.suggestions] "{added}"
return params
open LibrarySuggestions in
def elabGrindParamsAndSuggestions
(params : Grind.Params)
(ps : TSyntaxArray ``Parser.Tactic.grindParam)
(suggestions : Array Suggestion := #[])
(only : Bool) (lax : Bool := false) : TermElabM Grind.Params := do
let params elabGrindParams params ps (lax := lax) (only := only)
elabGrindSuggestions params suggestions
/-- Add all definitions from the current file. -/
def elabGrindLocals (params : Grind.Params) : MetaM Grind.Params := do
let env getEnv
let mut params := params
let mut added : Array Name := #[]
for (name, ci) in env.constants.map₂.toList do
-- Filter similar to LibrarySuggestions.isDeniedPremise (but inlined to avoid dependency)
-- Skip internal details, but allow private names (which are accessible from current module)
if name.isInternalDetail && !isPrivateName name then continue
if ( Meta.isInstance name) then continue
match ci with
| .defnInfo _ =>
try
params addEMatchTheorem params (mkIdent name) name (.default false) false (warn := false)
added := added.push name
catch _ => pure ()
| _ => continue
unless added.isEmpty do
trace[grind.debug.locals] "{added}"
return params
def mkGrindParams
(config : Grind.Config) (only : Bool) (ps : TSyntaxArray ``Parser.Tactic.grindParam) (mvarId : MVarId) :
TermElabM Grind.Params := do
let params if only then Grind.mkOnlyParams config else Grind.mkDefaultParams config
let suggestions if config.suggestions then
LibrarySuggestions.select mvarId { caller := some "grind" }
else
pure #[]
let mut params elabGrindParamsAndSuggestions params ps suggestions (only := only) (lax := config.lax)
let mut params elabGrindParams params ps (lax := config.lax) (only := only)
if config.suggestions then
params elabGrindSuggestions params ( LibrarySuggestions.select mvarId { caller := some "grind" })
if config.locals then
params elabGrindLocals params
trace[grind.debug.inj] "{params.extensions[0]!.inj.getOrigins.map (·.pp)}"
if params.anchorRefs?.isSome then
/-
@@ -289,21 +305,24 @@ def setGrindParams (stx : TSyntax `tactic) (params : Array Syntax) : TSyntax `ta
def getGrindParams (stx : TSyntax `tactic) : Array Syntax :=
stx.raw[grindParamsPos][1].getSepArgs
/-- Filter out `+suggestions` from the config syntax -/
def filterSuggestionsFromGrindConfig (config : TSyntax ``Lean.Parser.Tactic.optConfig) :
/-- Filter out `+suggestions` and `+locals` from the config syntax -/
def filterSuggestionsAndLocalsFromGrindConfig (config : TSyntax ``Lean.Parser.Tactic.optConfig) :
TSyntax ``Lean.Parser.Tactic.optConfig :=
let configItems := config.raw.getArgs
let filteredItems := configItems.filter fun item =>
-- Keep all items except +suggestions
-- Structure: null node -> configItem -> posConfigItem -> ["+", ident]
match item[0]? with
| some configItem => match configItem[0]? with
| some posConfigItem => match posConfigItem[1]? with
| some ident => !(posConfigItem.getKind == ``Lean.Parser.Tactic.posConfigItem && ident.getId == `suggestions)
| none => true
-- optConfig structure: (Tactic.optConfig [configItem1, configItem2, ...])
-- config.raw.getArgs returns #[null_node], so we need to filter the null node's children
let nullNode := config.raw[0]!
let configItems := nullNode.getArgs
let filteredItems := configItems.filter fun configItem =>
-- Keep all items except +suggestions and +locals
-- Structure: configItem -> posConfigItem -> ["+", ident]
match configItem[0]? with
| some posConfigItem => match posConfigItem[1]? with
| some ident =>
let id := ident.getId.eraseMacroScopes
!(posConfigItem.getKind == ``Lean.Parser.Tactic.posConfigItem && (id == `suggestions || id == `locals))
| none => true
| none => true
config.raw.setArgs filteredItems
config.raw.setArg 0 (nullNode.setArgs filteredItems)
private def elabGrindConfig' (config : TSyntax ``Lean.Parser.Tactic.optConfig) (interactive : Bool) : TacticM Grind.Config := do
if interactive then
@@ -345,7 +364,7 @@ def evalGrindTraceCore (stx : Syntax) (trace := true) (verbose := true) (useSorr
let finish Grind.Action.mkFinish
let goal :: _ Grind.getGoals
| -- Goal was closed during initialization
let configStx' := filterSuggestionsFromGrindConfig configStx
let configStx' := filterSuggestionsAndLocalsFromGrindConfig configStx
if termParamStxs.isEmpty then
let tac `(tactic| grind $configStx':optConfig only)
return #[tac]
@@ -357,7 +376,7 @@ def evalGrindTraceCore (stx : Syntax) (trace := true) (verbose := true) (useSorr
-- let saved ← saveState
match ( finish.run goal) with
| .closed seq =>
let configStx' := filterSuggestionsFromGrindConfig configStx
let configStx' := filterSuggestionsAndLocalsFromGrindConfig configStx
let tacs Grind.mkGrindOnlyTactics configStx' seq termParamStxs
let seq := Grind.Action.mkGrindSeq seq
let tac `(tactic| grind $configStx':optConfig => $seq:grindSeq)

View File

@@ -416,6 +416,28 @@ structure MkSimpContextResult where
/-- The elaborated simp arguments with syntax -/
simpArgs : Array (Syntax × ElabSimpArgResult) := #[]
/-- Add all definitions from the current file to unfold. -/
def elabSimpLocals (thms : SimpTheorems) (kind : SimpKind) : MetaM SimpTheorems := do
let env getEnv
let mut thms := thms
for (name, ci) in env.constants.map₂.toList do
-- Skip internal details, but allow private names (which are accessible from current module)
if name.isInternalDetail && !isPrivateName name then continue
if ( Meta.isInstance name) then continue
match ci with
| .defnInfo _ =>
-- Definitions are added to unfold
try
if kind == .dsimp then
thms := thms.addDeclToUnfoldCore name
else
let entries mkSimpEntryOfDeclToUnfold name
for entry in entries do
thms := thms.addSimpEntry entry
catch _ => pure () -- Skip definitions that can't be added
| _ => continue
return thms
/--
Create the `Simp.Context` for the `simp`, `dsimp`, and `simp_all` tactics.
If `kind != SimpKind.simp`, the `discharge` option must be `none`
@@ -437,14 +459,18 @@ def mkSimpContext (stx : Syntax) (eraseLocal : Bool) (kind := SimpKind.simp)
throwError "Tactic `dsimp` does not support the `discharger' option"
let dischargeWrapper mkDischargeWrapper stx[2]
let simpOnly := !stx[simpOnlyPos].isNone
let simpTheorems if simpOnly then
let mut simpTheorems if simpOnly then
simpOnlyBuiltins.foldlM (·.addConst ·) ({} : SimpTheorems)
else
simpTheorems
let simprocs if simpOnly then pure {} else Simp.getSimprocs
let congrTheorems getSimpCongrTheorems
let config elabSimpConfig stx[1] (kind := kind)
-- Add local definitions if +locals is enabled
if config.locals then
simpTheorems elabSimpLocals simpTheorems kind
let ctx Simp.mkContext
(config := ( elabSimpConfig stx[1] (kind := kind)))
(config := config)
(simpTheorems := #[simpTheorems])
congrTheorems
let r elabSimpArgs stx[4] (eraseLocal := eraseLocal) (kind := kind) (simprocs := #[simprocs]) (ignoreStarArg := ignoreStarArg) ctx

View File

@@ -21,19 +21,21 @@ The `simp?` tactic is a simple wrapper around the simp with trace behavior.
namespace Lean.Elab.Tactic
open Lean Elab Parser Tactic Meta Simp Tactic.TryThis
/-- Filter out `+suggestions` from the config syntax -/
def filterSuggestionsFromSimpConfig (cfg : TSyntax ``Lean.Parser.Tactic.optConfig) :
/-- Filter out `+suggestions` and `+locals` from the config syntax -/
def filterSuggestionsAndLocalsFromSimpConfig (cfg : TSyntax ``Lean.Parser.Tactic.optConfig) :
MetaM (TSyntax ``Lean.Parser.Tactic.optConfig) := do
-- The config has one arg: a null node containing configItem nodes
let nullNode := cfg.raw.getArg 0
let configItems := nullNode.getArgs
-- Filter out configItem nodes that contain +suggestions
-- Filter out configItem nodes that contain +suggestions or +locals
let filteredItems := configItems.filter fun item =>
match item[0]?, item.getKind with
| some posConfigItem, ``Lean.Parser.Tactic.configItem =>
match posConfigItem[1]?, posConfigItem.getKind with
| some ident, ``Lean.Parser.Tactic.posConfigItem => ident.getId != `suggestions
| some ident, ``Lean.Parser.Tactic.posConfigItem =>
let id := ident.getId.eraseMacroScopes
id != `suggestions && id != `locals
| _, _ => true
| _, _ => true
@@ -72,7 +74,7 @@ def mkSimpCallStx (stx : Syntax) (usedSimps : UsedSimps) : MetaM (TSyntax `tacti
else
`(tactic| simp%$tk $cfg:optConfig $[$discharger]? $[only%$o]? [$argsArray,*] $[$loc]?)
-- Build syntax for suggestion (without +suggestions config)
let filteredCfg filterSuggestionsFromSimpConfig cfg
let filteredCfg filterSuggestionsAndLocalsFromSimpConfig cfg
let stxForSuggestion if bang.isSome then
`(tactic| simp!%$tk $filteredCfg:optConfig $[$discharger]? $[only%$o]? [$argsArray,*] $[$loc]?)
else
@@ -118,7 +120,7 @@ def mkSimpCallStx (stx : Syntax) (usedSimps : UsedSimps) : MetaM (TSyntax `tacti
else
`(tactic| simp_all%$tk $cfg:optConfig $[$discharger]? $[only%$o]? [$argsArray,*])
-- Build syntax for suggestion (without +suggestions config)
let filteredCfg filterSuggestionsFromSimpConfig cfg
let filteredCfg filterSuggestionsAndLocalsFromSimpConfig cfg
let stxForSuggestion
if argsArray.isEmpty then
if bang.isSome then

View File

@@ -196,17 +196,22 @@ private def evalSuggestAtomic (tac : TSyntax `tactic) : TacticM (TSyntax `tactic
else
return tac
/-- Check if a config contains `+suggestions` -/
private def configHasSuggestions (config : TSyntax ``Lean.Parser.Tactic.optConfig) : Bool :=
let configItems := config.raw.getArgs
configItems.any fun item =>
match item[0]? with
| some configItem => match configItem[0]? with
/-- Check if a config contains `+suggestions` or `+locals` -/
private def configHasSuggestionsOrLocals (config : TSyntax ``Lean.Parser.Tactic.optConfig) : Bool :=
-- optConfig structure: (Tactic.optConfig [configItem1, configItem2, ...])
-- config.raw.getArgs returns #[null_node], so we need to access the null node's children
let nullNode := config.raw[0]?
match nullNode with
| some node =>
node.getArgs.any fun configItem =>
match configItem[0]? with
| some posConfigItem => match posConfigItem[1]? with
| some ident => posConfigItem.getKind == ``Lean.Parser.Tactic.posConfigItem && ident.getId == `suggestions
| some ident =>
let id := ident.getId.eraseMacroScopes
posConfigItem.getKind == ``Lean.Parser.Tactic.posConfigItem && (id == `suggestions || id == `locals)
| none => false
| none => false
| none => false
| none => false
private def grindTraceToGrind (tac : TSyntax `tactic) : TacticM (TSyntax `tactic) := do
match tac with
@@ -534,7 +539,7 @@ private def evalSuggestGrindTrace : TryTactic := fun tac => do
trace[try.debug] ">> {tac1}"
if ( read).config.only then
-- If config has +suggestions, only return the 'only' version, not the original
if configHasSuggestions configStx then
if configHasSuggestionsOrLocals configStx then
mkTrySuggestions tacs
else
mkTrySuggestions (#[tac] ++ tacs)
@@ -552,7 +557,7 @@ private def evalSuggestSimpTrace : TryTactic := fun tac => do (← getMainGoal).
if ( read).config.only then
let tac' mkSimpCallStx tac stats.usedTheorems
-- If config has +suggestions, only return the 'only' version, not the original
if configHasSuggestions configStx then
if configHasSuggestionsOrLocals configStx then
mkTrySuggestions #[tac']
else
mkTrySuggestions #[tac, tac']
@@ -565,7 +570,7 @@ private def evalSuggestSimpAllTrace : TryTactic := fun tac => do
| `(tactic| simp_all? $[!%$_bang]? $configStx:optConfig $(_discharger)? $[only%$_only]? $[[$_args,*]]?) =>
( getMainGoal).withContext do
withOriginalHeartbeats do
let hasSuggestions := configHasSuggestions configStx
let hasSuggestionsOrLocals := configHasSuggestionsOrLocals configStx
-- Get library suggestions if +suggestions is present
let config elabSimpConfig configStx (kind := .simpAll)
@@ -598,13 +603,13 @@ private def evalSuggestSimpAllTrace : TryTactic := fun tac => do
| some mvarId => replaceMainGoal [mvarId]
trace[try.debug] "`simp_all` succeeded"
if ( read).config.only then
-- Remove +suggestions from config for the output (similar to SimpTrace.lean)
let filteredCfg filterSuggestionsFromSimpConfig configStx
-- Remove +suggestions and +locals from config for the output (similar to SimpTrace.lean)
let filteredCfg filterSuggestionsAndLocalsFromSimpConfig configStx
-- Convert simp_all? to simp_all for mkSimpCallStx (similar to simpTraceToSimp)
let tacWithoutTrace `(tactic| simp_all $filteredCfg:optConfig $[only%$_only]? $[[$_args,*]]?)
let tac' mkSimpCallStx tacWithoutTrace stats.usedTheorems
-- If config has +suggestions, only return the 'only' version, not the original
if hasSuggestions then
-- If config has +suggestions or +locals, only return the 'only' version, not the original
if hasSuggestionsOrLocals then
mkTrySuggestions #[tac']
else
mkTrySuggestions #[tac, tac']
@@ -731,6 +736,15 @@ private partial def evalSuggestAttemptAllPar (tacs : Array (TSyntax ``Parser.Tac
else
throwError "`attempt_all_par` failed"
/-- `evalSuggest` for `first_par` tactic - returns first successful result, cancels others. -/
private partial def evalSuggestFirstPar (tacs : Array (TSyntax ``Parser.Tactic.tacticSeq)) : TryTacticM (TSyntax `tactic) := do
unless ( read).terminal do
throwError "invalid occurrence of `first_par` in non-terminal position for `try?` script{indentD (← read).root}"
let ctx read
let jobs : List (TacticM (TSyntax `tactic)) := tacs.toList.map fun tacSeq =>
withOriginalHeartbeats (evalSuggestTacticSeq tacSeq) ctx
TacticM.parFirst jobs
private partial def evalSuggestDefault (tac : TSyntax `tactic) : TryTacticM (TSyntax `tactic) := do
let kind := tac.raw.getKind
match ( getEvalFns kind) with
@@ -778,6 +792,7 @@ private partial def evalSuggestImpl : TryTactic := fun tac => do
| `(tactic| try $tac:tacticSeq) => evalSuggestTry tac
| `(tactic| attempt_all $[| $tacs]*) => evalSuggestAttemptAll tacs
| `(tactic| attempt_all_par $[| $tacs]*) => evalSuggestAttemptAllPar tacs
| `(tactic| first_par $[| $tacs]*) => evalSuggestFirstPar tacs
| _ =>
let k := tac.raw.getKind
if k == ``Parser.Tactic.seq1 then
@@ -821,13 +836,12 @@ private def addSuggestions (tk : Syntax) (s : Array Tactic.TryThis.Suggestion) :
Tactic.TryThis.addSuggestions tk (s.map fun stx => stx) (origSpan? := ( getRef))
def evalAndSuggest (tk : Syntax) (tac : TSyntax `tactic) (originalMaxHeartbeats : Nat) (config : Try.Config := {}) : TacticM Unit := do
let initialLog Core.getMessageLog
let tac' try
evalSuggest tac |>.run { terminal := true, root := tac, config, originalMaxHeartbeats }
catch _ =>
throwEvalAndSuggestFailed config
-- Restore message log to suppress "Try this" messages from intermediate tactic executions
Core.setMessageLog initialLog
-- Suppress "Try this" messages from intermediate tactic executions
let tac' withSuppressedMessages do
try
evalSuggest tac |>.run { terminal := true, root := tac, config, originalMaxHeartbeats }
catch _ =>
throwEvalAndSuggestFailed config
let s := (getSuggestions tac')[*...config.max].toArray
if s.isEmpty then
throwEvalAndSuggestFailed config
@@ -882,7 +896,10 @@ Note: We previously included `simp_all? +suggestions` here, but removed it due t
We would like to restore it in the future once `simp_all? +suggestions` is faster for general use.
-/
private def mkAtomicWithSuggestionsStx : CoreM (TSyntax `tactic) :=
`(tactic| grind? +suggestions)
`(tactic| first_par
| grind? +suggestions
| grind? +locals
| grind? +locals +suggestions)
/-- `simple` tactics -/
private def mkSimpleTacStx : CoreM (TSyntax `tactic) :=
@@ -947,8 +964,9 @@ private unsafe def mkTryEvalSuggestStxUnsafe (goal : MVarId) (info : Try.Info) :
let atomic `(tactic| attempt_all_par | $simple:tactic | $simp:tactic | $grind:tactic | simp_all)
let atomicSuggestions mkAtomicWithSuggestionsStx
let funInds mkAllFunIndStx info atomic
let inds mkAllIndStx info atomic
let atomicOrSuggestions `(tactic| first | $atomic:tactic | $atomicSuggestions:tactic)
let funInds mkAllFunIndStx info atomicOrSuggestions
let inds mkAllIndStx info atomicOrSuggestions
let extra `(tactic| (intros; first | $simple:tactic | $simp:tactic | exact?))
-- Collect user-defined suggestions (runs after built-in tactics)
@@ -985,13 +1003,12 @@ private def wrapSuggestionWithBy (sugg : Tactic.TryThis.Suggestion) : TacticM Ta
/-- Version of `evalAndSuggest` that wraps tactic suggestions with `by` for term mode. -/
private def evalAndSuggestWithBy (tk : Syntax) (tac : TSyntax `tactic) (originalMaxHeartbeats : Nat) (config : Try.Config) : TacticM Unit := do
let initialLog Core.getMessageLog
let tac' try
evalSuggest tac |>.run { terminal := true, root := tac, config, originalMaxHeartbeats }
catch _ =>
throwEvalAndSuggestFailed config
-- Restore message log to suppress "Try this" messages from intermediate tactic executions
Core.setMessageLog initialLog
-- Suppress "Try this" messages from intermediate tactic executions
let tac' withSuppressedMessages do
try
evalSuggest tac |>.run { terminal := true, root := tac, config, originalMaxHeartbeats }
catch _ =>
throwEvalAndSuggestFailed config
let suggestions := (getSuggestions tac')[*...config.max].toArray
if suggestions.isEmpty then
throwEvalAndSuggestFailed config

View File

@@ -1502,7 +1502,8 @@ def mkEmptyEnvironment (trustLevel : UInt32 := 0) : IO Environment := do
return {
base := .const {
const2ModIdx := {}
constants := {}
-- Make sure we return a sharing-friendly map set to stage 2, like in `finalizeImport`.
constants := SMap.empty.switch
header := { trustLevel }
extensions := exts
irBaseExts := exts

View File

@@ -9,7 +9,7 @@ prelude
public import Lean.CoreM
public import Lean.Meta.Basic
import Lean.Meta.Instances
import Lean.LibrarySuggestions.SymbolFrequency
import all Lean.LibrarySuggestions.SymbolFrequency
public import Lean.LibrarySuggestions.Basic
/-!
@@ -108,7 +108,7 @@ builtin_initialize sineQuaNonExt : PersistentEnvExtension (NameMap (List (Name
addImportedFn := fun mapss _ => pure mapss
addEntryFn := nofun
-- TODO: it would be nice to avoid the `toArray` here, e.g. via iterators.
exportEntriesFnEx := fun env _ _ => env.unsafeRunMetaM do return #[ prepareTriggers (env.constants.map₂.toArray.map (·.1))]
exportEntriesFnEx := fun env _ _ => unsafe env.unsafeRunMetaM do return #[ prepareTriggers (env.constants.map₂.toArray.map (·.1))]
statsFn := fun _ => "sine qua non premise selection extension"
}

View File

@@ -69,10 +69,10 @@ public def localSymbolFrequency (n : Name) : MetaM Nat := do
Helper function for running `MetaM` code during module export, when there is nothing but an `Environment` available.
Panics on errors.
-/
public def _root_.Lean.Environment.unsafeRunMetaM [Inhabited α] (env : Environment) (x : MetaM α) : α :=
match unsafe unsafeEIO ((((withoutExporting x).run' {} {}).run' { fileName := "symbolFrequency", fileMap := default } { env })) with
unsafe def _root_.Lean.Environment.unsafeRunMetaM [Inhabited α] (env : Environment) (x : MetaM α) : α :=
match unsafeEIO ((((withoutExporting x).run' {} {}).run' { fileName := "symbolFrequency", fileMap := default } { env })) with
| Except.ok a => a
| Except.error ex => panic! match unsafe unsafeIO ex.toMessageData.toString with
| Except.error ex => panic! match unsafeIO ex.toMessageData.toString with
| Except.ok s => s
| Except.error ex => ex.toString
@@ -90,7 +90,7 @@ builtin_initialize symbolFrequencyExt : PersistentEnvExtension (NameMap Nat) Emp
mkInitial := pure
addImportedFn := fun mapss _ => pure mapss
addEntryFn := nofun
exportEntriesFnEx := fun env _ _ => env.unsafeRunMetaM do return #[ cachedLocalSymbolFrequencyMap]
exportEntriesFnEx := fun env _ _ => unsafe env.unsafeRunMetaM do return #[ cachedLocalSymbolFrequencyMap]
statsFn := fun _ => "symbol frequency extension"
}

View File

@@ -59,3 +59,5 @@ public import Lean.Meta.Hint
public import Lean.Meta.MethodSpecs
public import Lean.Meta.CtorIdxHInj
public import Lean.Meta.Sym
public import Lean.Meta.MonadSimp
public import Lean.Meta.HaveTelescope

View File

@@ -1097,13 +1097,6 @@ Similar to `Expr.abstract`, but handles metavariables correctly.
def _root_.Lean.Expr.abstractM (e : Expr) (xs : Array Expr) : MetaM Expr :=
e.abstractRangeM xs.size xs
/--
Replace occurrences of the free variables `fvars` in `e` with `vs`.
Similar to `Expr.replaceFVars`, but handles metavariables correctly.
-/
def _root_.Lean.Expr.replaceFVarsM (e : Expr) (fvars : Array Expr) (vs : Array Expr) : MetaM Expr :=
return ( e.abstractM fvars).instantiateRev vs
/--
Collect forward dependencies for the free variables in `toRevert`.
Recall that when reverting free variables `xs`, we must also revert their forward dependencies.

View File

@@ -0,0 +1,461 @@
/-
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kyle Miller, Leonardo de Moura
-/
module
prelude
public import Lean.Meta.Basic
public import Lean.Meta.MonadSimp
import Lean.Util.CollectFVars
import Lean.Util.CollectLooseBVars
import Lean.Meta.InferType
import Lean.Meta.AppBuilder
public section
namespace Lean.Meta
/-!
Support for representing `HaveTelescope`s and simplifying them.
We use this module to implement both `Sym.simp` and `Meta.simp` using the `MonadSimp` adapter.
-/
/--
Information about a single `have` in a `have` telescope.
Created by `getHaveTelescopeInfo`.
-/
structure HaveInfo where
/-- Previous `have`s that the type of this `have` depends on, as indices into `HaveTelescopeInfo.haveInfo`.
Used in `computeFixedUsed` to compute used `have`s. -/
typeBackDeps : Std.HashSet Nat
/-- Previous `have`s that the value of this `have` depends on, as indices into `HaveTelescopeInfo.haveInfo`.
Used in `computeFixedUsed` to compute used `have`s. -/
valueBackDeps : Std.HashSet Nat
/-- The local decl for the `have`, so that the local context can be re-entered `have`-by-`have`.
N.B. This stores the fvarid for the `have` as well as cached versions of the value and type
instantiated with the fvars from the telescope. -/
decl : LocalDecl
/-- The level of the type for this `have`, cached. -/
level : Level
deriving Inhabited
/--
Information about a `have` telescope.
Created by `getHaveTelescopeInfo` and used in `simpHaveTelescope`.
The data is used to avoid applying `Expr.abstract` more than once on any given subexpression
when constructing terms and proofs during simplification. Abstraction is linear in the size `t` of a term,
and so in a depth-`n` telescope it is `O(nt)`, quadratic in `n`, since `n ≤ t`.
For example, given `have x₁ := v₁; ...; have xₙ := vₙ; b` and `h : b = b'`, we need to construct
```lean
have_body_congr (fun x₁ => ... have_body_congr (fun xₙ => h)))
```
We apply `Expr.abstract` to `h` *once* and then build the term, rather than
using `mkLambdaFVars #[fvarᵢ] pfᵢ` at each step.
As an additional optimization, we save the fvar-instantiated terms calculated by `getHaveTelescopeInfo`
so that we don't have to compute them again. This is only saving a constant factor.
It is also used for computing used `have`s in `computeFixedUsed`.
In `have x₁ := v; have x₂ := x₁; ⋯; have xₙ := xₙ₋₁; b`, if `xₙ` is unused in `b`, then all the
`have`s are unused. Without a global computation of used `have`s, the proof term would be quadratic
in the number of `have`s (with `n` iterations of `simp`). Knowing which `have`s are transitively
unused lets the proof term be linear in size.
-/
structure HaveTelescopeInfo where
/-- Information about each `have` in the telescope. -/
haveInfo : Array HaveInfo := #[]
/-- The set of `have`s that the body depends on, as indices into `haveInfo`.
Used in `computeFixedUsed` to compute used `have`s. -/
bodyDeps : Std.HashSet Nat := {}
/-- The set of `have`s that the type of the body depends on, as indices into `haveInfo`.
This is the set of fixed `have`s. -/
bodyTypeDeps : Std.HashSet Nat := {}
/-- A cached version of the telescope body, instantiated with fvars from each `HaveInfo.decl`. -/
body : Expr := Expr.const `_have_telescope_info_dummy_ []
/-- A cached version of the telescope body's type, instantiated with fvars from each `HaveInfo.decl`. -/
bodyType : Expr := Expr.const `_have_telescope_info_dummy_ []
/-- The universe level for the `have` expression, cached. -/
level : Level := Level.param `_have_telescope_info_dummy_
deriving Inhabited
/--
Efficiently collect dependency information for a `have` telescope.
This is part of a scheme to avoid quadratic overhead from the locally nameless discipline
(see `HaveTelescopeInfo` and `simpHaveTelescope`).
The expression `e` must not have loose bvars.
-/
def getHaveTelescopeInfo (e : Expr) : MetaM HaveTelescopeInfo := do
collect e 0 {} ( getLCtx) #[]
where
collect (e : Expr) (numHaves : Nat) (info : HaveTelescopeInfo) (lctx : LocalContext) (fvars : Array Expr) : MetaM HaveTelescopeInfo := do
/-
Gives indices into `fvars` that the uninstantiated expression `a` depends on, from collecting its bvars.
This is more efficient than collecting `fvars` from an instantiated expression,
since the expression likely contains many fvars for the pre-existing local context.
-/
let getBackDeps (a : Expr) : Std.HashSet Nat :=
a.collectLooseBVars.fold (init := {}) fun deps vidx =>
-- Since the original expression has no bvars, `vidx < numHaves` must be true.
deps.insert (numHaves - vidx - 1)
match e with
| .letE n t v b true =>
let typeBackDeps := getBackDeps t
let valueBackDeps := getBackDeps v
let t := t.instantiateRev fvars
let v := v.instantiateRev fvars
let level withLCtx' lctx <| getLevel t
let fvarId mkFreshFVarId
let decl := LocalDecl.ldecl 0 fvarId n t v true .default
let info := { info with haveInfo := info.haveInfo.push { typeBackDeps, valueBackDeps, decl, level } }
let lctx := lctx.addDecl decl
let fvars := fvars.push (mkFVar fvarId)
collect b (numHaves + 1) info lctx fvars
| _ =>
let bodyDeps := getBackDeps e
withLCtx' lctx do
let body := e.instantiateRev fvars
let bodyType inferType body
let level getLevel bodyType
-- Collect fvars appearing in the type of `e`. Computing `bodyType` in particular is where `MetaM` is necessary.
let bodyTypeFVarIds := (collectFVars {} bodyType).fvarSet
let bodyTypeDeps : Std.HashSet Nat := Nat.fold fvars.size (init := {}) fun idx _ deps =>
if bodyTypeFVarIds.contains fvars[idx].fvarId! then
deps.insert idx
else
deps
return { info with bodyDeps, bodyTypeDeps, body, bodyType, level }
/--
Computes which `have`s in the telescope are fixed and which are unused.
The length of the unused array may be less than the number of `have`s: use `unused.getD i true`.
-/
def HaveTelescopeInfo.computeFixedUsed (info : HaveTelescopeInfo) (keepUnused : Bool) :
MetaM (Array Bool × Array Bool) := do
let fixed go info.bodyTypeDeps
if keepUnused then
return (fixed, #[])
else
let used go info.bodyDeps
return (fixed, used)
where
updateArrayFromBackDeps (arr : Array Bool) (s : Std.HashSet Nat) : Array Bool :=
s.fold (init := arr) fun arr idx => arr.set! idx true
go init : MetaM (Array Bool) := do
let numHaves := info.haveInfo.size
let mut used : Array Bool := Array.replicate numHaves false
-- Initialize `used` with the body's dependencies.
-- There is no need to consider `info.bodyTypeDeps` in this computation.
used := updateArrayFromBackDeps used init
-- For each used `have`, in reverse order, update `used`.
for i in *...numHaves do
let idx := numHaves - i - 1
if used[idx]! then
let hinfo := info.haveInfo[idx]!
used := updateArrayFromBackDeps used hinfo.typeBackDeps
used := updateArrayFromBackDeps used hinfo.valueBackDeps
return used
/--
Auxiliary structure used to represent the return value of `simpHaveTelescopeAux`.
See `simpHaveTelescope` for an overview and `HaveTelescopeInfo` for an example.
-/
private structure SimpHaveResult where
/--
The simplified expression in `(fun x => b) v` form. Note that it may contain loose bound variables.
`simpHaveTelescope` attempts to minimize the quadratic overhead imposed
by the locally nameless discipline in a sequence of `have` expressions.
-/
expr : Expr
/--
The type of `expr`. It may contain loose bound variables like in the `expr` field.
Used in proof construction.
-/
exprType : Expr
/--
The initial expression in `(fun x => b) v` form.
-/
exprInit : Expr
/--
The expression `expr` in `have x := v; b` form.
-/
exprResult : Expr
/--
The proof that the simplified expression is equal to the input one.
It may contain loose bound variables like in the `expr` field.
-/
proof : Expr
/--
`modified := false` iff `expr` is structurally equal to the input expression.
When false, `proof` is `Eq.refl`.
-/
modified : Bool
/-
Re-enters the telescope recorded in `info` using `withExistingLocalDecls` while simplifying according to `fixed`/`used`.
Note that we must use the low-level `Expr` APIs because the expressions may contain loose bound variables.
Note also that we don't enter the body's local context all at once, since we need to be sure that
when we simplify values they have their correct local context.
-/
deriving Inhabited
variable {m} [Monad m] [MonadLiftT MetaM m] [MonadControlT MetaM m] [MonadSimp m]
/-
Re-enters the telescope recorded in `info` using `withExistingLocalDecls` while simplifying according to `fixed`/`used`.
Note that we must use the low-level `Expr` APIs because the expressions may contain loose bound variables.
Note also that we don't enter the body's local context all at once, since we need to be sure that
when we simplify values they have their correct local context.
-/
private def simpHaveTelescopeAux (info : HaveTelescopeInfo) (fixed : Array Bool) (used : Array Bool)
(e : Expr) (i : Nat) (xs : Array Expr) : m SimpHaveResult := do
if h : i < info.haveInfo.size then
let hinfo := info.haveInfo[i]
-- `x` and `val` are the fvar and value with respect to the local context.
let x := hinfo.decl.toExpr
let val := hinfo.decl.value true
-- Invariant: `v == val.abstract xs`.
let .letE n t v b true := e | unreachable!
-- Universe levels to use for each of the `have_*` theorems. It's the level of `val` and the level of the body.
let us := [hinfo.level, info.level]
if !used.getD i true then
/-
Unused `have`: do not add `x` to the local context then `simp` only the body.
-/
(do trace[Debug.Meta.Tactic.simp] "have telescope; unused {n} := {val}" : MetaM _)
/-
Complication: Unused `have`s might only be transitively unused.
This means that `b.hasLooseBVar 0` might actually be true.
This matters because `t` and `v` appear in the proof term.
We use a dummy `x` for debugging purposes. (Recall that `Expr.abstract` skips non-fvar/mvars.)
-/
let x := mkConst `_simp_let_unused_dummy
let rb simpHaveTelescopeAux info fixed used b (i + 1) (xs.push x)
-- TODO(kmill): This is a source of quadratic complexity. It might be possible to avoid this,
-- but we will need to carefully re-review the logic (note that `rb.proof` still refers to `x`).
let expr := rb.expr.lowerLooseBVars 1 1
let exprType := rb.exprType.lowerLooseBVars 1 1
let exprInit := mkApp (mkLambda n .default t rb.exprInit) v
let exprResult := rb.exprResult.lowerLooseBVars 1 1
if rb.modified then
let proof := mkApp6 (mkConst ``have_unused_dep' us) t exprType v (mkLambda n .default t rb.exprInit) expr
(mkLambda n .default t rb.proof)
return { expr, exprType, exprInit, exprResult, proof, modified := true }
else
-- If not modified, this must have been a non-transitively unused `have`, so no need for `dep` form.
let proof := mkApp6 (mkConst ``have_unused' us) t exprType v expr expr
(mkApp2 (mkConst ``Eq.refl [info.level]) exprType expr)
return { expr, exprType, exprInit, exprResult, proof, modified := true }
else if fixed.getD i true then
/-
Fixed `have` (like `CongrArgKind.fixed`): dsimp the value and simp the body.
The variable appears in the type of the body.
-/
let val' MonadSimp.dsimp val
(do trace[Debug.Meta.Tactic.simp] "have telescope; fixed {n} := {val} => {val'}" : MetaM _)
let vModified := val != val'
let v' := if vModified then val'.abstract xs else v
withExistingLocalDecls [hinfo.decl] <| MonadSimp.withNewLemmas #[x] do
let rb simpHaveTelescopeAux info fixed used b (i + 1) (xs.push x)
let expr := mkApp (mkLambda n .default t rb.expr) v'
let exprType := mkApp (mkLambda n .default t rb.exprType) v'
let exprInit := mkApp (mkLambda n .default t rb.exprInit) v
let exprResult := mkHave n t v' rb.exprResult
-- Note: if `vModified`, then the kernel will reduce the telescopes and potentially do an expensive defeq check.
-- If this is a performance issue, we could try using a `letFun` encoding here make use of the `is_def_eq_args` optimization.
-- Namely, to guide the defeqs via the sequence
-- `(fun x => b) v` = `letFun (fun x => b) v` = `letFun (fun x => b) v'` = `(fun x => b) v'`
if rb.modified then
let proof := mkApp6 (mkConst ``have_body_congr_dep' us) t (mkLambda n .default t rb.exprType) v'
(mkLambda n .default t rb.exprInit) (mkLambda n .default t rb.expr) (mkLambda n .default t rb.proof)
return { expr, exprType, exprInit, exprResult, proof, modified := true }
else
let proof := mkApp2 (mkConst ``Eq.refl [info.level]) exprType expr
return { expr, exprType, exprInit, exprResult, proof, modified := vModified }
else
/-
Non-fixed `have` (like `CongrArgKind.eq`): simp both the value and the body.
The variable does not appear in the type of the body.
-/
let (v', valModified, vproof) match ( MonadSimp.simp val) with
| .rfl => pure (v, false, mkApp2 (mkConst `Eq.refl [hinfo.level]) t v)
| .step val' h =>
(do trace[Debug.Meta.Tactic.simp] "have telescope; non-fixed {n} := {val} => {val'}" : MetaM _)
pure (val'.abstract xs, true, h.abstract xs)
withExistingLocalDecls [hinfo.decl] <| MonadSimp.withNewLemmas #[x] do
let rb simpHaveTelescopeAux info fixed used b (i + 1) (xs.push x)
let expr := mkApp (mkLambda n .default t rb.expr) v'
assert! !rb.exprType.hasLooseBVar 0
let exprType := rb.exprType.lowerLooseBVars 1 1
let exprInit := mkApp (mkLambda n .default t rb.exprInit) v
let exprResult := mkHave n t v' rb.exprResult
match valModified, rb.modified with
| false, false =>
let proof := mkApp2 (mkConst `Eq.refl [info.level]) exprType expr
return { expr, exprType, exprInit, exprResult, proof, modified := false }
| true, false =>
let proof := mkApp6 (mkConst ``have_val_congr' us) t exprType v v'
(mkLambda n .default t rb.exprInit) vproof
return { expr, exprType, exprInit, exprResult, proof, modified := true }
| false, true =>
let proof := mkApp6 (mkConst ``have_body_congr' us) t exprType v
(mkLambda n .default t rb.exprInit) (mkLambda n .default t rb.expr) (mkLambda n .default t rb.proof)
return { expr, exprType, exprInit, exprResult, proof, modified := true }
| true, true =>
let proof := mkApp8 (mkConst ``have_congr' us) t exprType v v'
(mkLambda n .default t rb.exprInit) (mkLambda n .default t rb.expr) vproof (mkLambda n .default t rb.proof)
return { expr, exprType, exprInit, exprResult, proof, modified := true }
else
-- Base case: simplify the body.
(do trace[Debug.Meta.Tactic.simp] "have telescope; simplifying body {info.body}" : MetaM _)
let exprType := info.bodyType.abstract xs
match ( MonadSimp.simp info.body) with
| .rfl =>
let proof := mkApp2 (mkConst `Eq.refl [info.level]) exprType e
return { expr := e, exprType, exprInit := e, exprResult := e, proof, modified := false }
| .step body' h =>
let expr := body'.abstract xs
let proof := h.abstract xs
-- Optimization: if the proof is a `simpHaveTelescope` proof, then remove the type hint
-- to avoid the zeta/beta reductions that the kernel would otherwise do.
-- In `SimpHaveResult.toResult` we insert two type hints; the inner one
-- encodes the `exprInit` and `expr`.
let detectSimpHaveLemma (proof : Expr) : Option SimpHaveResult := do
let_expr id eq proof' := proof | failure
guard <| eq.isAppOfArity ``Eq 3
let_expr id eq' proof'' := proof' | failure
let_expr Eq _ lhs rhs := eq' | failure
let .const n _ := proof''.getAppFn | failure
guard (n matches ``have_unused_dep' | ``have_unused' | ``have_body_congr_dep' | ``have_val_congr' | ``have_body_congr' | ``have_congr')
return { expr := rhs, exprType, exprInit := lhs, exprResult := expr, proof := proof'', modified := true }
if let some res := detectSimpHaveLemma proof then
return res
return { expr, exprType, exprInit := e, exprResult := expr, proof, modified := true }
/-- Configuration for specifying how unused let-declarations are eliminated. -/
inductive ZetaUnusedMode where
| /-- Do not eliminate unused `let`-declarations. -/
no
| /-- Simplify and eliminate unused `let`-declarations in a single pass. -/
singlePass
| /-- Simplify and then eliminate unused `let`-declarations. -/
twoPasses
/-- Remove unused-let declarations. -/
def zetaUnused (e : Expr) : MetaM Expr := do
letTelescope e fun xs body => do
let mut s := collectFVars {} body
let mut ys := #[]
let mut i := xs.size
while i > 0 do
i := i - 1
let x := xs[i]!
let xFVarId := x.fvarId!
if s.fvarSet.contains xFVarId then
let decl xFVarId.getDecl
s := collectFVars s decl.type
s := collectFVars s (decl.value (allowNondep := true))
ys := ys.push x
if ys.size == xs.size then
return e
else
mkLetFVars (generalizeNondepLet := false) ys.reverse body
/--
Constructs the final result. If `keepUnused` is `false`, it eliminates unused let-declarations from
`exprResult` using `zetaUnused` above. This flag is used when we set `ZetaUnusedMode.twoPasses`.
-/
private def SimpHaveResult.toResult (u : Level) (source : Expr) (result : SimpHaveResult) (keepUnused : Bool) : MetaM MonadSimp.Result := do
match result with
| { expr, exprType, exprInit, exprResult, proof, modified, .. } =>
if modified then
let proof :=
-- Add a type hint to convert back into `have` form.
mkExpectedPropHint (expectedProp := mkApp3 (mkConst ``Eq [u]) exprType source exprResult) <|
-- Add in a second type hint, for use in an optimization to avoid zeta/beta reductions in the kernel
-- The base case in `simpHaveTelescopeAux` detects this construction and uses `exprType`/`exprInit`
-- to construct the `SimpHaveResult`.
-- If the kernel were to support `have` forms of the congruence lemmas this would not be necessary.
mkExpectedPropHint (expectedProp := mkApp3 (mkConst ``Eq [u]) exprType exprInit expr) proof
if keepUnused then
return .step exprResult proof
else
let exprResult' zetaUnused exprResult
if exprResult' == exprResult then
return .step exprResult proof
else
let proof := mkApp6 (mkConst ``Eq.trans [u]) exprType source exprResult exprResult' proof
(mkApp2 (mkConst ``Eq.refl [u]) exprType exprResult')
return .step exprResult' proof
else if keepUnused then
return .rfl
else
let exprResult zetaUnused source
if exprResult == source then
return .rfl
else
let proof := mkApp2 (mkConst ``Eq.refl [u]) exprType exprResult
return .step exprResult proof
/--
Routine for simplifying `have` telescopes. Used by `simpLet`.
This is optimized to be able to handle deep `have` telescopes while avoiding quadratic complexity
arising from the locally nameless expression representations.
## Overview
Consider a `have` telescope:
```
have x₁ : T₁ := v₁; ...; have xₙ : Tₙ := vₙ; b
```
We say `xᵢ` is *fixed* if it appears in the type of `b`.
If `xᵢ` is fixed, then it can only be rewritten using definitional equalities.
Otherwise, we can freely rewrite the value using a propositional equality `vᵢ = vᵢ'`.
The body `b` can always be freely rewritten using a propositional equality `b = b'`.
(All the mentioned equalities must be type correct with respect to the obvious local contexts.)
If `xᵢ` neither appears in `b` nor any `Tⱼ` nor any `vⱼ`, then its `have` is *unused*.
Unused `have`s can be eliminated, which we do if `cfg.zetaUnused` is true.
We clear `have`s from the end, to be able to transitively clear chains of unused `have`s.
This is why we honor `zetaUnused` here even though `reduceStep` is also responsible for it;
`reduceStep` can only eliminate unused `have`s at the start of a telescope.
Eliminating all transitively unused `have`s at once like this also avoids quadratic complexity.
If `debug.simp.check.have` is enabled then we typecheck the resulting expression and proof.
## Proof generation
There are two main complications with generating proofs.
1. We work almost exclusively with expressions with loose bound variables,
to avoid overhead from instantiating and abstracting free variables,
which can lead to complexity quadratic in the telescope length.
(See `HaveTelescopeInfo`.)
2. We want to avoid triggering zeta reductions in the kernel.
Regarding this second point, the issue is that we cannot organize the proof using congruence theorems
in the obvious way. For example, given
```
theorem have_congr {α : Sort u} {β : Sort v} {a a' : α} {f f' : α → β}
(h₁ : a = a') (h₂ : ∀ x, f x = f' x) :
(have x := a; f x) = (have x := a'; f' x)
```
the kernel sees that the type of `have_congr (fun x => b) (fun x => b') h₁ h₂`
is `(have x := a; (fun x => b) x) = (have x := a'; (fun x => b') x)`,
since when instantiating values it does not beta reduce at the same time.
Hence, when `is_def_eq` is applied to the LHS and `have x := a; b` for example,
it will do `whnf_core` to both sides.
Instead, we use the form `(fun x => b) a = (fun x => b') a'` in the proofs,
which we can reliably match up without triggering beta reductions in the kernel.
The zeta/beta reductions are then limited to the type hint for the entire telescope,
when we convert this back into `have` form.
In the base case, we include an optimization to avoid unnecessary zeta/beta reductions,
by detecting a `simpHaveTelescope` proofs and removing the type hint.
-/
def simpHaveTelescope (e : Expr) (zetaUnusedMode : ZetaUnusedMode := .twoPasses) : m MonadSimp.Result := do
let info getHaveTelescopeInfo e
assert! !info.haveInfo.isEmpty
let (fixed, used) info.computeFixedUsed (keepUnused := zetaUnusedMode matches .no | .twoPasses)
let r simpHaveTelescopeAux info fixed used e 0 (Array.mkEmpty info.haveInfo.size)
r.toResult info.level e (keepUnused := zetaUnusedMode matches .no | .singlePass)
end Lean.Meta

View File

@@ -0,0 +1,25 @@
/-
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.Expr
public section
namespace Lean.Meta
/-!
Abstract simplifier API
-/
inductive MonadSimp.Result where
| rfl
| step (e : Expr) (h : Expr)
deriving Inhabited
class MonadSimp (m : Type Type) where
withNewLemmas (xs : Array Expr) (k : m α) : m α
dsimp : Expr m Expr
simp : Expr m MonadSimp.Result
end Lean.Meta

View File

@@ -144,4 +144,37 @@ def mkHaveS (x : Name) (t : Expr) (v : Expr) (b : Expr) : m Expr := do
assertShared b
share1 <| .letE x t v b true
@[inline] def _root_.Lean.Expr.updateAppS! (e : Expr) (newFn : Expr) (newArg : Expr) : m Expr := do
let .app fn arg := e | panic! "application expected"
if isSameExpr fn newFn && isSameExpr arg newArg then return e else mkAppS newFn newArg
@[inline] def _root_.Lean.Expr.updateMDataS! (e : Expr) (newExpr : Expr) : m Expr := do
let .mdata d a := e | panic! "mdata expected"
if isSameExpr a newExpr then return e else mkMDataS d newExpr
@[inline] def _root_.Lean.Expr.updateProjS! (e : Expr) (newExpr : Expr) : m Expr := do
let .proj s i a := e | panic! "proj expected"
if isSameExpr a newExpr then return e else mkProjS s i newExpr
@[inline] def _root_.Lean.Expr.updateForallS! (e : Expr) (newDomain : Expr) (newBody : Expr) : m Expr := do
let .forallE n d b bi := e | panic! "forall expected"
if isSameExpr d newDomain && isSameExpr b newBody then
return e
else
mkForallS n bi newDomain newBody
@[inline] def _root_.Lean.Expr.updateLambdaS! (e : Expr) (newDomain : Expr) (newBody : Expr) : m Expr := do
let .lam n d b bi := e | panic! "lambda expected"
if isSameExpr d newDomain && isSameExpr b newBody then
return e
else
mkLambdaS n bi newDomain newBody
@[inline] def _root_.Lean.Expr.updateLetS! (e : Expr) (newType : Expr) (newVal : Expr) (newBody : Expr) : m Expr := do
let .letE n t v b nondep := e | panic! "let expression expected"
if isSameExpr t newType && isSameExpr v newVal && isSameExpr b newBody then
return e
else
mkLetS n newType newVal newBody nondep
end Lean.Meta.Sym.Internal

View File

@@ -165,34 +165,39 @@ where
if let some r := ( get)[key]? then
return r
else
save key ( mkAppS ( visitAppDefault f offset) ( visitChild a offset))
save key ( e.updateAppS! ( visitAppDefault f offset) ( visitChild a offset))
| e => visitChild e offset
visitAppBeta (f : Expr) (argsRev : Array Expr) (offset : Nat) : M Expr := do
visitAppBeta (e : Expr) (f : Expr) (argsRev : Array Expr) (offset : Nat) (modified : Bool) : M Expr := do
match f with
| .app f a => visitAppBeta f (argsRev.push ( visitChild a offset)) offset
| .app f a =>
let a' visitChild a offset
visitAppBeta e f (argsRev.push a') offset (modified || !isSameExpr a a')
| .bvar bidx =>
let f visitBVar f bidx offset
betaRevS f argsRev
let f' visitBVar f bidx offset
if modified || !isSameExpr f f' then
betaRevS f' argsRev
else
return e
| _ => unreachable!
visitApp (f : Expr) (arg : Expr) (offset : Nat) : M Expr := do
let arg visitChild arg offset
visitApp (e : Expr) (f : Expr) (arg : Expr) (offset : Nat) (_ : e = .app f arg) : M Expr := do
let arg' visitChild arg offset
if f.getAppFn.isBVar then
visitAppBeta f #[arg] offset
visitAppBeta e f #[arg'] offset (!isSameExpr arg arg')
else
mkAppS ( visitAppDefault f offset) arg
e.updateAppS! ( visitAppDefault f offset) arg'
visit (e : Expr) (offset : Nat) : M Expr := do
match e with
match h : e with
| .lit _ | .mvar _ | .fvar _ | .const _ _ | .sort _ => unreachable!
| .bvar bidx => visitBVar e bidx offset
| .app f a => visitApp f a offset
| .mdata m a => mkMDataS m ( visitChild a offset)
| .proj s i a => mkProjS s i ( visitChild a offset)
| .forallE n d b bi => mkForallS n bi ( visitChild d offset) ( visitChild b (offset+1))
| .lam n d b bi => mkLambdaS n bi ( visitChild d offset) ( visitChild b (offset+1))
| .letE n t v b d => mkLetS n ( visitChild t offset) ( visitChild v offset) ( visitChild b (offset+1)) (nondep := d)
| .app f a => visitApp e f a offset h
| .mdata _ a => e.updateMDataS! ( visitChild a offset)
| .proj _ _ a => e.updateProjS! ( visitChild a offset)
| .forallE _ d b _ => e.updateForallS! ( visitChild d offset) ( visitChild b (offset+1))
| .lam _ d b _ => e.updateLambdaS! ( visitChild d offset) ( visitChild b (offset+1))
| .letE _ t v b _ => e.updateLetS! ( visitChild t offset) ( visitChild v offset) ( visitChild b (offset+1))
/--
Similar to `instantiateRevS`, but beta-reduces nested applications whose function becomes a lambda

View File

@@ -33,12 +33,12 @@ mutual
@[specialize] def visit (e : Expr) (offset : Nat) (fn : Expr Nat AlphaShareBuilderM (Option Expr)) : M Expr := do
match e with
| .lit _ | .mvar _ | .bvar _ | .fvar _ | .const _ _ | .sort _ => unreachable!
| .app f a => mkAppS ( visitChild f offset fn) ( visitChild a offset fn)
| .mdata m a => mkMDataS m ( visitChild a offset fn)
| .proj s i a => mkProjS s i ( visitChild a offset fn)
| .forallE n d b bi => mkForallS n bi ( visitChild d offset fn) ( visitChild b (offset+1) fn)
| .lam n d b bi => mkLambdaS n bi ( visitChild d offset fn) ( visitChild b (offset+1) fn)
| .letE n t v b d => mkLetS n ( visitChild t offset fn) ( visitChild v offset fn) ( visitChild b (offset+1) fn) (nondep := d)
| .app f a => e.updateAppS! ( visitChild f offset fn) ( visitChild a offset fn)
| .mdata _ a => e.updateMDataS! ( visitChild a offset fn)
| .proj _ _ a => e.updateProjS! ( visitChild a offset fn)
| .forallE _ d b _ => e.updateForallS! ( visitChild d offset fn) ( visitChild b (offset+1) fn)
| .lam _ d b _ => e.updateLambdaS! ( visitChild d offset fn) ( visitChild b (offset+1) fn)
| .letE _ t v b _ => e.updateLetS! ( visitChild t offset fn) ( visitChild v offset fn) ( visitChild b (offset+1) fn)
end
/--

View File

@@ -7,7 +7,10 @@ module
prelude
public import Lean.Meta.Basic
import Lean.Meta.InferType
import Lean.Meta.Closure
import Lean.Meta.AppBuilder
namespace Lean.Meta.Sym.Simp
/--
Given `xs` containing free variables
`(x₁ : α₁) (x₂ : α₂[x₁]) ... (xₙ : αₙ[x₁, ..., x_{n-1}])`
@@ -24,31 +27,67 @@ This auxiliary theorem is used by the simplifier when visiting lambda expression
public def mkFunextFor (xs : Array Expr) (β : Expr) : MetaM Expr := do
let type mkForallFVars xs β
let v getLevel β
let w getLevel type
withLocalDeclD `f type fun f =>
withLocalDeclD `g type fun g => do
let lhs := mkAppN f xs
let rhs := mkAppN g xs
let p := mkApp3 (mkConst ``Eq [v]) β lhs rhs
let p mkForallFVars xs p
withLocalDeclD `h p fun h => do
let mut result := mkAppN h xs |>.abstract xs
let mut i := xs.size
let mut β := β.abstract xs
let mut v := v
let mut f := mkAppN f xs |>.abstract xs
let mut g := mkAppN g xs |>.abstract xs
while i > 0 do
i := i - 1
let x := xs[i]!
let α_i inferType x
let u_i getLevel α_i
let α_i := α_i.abstractRange i xs
f := f.appFn!.lowerLooseBVars 1 1
g := g.appFn!.lowerLooseBVars 1 1
result := mkLambda `x default α_i result
result := mkApp5 (mkConst ``funext [u_i, v]) α_i (mkLambda `x .default α_i β) f g result
β := mkForall `x .default α_i β
v := mkLevelIMax' u_i v
mkLambdaFVars #[f, g, h] result
let eq := mkApp3 (mkConst ``Eq [v]) β (mkAppN f xs) (mkAppN g xs)
withLocalDeclD `h ( mkForallFVars xs eq) fun h => do
let eqv mkLambdaFVars #[f, g] ( mkForallFVars xs eq)
let quotEqv := mkApp2 (mkConst ``Quot [w]) type eqv
withLocalDeclD `f' quotEqv fun f' => do
let lift := mkApp6 (mkConst ``Quot.lift [w, v]) type eqv β
(mkLambda `f .default type (mkAppN (.bvar 0) xs))
(mkLambda `f .default type (mkLambda `g .default type (mkLambda `h .default (mkApp2 eqv (.bvar 1) (.bvar 0)) (mkAppN (.bvar 0) xs))))
f'
let extfunAppVal mkLambdaFVars (#[f'] ++ xs) lift
let extfunApp := extfunAppVal
let quotSound := mkApp5 (mkConst ``Quot.sound [w]) type eqv f g h
let Quot_mk_f := mkApp3 (mkConst ``Quot.mk [w]) type eqv f
let Quot_mk_g := mkApp3 (mkConst ``Quot.mk [w]) type eqv g
let result := mkApp6 (mkConst ``congrArg [w, w]) quotEqv type Quot_mk_f Quot_mk_g extfunApp quotSound
let result mkLambdaFVars #[f, g, h] result
return result
/--
Given `xs` containing free variables
`(x₁ : α₁) (x₂ : α₂[x₁]) ... (xₙ : αₙ[x₁, ..., x_{n-1}])`,
creates the custom forall congruence theorem
```
∀ (p q : (x₁ : α₁) → (x₂ : α₂[x₁]) → ... → (xₙ : αₙ[x₁, ..., x_{n-1}]) → Prop)
(h : ∀ x₁ ... xₙ, p x₁ ... xₙ = q x₁ ... xₙ),
(∀ x₁ ... xₙ, p x₁ ... xₙ) = (∀ x₁ ... xₙ, q x₁ ... xₙ)
```
The theorem has three arguments `p`, `q`, and `h`.
This auxiliary theorem is used by the simplifier when visiting forall expressions.
The proof uses the approach used in `mkFunextFor` followed by an `Eq.ndrec`.
-/
public def mkForallCongrFor (xs : Array Expr) : MetaM Expr := do
let prop := mkSort 0
let type mkForallFVars xs prop
let w getLevel type
withLocalDeclD `p type fun p =>
withLocalDeclD `q type fun q => do
let eq := mkApp3 (mkConst ``Eq [1]) prop (mkAppN p xs) (mkAppN q xs)
withLocalDeclD `h ( mkForallFVars xs eq) fun h => do
let eqv mkLambdaFVars #[p, q] ( mkForallFVars xs eq)
let quotEqv := mkApp2 (mkConst ``Quot [w]) type eqv
withLocalDeclD `p' quotEqv fun p' => do
let lift := mkApp6 (mkConst ``Quot.lift [w, 1]) type eqv prop
(mkLambda `p .default type (mkAppN (.bvar 0) xs))
(mkLambda `p .default type (mkLambda `q .default type (mkLambda `h .default (mkApp2 eqv (.bvar 1) (.bvar 0)) (mkAppN (.bvar 0) xs))))
p'
let extfunAppVal mkLambdaFVars (#[p'] ++ xs) lift
let extfunApp := extfunAppVal
let quotSound := mkApp5 (mkConst ``Quot.sound [w]) type eqv p q h
let Quot_mk_p := mkApp3 (mkConst ``Quot.mk [w]) type eqv p
let Quot_mk_q := mkApp3 (mkConst ``Quot.mk [w]) type eqv q
let p_eq_q := mkApp6 (mkConst ``congrArg [w, w]) quotEqv type Quot_mk_p Quot_mk_q extfunApp quotSound
let lhs mkForallFVars xs (mkAppN p xs)
let rhs mkForallFVars xs (mkAppN q xs)
let motive mkLambdaFVars #[q] (mkApp3 (mkConst ``Eq [1]) prop lhs rhs)
let rfl := mkApp2 (mkConst ``Eq.refl [1]) prop lhs
let result := mkApp6 (mkConst ``Eq.ndrec [0, w]) type p motive rfl q p_eq_q
let result mkLambdaFVars #[p, q, h] result
return result
end Lean.Meta.Sym.Simp

View File

@@ -6,6 +6,8 @@ Authors: Leonardo de Moura
module
prelude
public import Lean.Meta.Sym.Simp.SimpM
import Lean.Meta.MonadSimp
import Lean.Meta.HaveTelescope
import Lean.Meta.Sym.AlphaShareBuilder
import Lean.Meta.Sym.InferType
import Lean.Meta.Sym.Simp.Result
@@ -15,14 +17,19 @@ import Lean.Meta.Sym.Simp.Funext
namespace Lean.Meta.Sym.Simp
open Internal
instance : MonadSimp SimpM where
dsimp e := return e
withNewLemmas _ k := k
simp e := do match ( simp ( share e)) with
| .rfl _ => return .rfl
| .step e' h _ => return .step e' h
def simpLambda (e : Expr) : SimpM Result := do
-- **TODO**: Add free variable reuse
lambdaTelescope e fun xs b => do
match ( simp b) with
| .rfl _ => return .rfl
| .step b' h _ =>
let h mkLambdaFVars xs h
-- **TODO**: Add `mkLambdaFVarsS`?
let e' shareCommonInc ( mkLambdaFVars xs b')
let funext getFunext xs b
return .step e' (mkApp3 funext e e' h)
@@ -37,13 +44,61 @@ where
modify fun s => { s with funext := s.funext.insert { expr := key } h }
return h
def simpForall (_ : Expr) : SimpM Result := do
-- **TODO**
return .rfl
def simpArrow (e : Expr) : SimpM Result := do
let p := e.bindingDomain!
let q := e.bindingBody!
match ( simp p), ( simp q) with
| .rfl _, .rfl _ =>
return .rfl
| .step p' h _, .rfl _ =>
let u getLevel p
let v getLevel q
let e' e.updateForallS! p' q
return .step e' <| mkApp4 (mkConst ``implies_congr_left [u, v]) p p' q h
| .rfl _, .step q' h _ =>
let u getLevel p
let v getLevel q
let e' e.updateForallS! p q'
return .step e' <| mkApp4 (mkConst ``implies_congr_right [u, v]) p q q' h
| .step p' h₁ _, .step q' h₂ _ =>
let u getLevel p
let v getLevel q
let e' e.updateForallS! p' q'
return .step e' <| mkApp6 (mkConst ``implies_congr [u, v]) p p' q q' h₁ h₂
def simpLet (_ : Expr) : SimpM Result := do
-- **TODO**
return .rfl
def simpForall (e : Expr) : SimpM Result := do
if e.isArrow then
simpArrow e
else if ( isProp e) then
let n := getForallTelescopeSize e.bindingBody! 1
forallBoundedTelescope e n fun xs b => do
match ( simp b) with
| .rfl _ => return .rfl
| .step b' h _ =>
let h mkLambdaFVars xs h
let e' shareCommonInc ( mkForallFVars xs b')
-- **Note**: consider caching the forall-congr theorems
let hcongr mkForallCongrFor xs
return .step e' (mkApp3 hcongr ( mkLambdaFVars xs b) ( mkLambdaFVars xs b') h)
else
return .rfl
where
-- **Note**: Optimize if this is quadratic in practice
getForallTelescopeSize (e : Expr) (n : Nat) : Nat :=
match e with
| .forallE _ _ b _ => if b.hasLooseBVar 0 then getForallTelescopeSize b (n+1) else n
| _ => n
def simpLet (e : Expr) : SimpM Result := do
if !e.letNondep! then
/-
**Note**: We don't do anything if it is a dependent `let`.
Users may decide to `zeta`-expand them or apply `letToHave` at `pre`/`post`.
-/
return .rfl
else match ( Meta.simpHaveTelescope e) with
| .rfl => return .rfl
| .step e' h => return .step ( shareCommon e') h
def simpApp (e : Expr) : SimpM Result := do
congrArgs e

View File

@@ -51,13 +51,6 @@ def MVarId.congr? (mvarId : MVarId) : MetaM (Option (List MVarId)) :=
let some congrThm mkCongrSimp? lhs.getAppFn (subsingletonInstImplicitRhs := false) (maxArgs? := lhs.getAppNumArgs) | return none
applyCongrThm? mvarId congrThm
/--
Try to apply a `simp` congruence theorem and throw an error if it fails.
-/
def MVarId.congr (mvarId : MVarId) : MetaM (List MVarId) := do
let some mvarIds mvarId.congr? | throwError "Failed to apply `simp` congruence theorem"
return mvarIds
/--
Try to apply a `hcongr` congruence theorem, and then tries to close resulting goals
using `Eq.refl`, `HEq.refl`, and assumption.

View File

@@ -101,5 +101,7 @@ builtin_initialize registerTraceClass `grind.debug.proveEq
builtin_initialize registerTraceClass `grind.debug.pushNewFact
builtin_initialize registerTraceClass `grind.debug.appMap
builtin_initialize registerTraceClass `grind.debug.ext
builtin_initialize registerTraceClass `grind.debug.suggestions
builtin_initialize registerTraceClass `grind.debug.locals
end Lean

View File

@@ -13,6 +13,7 @@ public import Lean.Util.Heartbeats
import Init.Grind.Util
import Init.Try
import Lean.Elab.Tactic.Basic
import Lean.Linter.Deprecated
public section
@@ -83,9 +84,8 @@ def tryDischarger (mvarId : MVarId) : MetaM (Option (List MVarId)) := do
let tacStx `(tactic| try?)
let remainingGoals Elab.Term.TermElabM.run' <| Elab.Tactic.run subgoal do
-- Suppress info messages from try?
let initialLog Core.getMessageLog
Elab.Tactic.evalTactic tacStx
Core.setMessageLog initialLog
Elab.Tactic.withSuppressedMessages do
Elab.Tactic.evalTactic tacStx
if remainingGoals.isEmpty then
return some []
else
@@ -138,7 +138,9 @@ to find candidate lemmas.
open LazyDiscrTree (InitEntry findMatches)
private def addImport (name : Name) (constInfo : ConstantInfo) :
MetaM (Array (InitEntry (Name × DeclMod))) :=
MetaM (Array (InitEntry (Name × DeclMod))) := do
-- Don't report deprecated lemmas.
if Linter.isDeprecated ( getEnv) name then return #[]
-- Don't report lemmas from metaprogramming namespaces.
if name.isMetaprogramming then return #[] else
forallTelescope constInfo.type fun _ type => do

View File

@@ -62,13 +62,4 @@ def _root_.Lean.MVarId.hrefl (mvarId : MVarId) : MetaM Unit := do
let some [] observing? do mvarId.apply (mkConst ``HEq.refl [ mkFreshLevelMVar])
| throwTacticEx `hrefl mvarId
/--
Close given goal using `Subsingleton.helim`.
-/
def _root_.Lean.MVarId.helim (mvarId : MVarId) : MetaM (List MVarId) :=
mvarId.withContext do
let some subGoals observing? do mvarId.apply (mkConst ``Subsingleton.helim [ mkFreshLevelMVar])
| throwTacticEx `hrefl mvarId
return subGoals
end Lean.Meta

View File

@@ -13,6 +13,7 @@ public import Lean.Meta.Tactic.Refl
public import Lean.Meta.Tactic.SolveByElim
public import Lean.Meta.Tactic.TryThis
public import Lean.Util.Heartbeats
import Lean.Linter.Deprecated
public section
@@ -47,6 +48,8 @@ private def addImport (name : Name) (constInfo : ConstantInfo) :
MetaM (Array (InitEntry (Name × RwDirection))) := do
if constInfo.isUnsafe then return #[]
if !allowCompletion (getEnv) name then return #[]
-- Don't report deprecated lemmas.
if Linter.isDeprecated ( getEnv) name then return #[]
-- We now remove some injectivity lemmas which are not useful to rewrite by.
match name with
| .str _ n => if n = "injEq" n = "sizeOf_spec" n.endsWith "_inj" n.endsWith "_inj'" then return #[]

View File

@@ -4,18 +4,17 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Lean.Meta.Tactic.Replace
public import Lean.Meta.Tactic.Simp.Rewrite
public import Lean.Meta.Tactic.Simp.Diagnostics
public import Lean.Meta.Match.Value
public import Lean.Meta.MonadSimp
public import Lean.Util.CollectLooseBVars
import Lean.Meta.HaveTelescope
import Lean.PrettyPrinter
import Lean.ExtraModUses
public section
namespace Lean.Meta
namespace Simp
@@ -263,6 +262,16 @@ def withNewLemmas {α} (xs : Array Expr) (f : SimpM α) : SimpM α := do
else
withFreshCache do f
instance : MonadSimp SimpM where
simp e := do
let r simp e
if r.expr == e then
return .rfl
else
return .step r.expr ( r.getProof)
dsimp := dsimp
withNewLemmas := withNewLemmas
def simpProj (e : Expr) : SimpM Result := do
match ( withSimpMetaConfig <| reduceProj? e) with
| some e => return { expr := e }
@@ -378,393 +387,17 @@ def simpForall (e : Expr) : SimpM Result := withParent e do
else
return { expr := ( dsimp e) }
/--
Information about a single `have` in a `have` telescope.
Created by `getHaveTelescopeInfo`.
-/
structure HaveInfo where
/-- Previous `have`s that the type of this `have` depends on, as indices into `HaveTelescopeInfo.haveInfo`.
Used in `computeFixedUsed` to compute used `have`s. -/
typeBackDeps : Std.HashSet Nat
/-- Previous `have`s that the value of this `have` depends on, as indices into `HaveTelescopeInfo.haveInfo`.
Used in `computeFixedUsed` to compute used `have`s. -/
valueBackDeps : Std.HashSet Nat
/-- The local decl for the `have`, so that the local context can be re-entered `have`-by-`have`.
N.B. This stores the fvarid for the `have` as well as cached versions of the value and type
instantiated with the fvars from the telescope. -/
decl : LocalDecl
/-- The level of the type for this `have`, cached. -/
level : Level
deriving Inhabited
/--
Information about a `have` telescope.
Created by `getHaveTelescopeInfo` and used in `simpHaveTelescope`.
The data is used to avoid applying `Expr.abstract` more than once on any given subexpression
when constructing terms and proofs during simplification. Abstraction is linear in the size `t` of a term,
and so in a depth-`n` telescope it is `O(nt)`, quadratic in `n`, since `n ≤ t`.
For example, given `have x₁ := v₁; ...; have xₙ := vₙ; b` and `h : b = b'`, we need to construct
```lean
have_body_congr (fun x₁ => ... have_body_congr (fun xₙ => h)))
```
We apply `Expr.abstract` to `h` *once* and then build the term, rather than
using `mkLambdaFVars #[fvarᵢ] pfᵢ` at each step.
As an additional optimization, we save the fvar-instantiated terms calculated by `getHaveTelescopeInfo`
so that we don't have to compute them again. This is only saving a constant factor.
It is also used for computing used `have`s in `computeFixedUsed`.
In `have x₁ := v; have x₂ := x₁; ⋯; have xₙ := xₙ₋₁; b`, if `xₙ` is unused in `b`, then all the
`have`s are unused. Without a global computation of used `have`s, the proof term would be quadratic
in the number of `have`s (with `n` iterations of `simp`). Knowing which `have`s are transitively
unused lets the proof term be linear in size.
-/
structure HaveTelescopeInfo where
/-- Information about each `have` in the telescope. -/
haveInfo : Array HaveInfo := #[]
/-- The set of `have`s that the body depends on, as indices into `haveInfo`.
Used in `computeFixedUsed` to compute used `have`s. -/
bodyDeps : Std.HashSet Nat := {}
/-- The set of `have`s that the type of the body depends on, as indices into `haveInfo`.
This is the set of fixed `have`s. -/
bodyTypeDeps : Std.HashSet Nat := {}
/-- A cached version of the telescope body, instantiated with fvars from each `HaveInfo.decl`. -/
body : Expr := Expr.const `_have_telescope_info_dummy_ []
/-- A cached version of the telescope body's type, instantiated with fvars from each `HaveInfo.decl`. -/
bodyType : Expr := Expr.const `_have_telescope_info_dummy_ []
/-- The universe level for the `have` expression, cached. -/
level : Level := Level.param `_have_telescope_info_dummy_
deriving Inhabited
/--
Efficiently collect dependency information for a `have` telescope.
This is part of a scheme to avoid quadratic overhead from the locally nameless discipline
(see `HaveTelescopeInfo` and `simpHaveTelescope`).
The expression `e` must not have loose bvars.
-/
def getHaveTelescopeInfo (e : Expr) : MetaM HaveTelescopeInfo := do
collect e 0 {} ( getLCtx) #[]
where
collect (e : Expr) (numHaves : Nat) (info : HaveTelescopeInfo) (lctx : LocalContext) (fvars : Array Expr) : MetaM HaveTelescopeInfo := do
/-
Gives indices into `fvars` that the uninstantiated expression `a` depends on, from collecting its bvars.
This is more efficient than collecting `fvars` from an instantiated expression,
since the expression likely contains many fvars for the pre-existing local context.
-/
let getBackDeps (a : Expr) : Std.HashSet Nat :=
a.collectLooseBVars.fold (init := {}) fun deps vidx =>
-- Since the original expression has no bvars, `vidx < numHaves` must be true.
deps.insert (numHaves - vidx - 1)
match e with
| .letE n t v b true =>
let typeBackDeps := getBackDeps t
let valueBackDeps := getBackDeps v
let t := t.instantiateRev fvars
let v := v.instantiateRev fvars
let level withLCtx' lctx <| getLevel t
let fvarId mkFreshFVarId
let decl := LocalDecl.ldecl 0 fvarId n t v true .default
let info := { info with haveInfo := info.haveInfo.push { typeBackDeps, valueBackDeps, decl, level } }
let lctx := lctx.addDecl decl
let fvars := fvars.push (mkFVar fvarId)
collect b (numHaves + 1) info lctx fvars
| _ =>
let bodyDeps := getBackDeps e
withLCtx' lctx do
let body := e.instantiateRev fvars
let bodyType inferType body
let level getLevel bodyType
-- Collect fvars appearing in the type of `e`. Computing `bodyType` in particular is where `MetaM` is necessary.
let bodyTypeFVarIds := (collectFVars {} bodyType).fvarSet
let bodyTypeDeps : Std.HashSet Nat := Nat.fold fvars.size (init := {}) fun idx _ deps =>
if bodyTypeFVarIds.contains fvars[idx].fvarId! then
deps.insert idx
else
deps
return { info with bodyDeps, bodyTypeDeps, body, bodyType, level }
/--
Computes which `have`s in the telescope are fixed and which are unused.
The length of the unused array may be less than the number of `have`s: use `unused.getD i true`.
-/
def HaveTelescopeInfo.computeFixedUsed (info : HaveTelescopeInfo) (keepUnused : Bool) :
MetaM (Array Bool × Array Bool) := do
let fixed go info.bodyTypeDeps
if keepUnused then
return (fixed, #[])
else
let used go info.bodyDeps
return (fixed, used)
where
updateArrayFromBackDeps (arr : Array Bool) (s : Std.HashSet Nat) : Array Bool :=
s.fold (init := arr) fun arr idx => arr.set! idx true
go init : MetaM (Array Bool) := do
let numHaves := info.haveInfo.size
let mut used : Array Bool := Array.replicate numHaves false
-- Initialize `used` with the body's dependencies.
-- There is no need to consider `info.bodyTypeDeps` in this computation.
used := updateArrayFromBackDeps used init
-- For each used `have`, in reverse order, update `used`.
for i in *...numHaves do
let idx := numHaves - i - 1
if used[idx]! then
let hinfo := info.haveInfo[idx]!
used := updateArrayFromBackDeps used hinfo.typeBackDeps
used := updateArrayFromBackDeps used hinfo.valueBackDeps
return used
/--
Auxiliary structure used to represent the return value of `simpHaveTelescopeAux`.
See `simpHaveTelescope` for an overview and `HaveTelescopeInfo` for an example.
-/
private structure SimpHaveResult where
/--
The simplified expression in `(fun x => b) v` form. Note that it may contain loose bound variables.
`simpHaveTelescope` attempts to minimize the quadratic overhead imposed
by the locally nameless discipline in a sequence of `have` expressions.
-/
expr : Expr
/--
The type of `expr`. It may contain loose bound variables like in the `expr` field.
Used in proof construction.
-/
exprType : Expr
/--
The initial expression in `(fun x => b) v` form.
-/
exprInit : Expr
/--
The expression `expr` in `have x := v; b` form.
-/
exprResult : Expr
/--
The proof that the simplified expression is equal to the input one.
It may contain loose bound variables like in the `expr` field.
-/
proof : Expr
/--
`modified := false` iff `expr` is structurally equal to the input expression.
When false, `proof` is `Eq.refl`.
-/
modified : Bool
private def SimpHaveResult.toResult (u : Level) (source : Expr) : SimpHaveResult Result
| { expr, exprType, exprInit, exprResult, proof, modified, .. } =>
{ expr := exprResult
proof? :=
if modified then
-- Add a type hint to convert back into `have` form.
some <| mkExpectedPropHint (expectedProp := mkApp3 (mkConst ``Eq [u]) exprType source exprResult) <|
-- Add in a second type hint, for use in an optimization to avoid zeta/beta reductions in the kernel
-- The base case in `simpHaveTelescopeAux` detects this construction and uses `exprType`/`exprInit`
-- to construct the `SimpHaveResult`.
-- If the kernel were to support `have` forms of the congruence lemmas this would not be necessary.
mkExpectedPropHint (expectedProp := mkApp3 (mkConst ``Eq [u]) exprType exprInit expr) proof
else
none }
/--
Routine for simplifying `have` telescopes. Used by `simpLet`.
This is optimized to be able to handle deep `have` telescopes while avoiding quadratic complexity
arising from the locally nameless expression representations.
## Overview
Consider a `have` telescope:
```
have x₁ : T₁ := v₁; ...; have xₙ : Tₙ := vₙ; b
```
We say `xᵢ` is *fixed* if it appears in the type of `b`.
If `xᵢ` is fixed, then it can only be rewritten using definitional equalities.
Otherwise, we can freely rewrite the value using a propositional equality `vᵢ = vᵢ'`.
The body `b` can always be freely rewritten using a propositional equality `b = b'`.
(All the mentioned equalities must be type correct with respect to the obvious local contexts.)
If `xᵢ` neither appears in `b` nor any `Tⱼ` nor any `vⱼ`, then its `have` is *unused*.
Unused `have`s can be eliminated, which we do if `cfg.zetaUnused` is true.
We clear `have`s from the end, to be able to transitively clear chains of unused `have`s.
This is why we honor `zetaUnused` here even though `reduceStep` is also responsible for it;
`reduceStep` can only eliminate unused `have`s at the start of a telescope.
Eliminating all transitively unused `have`s at once like this also avoids quadratic complexity.
If `debug.simp.check.have` is enabled then we typecheck the resulting expression and proof.
## Proof generation
There are two main complications with generating proofs.
1. We work almost exclusively with expressions with loose bound variables,
to avoid overhead from instantiating and abstracting free variables,
which can lead to complexity quadratic in the telescope length.
(See `HaveTelescopeInfo`.)
2. We want to avoid triggering zeta reductions in the kernel.
Regarding this second point, the issue is that we cannot organize the proof using congruence theorems
in the obvious way. For example, given
```
theorem have_congr {α : Sort u} {β : Sort v} {a a' : α} {f f' : α → β}
(h₁ : a = a') (h₂ : ∀ x, f x = f' x) :
(have x := a; f x) = (have x := a'; f' x)
```
the kernel sees that the type of `have_congr (fun x => b) (fun x => b') h₁ h₂`
is `(have x := a; (fun x => b) x) = (have x := a'; (fun x => b') x)`,
since when instantiating values it does not beta reduce at the same time.
Hence, when `is_def_eq` is applied to the LHS and `have x := a; b` for example,
it will do `whnf_core` to both sides.
Instead, we use the form `(fun x => b) a = (fun x => b') a'` in the proofs,
which we can reliably match up without triggering beta reductions in the kernel.
The zeta/beta reductions are then limited to the type hint for the entire telescope,
when we convert this back into `have` form.
In the base case, we include an optimization to avoid unnecessary zeta/beta reductions,
by detecting a `simpHaveTelescope` proofs and removing the type hint.
-/
/-- Adapter for `Meta.simpHaveTelescope` -/
def simpHaveTelescope (e : Expr) : SimpM Result := do
Prod.fst <$> withTraceNode `Debug.Meta.Tactic.simp (fun
| .ok (_, used, fixed, modified) => pure m!"{checkEmoji} have telescope; used: {used}; fixed: {fixed}; modified: {modified}"
| .error ex => pure m!"{crossEmoji} {ex.toMessageData}") do
let info getHaveTelescopeInfo e
assert! !info.haveInfo.isEmpty
let (fixed, used) info.computeFixedUsed (keepUnused := !( getConfig).zetaUnused)
let r simpHaveTelescopeAux info fixed used e 0 (Array.mkEmpty info.haveInfo.size)
if r.modified && debug.simp.check.have.get ( getOptions) then
check r.expr
check r.proof
return (r.toResult info.level e, used, fixed, r.modified)
where
/-
Re-enters the telescope recorded in `info` using `withExistingLocalDecls` while simplifying according to `fixed`/`used`.
Note that we must use the low-level `Expr` APIs because the expressions may contain loose bound variables.
Note also that we don't enter the body's local context all at once, since we need to be sure that
when we simplify values they have their correct local context.
-/
simpHaveTelescopeAux (info : HaveTelescopeInfo) (fixed : Array Bool) (used : Array Bool) (e : Expr) (i : Nat) (xs : Array Expr) : SimpM SimpHaveResult := do
if h : i < info.haveInfo.size then
let hinfo := info.haveInfo[i]
-- `x` and `val` are the fvar and value with respect to the local context.
let x := hinfo.decl.toExpr
let val := hinfo.decl.value true
-- Invariant: `v == val.abstract xs`.
let .letE n t v b true := e | unreachable!
-- Universe levels to use for each of the `have_*` theorems. It's the level of `val` and the level of the body.
let us := [hinfo.level, info.level]
if !used.getD i true then
/-
Unused `have`: do not add `x` to the local context then `simp` only the body.
-/
trace[Debug.Meta.Tactic.simp] "have telescope; unused {n} := {val}"
/-
Complication: Unused `have`s might only be transitively unused.
This means that `b.hasLooseBVar 0` might actually be true.
This matters because `t` and `v` appear in the proof term.
We use a dummy `x` for debugging purposes. (Recall that `Expr.abstract` skips non-fvar/mvars.)
-/
let x := mkConst `_simp_let_unused_dummy
let rb simpHaveTelescopeAux info fixed used b (i + 1) (xs.push x)
-- TODO(kmill): This is a source of quadratic complexity. It might be possible to avoid this,
-- but we will need to carefully re-review the logic (note that `rb.proof` still refers to `x`).
let expr := rb.expr.lowerLooseBVars 1 1
let exprType := rb.exprType.lowerLooseBVars 1 1
let exprInit := mkApp (mkLambda n .default t rb.exprInit) v
let exprResult := rb.exprResult.lowerLooseBVars 1 1
if rb.modified then
let proof := mkApp6 (mkConst ``have_unused_dep' us) t exprType v (mkLambda n .default t rb.exprInit) expr
(mkLambda n .default t rb.proof)
return { expr, exprType, exprInit, exprResult, proof, modified := true }
else
-- If not modified, this must have been a non-transitively unused `have`, so no need for `dep` form.
let proof := mkApp6 (mkConst ``have_unused' us) t exprType v expr expr
(mkApp2 (mkConst ``Eq.refl [info.level]) exprType expr)
return { expr, exprType, exprInit, exprResult, proof, modified := true }
else if fixed.getD i true then
/-
Fixed `have` (like `CongrArgKind.fixed`): dsimp the value and simp the body.
The variable appears in the type of the body.
-/
let val' dsimp val
trace[Debug.Meta.Tactic.simp] "have telescope; fixed {n} := {val} => {val'}"
let vModified := val != val'
let v' := if vModified then val'.abstract xs else v
withExistingLocalDecls [hinfo.decl] <| withNewLemmas #[x] do
let rb simpHaveTelescopeAux info fixed used b (i + 1) (xs.push x)
let expr := mkApp (mkLambda n .default t rb.expr) v'
let exprType := mkApp (mkLambda n .default t rb.exprType) v'
let exprInit := mkApp (mkLambda n .default t rb.exprInit) v
let exprResult := mkHave n t v' rb.exprResult
-- Note: if `vModified`, then the kernel will reduce the telescopes and potentially do an expensive defeq check.
-- If this is a performance issue, we could try using a `letFun` encoding here make use of the `is_def_eq_args` optimization.
-- Namely, to guide the defeqs via the sequence
-- `(fun x => b) v` = `letFun (fun x => b) v` = `letFun (fun x => b) v'` = `(fun x => b) v'`
if rb.modified then
let proof := mkApp6 (mkConst ``have_body_congr_dep' us) t (mkLambda n .default t rb.exprType) v'
(mkLambda n .default t rb.exprInit) (mkLambda n .default t rb.expr) (mkLambda n .default t rb.proof)
return { expr, exprType, exprInit, exprResult, proof, modified := true }
else
let proof := mkApp2 (mkConst ``Eq.refl [info.level]) exprType expr
return { expr, exprType, exprInit, exprResult, proof, modified := vModified }
else
/-
Non-fixed `have` (like `CongrArgKind.eq`): simp both the value and the body.
The variable does not appear in the type of the body.
-/
let rval' simp val
trace[Debug.Meta.Tactic.simp] "have telescope; non-fixed {n} := {val} => {rval'.expr}"
let valModified := rval'.expr != val
let v' := if valModified then rval'.expr.abstract xs else v
let vproof if valModified then
pure <| ( rval'.getProof).abstract xs
else
pure <| mkApp2 (mkConst `Eq.refl [hinfo.level]) t v
withExistingLocalDecls [hinfo.decl] <| withNewLemmas #[x] do
let rb simpHaveTelescopeAux info fixed used b (i + 1) (xs.push x)
let expr := mkApp (mkLambda n .default t rb.expr) v'
assert! !rb.exprType.hasLooseBVar 0
let exprType := rb.exprType.lowerLooseBVars 1 1
let exprInit := mkApp (mkLambda n .default t rb.exprInit) v
let exprResult := mkHave n t v' rb.exprResult
match valModified, rb.modified with
| false, false =>
let proof := mkApp2 (mkConst `Eq.refl [info.level]) exprType expr
return { expr, exprType, exprInit, exprResult, proof, modified := false }
| true, false =>
let proof := mkApp6 (mkConst ``have_val_congr' us) t exprType v v'
(mkLambda n .default t rb.exprInit) vproof
return { expr, exprType, exprInit, exprResult, proof, modified := true }
| false, true =>
let proof := mkApp6 (mkConst ``have_body_congr' us) t exprType v
(mkLambda n .default t rb.exprInit) (mkLambda n .default t rb.expr) (mkLambda n .default t rb.proof)
return { expr, exprType, exprInit, exprResult, proof, modified := true }
| true, true =>
let proof := mkApp8 (mkConst ``have_congr' us) t exprType v v'
(mkLambda n .default t rb.exprInit) (mkLambda n .default t rb.expr) vproof (mkLambda n .default t rb.proof)
return { expr, exprType, exprInit, exprResult, proof, modified := true }
else
-- Base case: simplify the body.
trace[Debug.Meta.Tactic.simp] "have telescope; simplifying body {info.body}"
let r simp info.body
let exprType := info.bodyType.abstract xs
if r.expr == info.body then
let proof := mkApp2 (mkConst `Eq.refl [info.level]) exprType e
return { expr := e, exprType, exprInit := e, exprResult := e, proof, modified := false }
else
let expr := r.expr.abstract xs
let proof := ( r.getProof).abstract xs
-- Optimization: if the proof is a `simpHaveTelescope` proof, then remove the type hint
-- to avoid the zeta/beta reductions that the kernel would otherwise do.
-- In `SimpHaveResult.toResult` we insert two type hints; the inner one
-- encodes the `exprInit` and `expr`.
let detectSimpHaveLemma (proof : Expr) : Option SimpHaveResult := do
let_expr id eq proof' := proof | failure
guard <| eq.isAppOfArity ``Eq 3
let_expr id eq' proof'' := proof' | failure
let_expr Eq _ lhs rhs := eq' | failure
let .const n _ := proof''.getAppFn | failure
guard (n matches ``have_unused_dep' | ``have_unused' | ``have_body_congr_dep' | ``have_val_congr' | ``have_body_congr' | ``have_congr')
return { expr := rhs, exprType, exprInit := lhs, exprResult := expr, proof := proof'', modified := true }
if let some res := detectSimpHaveLemma proof then
return res
return { expr, exprType, exprInit := e, exprResult := expr, proof, modified := true }
-- **Note**: Eliminating unused-let declarations in a single pass may produce O(n^2) proofs.
let zetaUnusedMode := if ( getConfig).zetaUnused then .singlePass else .no
match ( Meta.simpHaveTelescope e zetaUnusedMode) with
| .rfl => return { expr := e }
| .step e' h =>
if debug.simp.check.have.get ( getOptions) then
check e'
check h
return { expr := e', proof? := h }
/--
Routine for simplifying `let` expressions.

115
src/LeanChecker.lean Normal file
View File

@@ -0,0 +1,115 @@
/-
Copyright (c) 2023 Kim Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison, Sebastian Ullrich
-/
import Lean.CoreM
import Lean.Replay
import LeanChecker.Replay
import Lake.Load.Manifest
open Lean
unsafe def replayFromImports (module : Name) : IO Unit := do
let mFile findOLean module
unless ( mFile.pathExists) do
throw <| IO.userError s!"object file '{mFile}' of module {module} does not exist"
-- Load all module data parts (exported, server, private)
let mut fnames := #[mFile]
let sFile := OLeanLevel.server.adjustFileName mFile
if ( sFile.pathExists) then
fnames := fnames.push sFile
let pFile := OLeanLevel.private.adjustFileName mFile
if ( pFile.pathExists) then
fnames := fnames.push pFile
let parts readModuleDataParts fnames
if h : parts.size = 0 then throw <| IO.userError "failed to read module data" else
let (mod, _) := parts[0]
let (_, s) importModulesCore mod.imports |>.run
let env finalizeImport s mod.imports {} 0 false false (isModule := true)
let mut newConstants := {}
-- Collect constants from last ("most private") part, which subsumes all prior ones
for name in parts[parts.size-1].1.constNames, ci in parts[parts.size-1].1.constants do
newConstants := newConstants.insert name ci
let env' env.replay' newConstants
env'.freeRegions
unsafe def replayFromFresh (module : Name) : IO Unit := do
Lean.withImportModules #[{module}] {} fun env => do
discard <| ( mkEmptyEnvironment).replay' env.constants.map₁
/-- Read the name of the main module from the `lake-manifest`. -/
-- This has been copied from `ImportGraph.getCurrentModule` in the
-- https://github.com/leanprover-community/import-graph repository.
def getCurrentModule : IO Name := do
match ( Lake.Manifest.load? "lake-manifest.json") with
| none =>
-- TODO: should this be caught?
pure .anonymous
| some manifest =>
-- TODO: This assumes that the `package` and the default `lean_lib`
-- have the same name up to capitalisation.
-- Would be better to read the `.defaultTargets` from the
-- `← getRootPackage` from `Lake`, but I can't make that work with the monads involved.
return manifest.name.capitalize
/--
Run as e.g. `leanchecker` to check everything in the current project.
or e.g. `leanchecker Mathlib.Data.Nat` to check everything with module name
starting with `Mathlib.Data.Nat`.
This will replay all the new declarations from the target file into the `Environment`
as it was at the beginning of the file, using the kernel to check them.
You can also use `leanchecker --fresh Mathlib.Data.Nat.Prime.Basic`
to replay all the constants (both imported and defined in that file) into a fresh environment.
This can only be used on a single file.
This is not an external verifier, simply a tool to detect "environment hacking".
-/
unsafe def main (args : List String) : IO UInt32 := do
-- Contributor's note: lean4lean is intended to have a CLI interface matching leanchecker,
-- so if you want to make a change here please either make a sibling PR to
-- https://github.com/digama0/lean4lean or ping @digama0 (Mario Carneiro) to go fix it.
initSearchPath ( findSysroot)
let (flags, args) := args.partition fun s => s.startsWith "-"
let verbose := "-v" flags || "--verbose" flags
let fresh := "--fresh" flags
let targets do
match args with
| [] => pure [ getCurrentModule]
| args => args.mapM fun arg => do
let mod := arg.toName
if mod.isAnonymous then
throw <| IO.userError s!"Could not resolve module: {arg}"
else
pure mod
let mut targetModules := []
let sp searchPathRef.get
for target in targets do
let mut found := false
for path in ( SearchPath.findAllWithExt sp "olean") do
if let some m := ( searchModuleNameOfFileName path sp) then
if !fresh && target.isPrefixOf m || target == m then
targetModules := targetModules.insert m
found := true
if not found then
throw <| IO.userError s!"Could not find any oleans for: {target}"
if fresh then
if targetModules.length != 1 then
throw <| IO.userError s!"--fresh flag is only valid when specifying a single module:\n\
{targetModules}"
for m in targetModules do
if verbose then IO.println s!"replaying {m} with --fresh"
replayFromFresh m
else
let mut tasks := #[]
for m in targetModules do
tasks := tasks.push (m, IO.asTask (replayFromImports m))
for (m, t) in tasks do
if verbose then IO.println s!"replaying {m}"
if let .error e := t.get then
IO.eprintln s!"leanchecker found a problem in {m}"
throw e
return 0

191
src/LeanChecker/Replay.lean Normal file
View File

@@ -0,0 +1,191 @@
/-
Copyright (c) 2023 Kim Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Lean.CoreM
import Lean.AddDecl
import Lean.Util.FoldConsts
/-!
# `Lean.Environment.replay`
`replay env constantMap` will "replay" all the constants in `constantMap : HashMap Name ConstantInfo` into `env`,
sending each declaration to the kernel for checking.
`replay` does not send constructors or recursors in `constantMap` to the kernel,
but rather checks that they are identical to constructors or recursors generated in the environment
after replaying any inductive definitions occurring in `constantMap`.
`replay` can be used either as:
* a verifier for an `Environment`, by sending everything to the kernel, or
* a mechanism to safely transfer constants from one `Environment` to another.
## Implementation notes
This is a patched version of `Lean.Environment.Replay`, which is in the `lean4` repository
up to `v4.18.0`, but will be deprecated in `v4.19.0` and then removed. Once it it removed,
the prime on the `Replay'` namespace, the prime on `Lean.Environment.replay'`
should be removed here.
-/
namespace Lean.Environment
namespace Replay'
structure Context where
newConstants : Std.HashMap Name ConstantInfo
structure State where
env : Kernel.Environment
remaining : NameSet := {}
pending : NameSet := {}
postponedConstructors : NameSet := {}
postponedRecursors : NameSet := {}
abbrev M := ReaderT Context <| StateRefT State IO
/-- Check if a `Name` still needs processing. If so, move it from `remaining` to `pending`. -/
def isTodo (name : Name) : M Bool := do
let r := ( get).remaining
if r.contains name then
modify fun s => { s with remaining := s.remaining.erase name, pending := s.pending.insert name }
return true
else
return false
/-- Use the current `Environment` to throw a `Kernel.Exception`. -/
def throwKernelException (ex : Kernel.Exception) : M Unit := do
throw <| .userError <| ( ex.toMessageData {} |>.toString)
/-- Add a declaration, possibly throwing a `Kernel.Exception`. -/
def addDecl (d : Declaration) : M Unit := do
match ( get).env.addDeclCore 0 d (cancelTk? := none) with
| .ok env => modify fun s => { s with env := env }
| .error ex => throwKernelException ex
mutual
/--
Check if a `Name` still needs to be processed (i.e. is in `remaining`).
If so, recursively replay any constants it refers to,
to ensure we add declarations in the right order.
The construct the `Declaration` from its stored `ConstantInfo`,
and add it to the environment.
-/
partial def replayConstant (name : Name) : M Unit := do
if isTodo name then
let some ci := ( read).newConstants[name]? | unreachable!
replayConstants ci.getUsedConstantsAsSet
-- Check that this name is still pending: a mutual block may have taken care of it.
if ( get).pending.contains name then
match ci with
| .defnInfo info =>
addDecl (Declaration.defnDecl info)
| .thmInfo info =>
-- Ignore duplicate theorems. This code is identical to that in `finalizeImport` before it
-- added extended duplicates support for the module system, which is not relevant for us
-- here as we always load all .olean information. We need this case *because* of the module
-- system -- as we have more data loaded than it, we might encounter duplicate private
-- theorems where elaboration under the module system would have only one of them in scope.
if let some (.thmInfo info') := ( get).env.find? ci.name then
if info.name == info'.name &&
info.type == info'.type &&
info.levelParams == info'.levelParams &&
info.all == info'.all
then
return
addDecl (Declaration.thmDecl info)
| .axiomInfo info =>
addDecl (Declaration.axiomDecl info)
| .opaqueInfo info =>
addDecl (Declaration.opaqueDecl info)
| .inductInfo info =>
let lparams := info.levelParams
let nparams := info.numParams
let all info.all.mapM fun n => do pure <| (( read).newConstants[n]!)
for o in all do
modify fun s =>
{ s with remaining := s.remaining.erase o.name, pending := s.pending.erase o.name }
let ctorInfo all.mapM fun ci => do
pure (ci, ci.inductiveVal!.ctors.mapM fun n => do
pure (( read).newConstants[n]!))
-- Make sure we are really finished with the constructors.
for (_, ctors) in ctorInfo do
for ctor in ctors do
replayConstants ctor.getUsedConstantsAsSet
let types : List InductiveType := ctorInfo.map fun ci, ctors =>
{ name := ci.name
type := ci.type
ctors := ctors.map fun ci => { name := ci.name, type := ci.type } }
addDecl (Declaration.inductDecl lparams nparams types false)
-- We postpone checking constructors,
-- and at the end make sure they are identical
-- to the constructors generated when we replay the inductives.
| .ctorInfo info =>
modify fun s => { s with postponedConstructors := s.postponedConstructors.insert info.name }
-- Similarly we postpone checking recursors.
| .recInfo info =>
modify fun s => { s with postponedRecursors := s.postponedRecursors.insert info.name }
| .quotInfo _ =>
-- `Quot.lift` and `Quot.ind` have types that reference `Eq`,
-- so we need to ensure `Eq` is replayed before adding the quotient declaration.
replayConstant `Eq
addDecl (Declaration.quotDecl)
modify fun s => { s with pending := s.pending.erase name }
/-- Replay a set of constants one at a time. -/
partial def replayConstants (names : NameSet) : M Unit := do
for n in names do replayConstant n
end
/--
Check that all postponed constructors are identical to those generated
when we replayed the inductives.
-/
def checkPostponedConstructors : M Unit := do
for ctor in ( get).postponedConstructors do
match ( get).env.find? ctor, ( read).newConstants[ctor]? with
| some (.ctorInfo info), some (.ctorInfo info') =>
if ! (info == info') then throw <| IO.userError s!"Invalid constructor {ctor}"
| _, _ => throw <| IO.userError s!"No such constructor {ctor}"
/--
Check that all postponed recursors are identical to those generated
when we replayed the inductives.
-/
def checkPostponedRecursors : M Unit := do
for ctor in ( get).postponedRecursors do
match ( get).env.find? ctor, ( read).newConstants[ctor]? with
| some (.recInfo info), some (.recInfo info') =>
if ! (info == info') then throw <| IO.userError s!"Invalid recursor {ctor}"
| _, _ => throw <| IO.userError s!"No such recursor {ctor}"
end Replay'
open Replay'
/--
"Replay" some constants into an `Environment`, sending them to the kernel for checking.
Throws a `IO.userError` if the kernel rejects a constant,
or if there are malformed recursors or constructors for inductive types.
-/
def replay' (newConstants : Std.HashMap Name ConstantInfo) (env : Environment) : IO Environment := do
let mut remaining : NameSet :=
for (n, ci) in newConstants.toList do
-- We skip unsafe constants, and also partial constants.
-- Later we may want to handle partial constants.
if !ci.isUnsafe && !ci.isPartial then
remaining := remaining.insert n
let (_, s) StateRefT'.run (s := { env := env.toKernelEnv, remaining }) do
ReaderT.run (r := { newConstants }) do
for n in remaining do
replayConstant n
checkPostponedConstructors
checkPostponedRecursors
return .ofKernelEnv s.env

View File

@@ -107,7 +107,7 @@ def containsThenInsertIfNew [EquivBEq α] [LawfulHashable α]
replaced, r
/--
Checks whether a key is present in a map, returning the associate value, and inserts a value for
Checks whether a key is present in a map, returning the associated value, and inserts a value for
the key if it was not found.
If the returned value is `some v`, then the returned map is unaltered. If it is `none`, then the

View File

@@ -108,7 +108,7 @@ instance : LawfulSingleton (α × β) (HashMap α β) := ⟨fun _ => rfl⟩
replaced, r
/--
Checks whether a key is present in a map, returning the associate value, and inserts a value for
Checks whether a key is present in a map, returning the associated value, and inserts a value for
the key if it was not found.
If the returned value is `some v`, then the returned map is unaltered. If it is `none`, then the

View File

@@ -220,7 +220,7 @@ Decomposing assertions in postconditions into conjunctions of simpler predicates
chance that automation will be able to prove the entailment of the postcondition and the next precondition.
-/
class IsAnd (P : SPred σs) (Q₁ Q₂ : outParam (SPred σs)) where
/-- A proof the the decomposition is logically equivalent to the original predicate. -/
/-- A proof that the decomposition is logically equivalent to the original predicate. -/
to_and : P Q₁ Q₂
instance (σs) (Q₁ Q₂ : SPred σs) : IsAnd (σs:=σs) spred(Q₁ Q₂) Q₁ Q₂ where to_and := .rfl
instance (σs) : IsAnd (σs:=σs) p q p q where to_and := pure_and.symm

View File

@@ -9,6 +9,7 @@ prelude
public import Std.Internal.Async
public import Std.Internal.Parsec
public import Std.Internal.UV
public import Std.Internal.Http
@[expose] public section

View File

@@ -22,13 +22,13 @@ namespace Async
This module provides a layered approach to asynchronous programming, combining monadic types,
type classes, and concrete task types that work together in a cohesive system.
- **Monadic Types**: These types provide a good way to to chain and manipulate context. These
- **Monadic Types**: These types provide a good way to chain and manipulate context. These
can contain a `Task`, enabling manipulation of both asynchronous and synchronous code.
- **Concrete Task Types**: Concrete units of work that can be executed within these contexts.
## Monadic Types
These types provide a good way to to chain and manipulate context. These can contain a `Task`,
These types provide a good way to chain and manipulate context. These can contain a `Task`,
enabling manipulation of both asynchronous and synchronous code.
- `BaseAsync`: A monadic type for infallible asynchronous computations
@@ -548,7 +548,7 @@ def concurrentlyAll (xs : Array (BaseAsync α)) (prio := Task.Priority.default)
/--
Runs all computations concurrently and returns the result of the first one to finish.
All other results are lost, and the tasks are not cancelled, so they'll continue their executing
All other results are lost, and the tasks are not cancelled, so they'll continue their execution
until the end.
-/
@[inline, specialize]
@@ -753,6 +753,10 @@ instance : MonadLift (EIO ε) (EAsync ε) where
instance : MonadLift BaseAsync (EAsync ε) where
monadLift x := .mk <| x.map (.ok)
instance : MonadAttach BaseAsync := .trivial
instance : MonadAttach (EAsync ε) := .trivial
@[inline]
protected partial def forIn
{β : Type} (init : β)
@@ -829,7 +833,7 @@ def concurrentlyAll (xs : Array (EAsync ε α)) (prio := Task.Priority.default)
/--
Runs all computations concurrently and returns the result of the first one to finish.
All other results are lost, and the tasks are not cancelled, so they'll continue their executing
All other results are lost, and the tasks are not cancelled, so they'll continue their execution
until the end.
-/
@[inline, specialize]
@@ -969,7 +973,7 @@ def concurrentlyAll (xs : Array (Async α)) (prio := Task.Priority.default) : As
/--
Runs all computations concurrently and returns the result of the first one to finish.
All other results are lost, and the tasks are not cancelled, so they'll continue their executing
All other results are lost, and the tasks are not cancelled, so they'll continue their execution
until the end.
-/
@[inline, specialize]

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