mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-22 12:54:06 +00:00
Compare commits
203 Commits
test_exter
...
simproc_do
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
400c8c5e37 | ||
|
|
cfeb957807 | ||
|
|
43bbedca46 | ||
|
|
509f35df02 | ||
|
|
732b266de0 | ||
|
|
1d8cf38ff9 | ||
|
|
a4226a4f6d | ||
|
|
76224e409b | ||
|
|
c3383de6ff | ||
|
|
e5b1c87606 | ||
|
|
da072c2ec8 | ||
|
|
d3c71ce2ff | ||
|
|
da21ef4fe8 | ||
|
|
168217b2bd | ||
|
|
8deb1838aa | ||
|
|
3d1b3c6b44 | ||
|
|
676121c71d | ||
|
|
6439d93389 | ||
|
|
e4e6601546 | ||
|
|
01469bdbd6 | ||
|
|
01750e2139 | ||
|
|
8037a8733d | ||
|
|
c4e6e48690 | ||
|
|
9cfca51257 | ||
|
|
de886c617d | ||
|
|
755b59c2cf | ||
|
|
266075b8a4 | ||
|
|
8db28ac32f | ||
|
|
b4a290a203 | ||
|
|
03f344a35f | ||
|
|
a48ca7b0a4 | ||
|
|
1cb1602977 | ||
|
|
c98deeb709 | ||
|
|
cd0be38bb4 | ||
|
|
578a2308b1 | ||
|
|
279607f5f8 | ||
|
|
456e435fe0 | ||
|
|
31981090e4 | ||
|
|
dd77dbdc11 | ||
|
|
fcb30c269b | ||
|
|
5f59d7f7b4 | ||
|
|
1364157e91 | ||
|
|
a524fd4be8 | ||
|
|
de23226d0c | ||
|
|
550fa6994e | ||
|
|
f9e5f1f1fd | ||
|
|
6b0e7e1f46 | ||
|
|
9fb44fae29 | ||
|
|
1f4359cc80 | ||
|
|
8293fd4e09 | ||
|
|
2beb948a3b | ||
|
|
409c6cac4c | ||
|
|
ec39de8cae | ||
|
|
586c3f9140 | ||
|
|
feda615ed5 | ||
|
|
4f41ccfcbf | ||
|
|
e9f69d1068 | ||
|
|
5cc9f6f9cb | ||
|
|
09aa845940 | ||
|
|
73b87f2558 | ||
|
|
c0f264ffe0 | ||
|
|
52d0f715c3 | ||
|
|
ec30da8af7 | ||
|
|
27b7002138 | ||
|
|
a2ed4db562 | ||
|
|
628633d02e | ||
|
|
f8edf452de | ||
|
|
12dc171c48 | ||
|
|
42e6214a42 | ||
|
|
53af5ead53 | ||
|
|
b706c0064e | ||
|
|
8e1b51701b | ||
|
|
ad068824d0 | ||
|
|
7c4c57759d | ||
|
|
1118931516 | ||
|
|
7150638836 | ||
|
|
30693a2dae | ||
|
|
368ead54b2 | ||
|
|
7c10415cd8 | ||
|
|
b5122b6a7b | ||
|
|
8bc1a9c4ba | ||
|
|
4169cac51f | ||
|
|
c394a834c3 | ||
|
|
9069c538ad | ||
|
|
4e16eb0476 | ||
|
|
e924ef229c | ||
|
|
8012eedab5 | ||
|
|
33c53a2418 | ||
|
|
3b9b13b706 | ||
|
|
94d51b2321 | ||
|
|
0342d62109 | ||
|
|
4e5ce6b65d | ||
|
|
e11b320cd6 | ||
|
|
cb6bfefc7a | ||
|
|
25ea5f6fa1 | ||
|
|
4958404f37 | ||
|
|
3e11b5fe15 | ||
|
|
57bc058209 | ||
|
|
610fa69f15 | ||
|
|
3a9b594fc5 | ||
|
|
0bc8fe48e3 | ||
|
|
7350d0a3ff | ||
|
|
b376b1594e | ||
|
|
88801166b6 | ||
|
|
ad58deeae3 | ||
|
|
666d454b42 | ||
|
|
b7efd200f0 | ||
|
|
e83e467667 | ||
|
|
2efa9de78a | ||
|
|
25baf73005 | ||
|
|
0bd424b5e6 | ||
|
|
d841ef5eb5 | ||
|
|
188ff2dd20 | ||
|
|
7564b204ec | ||
|
|
6fd7350c7b | ||
|
|
7ed4d1c432 | ||
|
|
5f847c4ce3 | ||
|
|
090d158fb9 | ||
|
|
81ced3bd0f | ||
|
|
ab721c64b3 | ||
|
|
93369e8773 | ||
|
|
23f2314da7 | ||
|
|
8a23c294a4 | ||
|
|
a7a3ae13dd | ||
|
|
5edd59806c | ||
|
|
a2aadee28f | ||
|
|
923216f9a9 | ||
|
|
0f9702f4b4 | ||
|
|
df53e6c4cf | ||
|
|
916c97b625 | ||
|
|
439689b219 | ||
|
|
1d78712b6c | ||
|
|
39f716f902 | ||
|
|
22c8154811 | ||
|
|
05e9983e25 | ||
|
|
f51b356002 | ||
|
|
ec9570fdd0 | ||
|
|
b37fdea5bf | ||
|
|
29c245ceba | ||
|
|
b8b49c50b9 | ||
|
|
127b309a0d | ||
|
|
b7c3ff6e6d | ||
|
|
0aa2b83450 | ||
|
|
684f32fabe | ||
|
|
eefcbbb37b | ||
|
|
903493799d | ||
|
|
7d90b0558e | ||
|
|
504b6dc93f | ||
|
|
6998acad66 | ||
|
|
cc1dcf8043 | ||
|
|
f54bce2abb | ||
|
|
1145976ff9 | ||
|
|
13d41f82d7 | ||
|
|
caf7a21c6f | ||
|
|
7c38649527 | ||
|
|
d1a15dea03 | ||
|
|
f1f8db4856 | ||
|
|
bcc49d1c5f | ||
|
|
63d00ea3c2 | ||
|
|
fdc52e0ea9 | ||
|
|
767139b235 | ||
|
|
bddb2152e5 | ||
|
|
8d04ac171d | ||
|
|
ae6fe098cb | ||
|
|
79c7b27034 | ||
|
|
2644b239a3 | ||
|
|
eb432cd3b7 | ||
|
|
312ea12bc2 | ||
|
|
67bfa19ce0 | ||
|
|
3335b2a01e | ||
|
|
78816a3ee7 | ||
|
|
7acbee8ae4 | ||
|
|
4dd59690e0 | ||
|
|
a2226a43ac | ||
|
|
62c3e56247 | ||
|
|
89d7eb8b78 | ||
|
|
8475ec7e36 | ||
|
|
4497aba1a9 | ||
|
|
cddc8089bc | ||
|
|
ce15b43798 | ||
|
|
430f4d28e4 | ||
|
|
d279a4871f | ||
|
|
f208d7b50f | ||
|
|
df18f3f1ff | ||
|
|
fbcfe6596e | ||
|
|
b5b664e570 | ||
|
|
2f216b5255 | ||
|
|
d4dca3baac | ||
|
|
de7d78a9f1 | ||
|
|
6a629f7d7f | ||
|
|
f74516a032 | ||
|
|
78200b309f | ||
|
|
b120080b85 | ||
|
|
4b8c342833 | ||
|
|
fa26d222cb | ||
|
|
e2f957109f | ||
|
|
20dd63aabf | ||
|
|
c656e71eb8 | ||
|
|
104c92d4f3 | ||
|
|
5cd90f5826 | ||
|
|
178ab8ef2e | ||
|
|
e6c0484074 | ||
|
|
dd42a0919d |
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
15
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,13 +1,14 @@
|
||||
# Read and remove this section before submitting
|
||||
# Read this section before submitting
|
||||
|
||||
* Ensure your PR follows the [External Contribution Guidelines](https://github.com/leanprover/lean4/blob/master/CONTRIBUTING.md).
|
||||
* Please make sure the PR has excellent documentation and tests. If we label it `missing documentation` or `missing tests` then it needs fixing!
|
||||
* Add the link to your `RFC` or `bug` issue below.
|
||||
* Include the link to your `RFC` or `bug` issue in the description.
|
||||
* If the issue does not already have approval from a developer, submit the PR as draft.
|
||||
* Remove this section before submitting.
|
||||
* The PR title/description will become the commit message. Keep it up-to-date as the PR evolves.
|
||||
* If you rebase your PR onto `nightly-with-mathlib` then CI will test Mathlib against your PR.
|
||||
* You can manage the `awaiting-review`, `awaiting-author`, and `WIP` labels yourself, by writing a comment containing one of these labels on its own line.
|
||||
* Remove this section, up to and including the `---` before submitting.
|
||||
|
||||
You can manage the `awaiting-review`, `awaiting-author`, and `WIP` labels yourself, by writing a comment containing one of these labels on its own line.
|
||||
---
|
||||
|
||||
# Summary
|
||||
|
||||
Link to `RFC` or `bug` issue:
|
||||
Closes #0000 (`RFC` or `bug` issue number fixed by this PR, if any)
|
||||
|
||||
22
.github/workflows/actionlint.yml
vendored
Normal file
22
.github/workflows/actionlint.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Actionlint
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
paths:
|
||||
- '.github/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/**'
|
||||
merge_group:
|
||||
|
||||
jobs:
|
||||
actionlint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: actionlint
|
||||
uses: raven-actions/actionlint@v1
|
||||
with:
|
||||
pyflakes: false # we do not use python scripts
|
||||
46
.github/workflows/ci.yml
vendored
46
.github/workflows/ci.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
github.event_name == 'pull_request' && !contains( github.event.pull_request.labels.*.name, 'full-ci')
|
||||
}}
|
||||
run: |
|
||||
echo "quick=${{env.quick}}" >> $GITHUB_OUTPUT
|
||||
echo "quick=${{env.quick}}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Configure build matrix
|
||||
id: set-matrix
|
||||
@@ -124,10 +124,11 @@ jobs:
|
||||
"release": true,
|
||||
"quick": false,
|
||||
"cross": true,
|
||||
"cross_target": "aarch64-apple-darwin",
|
||||
"shell": "bash -euxo pipefail {0}",
|
||||
"CMAKE_OPTIONS": "-DUSE_GMP=OFF -DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
|
||||
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-apple-darwin.tar.zst https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst",
|
||||
"prepare-llvm": "EXTRA_FLAGS=--target=aarch64-apple-darwin ../script/prepare-llvm-macos.sh lean-llvm-aarch64-* lean-llvm-x86_64-*",
|
||||
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm-aarch64-* lean-llvm-x86_64-*",
|
||||
"binary-check": "otool -L",
|
||||
"tar": "gtar" // https://github.com/actions/runner-images/issues/2619
|
||||
},
|
||||
@@ -151,9 +152,10 @@ jobs:
|
||||
"release": true,
|
||||
"quick": false,
|
||||
"cross": true,
|
||||
"cross_target": "aarch64-unknown-linux-gnu",
|
||||
"shell": "nix-shell --arg pkgsDist \"import (fetchTarball \\\"channel:nixos-19.03\\\") {{ localSystem.config = \\\"aarch64-unknown-linux-gnu\\\"; }}\" --run \"bash -euxo pipefail {0}\"",
|
||||
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-linux-gnu.tar.zst https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-linux-gnu.tar.zst",
|
||||
"prepare-llvm": "EXTRA_FLAGS=--target=aarch64-unknown-linux-gnu ../script/prepare-llvm-linux.sh lean-llvm-aarch64-* lean-llvm-x86_64-*"
|
||||
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm-aarch64-* lean-llvm-x86_64-*"
|
||||
},
|
||||
{
|
||||
"name": "Linux 32bit",
|
||||
@@ -201,8 +203,8 @@ jobs:
|
||||
git fetch nightly --tags
|
||||
LEAN_VERSION_STRING="nightly-$(date -u +%F)"
|
||||
# do nothing if commit already has a different tag
|
||||
if [[ $(git name-rev --name-only --tags --no-undefined HEAD 2> /dev/null || echo $LEAN_VERSION_STRING) == $LEAN_VERSION_STRING ]]; then
|
||||
echo "nightly=$LEAN_VERSION_STRING" >> $GITHUB_OUTPUT
|
||||
if [[ "$(git name-rev --name-only --tags --no-undefined HEAD 2> /dev/null || echo "$LEAN_VERSION_STRING")" == "$LEAN_VERSION_STRING" ]]; then
|
||||
echo "nightly=$LEAN_VERSION_STRING" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -210,7 +212,7 @@ jobs:
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.repository == 'leanprover/lean4'
|
||||
id: set-release
|
||||
run: |
|
||||
TAG_NAME=${GITHUB_REF##*/}
|
||||
TAG_NAME="${GITHUB_REF##*/}"
|
||||
|
||||
# From https://github.com/fsaintjacques/semver-tool/blob/master/src/semver
|
||||
|
||||
@@ -227,11 +229,13 @@ jobs:
|
||||
|
||||
if [[ ${TAG_NAME} =~ ${SEMVER_REGEX} ]]; then
|
||||
echo "Tag ${TAG_NAME} matches SemVer regex, with groups ${BASH_REMATCH[1]} ${BASH_REMATCH[2]} ${BASH_REMATCH[3]} ${BASH_REMATCH[4]}"
|
||||
echo "LEAN_VERSION_MAJOR=${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT
|
||||
echo "LEAN_VERSION_MINOR=${BASH_REMATCH[2]}" >> $GITHUB_OUTPUT
|
||||
echo "LEAN_VERSION_PATCH=${BASH_REMATCH[3]}" >> $GITHUB_OUTPUT
|
||||
echo "LEAN_SPECIAL_VERSION_DESC=${BASH_REMATCH[4]##-}" >> $GITHUB_OUTPUT
|
||||
echo "RELEASE_TAG=$TAG_NAME" >> $GITHUB_OUTPUT
|
||||
{
|
||||
echo "LEAN_VERSION_MAJOR=${BASH_REMATCH[1]}"
|
||||
echo "LEAN_VERSION_MINOR=${BASH_REMATCH[2]}"
|
||||
echo "LEAN_VERSION_PATCH=${BASH_REMATCH[3]}"
|
||||
echo "LEAN_SPECIAL_VERSION_DESC=${BASH_REMATCH[4]##-}"
|
||||
echo "RELEASE_TAG=$TAG_NAME"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "Tag ${TAG_NAME} did not match SemVer regex."
|
||||
fi
|
||||
@@ -319,9 +323,15 @@ jobs:
|
||||
mkdir build
|
||||
cd build
|
||||
ulimit -c unlimited # coredumps
|
||||
# arguments passed to `cmake`
|
||||
# this also enables githash embedding into stage 1 library
|
||||
OPTIONS=(-DCHECK_OLEAN_VERSION=ON)
|
||||
OPTIONS+=(-DLEAN_EXTRA_MAKE_OPTS=-DwarningAsError=true)
|
||||
if [[ -n '${{ matrix.cross_target }}' ]]; then
|
||||
# used by `prepare-llvm`
|
||||
export EXTRA_FLAGS=--target=${{ matrix.cross_target }}
|
||||
OPTIONS+=(-DLEAN_PLATFORM_TARGET=${{ matrix.cross_target }})
|
||||
fi
|
||||
if [[ -n '${{ matrix.prepare-llvm }}' ]]; then
|
||||
wget -q ${{ matrix.llvm-url }}
|
||||
PREPARE="$(${{ matrix.prepare-llvm }})"
|
||||
@@ -405,7 +415,7 @@ jobs:
|
||||
- name: CCache stats
|
||||
run: ccache -s
|
||||
- name: Show stacktrace for coredumps
|
||||
if: ${{ failure() }} && matrix.os == 'ubuntu-latest'
|
||||
if: ${{ failure() && matrix.os == 'ubuntu-latest' }}
|
||||
run: |
|
||||
for c in coredumps/*; do
|
||||
progbin="$(file $c | sed "s/.*execfn: '\([^']*\)'.*/\1/")"
|
||||
@@ -413,7 +423,7 @@ jobs:
|
||||
done
|
||||
- name: Upload coredumps
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ failure() }} && matrix.os == 'ubuntu-latest'
|
||||
if: ${{ failure() && matrix.os == 'ubuntu-latest' }}
|
||||
with:
|
||||
name: coredumps-${{ matrix.name }}
|
||||
path: |
|
||||
@@ -480,16 +490,16 @@ jobs:
|
||||
run: |
|
||||
git remote add nightly https://foo:'${{ secrets.PUSH_NIGHTLY_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-nightly.git
|
||||
git fetch nightly --tags
|
||||
git tag ${{ needs.configure.outputs.nightly }}
|
||||
git push nightly ${{ needs.configure.outputs.nightly }}
|
||||
git tag "${{ needs.configure.outputs.nightly }}"
|
||||
git push nightly "${{ needs.configure.outputs.nightly }}"
|
||||
git push -f origin refs/tags/${{ needs.configure.outputs.nightly }}:refs/heads/nightly
|
||||
last_tag=$(git log HEAD^ --simplify-by-decoration --pretty="format:%d" | grep -o "nightly-[-0-9]*" | head -n 1)
|
||||
last_tag="$(git log HEAD^ --simplify-by-decoration --pretty="format:%d" | grep -o "nightly-[-0-9]*" | head -n 1)"
|
||||
echo -e "*Changes since ${last_tag}:*\n\n" > diff.md
|
||||
git show $last_tag:RELEASES.md > old.md
|
||||
git show "$last_tag":RELEASES.md > old.md
|
||||
#./script/diff_changelogs.py old.md doc/changes.md >> diff.md
|
||||
diff --changed-group-format='%>' --unchanged-group-format='' old.md RELEASES.md >> diff.md || true
|
||||
echo -e "\n*Full commit log*\n" >> diff.md
|
||||
git log --oneline $last_tag..HEAD | sed 's/^/* /' >> diff.md
|
||||
git log --oneline "$last_tag"..HEAD | sed 's/^/* /' >> diff.md
|
||||
- name: Release Nightly
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
|
||||
38
.github/workflows/nix-ci.yml
vendored
38
.github/workflows/nix-ci.yml
vendored
@@ -87,7 +87,17 @@ jobs:
|
||||
run: |
|
||||
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc#{lean-mdbook,leanInk,alectryon,test,inked} -o push-doc
|
||||
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc
|
||||
# https://github.com/netlify/cli/issues/1809
|
||||
cp -r --dereference ./result ./dist
|
||||
if: matrix.name == 'Nix Linux'
|
||||
- name: Check manual for broken links
|
||||
id: lychee
|
||||
uses: lycheeverse/lychee-action@v1.9.0
|
||||
with:
|
||||
fail: false # report errors but do not block CI on temporary failures
|
||||
# gmplib.org consistently times out from GH actions
|
||||
# the GitHub token is to avoid rate limiting
|
||||
args: --base './dist' --no-progress --github-token ${{ secrets.GITHUB_TOKEN }} --exclude 'gmplib.org' './dist/**/*.html'
|
||||
- name: Push to Cachix
|
||||
run: |
|
||||
[ -z "${{ secrets.CACHIX_AUTH_TOKEN }}" ] || cachix push -j4 lean4 ./push-* || true
|
||||
@@ -95,13 +105,29 @@ jobs:
|
||||
run: |
|
||||
rm -rf nix-store-cache || true
|
||||
nix copy ./push-* --to file://$PWD/nix-store-cache?compression=none
|
||||
- name: Publish manual
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
- id: deploy-info
|
||||
name: Compute Deployment Metadata
|
||||
run: |
|
||||
set -e
|
||||
python3 -c 'import base64; print("alias="+base64.urlsafe_b64encode(bytes.fromhex("${{github.sha}}")).decode("utf-8").rstrip("="))' >> "$GITHUB_OUTPUT"
|
||||
echo "message=`git log -1 --pretty=format:"%s"`" >> "$GITHUB_OUTPUT"
|
||||
- name: Publish manual to Netlify
|
||||
uses: nwtgck/actions-netlify@v2.0
|
||||
id: publish-manual
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./result
|
||||
destination_dir: ./doc
|
||||
if: matrix.name == 'Nix Linux' && github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||
publish-dir: ./dist
|
||||
production-branch: master
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
deploy-message: |
|
||||
${{ github.event_name == 'pull_request' && format('pr#{0}: {1}', github.event.number, github.event.pull_request.title) || format('ref/{0}: {1}', github.ref_name, steps.deploy-info.outputs.message) }}
|
||||
alias: ${{ steps.deploy-info.outputs.alias }}
|
||||
enable-commit-comment: false
|
||||
enable-pull-request-comment: false
|
||||
github-deployment-environment: "lean-lang.org/lean4/doc"
|
||||
fails-without-credentials: false
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: "b8e805d2-7e9b-4f80-91fb-a84d72fc4a68"
|
||||
- name: Fixup CCache Cache
|
||||
run: |
|
||||
sudo chown -R $USER /nix/var/cache
|
||||
|
||||
224
.github/workflows/pr-release.yml
vendored
224
.github/workflows/pr-release.yml
vendored
@@ -6,6 +6,10 @@
|
||||
# Instead we use `workflow_run`, which essentially allows us to escalate privileges
|
||||
# (but only runs the CI as described in the `master` branch, not in the PR branch).
|
||||
|
||||
# The main specification/documentation for this workflow is at
|
||||
# https://leanprover-community.github.io/contribute/tags_and_branches.html
|
||||
# Keep that in sync!
|
||||
|
||||
name: PR release
|
||||
|
||||
on:
|
||||
@@ -16,27 +20,16 @@ on:
|
||||
jobs:
|
||||
on-success:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.workflow_run.conclusion == 'success' && github.repository == 'leanprover/lean4'
|
||||
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request' && github.repository == 'leanprover/lean4'
|
||||
steps:
|
||||
- name: Retrieve information about the original workflow
|
||||
uses: potiuk/get-workflow-origin@v1_1 # https://github.com/marketplace/actions/get-workflow-origin
|
||||
# This action is deprecated and archived, but it seems hard to find a better solution for getting the PR number
|
||||
# see https://github.com/orgs/community/discussions/25220 for some discussion
|
||||
id: workflow-info
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
sourceRunId: ${{ github.event.workflow_run.id }}
|
||||
- name: Checkout
|
||||
# Only proceed if the previous workflow had a pull request number.
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.PR_RELEASES_TOKEN }}
|
||||
# Since `workflow_run` runs on master, we need to specify which commit to check out,
|
||||
# so that we tag the PR.
|
||||
# It's important that we use `sourceHeadSha` here, not `targetCommitSha`
|
||||
# as we *don't* want the synthetic merge with master.
|
||||
ref: ${{ steps.workflow-info.outputs.sourceHeadSha }}
|
||||
# We need a full checkout, so that we can push the PR commits to the `lean4-pr-releases` repo.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download artifact from the previous workflow.
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
@@ -47,14 +40,22 @@ jobs:
|
||||
path: artifacts
|
||||
name: build-.*
|
||||
name_is_regexp: true
|
||||
- name: Prepare release
|
||||
|
||||
- name: Push tag
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
run: |
|
||||
git init --bare lean4.git
|
||||
git -C lean4.git remote add origin https://github.com/${{ github.repository_owner }}/lean4.git
|
||||
git -C lean4.git fetch -n origin master
|
||||
git -C lean4.git fetch -n origin "${{ steps.workflow-info.outputs.sourceHeadSha }}"
|
||||
git -C lean4.git tag -f pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} "${{ steps.workflow-info.outputs.sourceHeadSha }}"
|
||||
git -C lean4.git remote add pr-releases https://foo:'${{ secrets.PR_RELEASES_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-pr-releases.git
|
||||
git -C lean4.git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
- name: Delete existing release if present
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
run: |
|
||||
git remote add pr-releases https://foo:'${{ secrets.PR_RELEASES_TOKEN }}'@github.com/${{ github.repository_owner }}/lean4-pr-releases.git
|
||||
# Try to delete any existing release for the current PR.
|
||||
gh release delete --repo ${{ github.repository_owner }}/lean4-pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }} -y || true
|
||||
git tag -f pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
git push -f pr-releases pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
|
||||
- name: Release
|
||||
@@ -72,19 +73,41 @@ jobs:
|
||||
# The token used here must have `workflow` privileges.
|
||||
GITHUB_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
|
||||
|
||||
- name: Report release status
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
await github.rest.repos.createCommitStatus({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
sha: "${{ steps.workflow-info.outputs.sourceHeadSha }}",
|
||||
state: "success",
|
||||
context: "PR toolchain",
|
||||
description: "${{ github.repository_owner }}/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}",
|
||||
});
|
||||
|
||||
- name: Add label
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
number: ${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
labels: toolchain-available
|
||||
script: |
|
||||
await github.rest.issues.addLabels({
|
||||
issue_number: ${{ steps.workflow-info.outputs.pullRequestNumber }},
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['toolchain-available']
|
||||
})
|
||||
|
||||
# Next, determine the most recent nightly release in this PR's history.
|
||||
- name: Find most recent nightly
|
||||
- name: Find most recent nightly in feature branch
|
||||
id: most-recent-nightly-tag
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
run: |
|
||||
echo "MOST_RECENT_NIGHTLY=$(script/most-recent-nightly-tag.sh)" >> $GITHUB_ENV
|
||||
git -C lean4.git remote add nightly https://github.com/leanprover/lean4-nightly.git
|
||||
git -C lean4.git fetch nightly '+refs/tags/nightly-*:refs/tags/nightly-*'
|
||||
git -C lean4.git tag --merged "${{ steps.workflow-info.outputs.sourceHeadSha }}" --list "nightly-*" \
|
||||
| sort -rV | head -n 1 | sed "s/^nightly-*/MOST_RECENT_NIGHTLY=/" | tee -a "$GITHUB_ENV"
|
||||
|
||||
- name: 'Setup jq'
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
@@ -95,43 +118,57 @@ jobs:
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
|
||||
id: ready
|
||||
run: |
|
||||
echo "Most recent nightly: $MOST_RECENT_NIGHTLY"
|
||||
NIGHTLY_SHA=$(git rev-parse nightly-$MOST_RECENT_NIGHTLY^{commit})
|
||||
echo "SHA of most recent nightly: $NIGHTLY_SHA"
|
||||
MERGE_BASE_SHA=$(git merge-base origin/master HEAD)
|
||||
echo "Most recent nightly release in your branch: $MOST_RECENT_NIGHTLY"
|
||||
NIGHTLY_SHA=$(git -C lean4.git rev-parse "nightly-$MOST_RECENT_NIGHTLY^{commit}")
|
||||
echo "SHA of most recent nightly release: $NIGHTLY_SHA"
|
||||
MERGE_BASE_SHA=$(git -C lean4.git merge-base origin/master "${{ steps.workflow-info.outputs.sourceHeadSha }}")
|
||||
echo "SHA of merge-base: $MERGE_BASE_SHA"
|
||||
if [ "$NIGHTLY_SHA" = "$MERGE_BASE_SHA" ]; then
|
||||
echo "Most recent nightly tag agrees with the merge base."
|
||||
echo "The merge base of this PR coincides with the nightly release"
|
||||
|
||||
REMOTE_BRANCHES=$(git ls-remote -h https://github.com/leanprover-community/mathlib4.git nightly-testing-$MOST_RECENT_NIGHTLY)
|
||||
MATHLIB_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover-community/mathlib4.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
|
||||
|
||||
if [[ -n "$REMOTE_BRANCHES" ]]; then
|
||||
echo "... and Mathlib has a 'nightly-testing-$MOST_RECENT_NIGHTLY' branch."
|
||||
if [[ -n "$MATHLIB_REMOTE_TAGS" ]]; then
|
||||
echo "... and Mathlib has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
MESSAGE=""
|
||||
else
|
||||
echo "... but Mathlib does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' branch."
|
||||
MESSAGE="- ❗ Mathlib CI can not be attempted yet, as the 'nightly-testing-$MOST_RECENT_NIGHTLY' branch does not exist there yet. We will retry when you push more commits. It may be necessary to rebase onto 'nightly' tomorrow."
|
||||
echo "... but Mathlib does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
MESSAGE="- ❗ Mathlib CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Mathlib CI should run now."
|
||||
fi
|
||||
|
||||
STD_REMOTE_TAGS="$(git ls-remote https://github.com/leanprover/std4.git nightly-testing-"$MOST_RECENT_NIGHTLY")"
|
||||
|
||||
if [[ -n "$STD_REMOTE_TAGS" ]]; then
|
||||
echo "... and Std has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
MESSAGE=""
|
||||
else
|
||||
echo "... but Std does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
|
||||
MESSAGE="- ❗ Std CI can not be attempted yet, as the \`nightly-testing-$MOST_RECENT_NIGHTLY\` tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto \`nightly-with-mathlib\`, Std CI should run now."
|
||||
fi
|
||||
|
||||
else
|
||||
echo "The most recently nightly tag on this branch has SHA: $NIGHTLY_SHA"
|
||||
echo "but 'git merge-base origin/master HEAD' reported: $MERGE_BASE_SHA"
|
||||
git log -10
|
||||
git -C lean4.git log -10 origin/master
|
||||
|
||||
MESSAGE="- ❗ Mathlib CI will not be attempted unless you rebase your PR onto the 'nightly' branch."
|
||||
MESSAGE="- ❗ Std/Mathlib CI will not be attempted unless your PR branches off the \`nightly-with-mathlib\` branch."
|
||||
fi
|
||||
|
||||
if [[ -n "$MESSAGE" ]]; then
|
||||
|
||||
echo "Checking existing messages"
|
||||
|
||||
# The code for updating comments is duplicated in mathlib's
|
||||
# scripts/lean-pr-testing-comments.sh
|
||||
# so keep in sync
|
||||
|
||||
# Use GitHub API to check if a comment already exists
|
||||
existing_comment=$(curl -L -s -H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
|
||||
existing_comment="$(curl -L -s -H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments" \
|
||||
| jq '.[] | select(.body | startswith("- ❗ Mathlib") or startswith("- ✅ Mathlib") or startswith("- ❌ Mathlib") or startswith("- 💥 Mathlib") or startswith("- 🟡 Mathlib"))')
|
||||
existing_comment_id=$(echo "$existing_comment" | jq -r .id)
|
||||
existing_comment_body=$(echo "$existing_comment" | jq -r .body)
|
||||
| jq 'first(.[] | select(.body | test("^- . Mathlib") or startswith("Mathlib CI status")) | select(.user.login == "leanprover-community-mathlib4-bot"))')"
|
||||
existing_comment_id="$(echo "$existing_comment" | jq -r .id)"
|
||||
existing_comment_body="$(echo "$existing_comment" | jq -r .body)"
|
||||
|
||||
if [[ "$existing_comment_body" != *"$MESSAGE"* ]]; then
|
||||
MESSAGE="$MESSAGE ($(date "+%Y-%m-%d %H:%M:%S"))"
|
||||
@@ -141,13 +178,14 @@ jobs:
|
||||
# Append new result to the existing comment or post a new comment
|
||||
# It's essential we use the MATHLIB4_BOT token here, so that Mathlib CI can subsequently edit the comment.
|
||||
if [ -z "$existing_comment_id" ]; then
|
||||
INTRO="Mathlib CI status ([docs](https://leanprover-community.github.io/contribute/tags_and_branches.html)):"
|
||||
# Post new comment with a bullet point
|
||||
echo "Posting as new comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
|
||||
curl -L -s \
|
||||
-X POST \
|
||||
-H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-d "$(jq --null-input --arg val "$MESSAGE" '{"body": $val}')" \
|
||||
-d "$(jq --null-input --arg intro "$INTRO" --arg val "$MESSAGE" '{"body":($intro + "\n" + $val)}')" \
|
||||
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
|
||||
else
|
||||
# Append new result to the existing comment
|
||||
@@ -162,18 +200,93 @@ jobs:
|
||||
else
|
||||
echo "The message already exists in the comment body."
|
||||
fi
|
||||
echo "::set-output name=mathlib_ready::false"
|
||||
echo "mathlib_ready=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "::set-output name=mathlib_ready::true"
|
||||
echo "mathlib_ready=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Report mathlib base
|
||||
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true' }}
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const description =
|
||||
process.env.MOST_RECENT_NIGHTLY ?
|
||||
"nightly-" + process.env.MOST_RECENT_NIGHTLY :
|
||||
"not branched off nightly";
|
||||
await github.rest.repos.createCommitStatus({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
sha: "${{ steps.workflow-info.outputs.sourceHeadSha }}",
|
||||
state: "success",
|
||||
context: "PR branched off:",
|
||||
description: description,
|
||||
});
|
||||
|
||||
# We next automatically create a Std branch using this toolchain.
|
||||
# Std doesn't itself have a mechanism to report results of CI from this branch back to Lean
|
||||
# Instead this is taken care of by Mathlib CI, which will fail if Std fails.
|
||||
- name: Cleanup workspace
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
run: |
|
||||
sudo rm -rf ./*
|
||||
|
||||
# Checkout the Std repository with all branches
|
||||
- name: Checkout Std repository
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: leanprover/std4
|
||||
token: ${{ secrets.MATHLIB4_BOT }}
|
||||
ref: nightly-testing
|
||||
fetch-depth: 0 # This ensures we check out all tags and branches.
|
||||
|
||||
- name: Check if tag exists
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
id: check_std_tag
|
||||
run: |
|
||||
git config user.name "leanprover-community-mathlib4-bot"
|
||||
git config user.email "leanprover-community-mathlib4-bot@users.noreply.github.com"
|
||||
|
||||
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
|
||||
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
|
||||
else
|
||||
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' tag at Std. Falling back to 'nightly-testing'."
|
||||
BASE=nightly-testing
|
||||
fi
|
||||
|
||||
echo "Using base branch: $BASE"
|
||||
|
||||
EXISTS="$(git ls-remote --heads origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} | wc -l)"
|
||||
echo "Branch exists: $EXISTS"
|
||||
if [ "$EXISTS" = "0" ]; then
|
||||
echo "Branch does not exist, creating it."
|
||||
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
|
||||
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
|
||||
git add lean-toolchain
|
||||
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
|
||||
else
|
||||
echo "Branch already exists, pushing an empty commit."
|
||||
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
# The Std `nightly-testing` or `nightly-testing-YYYY-MM-DD` branch may have moved since this branch was created, so merge their changes.
|
||||
# (This should no longer be possible once `nightly-testing-YYYY-MM-DD` is a tag, but it is still safe to merge.)
|
||||
git merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
|
||||
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
|
||||
fi
|
||||
|
||||
- name: Push changes
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
run: |
|
||||
git push origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
|
||||
|
||||
# We next automatically create a Mathlib branch using this toolchain.
|
||||
# Mathlib CI will be responsible for reporting back success or failure
|
||||
# to the PR comments asynchronously.
|
||||
- name: Cleanup workspace
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
run: |
|
||||
sudo rm -rf *
|
||||
sudo rm -rf ./*
|
||||
|
||||
# Checkout the mathlib4 repository with all branches
|
||||
- name: Checkout mathlib4 repository
|
||||
@@ -185,37 +298,38 @@ jobs:
|
||||
ref: nightly-testing
|
||||
fetch-depth: 0 # This ensures we check out all tags and branches.
|
||||
|
||||
- name: Check if branch exists
|
||||
- name: Check if tag exists
|
||||
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
|
||||
id: check_branch
|
||||
id: check_mathlib_tag
|
||||
run: |
|
||||
git config user.name "leanprover-community-mathlib4-bot"
|
||||
git config user.email "leanprover-community-mathlib4-bot@users.noreply.github.com"
|
||||
|
||||
if git branch -r | grep -q "nightly-testing-${MOST_RECENT_NIGHTLY}"; then
|
||||
BASE=nightly-testing-${MOST_RECENT_NIGHTLY}
|
||||
if git ls-remote --heads --tags --exit-code origin "nightly-testing-${MOST_RECENT_NIGHTLY}" >/dev/null; then
|
||||
BASE="nightly-testing-${MOST_RECENT_NIGHTLY}"
|
||||
else
|
||||
echo "This shouldn't be possible: couldn't find a 'nightly-testing-${MOST_RECENT_NIGHTLY}' branch at Mathlib. Falling back to 'nightly-testing'."
|
||||
BASE=nightly-testing
|
||||
fi
|
||||
|
||||
echo "Using base branch: $BASE"
|
||||
echo "Using base tag: $BASE"
|
||||
|
||||
git checkout $BASE
|
||||
|
||||
EXISTS=$(git ls-remote --heads origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} | wc -l)
|
||||
EXISTS="$(git ls-remote --heads origin lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} | wc -l)"
|
||||
echo "Branch exists: $EXISTS"
|
||||
if [ "$EXISTS" = "0" ]; then
|
||||
echo "Branch does not exist, creating it."
|
||||
git checkout -b lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE"
|
||||
echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
|
||||
git add lean-toolchain
|
||||
sed -i "s/require std from git \"https:\/\/github.com\/leanprover\/std4\" @ \".\+\"/require std from git \"https:\/\/github.com\/leanprover\/std4\" @ \"nightly-testing-${MOST_RECENT_NIGHTLY}\"/" lakefile.lean
|
||||
git add lakefile.lean
|
||||
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
|
||||
else
|
||||
echo "Branch already exists, pushing an empty commit."
|
||||
git checkout lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
# The Mathlib `nightly-testing` or `nightly-testing-YYYY-MM-DD` branch may have moved since this branch was created, so merge their changes.
|
||||
git merge $BASE --strategy-option ours --no-commit --allow-unrelated-histories
|
||||
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}
|
||||
# The Mathlib `nightly-testing` branch or `nightly-testing-YYYY-MM-DD` tag may have moved since this branch was created, so merge their changes.
|
||||
# (This should no longer be possible once `nightly-testing-YYYY-MM-DD` is a tag, but it is still safe to merge.)
|
||||
git merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
|
||||
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
|
||||
fi
|
||||
|
||||
|
||||
64
.github/workflows/update-stage0.yml
vendored
Normal file
64
.github/workflows/update-stage0.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
name: Update stage0
|
||||
|
||||
# This action will update stage0 on master as soon as
|
||||
# src/stdlib_flags.h and stage0/src/stdlib_flags.h
|
||||
# are out of sync there, or when manually triggered.
|
||||
# The update bypasses the merge queue to be quick.
|
||||
# Also see <doc/dev/bootstrap.md>.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: stage0
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
update-stage0:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# 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@v3
|
||||
with:
|
||||
ssh-key: ${{secrets.STAGE0_SSH_KEY}}
|
||||
- run: echo "should_update_stage0=yes" >> "$GITHUB_ENV"
|
||||
- name: Check if automatic update is needed
|
||||
if: github.event_name == 'push'
|
||||
run: |
|
||||
if diff -u src/stdlib_flags.h stage0/src/stdlib_flags.h
|
||||
then
|
||||
echo "src/stdlib_flags.h and stage0/src/stdlib_flags.h agree, nothing to do"
|
||||
echo "should_update_stage0=no" >> "$GITHUB_ENV"
|
||||
fi
|
||||
- name: Setup git user
|
||||
if: env.should_update_stage0 == 'yes'
|
||||
run: |
|
||||
git config --global user.name "Lean stage0 autoupdater"
|
||||
git config --global user.email "<>"
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
uses: DeterminateSystems/nix-installer-action@main
|
||||
# Would be nice, but does not work yet:
|
||||
# https://github.com/DeterminateSystems/magic-nix-cache/issues/39
|
||||
# This action does not run that often and building runs in a few minutes, so ok for now
|
||||
#- if: env.should_update_stage0 == 'yes'
|
||||
# uses: DeterminateSystems/magic-nix-cache-action@v2
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
name: Install Cachix
|
||||
uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: lean4
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
run: nix run .#update-stage0-commit
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
run: git show --stat
|
||||
- if: env.should_update_stage0 == 'yes' && github.event_name == 'push'
|
||||
name: Sanity check # to avoid loops
|
||||
run: |
|
||||
diff -u src/stdlib_flags.h stage0/src/stdlib_flags.h || exit 1
|
||||
- if: env.should_update_stage0 == 'yes'
|
||||
run: git push origin
|
||||
@@ -1,13 +1,8 @@
|
||||
This is the repository for **Lean 4**.
|
||||
|
||||
We provide [nightly releases](https://github.com/leanprover/lean4-nightly/releases)
|
||||
and have just begun regular [stable point releases](https://github.com/leanprover/lean4/releases).
|
||||
|
||||
# About
|
||||
|
||||
- [Quickstart](https://github.com/leanprover/lean4/blob/master/doc/quickstart.md)
|
||||
- [Walkthrough installation video](https://www.youtube.com/watch?v=yZo6k48L0VY)
|
||||
- [Quick tour video](https://youtu.be/zyXtbb_eYbY)
|
||||
- [Quickstart](https://lean-lang.org/lean4/doc/quickstart.html)
|
||||
- [Homepage](https://lean-lang.org)
|
||||
- [Theorem Proving Tutorial](https://lean-lang.org/theorem_proving_in_lean4/)
|
||||
- [Functional Programming in Lean](https://lean-lang.org/functional_programming_in_lean/)
|
||||
|
||||
308
RELEASES.md
308
RELEASES.md
@@ -8,7 +8,241 @@ This file contains work-in-progress notes for the upcoming release, as well as p
|
||||
Please check the [releases](https://github.com/leanprover/lean4/releases) page for the current status
|
||||
of each version.
|
||||
|
||||
v4.5.0 (development in progress)
|
||||
v4.7.0 (development in progress)
|
||||
---------
|
||||
|
||||
v4.6.0
|
||||
---------
|
||||
|
||||
* Add custom simplification procedures (aka `simproc`s) to `simp`. Simprocs can be triggered by the simplifier on a specified term-pattern. Here is an small example:
|
||||
```lean
|
||||
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.Nat
|
||||
|
||||
def foo (x : Nat) : Nat :=
|
||||
x + 10
|
||||
|
||||
/--
|
||||
The `simproc` `reduceFoo` is invoked on terms that match the pattern `foo _`.
|
||||
-/
|
||||
simproc reduceFoo (foo _) :=
|
||||
/- A term of type `Expr → SimpM Step -/
|
||||
fun e => do
|
||||
/-
|
||||
The `Step` type has three constructors: `.done`, `.visit`, `.continue`.
|
||||
* The constructor `.done` instructs `simp` that the result does
|
||||
not need to be simplied further.
|
||||
* The constructor `.visit` instructs `simp` to visit the resulting expression.
|
||||
* The constructor `.continue` instructs `simp` to try other simplification procedures.
|
||||
|
||||
All three constructors take a `Result`. The `.continue` contructor may also take `none`.
|
||||
`Result` has two fields `expr` (the new expression), and `proof?` (an optional proof).
|
||||
If the new expression is definitionally equal to the input one, then `proof?` can be omitted or set to `none`.
|
||||
-/
|
||||
/- `simp` uses matching modulo reducibility. So, we ensure the term is a `foo`-application. -/
|
||||
unless e.isAppOfArity ``foo 1 do
|
||||
return .continue
|
||||
/- `Nat.fromExpr?` tries to convert an expression into a `Nat` value -/
|
||||
let some n ← Nat.fromExpr? e.appArg!
|
||||
| return .continue
|
||||
return .done { expr := Lean.mkNatLit (n+10) }
|
||||
```
|
||||
We disable simprocs support by using the command `set_option simprocs false`. This command is particularly useful when porting files to v4.6.0.
|
||||
Simprocs can be scoped, manually added to `simp` commands, and suppressed using `-`. They are also supported by `simp?`. `simp only` does not execute any `simproc`. Here are some examples for the `simproc` defined above.
|
||||
```lean
|
||||
example : x + foo 2 = 12 + x := by
|
||||
set_option simprocs false in
|
||||
/- This `simp` command does not make progress since `simproc`s are disabled. -/
|
||||
fail_if_success simp
|
||||
simp_arith
|
||||
|
||||
example : x + foo 2 = 12 + x := by
|
||||
/- `simp only` must not use the default simproc set. -/
|
||||
fail_if_success simp only
|
||||
simp_arith
|
||||
|
||||
example : x + foo 2 = 12 + x := by
|
||||
/-
|
||||
`simp only` does not use the default simproc set,
|
||||
but we can provide simprocs as arguments. -/
|
||||
simp only [reduceFoo]
|
||||
simp_arith
|
||||
|
||||
example : x + foo 2 = 12 + x := by
|
||||
/- We can use `-` to disable `simproc`s. -/
|
||||
fail_if_success simp [-reduceFoo]
|
||||
simp_arith
|
||||
```
|
||||
The command `register_simp_attr <id>` now creates a `simp` **and** a `simproc` set with the name `<id>`. The following command instructs Lean to insert the `reduceFoo` simplification procedure into the set `my_simp`. If no set is specified, Lean uses the default `simp` set.
|
||||
```lean
|
||||
simproc [my_simp] reduceFoo (foo _) := ...
|
||||
```
|
||||
|
||||
* The syntax of the `termination_by` and `decreasing_by` termination hints is overhauled:
|
||||
|
||||
* They are now placed directly after the function they apply to, instead of
|
||||
after the whole `mutual` block.
|
||||
* Therefore, the function name no longer has to be mentioned in the hint.
|
||||
* If the function has a `where` clause, the `termination_by` and
|
||||
`decreasing_by` for that function come before the `where`. The
|
||||
functions in the `where` clause can have their own termination hints, each
|
||||
following the corresponding definition.
|
||||
* The `termination_by` clause can only bind “extra parameters”, that are not
|
||||
already bound by the function header, but are bound in a lambda (`:= fun x
|
||||
y z =>`) or in patterns (`| x, n + 1 => …`). These extra parameters used to
|
||||
be understood as a suffix of the function parameters; now it is a prefix.
|
||||
|
||||
Migration guide: In simple cases just remove the function name, and any
|
||||
variables already bound at the header.
|
||||
```diff
|
||||
def foo : Nat → Nat → Nat := …
|
||||
-termination_by foo a b => a - b
|
||||
+termination_by a b => a - b
|
||||
```
|
||||
or
|
||||
```diff
|
||||
def foo : Nat → Nat → Nat := …
|
||||
-termination_by _ a b => a - b
|
||||
+termination_by a b => a - b
|
||||
```
|
||||
|
||||
If the parameters are bound in the function header (before the `:`), remove them as well:
|
||||
```diff
|
||||
def foo (a b : Nat) : Nat := …
|
||||
-termination_by foo a b => a - b
|
||||
+termination_by a - b
|
||||
```
|
||||
|
||||
Else, if there are multiple extra parameters, make sure to refer to the right
|
||||
ones; the bound variables are interpreted from left to right, no longer from
|
||||
right to left:
|
||||
```diff
|
||||
def foo : Nat → Nat → Nat → Nat
|
||||
| a, b, c => …
|
||||
-termination_by foo b c => b
|
||||
+termination_by a b => b
|
||||
```
|
||||
|
||||
In the case of a `mutual` block, place the termination arguments (without the
|
||||
function name) next to the function definition:
|
||||
```diff
|
||||
-mutual
|
||||
-def foo : Nat → Nat → Nat := …
|
||||
-def bar : Nat → Nat := …
|
||||
-end
|
||||
-termination_by
|
||||
- foo a b => a - b
|
||||
- bar a => a
|
||||
+mutual
|
||||
+def foo : Nat → Nat → Nat := …
|
||||
+termination_by a b => a - b
|
||||
+def bar : Nat → Nat := …
|
||||
+termination_by a => a
|
||||
+end
|
||||
```
|
||||
|
||||
Similarly, if you have (mutual) recursion through `where` or `let rec`, the
|
||||
termination hints are now placed directly after the function they apply to:
|
||||
```diff
|
||||
-def foo (a b : Nat) : Nat := …
|
||||
- where bar (x : Nat) : Nat := …
|
||||
-termination_by
|
||||
- foo a b => a - b
|
||||
- bar x => x
|
||||
+def foo (a b : Nat) : Nat := …
|
||||
+termination_by a - b
|
||||
+ where
|
||||
+ bar (x : Nat) : Nat := …
|
||||
+ termination_by x
|
||||
|
||||
-def foo (a b : Nat) : Nat :=
|
||||
- let rec bar (x : Nat) : Nat := …
|
||||
- …
|
||||
-termination_by
|
||||
- foo a b => a - b
|
||||
- bar x => x
|
||||
+def foo (a b : Nat) : Nat :=
|
||||
+ let rec bar (x : Nat) : Nat := …
|
||||
+ termination_by x
|
||||
+ …
|
||||
+termination_by a - b
|
||||
```
|
||||
|
||||
In cases where a single `decreasing_by` clause applied to multiple mutually
|
||||
recursive functions before, the tactic now has to be duplicated.
|
||||
|
||||
* The semantics of `decreasing_by` changed; the tactic is applied to all
|
||||
termination proof goals together, not individually.
|
||||
|
||||
This helps when writing termination proofs interactively, as one can focus
|
||||
each subgoal individually, for example using `·`. Previously, the given
|
||||
tactic script had to work for _all_ goals, and one had to resort to tactic
|
||||
combinators like `first`:
|
||||
|
||||
```diff
|
||||
def foo (n : Nat) := … foo e1 … foo e2 …
|
||||
-decreasing_by
|
||||
-simp_wf
|
||||
-first | apply something_about_e1; …
|
||||
- | apply something_about_e2; …
|
||||
+decreasing_by
|
||||
+all_goals simp_wf
|
||||
+· apply something_about_e1; …
|
||||
+· apply something_about_e2; …
|
||||
```
|
||||
|
||||
To obtain the old behaviour of applying a tactic to each goal individually,
|
||||
use `all_goals`:
|
||||
```diff
|
||||
def foo (n : Nat) := …
|
||||
-decreasing_by some_tactic
|
||||
+decreasing_by all_goals some_tactic
|
||||
```
|
||||
|
||||
In the case of mutual recursion each `decreasing_by` now applies to just its
|
||||
function. If some functions in a recursive group do not have their own
|
||||
`decreasing_by`, the default `decreasing_tactic` is used. If the same tactic
|
||||
ought to be applied to multiple functions, the `decreasing_by` clause has to
|
||||
be repeated at each of these functions.
|
||||
|
||||
* Modify `InfoTree.context` to facilitate augmenting it with partial contexts while elaborating a command. This breaks backwards compatibility with all downstream projects that traverse the `InfoTree` manually instead of going through the functions in `InfoUtils.lean`, as well as those manually creating and saving `InfoTree`s. See [PR #3159](https://github.com/leanprover/lean4/pull/3159) for how to migrate your code.
|
||||
|
||||
* Add language server support for [call hierarchy requests](https://www.youtube.com/watch?v=r5LA7ivUb2c) ([PR #3082](https://github.com/leanprover/lean4/pull/3082)). The change to the .ilean format in this PR means that projects must be fully rebuilt once in order to generate .ilean files with the new format before features like "find references" work correctly again.
|
||||
|
||||
* Structure instances with multiple sources (for example `{a, b, c with x := 0}`) now have their fields filled from these sources
|
||||
in strict left-to-right order. Furthermore, the structure instance elaborator now aggressively use sources to fill in subobject
|
||||
fields, which prevents unnecessary eta expansion of the sources,
|
||||
and hence greatly reduces the reliance on costly structure eta reduction. This has a large impact on mathlib,
|
||||
reducing total CPU instructions by 3% and enabling impactful refactors like leanprover-community/mathlib4#8386
|
||||
which reduces the build time by almost 20%.
|
||||
See PR [#2478](https://github.com/leanprover/lean4/pull/2478) and RFC [#2451](https://github.com/leanprover/lean4/issues/2451).
|
||||
|
||||
* Add pretty printer settings to omit deeply nested terms (`pp.deepTerms false` and `pp.deepTerms.threshold`) ([PR #3201](https://github.com/leanprover/lean4/pull/3201))
|
||||
|
||||
* Add pretty printer options `pp.numeralTypes` and `pp.natLit`.
|
||||
When `pp.numeralTypes` is true, then natural number literals, integer literals, and rational number literals
|
||||
are pretty printed with type ascriptions, such as `(2 : Rat)`, `(-2 : Rat)`, and `(-2 / 3 : Rat)`.
|
||||
When `pp.natLit` is true, then raw natural number literals are pretty printed as `nat_lit 2`.
|
||||
[PR #2933](https://github.com/leanprover/lean4/pull/2933) and [RFC #3021](https://github.com/leanprover/lean4/issues/3021).
|
||||
|
||||
Lake updates:
|
||||
* improved platform information & control [#3226](https://github.com/leanprover/lean4/pull/3226)
|
||||
* `lake update` from unsupported manifest versions [#3149](https://github.com/leanprover/lean4/pull/3149)
|
||||
|
||||
Other improvements:
|
||||
* make `intro` be aware of `let_fun` [#3115](https://github.com/leanprover/lean4/pull/3115)
|
||||
* produce simpler proof terms in `rw` [#3121](https://github.com/leanprover/lean4/pull/3121)
|
||||
* fuse nested `mkCongrArg` calls in proofs generated by `simp` [#3203](https://github.com/leanprover/lean4/pull/3203)
|
||||
* `induction using` followed by a general term [#3188](https://github.com/leanprover/lean4/pull/3188)
|
||||
* allow generalization in `let` [#3060](https://github.com/leanprover/lean4/pull/3060, fixing [#3065](https://github.com/leanprover/lean4/issues/3065)
|
||||
* reducing out-of-bounds `swap!` should return `a`, not `default`` [#3197](https://github.com/leanprover/lean4/pull/3197), fixing [#3196](https://github.com/leanprover/lean4/issues/3196)
|
||||
* derive `BEq` on structure with `Prop`-fields [#3191](https://github.com/leanprover/lean4/pull/3191), fixing [#3140](https://github.com/leanprover/lean4/issues/3140)
|
||||
* refine through more `casesOnApp`/`matcherApp` [#3176](https://github.com/leanprover/lean4/pull/3176), fixing [#3175](https://github.com/leanprover/lean4/pull/3175)
|
||||
* do not strip dotted components from lean module names [#2994](https://github.com/leanprover/lean4/pull/2994), fixing [#2999](https://github.com/leanprover/lean4/issues/2999)
|
||||
* fix `deriving` only deriving the first declaration for some handlers [#3058](https://github.com/leanprover/lean4/pull/3058), fixing [#3057](https://github.com/leanprover/lean4/issues/3057)
|
||||
* do not instantiate metavariables in kabstract/rw for disallowed occurrences [#2539](https://github.com/leanprover/lean4/pull/2539), fixing [#2538](https://github.com/leanprover/lean4/issues/2538)
|
||||
* hover info for `cases h : ...` [#3084](https://github.com/leanprover/lean4/pull/3084)
|
||||
|
||||
v4.5.0
|
||||
---------
|
||||
|
||||
* Modify the lexical syntax of string literals to have string gaps, which are escape sequences of the form `"\" newline whitespace*`.
|
||||
@@ -20,23 +254,89 @@ v4.5.0 (development in progress)
|
||||
```
|
||||
[PR #2821](https://github.com/leanprover/lean4/pull/2821) and [RFC #2838](https://github.com/leanprover/lean4/issues/2838).
|
||||
|
||||
* Add raw string literal syntax. For example, `r"\n"` is equivalent to `"\\n"`, with no escape processing.
|
||||
To include double quote characters in a raw string one can add sufficiently many `#` characters before and after
|
||||
the bounding `"`s, as in `r#"the "the" is in quotes"#` for `"the \"the\" is in quotes"`.
|
||||
[PR #2929](https://github.com/leanprover/lean4/pull/2929) and [issue #1422](https://github.com/leanprover/lean4/issues/1422).
|
||||
|
||||
* The low-level `termination_by'` clause is no longer supported.
|
||||
|
||||
Migration guide: Use `termination_by` instead, e.g.:
|
||||
```diff
|
||||
-termination_by' measure (fun ⟨i, _⟩ => as.size - i)
|
||||
+termination_by i _ => as.size - i
|
||||
```
|
||||
|
||||
If the well-founded relation you want to use is not the one that the
|
||||
`WellFoundedRelation` type class would infer for your termination argument,
|
||||
you can use `WellFounded.wrap` from the std libarary to explicitly give one:
|
||||
```diff
|
||||
-termination_by' ⟨r, hwf⟩
|
||||
+termination_by x => hwf.wrap x
|
||||
```
|
||||
|
||||
* Support snippet edits in LSP `TextEdit`s. See `Lean.Lsp.SnippetString` for more details.
|
||||
|
||||
* Deprecations and changes in the widget API.
|
||||
- `Widget.UserWidgetDefinition` is deprecated in favour of `Widget.Module`. The annotation `@[widget]` is deprecated in favour of `@[widget_module]`. To migrate a definition of type `UserWidgetDefinition`, remove the `name` field and replace the type with `Widget.Module`. Removing the `name` results in a title bar no longer being drawn above your panel widget. To add it back, draw it as part of the component using `<details open=true><summary class='mv2 pointer'>{name}</summary>{rest_of_widget}</details>`. See an example migration [here](https://github.com/leanprover/std4/pull/475/files#diff-857376079661a0c28a53b7ff84701afabbdf529836a6944d106c5294f0e68109R43-R83).
|
||||
- The new command `show_panel_widgets` allows displaying always-on and locally-on panel widgets.
|
||||
- `RpcEncodable` widget props can now be stored in the infotree.
|
||||
- See [RFC 2963](https://github.com/leanprover/lean4/issues/2963) for more details and motivation.
|
||||
|
||||
* If no usable lexicographic order can be found automatically for a termination proof, explain why.
|
||||
See [feat: GuessLex: if no measure is found, explain why](https://github.com/leanprover/lean4/pull/2960).
|
||||
|
||||
* Option to print [inferred termination argument](https://github.com/leanprover/lean4/pull/3012).
|
||||
With `set_option showInferredTerminationBy true` you will get messages like
|
||||
```
|
||||
Inferred termination argument:
|
||||
termination_by
|
||||
ackermann n m => (sizeOf n, sizeOf m)
|
||||
```
|
||||
for automatically generated `termination_by` clauses.
|
||||
|
||||
* More detailed error messages for [invalid mutual blocks](https://github.com/leanprover/lean4/pull/2949).
|
||||
|
||||
* [Multiple](https://github.com/leanprover/lean4/pull/2923) [improvements](https://github.com/leanprover/lean4/pull/2969) to the output of `simp?` and `simp_all?`.
|
||||
|
||||
* Tactics with `withLocation *` [no longer fail](https://github.com/leanprover/lean4/pull/2917) if they close the main goal.
|
||||
|
||||
* Implementation of a `test_extern` command for writing tests for `@[extern]` and `@[implemented_by]` functions.
|
||||
Usage is
|
||||
```
|
||||
import Lean.Util.TestExtern
|
||||
|
||||
test_extern Nat.add 17 37
|
||||
```
|
||||
The head symbol must be the constant with the `@[extern]` or `@[implemented_by]` attribute. The return type must have a `DecidableEq` instance.
|
||||
|
||||
Bug fixes for
|
||||
[#2853](https://github.com/leanprover/lean4/issues/2853), [#2953](https://github.com/leanprover/lean4/issues/2953), [#2966](https://github.com/leanprover/lean4/issues/2966),
|
||||
[#2971](https://github.com/leanprover/lean4/issues/2971), [#2990](https://github.com/leanprover/lean4/issues/2990), [#3094](https://github.com/leanprover/lean4/issues/3094).
|
||||
|
||||
Bug fix for [eager evaluation of default value](https://github.com/leanprover/lean4/pull/3043) in `Option.getD`.
|
||||
Avoid [panic in `leanPosToLspPos`](https://github.com/leanprover/lean4/pull/3071) when file source is unavailable.
|
||||
Improve [short-circuiting behavior](https://github.com/leanprover/lean4/pull/2972) for `List.all` and `List.any`.
|
||||
|
||||
Several Lake bug fixes: [#3036](https://github.com/leanprover/lean4/issues/3036), [#3064](https://github.com/leanprover/lean4/issues/3064), [#3069](https://github.com/leanprover/lean4/issues/3069).
|
||||
|
||||
v4.4.0
|
||||
---------
|
||||
|
||||
* Lake and the language server now support per-package server options using the `moreServerOptions` config field, as well as options that apply to both the language server and `lean` using the `leanOptions` config field. Setting either of these fields instead of `moreServerArgs` ensures that viewing files from a dependency uses the options for that dependency. Additionally, `moreServerArgs` is being deprecated in favor of the `moreGlobalServerArgs` field. See PR [#2858](https://github.com/leanprover/lean4/pull/2858).
|
||||
|
||||
|
||||
A Lakefile with the following deprecated package declaration:
|
||||
```lean
|
||||
def moreServerArgs := #[
|
||||
"-Dpp.unicode.fun=true"
|
||||
]
|
||||
def moreLeanArgs := moreServerArgs
|
||||
|
||||
|
||||
package SomePackage where
|
||||
moreServerArgs := moreServerArgs
|
||||
moreLeanArgs := moreLeanArgs
|
||||
```
|
||||
|
||||
|
||||
... can be updated to the following package declaration to use per-package options:
|
||||
```lean
|
||||
package SomePackage where
|
||||
|
||||
@@ -483,7 +483,43 @@ def baz : Char → Nat
|
||||
| _ => 3
|
||||
```
|
||||
|
||||
If any of the terms ``tᵢ`` in the template above contain a recursive call to ``foo``, the equation compiler tries to interpret the definition as a structural recursion. In order for that to succeed, the recursive arguments must be subterms of the corresponding arguments on the left-hand side. The function is then defined using a *course of values* recursion, using automatically generated functions ``below`` and ``brec`` in the namespace corresponding to the inductive type of the recursive argument. In this case the defining equations hold definitionally, possibly with additional case splits.
|
||||
The case where patterns are matched against an argument whose type is an inductive family is known as *dependent pattern matching*. This is more complicated, because the type of the function being defined can impose constraints on the patterns that are matched. In this case, the equation compiler will detect inconsistent cases and rule them out.
|
||||
|
||||
```lean
|
||||
universe u
|
||||
|
||||
inductive Vector (α : Type u) : Nat → Type u
|
||||
| nil : Vector α 0
|
||||
| cons : α → Vector α n → Vector α (n+1)
|
||||
|
||||
namespace Vector
|
||||
|
||||
def head : Vector α (n+1) → α
|
||||
| cons h t => h
|
||||
|
||||
def tail : Vector α (n+1) → Vector α n
|
||||
| cons h t => t
|
||||
|
||||
def map (f : α → β → γ) : Vector α n → Vector β n → Vector γ n
|
||||
| nil, nil => nil
|
||||
| cons a va, cons b vb => cons (f a b) (map f va vb)
|
||||
|
||||
end Vector
|
||||
```
|
||||
|
||||
.. _recursive_functions:
|
||||
|
||||
Recursive functions
|
||||
===================
|
||||
|
||||
Lean must ensure that a recursive function terminates, for which there are two strategies: _structural recursion_, in which all recursive calls are made on smaller parts of the input data, and _well-founded recursion_, in which recursive calls are justified by showing that arguments to recursive calls are smaller according to some other measure.
|
||||
|
||||
Structural recursion
|
||||
--------------------
|
||||
|
||||
If the definition of a function contains recursive calls, Lean first tries to interpret the definition as a structural recursion. In order for that to succeed, the recursive arguments must be subterms of the corresponding arguments on the left-hand side.
|
||||
|
||||
The function is then defined using a *course of values* recursion, using automatically generated functions ``below`` and ``brec`` in the namespace corresponding to the inductive type of the recursive argument. In this case the defining equations hold definitionally, possibly with additional case splits.
|
||||
|
||||
```lean
|
||||
namespace Hide
|
||||
@@ -504,7 +540,12 @@ example : append [(1 : Nat), 2, 3] [4, 5] = [1, 2, 3, 4, 5] => rfl
|
||||
end Hide
|
||||
```
|
||||
|
||||
If structural recursion fails, the equation compiler falls back on well-founded recursion. It tries to infer an instance of ``SizeOf`` for the type of each argument, and then show that each recursive call is decreasing under the lexicographic order of the arguments with respect to ``sizeOf`` measure. If it fails, the error message provides information as to the goal that Lean tried to prove. Lean uses information in the local context, so you can often provide the relevant proof manually using ``have`` in the body of the definition. In this case of well-founded recursion, the defining equations hold only propositionally, and can be accessed using ``simp`` and ``rewrite`` with the name ``foo``.
|
||||
Well-founded recursion
|
||||
---------------------
|
||||
|
||||
If structural recursion fails, the equation compiler falls back on well-founded recursion. It tries to infer an instance of ``SizeOf`` for the type of each argument, and then tries to find a permutation of the arguments such that each recursive call is decreasing under the lexicographic order with respect to ``sizeOf`` measures. Lean uses information in the local context, so you can often provide the relevant proof manually using ``have`` in the body of the definition.
|
||||
|
||||
In the case of well-founded recursion, the equation used to declare the function holds only propositionally, but not definitionally, and can be accessed using ``unfold``, ``simp`` and ``rewrite`` with the function name (for example ``unfold foo`` or ``simp [foo]``, where ``foo`` is the function defined with well-founded recursion).
|
||||
|
||||
```lean
|
||||
namespace Hide
|
||||
@@ -528,9 +569,53 @@ by rw [div]; rfl
|
||||
end Hide
|
||||
```
|
||||
|
||||
If Lean cannot find a permutation of the arguments for which all recursive calls are decreasing, it will print a table that contains, for every recursive call, which arguments Lean could prove to be decreasing. For example, a function with three recursive calls and four parameters might cause the following message to be printed
|
||||
|
||||
```
|
||||
example.lean:37:0-43:31: error: Could not find a decreasing measure.
|
||||
The arguments relate at each recursive call as follows:
|
||||
(<, ≤, =: relation proved, ? all proofs failed, _: no proof attempted)
|
||||
x1 x2 x3 x4
|
||||
1) 39:6-27 = = _ =
|
||||
2) 40:6-25 = ? _ <
|
||||
3) 41:6-25 < _ _ _
|
||||
Please use `termination_by` to specify a decreasing measure.
|
||||
```
|
||||
|
||||
This table should be read as follows:
|
||||
|
||||
* In the first recursive call, in line 39, arguments 1, 2 and 4 are equal to the function's parameters.
|
||||
* The second recursive call, in line 40, has an equal first argument, a smaller fourth argument, and nothing could be inferred for the second argument.
|
||||
* The third recursive call, in line 41, has a decreasing first argument.
|
||||
* No other proofs were attempted, either because the parameter has a type without a non-trivial ``WellFounded`` instance (parameter 3), or because it is already clear that no decreasing measure can be found.
|
||||
|
||||
|
||||
Lean will print the termination argument it found if ``set_option showInferredTerminationBy true`` is set.
|
||||
|
||||
If Lean does not find the termination argument, or if you want to be explicit, you can append a `termination_by` clause to the function definition, after the function's body, but before the `where` clause if present. It is of the form
|
||||
```
|
||||
termination_by e
|
||||
```
|
||||
where ``e`` is an expression that depends on the parameters of the function and should be decreasing at each recursive call. The type of `e` should be an instance of the class ``WellFoundedRelation``, which determines how to compare two values of that type.
|
||||
|
||||
If ``f`` has parameters “after the ``:``” (for example when defining functions via patterns using `|`), then these can be brought into scope using the syntax
|
||||
```
|
||||
termination_by a₁ … aₙ => e
|
||||
```
|
||||
|
||||
By default, Lean uses the tactic ``decreasing_tactic`` when proving that an argument is decreasing; see its documentation for how to globally extend it. You can also choose to use a different tactic for a given function definition with the clause
|
||||
```
|
||||
decreasing_by <tac>
|
||||
```
|
||||
which should come after ``termination_by`, if present.
|
||||
|
||||
|
||||
Note that recursive definitions can in general require nested recursions, that is, recursion on different arguments of ``foo`` in the template above. The equation compiler handles this by abstracting later arguments, and recursively defining higher-order functions to meet the specification.
|
||||
|
||||
The equation compiler also allows mutual recursive definitions, with a syntax similar to that of [Mutual and Nested Inductive Definitions](#mutual-and-nested-inductive-definitions). They are compiled using well-founded recursion, and so once again the defining equations hold only propositionally.
|
||||
Mutual recursion
|
||||
----------------
|
||||
|
||||
The equation compiler also allows mutual recursive definitions, with a syntax similar to that of [Mutual and Nested Inductive Definitions](#mutual-and-nested-inductive-definitions). Mutual definitions are always compiled using well-founded recursion, and so once again the defining equations hold only propositionally.
|
||||
|
||||
```lean
|
||||
mutual
|
||||
@@ -587,29 +672,31 @@ def num_consts_lst : List Term → Nat
|
||||
end
|
||||
```
|
||||
|
||||
The case where patterns are matched against an argument whose type is an inductive family is known as *dependent pattern matching*. This is more complicated, because the type of the function being defined can impose constraints on the patterns that are matched. In this case, the equation compiler will detect inconsistent cases and rule them out.
|
||||
In a set of mutually recursive function, either all or no functions must have an explicit termination argument (``termination_by``). A change of the default termination tactic (``decreasing_by``) only affects the proofs about the recursive calls of that function, not the other functions in the group.
|
||||
|
||||
```lean
|
||||
universe u
|
||||
```
|
||||
mutual
|
||||
theorem even_of_odd_succ : ∀ n, Odd (n + 1) → Even n
|
||||
| _, odd_succ n h => h
|
||||
termination_by n h => h
|
||||
decreasing_by decreasing_tactic
|
||||
|
||||
inductive Vector (α : Type u) : Nat → Type u
|
||||
| nil : Vector α 0
|
||||
| cons : α → Vector α n → Vector α (n+1)
|
||||
theorem odd_of_even_succ : ∀ n, Even (n + 1) → Odd n
|
||||
| _, even_succ n h => h
|
||||
termination_by n h => h
|
||||
end
|
||||
```
|
||||
|
||||
namespace Vector
|
||||
Another way to express mutual recursion is using local function definitions in ``where`` or ``let rec`` clauses: these can be mutually recursive with each other and their containing function:
|
||||
|
||||
def head {α : Type} : Vector α (n+1) → α
|
||||
| cons h t => h
|
||||
|
||||
def tail {α : Type} : Vector α (n+1) → Vector α n
|
||||
| cons h t => t
|
||||
|
||||
def map {α β γ : Type} (f : α → β → γ) :
|
||||
∀ {n}, Vector α n → Vector β n → Vector γ n
|
||||
| 0, nil, nil => nil
|
||||
| n+1, cons a va, cons b vb => cons (f a b) (map f va vb)
|
||||
|
||||
end Vector
|
||||
```
|
||||
theorem even_of_odd_succ : ∀ n, Odd (n + 1) → Even n
|
||||
| _, odd_succ n h => h
|
||||
termination_by n h => h
|
||||
where
|
||||
theorem odd_of_even_succ : ∀ n, Even (n + 1) → Odd n
|
||||
| _, even_succ n h => h
|
||||
termination_by n h => h
|
||||
```
|
||||
|
||||
.. _match_expressions:
|
||||
|
||||
@@ -65,16 +65,36 @@ You now have a Lean binary and library that include your changes, though their
|
||||
own compilation was not influenced by them, that you can use to test your
|
||||
changes on test programs whose compilation *will* be influenced by the changes.
|
||||
|
||||
Finally, when we want to use new language features in the library, we need to
|
||||
update the stage 0 compiler, which can be done via `make -C stageN update-stage0`.
|
||||
`make update-stage0` without `-C` defaults to stage1.
|
||||
## Updating stage0
|
||||
|
||||
Updates to `stage0` should be their own commits in the Git history. In
|
||||
other words, before running `make update-stage0`, please commit your
|
||||
work. Then, commit the updated `stage0` compiler code with the commit message:
|
||||
Finally, when we want to use new language features in the library, we need to
|
||||
update the archived C source code of the stage 0 compiler in `stage0/src`.
|
||||
|
||||
The github repository will automatically update stage0 on `master` once
|
||||
`src/stdlib_flags.h` and `stage0/src/stdlib_flags.h` are out of sync.
|
||||
|
||||
If you have write access to the lean4 repository, you can also also manually
|
||||
trigger that process, for example to be able to use new features in the compiler itself.
|
||||
You can do that on <https://github.com/nomeata/lean4/actions/workflows/update-stage0.yml>
|
||||
or using Github CLI with
|
||||
```
|
||||
gh workflow run update-stage0.yml
|
||||
```
|
||||
|
||||
Leaving stage0 updates to the CI automation is preferrable, but should you need
|
||||
to do it locally, you can use `make update-stage0` in `build/release`, to
|
||||
update `stage0` from `stage1`, `make -C stageN update-stage0` to update from
|
||||
another stage, or `nix run .#update-stage0-commit` to update using nix.
|
||||
|
||||
Updates to `stage0` should be their own commits in the Git history. So should
|
||||
you have to include the stage0 update in your PR (rather than using above
|
||||
automation after merging changes), commit your work before running `make
|
||||
update-stage0`, commit the updated `stage0` compiler code with the commit
|
||||
message:
|
||||
```
|
||||
chore: update stage0
|
||||
```
|
||||
and coordinate with the admins to not squash your PR.
|
||||
|
||||
## Further Bootstrapping Complications
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ theorem List.palindrome_ind (motive : List α → Prop)
|
||||
have ih := palindrome_ind motive h₁ h₂ h₃ (a₂::as').dropLast
|
||||
have : [a₁] ++ (a₂::as').dropLast ++ [(a₂::as').last (by simp)] = a₁::a₂::as' := by simp
|
||||
this ▸ h₃ _ _ _ ih
|
||||
termination_by _ as => as.length
|
||||
termination_by as.length
|
||||
|
||||
/-!
|
||||
We use our new induction principle to prove that if `as.reverse = as`, then `Palindrome as` holds.
|
||||
|
||||
@@ -15,9 +15,8 @@ sections of a Lean document. User widgets are rendered in the Lean infoview.
|
||||
To try it out, simply type in the following code and place your cursor over the `#widget` command.
|
||||
-/
|
||||
|
||||
@[widget]
|
||||
def helloWidget : UserWidgetDefinition where
|
||||
name := "Hello"
|
||||
@[widget_module]
|
||||
def helloWidget : Widget.Module where
|
||||
javascript := "
|
||||
import * as React from 'react';
|
||||
export default function(props) {
|
||||
@@ -25,7 +24,7 @@ def helloWidget : UserWidgetDefinition where
|
||||
return React.createElement('p', {}, name + '!')
|
||||
}"
|
||||
|
||||
#widget helloWidget .null
|
||||
#widget helloWidget
|
||||
|
||||
/-!
|
||||
If you want to dive into a full sample right away, check out
|
||||
@@ -56,7 +55,11 @@ to the React component. In our first invocation of `#widget`, we set it to `.nul
|
||||
happens when you type in:
|
||||
-/
|
||||
|
||||
#widget helloWidget (Json.mkObj [("name", "<your name here>")])
|
||||
structure HelloWidgetProps where
|
||||
name? : Option String := none
|
||||
deriving Server.RpcEncodable
|
||||
|
||||
#widget helloWidget with { name? := "<your name here>" : HelloWidgetProps }
|
||||
|
||||
/-!
|
||||
💡 NOTE: The RPC system presented below does not depend on JavaScript. However the primary use case
|
||||
@@ -132,9 +135,8 @@ on this we either display an `InteractiveCode` with the type, `mapRpcError` the
|
||||
to turn it into a readable message, or show a `Loading..` message, respectively.
|
||||
-/
|
||||
|
||||
@[widget]
|
||||
def checkWidget : UserWidgetDefinition where
|
||||
name := "#check as a service"
|
||||
@[widget_module]
|
||||
def checkWidget : Widget.Module where
|
||||
javascript := "
|
||||
import * as React from 'react';
|
||||
const e = React.createElement;
|
||||
@@ -160,7 +162,7 @@ export default function(props) {
|
||||
Finally we can try out the widget.
|
||||
-/
|
||||
|
||||
#widget checkWidget .null
|
||||
#widget checkWidget
|
||||
|
||||
/-!
|
||||

|
||||
@@ -193,9 +195,8 @@ interact with the text editor.
|
||||
You can see the full API for this [here](https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview-api/src/infoviewApi.ts#L52)
|
||||
-/
|
||||
|
||||
@[widget]
|
||||
def insertTextWidget : UserWidgetDefinition where
|
||||
name := "textInserter"
|
||||
@[widget_module]
|
||||
def insertTextWidget : Widget.Module where
|
||||
javascript := "
|
||||
import * as React from 'react';
|
||||
const e = React.createElement;
|
||||
@@ -213,4 +214,4 @@ export default function(props) {
|
||||
|
||||
/-! Finally, we can try this out: -/
|
||||
|
||||
#widget insertTextWidget .null
|
||||
#widget insertTextWidget
|
||||
|
||||
9
doc/flake.lock
generated
9
doc/flake.lock
generated
@@ -69,15 +69,16 @@
|
||||
"leanInk": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1666154782,
|
||||
"narHash": "sha256-0ELqEca6jZT4BW/mqkDD+uYuxW5QlZUFlNwZkvugsg8=",
|
||||
"owner": "digama0",
|
||||
"lastModified": 1704976501,
|
||||
"narHash": "sha256-FSBUsbX0HxakSnYRYzRBDN2YKmH9EkA0q9p7TSPEJTI=",
|
||||
"owner": "leanprover",
|
||||
"repo": "LeanInk",
|
||||
"rev": "12a2aec9b5f4aa84e84fb01a9af1da00d8aaff4e",
|
||||
"rev": "51821e3c2c032c88e4b2956483899d373ec090c4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "leanprover",
|
||||
"ref": "refs/pull/57/merge",
|
||||
"repo": "LeanInk",
|
||||
"type": "github"
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
flake = false;
|
||||
};
|
||||
inputs.leanInk = {
|
||||
url = "github:leanprover/LeanInk";
|
||||
url = "github:leanprover/LeanInk/refs/pull/57/merge";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ def fact x :=
|
||||
|
||||
#eval fact 100
|
||||
```
|
||||
By default, Lean only accepts total functions. The `partial` keyword should be used when Lean cannot
|
||||
establish that a function always terminates.
|
||||
By default, Lean only accepts total functions.
|
||||
The `partial` keyword may be used to define a recursive function without a termination proof; `partial` functions compute in compiled programs, but are opaque in proofs and during type checking.
|
||||
```lean
|
||||
partial def g (x : Nat) (p : Nat -> Bool) : Nat :=
|
||||
if p x then
|
||||
|
||||
BIN
doc/images/setup_guide.png
Normal file
BIN
doc/images/setup_guide.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
BIN
doc/images/show-setup-guide.png
Normal file
BIN
doc/images/show-setup-guide.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
@@ -8,7 +8,7 @@ A Lean program consists of a stream of UTF-8 tokens where each token
|
||||
is one of the following:
|
||||
|
||||
```
|
||||
token: symbol | command | ident | string | char | numeral |
|
||||
token: symbol | command | ident | string | raw_string | char | numeral |
|
||||
: decimal | doc_comment | mod_doc_comment | field_notation
|
||||
```
|
||||
|
||||
@@ -94,6 +94,22 @@ So the complete syntax is:
|
||||
string_gap : "\" newline whitespace*
|
||||
```
|
||||
|
||||
Raw String Literals
|
||||
===================
|
||||
|
||||
Raw string literals are string literals without any escape character processing.
|
||||
They begin with `r##...#"` (with zero or more `#` characters) and end with `"#...##` (with the same number of `#` characters).
|
||||
The contents of a raw string literal may contain `"##..#` so long as the number of `#` characters
|
||||
is less than the number of `#` characters used to begin the raw string literal.
|
||||
|
||||
```
|
||||
raw_string : raw_string_aux(0) | raw_string_aux(1) | raw_string_aux(2) | ...
|
||||
raw_string_aux(n) : 'r' '#'{n} '"' raw_string_item '"' '#'{n}
|
||||
raw_string_item(n) : raw_string_char | raw_string_quote(n)
|
||||
raw_string_char : [^"]
|
||||
raw_string_quote(n) : '"' '#'{0..n-1}
|
||||
```
|
||||
|
||||
Char Literals
|
||||
=============
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ Platform-Specific Setup
|
||||
|
||||
- [Linux (Ubuntu)](ubuntu.md)
|
||||
- [Windows (msys2)](msys2.md)
|
||||
- [Windows (Visual Studio)](msvc.md)
|
||||
- [Windows (WSL)](wsl.md)
|
||||
- [macOS (homebrew)](osx-10.9.md)
|
||||
- Linux/macOS/WSL via [Nix](https://nixos.org/nix/): Call `nix-shell` in the project root. That's it.
|
||||
|
||||
@@ -60,7 +60,7 @@ While parsing `a * (b + c)`, `(b + c)` is assigned a precedence `60` by the addi
|
||||
the right argument to have precedence **at least** 71. Thus, this parse is invalid. In contrast, `(a * b) + c` assigns
|
||||
a precedence of `70` to `(a * b)`. This is compatible with addition which expects the left argument to have precedence
|
||||
**at least `60` ** (`70` is greater than `60`). Thus, the string `a * b + c` is parsed as `(a * b) + c`.
|
||||
For more details, please look at the [Lean manual on syntax extensions](../syntax.md#notations-and-precedence).
|
||||
For more details, please look at the [Lean manual on syntax extensions](./notation.md#notations-and-precedence).
|
||||
|
||||
To go from strings into `Arith`, we define a macro to
|
||||
translate the syntax category `arith` into an `Arith` inductive value that
|
||||
|
||||
@@ -1,55 +1,18 @@
|
||||
# Quickstart
|
||||
|
||||
These instructions will walk you through setting up Lean using the "basic" setup and VS Code as the editor.
|
||||
See [Setup](./setup.md) for other ways, supported platforms, and more details on setting up Lean.
|
||||
|
||||
See quick [walkthrough demo video](https://www.youtube.com/watch?v=yZo6k48L0VY).
|
||||
These instructions will walk you through setting up Lean 4 together with VS Code as an editor for Lean 4.
|
||||
See [Setup](./setup.md) for supported platforms and other ways to set up Lean 4.
|
||||
|
||||
1. Install [VS Code](https://code.visualstudio.com/).
|
||||
|
||||
1. Launch VS Code and install the `lean4` extension.
|
||||
1. Launch VS Code and install the `lean4` extension by clicking on the "Extensions" sidebar entry and searching for "lean4".
|
||||
|
||||

|
||||
|
||||
1. Create a new file using "File > New Text File" (`Ctrl+N`). Click the `Select a language` prompt, type in `lean4`, and hit ENTER. You should see the following popup:
|
||||

|
||||
1. Open the Lean 4 setup guide by creating a new text file using "File > New Text File" (`Ctrl+N`), clicking on the ∀-symbol in the top right and selecting "Documentation… > Setup: Show Setup Guide".
|
||||
|
||||
Click the "Install Lean using Elan" button. You should see some progress output like this:
|
||||

|
||||
|
||||
```
|
||||
info: syncing channel updates for 'stable'
|
||||
info: latest update on stable, lean version v4.0.0
|
||||
info: downloading component 'lean'
|
||||
```
|
||||
If there is no popup, you probably have Elan installed already.
|
||||
You may want to make sure that your default toolchain is Lean 4 in this case by running `elan default leanprover/lean4:stable` and reopen the file, as the next step will fail otherwise.
|
||||
1. Follow the Lean 4 setup guide. It will walk you through learning resources for Lean 4, teach you how to set up Lean's dependencies on your platform, install Lean 4 for you at the click of a button and help you set up your first project.
|
||||
|
||||
1. While it is installing, you can paste the following Lean program into the new file:
|
||||
|
||||
```lean
|
||||
#eval Lean.versionString
|
||||
```
|
||||
|
||||
When the installation has finished, the Lean Language Server should start automatically and you should get syntax-highlighting and a "Lean Infoview" popping up on the right. You will see the output of the `#eval` statement when
|
||||
you place your cursor at the end of the statement.
|
||||
|
||||

|
||||
|
||||
You are set up!
|
||||
|
||||
## Create a Lean Project
|
||||
|
||||
*If your goal is to contribute to [mathlib4](https://github.com/leanprover-community/mathlib4) or use it as a dependency, please see its readme for specific instructions on how to do that.*
|
||||
|
||||
You can now create a Lean project in a new folder. Run `lake init foo` from "View > Terminal" to create a package, followed by `lake build` to get an executable version of your Lean program.
|
||||
On Linux/macOS, you first have to follow the instructions printed by the Lean installation or log out and in again for the Lean executables to be available in you terminal.
|
||||
|
||||
Note: Packages **have** to be opened using "File > Open Folder..." for imports to work.
|
||||
Saved changes are visible in other files after running "Lean 4: Refresh File Dependencies" (`Ctrl+Shift+X`).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**The InfoView says "Waiting for Lean server to start..." forever.**
|
||||
|
||||
Check that the VS Code Terminal is not showing some installation errors from `elan`.
|
||||
If that doesn't work, try also running the VS Code command `Developer: Reload Window`.
|
||||

|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
### Tier 1
|
||||
|
||||
Platforms built & tested by our CI, available as nightly releases via elan (see below)
|
||||
Platforms built & tested by our CI, available as binary releases via elan (see below)
|
||||
|
||||
* x86-64 Linux with glibc 2.27+
|
||||
* x86-64 macOS 10.15+
|
||||
@@ -10,7 +10,7 @@ Platforms built & tested by our CI, available as nightly releases via elan (see
|
||||
|
||||
### Tier 2
|
||||
|
||||
Platforms cross-compiled but not tested by our CI, available as nightly releases
|
||||
Platforms cross-compiled but not tested by our CI, available as binary releases
|
||||
|
||||
Releases may be silently broken due to the lack of automated testing.
|
||||
Issue reports and fixes are welcome.
|
||||
@@ -50,10 +50,10 @@ Foo.lean # main file, import via `import Foo`
|
||||
Foo/
|
||||
A.lean # further files, import via e.g. `import Foo.A`
|
||||
A/... # further nesting
|
||||
build/ # `lake` build output directory
|
||||
.lake/ # `lake` build output directory
|
||||
```
|
||||
|
||||
After running `lake build` you will see a binary named `./build/bin/foo` and when you run it you should see the output:
|
||||
After running `lake build` you will see a binary named `./.lake/build/bin/foo` and when you run it you should see the output:
|
||||
```
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
@@ -67,6 +67,9 @@ theorem funext {f₁ f₂ : ∀ (x : α), β x} (h : ∀ x, f₁ x = f₂ x) : f
|
||||
\end{document}
|
||||
```
|
||||
|
||||
If your version of `minted` is v2.7 or newer, but before v3.0,
|
||||
you will additionally need to follow the workaround described in https://github.com/gpoore/minted/issues/360.
|
||||
|
||||
You can then compile `test.tex` by executing the following command:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -15,7 +15,7 @@ The most fundamental pieces of any Lean program are functions organized into nam
|
||||
[Functions](./functions.md) perform work on inputs to produce outputs,
|
||||
and they are organized under [namespaces](./namespaces.md),
|
||||
which are the primary way you group things in Lean.
|
||||
They are defined using the [`def`](./definitions.md) command,
|
||||
They are defined using the `def` command,
|
||||
which give the function a name and define its arguments.
|
||||
|
||||
```lean
|
||||
|
||||
@@ -99,11 +99,11 @@ Let us start with the first step of the program above, declaring an appropriate
|
||||
|
||||
```lean
|
||||
# namespace Ex
|
||||
class Inhabited (a : Type u) where
|
||||
class Inhabited (a : Sort u) where
|
||||
default : a
|
||||
|
||||
#check @Inhabited.default
|
||||
-- Inhabited.default : {a : Type u} → [self : Inhabited a] → a
|
||||
-- Inhabited.default : {a : Sort u} → [self : Inhabited a] → a
|
||||
# end Ex
|
||||
```
|
||||
Note `Inhabited.default` doesn't have any explicit argument.
|
||||
@@ -114,7 +114,7 @@ Now we populate the class with some instances:
|
||||
|
||||
```lean
|
||||
# namespace Ex
|
||||
# class Inhabited (a : Type _) where
|
||||
# class Inhabited (a : Sort _) where
|
||||
# default : a
|
||||
instance : Inhabited Bool where
|
||||
default := true
|
||||
@@ -138,7 +138,7 @@ instance : Inhabited Prop where
|
||||
You can use the command `export` to create the alias `default` for `Inhabited.default`
|
||||
```lean
|
||||
# namespace Ex
|
||||
# class Inhabited (a : Type _) where
|
||||
# class Inhabited (a : Sort _) where
|
||||
# default : a
|
||||
# instance : Inhabited Bool where
|
||||
# default := true
|
||||
@@ -174,7 +174,7 @@ instance [Inhabited a] [Inhabited b] : Inhabited (a × b) where
|
||||
With this added to the earlier instance declarations, type class instance can infer, for example, a default element of ``Nat × Bool``:
|
||||
```lean
|
||||
# namespace Ex
|
||||
# class Inhabited (a : Type u) where
|
||||
# class Inhabited (a : Sort u) where
|
||||
# default : a
|
||||
# instance : Inhabited Bool where
|
||||
# default := true
|
||||
@@ -191,8 +191,14 @@ instance [Inhabited a] [Inhabited b] : Inhabited (a × b) where
|
||||
```
|
||||
Similarly, we can inhabit type function with suitable constant functions:
|
||||
```lean
|
||||
# namespace Ex
|
||||
# class Inhabited (a : Sort u) where
|
||||
# default : a
|
||||
# opaque default [Inhabited a] : a :=
|
||||
# Inhabited.default
|
||||
instance [Inhabited b] : Inhabited (a -> b) where
|
||||
default := fun _ => default
|
||||
# end Ex
|
||||
```
|
||||
As an exercise, try defining default instances for other types, such as `List` and `Sum` types.
|
||||
|
||||
|
||||
@@ -37,6 +37,6 @@ Lean has numerous features, including:
|
||||
- [Extensible syntax](./syntax.md)
|
||||
- Hygienic macros
|
||||
- [Dependent types](https://lean-lang.org/theorem_proving_in_lean4/dependent_type_theory.html)
|
||||
- [Metaprogramming](./metaprogramming.md)
|
||||
- [Metaprogramming](./macro_overview.md)
|
||||
- Multithreading
|
||||
- Verification: you can prove properties of your functions using Lean itself
|
||||
|
||||
@@ -48,5 +48,10 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"extensions": {
|
||||
"recommendations": [
|
||||
"leanprover.lean4"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Prefix for tags to search for
|
||||
tag_prefix="nightly-"
|
||||
|
||||
# Fetch all tags from the remote repository
|
||||
git fetch https://github.com/leanprover/lean4-nightly.git --tags > /dev/null
|
||||
|
||||
# Get the most recent commit that has a matching tag
|
||||
tag_name=$(git tag --merged HEAD --list "${tag_prefix}*" | sort -rV | head -n 1 | sed "s/^$tag_prefix//")
|
||||
|
||||
if [ -z "$tag_name" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$tag_name"
|
||||
@@ -9,7 +9,7 @@ endif()
|
||||
include(ExternalProject)
|
||||
project(LEAN CXX C)
|
||||
set(LEAN_VERSION_MAJOR 4)
|
||||
set(LEAN_VERSION_MINOR 5)
|
||||
set(LEAN_VERSION_MINOR 7)
|
||||
set(LEAN_VERSION_PATCH 0)
|
||||
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise.
|
||||
set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
|
||||
@@ -18,6 +18,14 @@ if (LEAN_SPECIAL_VERSION_DESC)
|
||||
string(APPEND LEAN_VERSION_STRING "-${LEAN_SPECIAL_VERSION_DESC}")
|
||||
endif()
|
||||
|
||||
set(LEAN_PLATFORM_TARGET "" CACHE STRING "LLVM triple of the target platform")
|
||||
if (NOT LEAN_PLATFORM_TARGET)
|
||||
# this may fail when the compiler is not clang, but this should only happen in local builds where
|
||||
# the value of the variable is not of immediate relevance
|
||||
execute_process(COMMAND ${CMAKE_C_COMPILER} --print-target-triple
|
||||
OUTPUT_VARIABLE LEAN_PLATFORM_TARGET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
|
||||
set(LEAN_EXTRA_LINKER_FLAGS "" CACHE STRING "Additional flags used by the linker")
|
||||
set(LEAN_EXTRA_CXX_FLAGS "" CACHE STRING "Additional flags used by the C++ compiler")
|
||||
set(LEAN_TEST_VARS "LEAN_CC=${CMAKE_C_COMPILER}" CACHE STRING "Additional environment variables used when running tests")
|
||||
|
||||
@@ -23,4 +23,5 @@ import Init.NotationExtra
|
||||
import Init.SimpLemmas
|
||||
import Init.Hints
|
||||
import Init.Conv
|
||||
import Init.Simproc
|
||||
import Init.SizeOfLemmas
|
||||
|
||||
@@ -22,7 +22,7 @@ noncomputable def choose {α : Sort u} {p : α → Prop} (h : ∃ x, p x) : α :
|
||||
theorem choose_spec {α : Sort u} {p : α → Prop} (h : ∃ x, p x) : p (choose h) :=
|
||||
(indefiniteDescription p h).property
|
||||
|
||||
/-- Diaconescu's theorem: excluded middle from choice, Function extensionality and propositional extensionality. -/
|
||||
/-- **Diaconescu's theorem**: excluded middle from choice, Function extensionality and propositional extensionality. -/
|
||||
theorem em (p : Prop) : p ∨ ¬p :=
|
||||
let U (x : Prop) : Prop := x = True ∨ p
|
||||
let V (x : Prop) : Prop := x = False ∨ p
|
||||
|
||||
@@ -411,9 +411,10 @@ set_option linter.unusedVariables.funArgs false in
|
||||
be available and then calls `f` on the result.
|
||||
|
||||
`prio`, if provided, is the priority of the task.
|
||||
If `sync` is set to true, `f` is executed on the current thread if `x` has already finished.
|
||||
-/
|
||||
@[noinline, extern "lean_task_map"]
|
||||
protected def map {α : Type u} {β : Type v} (f : α → β) (x : Task α) (prio := Priority.default) : Task β :=
|
||||
protected def map (f : α → β) (x : Task α) (prio := Priority.default) (sync := false) : Task β :=
|
||||
⟨f x.get⟩
|
||||
|
||||
set_option linter.unusedVariables.funArgs false in
|
||||
@@ -424,9 +425,11 @@ for the value of `x` to be available and then calls `f` on the result,
|
||||
resulting in a new task which is then run for a result.
|
||||
|
||||
`prio`, if provided, is the priority of the task.
|
||||
If `sync` is set to true, `f` is executed on the current thread if `x` has already finished.
|
||||
-/
|
||||
@[noinline, extern "lean_task_bind"]
|
||||
protected def bind {α : Type u} {β : Type v} (x : Task α) (f : α → Task β) (prio := Priority.default) : Task β :=
|
||||
protected def bind (x : Task α) (f : α → Task β) (prio := Priority.default) (sync := false) :
|
||||
Task β :=
|
||||
⟨(f x.get).get⟩
|
||||
|
||||
end Task
|
||||
@@ -1680,40 +1683,92 @@ So, you are mainly losing the capability of type checking your development using
|
||||
-/
|
||||
axiom ofReduceNat (a b : Nat) (h : reduceNat a = b) : a = b
|
||||
|
||||
end Lean
|
||||
|
||||
namespace Std
|
||||
variable {α : Sort u}
|
||||
|
||||
/--
|
||||
`IsAssociative op` says that `op` is an associative operation,
|
||||
i.e. `(a ∘ b) ∘ c = a ∘ (b ∘ c)`. It is used by the `ac_rfl` tactic.
|
||||
`Associative op` indicates `op` is an associative operation,
|
||||
i.e. `(a ∘ b) ∘ c = a ∘ (b ∘ c)`.
|
||||
-/
|
||||
class IsAssociative {α : Sort u} (op : α → α → α) where
|
||||
class Associative (op : α → α → α) : Prop where
|
||||
/-- An associative operation satisfies `(a ∘ b) ∘ c = a ∘ (b ∘ c)`. -/
|
||||
assoc : (a b c : α) → op (op a b) c = op a (op b c)
|
||||
|
||||
/--
|
||||
`IsCommutative op` says that `op` is a commutative operation,
|
||||
i.e. `a ∘ b = b ∘ a`. It is used by the `ac_rfl` tactic.
|
||||
`Commutative op` says that `op` is a commutative operation,
|
||||
i.e. `a ∘ b = b ∘ a`.
|
||||
-/
|
||||
class IsCommutative {α : Sort u} (op : α → α → α) where
|
||||
class Commutative (op : α → α → α) : Prop where
|
||||
/-- A commutative operation satisfies `a ∘ b = b ∘ a`. -/
|
||||
comm : (a b : α) → op a b = op b a
|
||||
|
||||
/--
|
||||
`IsIdempotent op` says that `op` is an idempotent operation,
|
||||
i.e. `a ∘ a = a`. It is used by the `ac_rfl` tactic
|
||||
(which also simplifies up to idempotence when available).
|
||||
`IdempotentOp op` indicates `op` is an idempotent binary operation.
|
||||
i.e. `a ∘ a = a`.
|
||||
-/
|
||||
class IsIdempotent {α : Sort u} (op : α → α → α) where
|
||||
class IdempotentOp (op : α → α → α) : Prop where
|
||||
/-- An idempotent operation satisfies `a ∘ a = a`. -/
|
||||
idempotent : (x : α) → op x x = x
|
||||
|
||||
/--
|
||||
`IsNeutral op e` says that `e` is a neutral operation for `op`,
|
||||
i.e. `a ∘ e = a = e ∘ a`. It is used by the `ac_rfl` tactic
|
||||
(which also simplifies neutral elements when available).
|
||||
-/
|
||||
class IsNeutral {α : Sort u} (op : α → α → α) (neutral : α) where
|
||||
/-- A neutral element can be cancelled on the left: `e ∘ a = a`. -/
|
||||
left_neutral : (a : α) → op neutral a = a
|
||||
/-- A neutral element can be cancelled on the right: `a ∘ e = a`. -/
|
||||
right_neutral : (a : α) → op a neutral = a
|
||||
`LeftIdentify op o` indicates `o` is a left identity of `op`.
|
||||
|
||||
end Lean
|
||||
This class does not require a proof that `o` is an identity, and
|
||||
is used primarily for infering the identity using class resoluton.
|
||||
-/
|
||||
class LeftIdentity (op : α → β → β) (o : outParam α) : Prop
|
||||
|
||||
/--
|
||||
`LawfulLeftIdentify op o` indicates `o` is a verified left identity of
|
||||
`op`.
|
||||
-/
|
||||
class LawfulLeftIdentity (op : α → β → β) (o : outParam α) extends LeftIdentity op o : Prop where
|
||||
/-- Left identity `o` is an identity. -/
|
||||
left_id : ∀ a, op o a = a
|
||||
|
||||
/--
|
||||
`RightIdentify op o` indicates `o` is a right identity `o` of `op`.
|
||||
|
||||
This class does not require a proof that `o` is an identity, and is used
|
||||
primarily for infering the identity using class resoluton.
|
||||
-/
|
||||
class RightIdentity (op : α → β → α) (o : outParam β) : Prop
|
||||
|
||||
/--
|
||||
`LawfulRightIdentify op o` indicates `o` is a verified right identity of
|
||||
`op`.
|
||||
-/
|
||||
class LawfulRightIdentity (op : α → β → α) (o : outParam β) extends RightIdentity op o : Prop where
|
||||
/-- Right identity `o` is an identity. -/
|
||||
right_id : ∀ a, op a o = a
|
||||
|
||||
/--
|
||||
`Identity op o` indicates `o` is a left and right identity of `op`.
|
||||
|
||||
This class does not require a proof that `o` is an identity, and is used
|
||||
primarily for infering the identity using class resoluton.
|
||||
-/
|
||||
class Identity (op : α → α → α) (o : outParam α) extends LeftIdentity op o, RightIdentity op o : Prop
|
||||
|
||||
/--
|
||||
`LawfulIdentity op o` indicates `o` is a verified left and right
|
||||
identity of `op`.
|
||||
-/
|
||||
class LawfulIdentity (op : α → α → α) (o : outParam α) extends Identity op o, LawfulLeftIdentity op o, LawfulRightIdentity op o : Prop
|
||||
|
||||
/--
|
||||
`LawfulCommIdentity` can simplify defining instances of `LawfulIdentity`
|
||||
on commutative functions by requiring only a left or right identity
|
||||
proof.
|
||||
|
||||
This class is intended for simplifying defining instances of
|
||||
`LawfulIdentity` and functions needed commutative operations with
|
||||
identity should just add a `LawfulIdentity` constraint.
|
||||
-/
|
||||
class LawfulCommIdentity (op : α → α → α) (o : outParam α) [hc : Commutative op] extends LawfulIdentity op o : Prop where
|
||||
left_id a := Eq.trans (hc.comm o a) (right_id a)
|
||||
right_id a := Eq.trans (hc.comm a o) (left_id a)
|
||||
|
||||
end Std
|
||||
|
||||
@@ -14,15 +14,17 @@ inductive Expr
|
||||
| op (lhs rhs : Expr)
|
||||
deriving Inhabited, Repr, BEq
|
||||
|
||||
open Std
|
||||
|
||||
structure Variable {α : Sort u} (op : α → α → α) : Type u where
|
||||
value : α
|
||||
neutral : Option $ IsNeutral op value
|
||||
neutral : Option $ PLift (LawfulIdentity op value)
|
||||
|
||||
structure Context (α : Sort u) where
|
||||
op : α → α → α
|
||||
assoc : IsAssociative op
|
||||
comm : Option $ IsCommutative op
|
||||
idem : Option $ IsIdempotent op
|
||||
assoc : Associative op
|
||||
comm : Option $ PLift $ Commutative op
|
||||
idem : Option $ PLift $ IdempotentOp op
|
||||
vars : List (Variable op)
|
||||
arbitrary : α
|
||||
|
||||
@@ -128,7 +130,14 @@ theorem Context.mergeIdem_head2 (h : x ≠ y) : mergeIdem (x :: y :: ys) = x ::
|
||||
simp [mergeIdem, mergeIdem.loop, h]
|
||||
|
||||
theorem Context.evalList_mergeIdem (ctx : Context α) (h : ContextInformation.isIdem ctx) (e : List Nat) : evalList α ctx (mergeIdem e) = evalList α ctx e := by
|
||||
have h : IsIdempotent ctx.op := by simp [ContextInformation.isIdem, Option.isSome] at h; cases h₂ : ctx.idem <;> simp [h₂] at h; assumption
|
||||
have h : IdempotentOp ctx.op := by
|
||||
simp [ContextInformation.isIdem, Option.isSome] at h;
|
||||
match h₂ : ctx.idem with
|
||||
| none =>
|
||||
simp [h₂] at h
|
||||
| some val =>
|
||||
simp [h₂] at h
|
||||
exact val.down
|
||||
induction e using List.two_step_induction with
|
||||
| empty => rfl
|
||||
| single => rfl
|
||||
@@ -169,7 +178,7 @@ theorem Context.sort_loop_nonEmpty (xs : List Nat) (h : xs ≠ []) : sort.loop x
|
||||
|
||||
theorem Context.evalList_insert
|
||||
(ctx : Context α)
|
||||
(h : IsCommutative ctx.op)
|
||||
(h : Commutative ctx.op)
|
||||
(x : Nat)
|
||||
(xs : List Nat)
|
||||
: evalList α ctx (insert x xs) = evalList α ctx (x::xs) := by
|
||||
@@ -190,7 +199,7 @@ theorem Context.evalList_insert
|
||||
|
||||
theorem Context.evalList_sort_congr
|
||||
(ctx : Context α)
|
||||
(h : IsCommutative ctx.op)
|
||||
(h : Commutative ctx.op)
|
||||
(h₂ : evalList α ctx a = evalList α ctx b)
|
||||
(h₃ : a ≠ [])
|
||||
(h₄ : b ≠ [])
|
||||
@@ -209,7 +218,7 @@ theorem Context.evalList_sort_congr
|
||||
|
||||
theorem Context.evalList_sort_loop_swap
|
||||
(ctx : Context α)
|
||||
(h : IsCommutative ctx.op)
|
||||
(h : Commutative ctx.op)
|
||||
(xs ys : List Nat)
|
||||
: evalList α ctx (sort.loop xs (y::ys)) = evalList α ctx (sort.loop (y::xs) ys) := by
|
||||
induction ys generalizing y xs with
|
||||
@@ -224,7 +233,7 @@ theorem Context.evalList_sort_loop_swap
|
||||
|
||||
theorem Context.evalList_sort_cons
|
||||
(ctx : Context α)
|
||||
(h : IsCommutative ctx.op)
|
||||
(h : Commutative ctx.op)
|
||||
(x : Nat)
|
||||
(xs : List Nat)
|
||||
: evalList α ctx (sort (x :: xs)) = evalList α ctx (x :: sort xs) := by
|
||||
@@ -247,7 +256,14 @@ theorem Context.evalList_sort_cons
|
||||
all_goals simp [insert_nonEmpty]
|
||||
|
||||
theorem Context.evalList_sort (ctx : Context α) (h : ContextInformation.isComm ctx) (e : List Nat) : evalList α ctx (sort e) = evalList α ctx e := by
|
||||
have h : IsCommutative ctx.op := by simp [ContextInformation.isComm, Option.isSome] at h; cases h₂ : ctx.comm <;> simp [h₂] at h; assumption
|
||||
have h : Commutative ctx.op := by
|
||||
simp [ContextInformation.isComm, Option.isSome] at h
|
||||
match h₂ : ctx.comm with
|
||||
| none =>
|
||||
simp only [h₂] at h
|
||||
| some val =>
|
||||
simp [h₂] at h
|
||||
exact val.down
|
||||
induction e using List.two_step_induction with
|
||||
| empty => rfl
|
||||
| single => rfl
|
||||
@@ -269,10 +285,12 @@ theorem Context.toList_nonEmpty (e : Expr) : e.toList ≠ [] := by
|
||||
theorem Context.unwrap_isNeutral
|
||||
{ctx : Context α}
|
||||
{x : Nat}
|
||||
: ContextInformation.isNeutral ctx x = true → IsNeutral (EvalInformation.evalOp ctx) (EvalInformation.evalVar (β := α) ctx x) := by
|
||||
: ContextInformation.isNeutral ctx x = true → LawfulIdentity (EvalInformation.evalOp ctx) (EvalInformation.evalVar (β := α) ctx x) := by
|
||||
simp [ContextInformation.isNeutral, Option.isSome, EvalInformation.evalOp, EvalInformation.evalVar]
|
||||
match (var ctx x).neutral with
|
||||
| some hn => intro; assumption
|
||||
| some hn =>
|
||||
intro
|
||||
exact hn.down
|
||||
| none => intro; contradiction
|
||||
|
||||
theorem Context.evalList_removeNeutrals (ctx : Context α) (e : List Nat) : evalList α ctx (removeNeutrals ctx e) = evalList α ctx e := by
|
||||
@@ -283,10 +301,12 @@ theorem Context.evalList_removeNeutrals (ctx : Context α) (e : List Nat) : eval
|
||||
case h_1 => rfl
|
||||
case h_2 h => split at h <;> simp_all
|
||||
| step x y ys ih =>
|
||||
cases h₁ : ContextInformation.isNeutral ctx x <;> cases h₂ : ContextInformation.isNeutral ctx y <;> cases h₃ : removeNeutrals.loop ctx ys
|
||||
cases h₁ : ContextInformation.isNeutral ctx x <;>
|
||||
cases h₂ : ContextInformation.isNeutral ctx y <;>
|
||||
cases h₃ : removeNeutrals.loop ctx ys
|
||||
<;> simp [removeNeutrals, removeNeutrals.loop, h₁, h₂, h₃, evalList, ←ih]
|
||||
<;> (try simp [unwrap_isNeutral h₂ |>.2])
|
||||
<;> (try simp [unwrap_isNeutral h₁ |>.1])
|
||||
<;> (try simp [unwrap_isNeutral h₂ |>.right_id])
|
||||
<;> (try simp [unwrap_isNeutral h₁ |>.left_id])
|
||||
|
||||
theorem Context.evalList_append
|
||||
(ctx : Context α)
|
||||
|
||||
@@ -71,6 +71,12 @@ abbrev getLit {α : Type u} {n : Nat} (a : Array α) (i : Nat) (h₁ : a.size =
|
||||
def uset (a : Array α) (i : USize) (v : α) (h : i.toNat < a.size) : Array α :=
|
||||
a.set ⟨i.toNat, h⟩ v
|
||||
|
||||
/--
|
||||
Swaps two entries in an array.
|
||||
|
||||
This will perform the update destructively provided that `a` has a reference
|
||||
count of 1 when called.
|
||||
-/
|
||||
@[extern "lean_array_fswap"]
|
||||
def swap (a : Array α) (i j : @& Fin a.size) : Array α :=
|
||||
let v₁ := a.get i
|
||||
@@ -78,12 +84,18 @@ def swap (a : Array α) (i j : @& Fin a.size) : Array α :=
|
||||
let a' := a.set i v₂
|
||||
a'.set (size_set a i v₂ ▸ j) v₁
|
||||
|
||||
/--
|
||||
Swaps two entries in an array, or panics if either index is out of bounds.
|
||||
|
||||
This will perform the update destructively provided that `a` has a reference
|
||||
count of 1 when called.
|
||||
-/
|
||||
@[extern "lean_array_swap"]
|
||||
def swap! (a : Array α) (i j : @& Nat) : Array α :=
|
||||
if h₁ : i < a.size then
|
||||
if h₂ : j < a.size then swap a ⟨i, h₁⟩ ⟨j, h₂⟩
|
||||
else panic! "index out of bounds"
|
||||
else panic! "index out of bounds"
|
||||
else a
|
||||
else a
|
||||
|
||||
@[inline] def swapAt (a : Array α) (i : Fin a.size) (v : α) : α × Array α :=
|
||||
let e := a.get i
|
||||
@@ -276,8 +288,8 @@ def mapM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : α
|
||||
map (i+1) (r.push (← f as[i]))
|
||||
else
|
||||
pure r
|
||||
termination_by as.size - i
|
||||
map 0 (mkEmpty as.size)
|
||||
termination_by map => as.size - i
|
||||
|
||||
@[inline]
|
||||
def mapIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (as : Array α) (f : Fin as.size → α → m β) : m (Array β) :=
|
||||
@@ -348,12 +360,12 @@ def anyM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as :
|
||||
loop (j+1)
|
||||
else
|
||||
pure false
|
||||
termination_by stop - j
|
||||
loop start
|
||||
if h : stop ≤ as.size then
|
||||
any stop h
|
||||
else
|
||||
any as.size (Nat.le_refl _)
|
||||
termination_by loop i j => stop - j
|
||||
|
||||
@[inline]
|
||||
def allM {α : Type u} {m : Type → Type w} [Monad m] (p : α → m Bool) (as : Array α) (start := 0) (stop := as.size) : m Bool :=
|
||||
@@ -523,7 +535,7 @@ def isEqvAux (a b : Array α) (hsz : a.size = b.size) (p : α → α → Bool) (
|
||||
p a[i] b[i] && isEqvAux a b hsz p (i+1)
|
||||
else
|
||||
true
|
||||
termination_by _ => a.size - i
|
||||
termination_by a.size - i
|
||||
|
||||
@[inline] def isEqv (a b : Array α) (p : α → α → Bool) : Bool :=
|
||||
if h : a.size = b.size then
|
||||
@@ -627,7 +639,7 @@ def indexOfAux [BEq α] (a : Array α) (v : α) (i : Nat) : Option (Fin a.size)
|
||||
if a.get idx == v then some idx
|
||||
else indexOfAux a v (i+1)
|
||||
else none
|
||||
termination_by _ => a.size - i
|
||||
termination_by a.size - i
|
||||
|
||||
def indexOf? [BEq α] (a : Array α) (v : α) : Option (Fin a.size) :=
|
||||
indexOfAux a v 0
|
||||
@@ -659,7 +671,7 @@ where
|
||||
loop as (i+1) ⟨j-1, this⟩
|
||||
else
|
||||
as
|
||||
termination_by _ => j - i
|
||||
termination_by j - i
|
||||
|
||||
def popWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
if h : as.size > 0 then
|
||||
@@ -669,7 +681,7 @@ def popWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
as
|
||||
else
|
||||
as
|
||||
termination_by popWhile as => as.size
|
||||
termination_by as.size
|
||||
|
||||
def takeWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
let rec go (i : Nat) (r : Array α) : Array α :=
|
||||
@@ -681,8 +693,8 @@ def takeWhile (p : α → Bool) (as : Array α) : Array α :=
|
||||
r
|
||||
else
|
||||
r
|
||||
termination_by as.size - i
|
||||
go 0 #[]
|
||||
termination_by go i r => as.size - i
|
||||
|
||||
def eraseIdxAux (i : Nat) (a : Array α) : Array α :=
|
||||
if h : i < a.size then
|
||||
@@ -692,7 +704,7 @@ def eraseIdxAux (i : Nat) (a : Array α) : Array α :=
|
||||
eraseIdxAux (i+1) a'
|
||||
else
|
||||
a.pop
|
||||
termination_by _ => a.size - i
|
||||
termination_by a.size - i
|
||||
|
||||
def feraseIdx (a : Array α) (i : Fin a.size) : Array α :=
|
||||
eraseIdxAux (i.val + 1) a
|
||||
@@ -707,7 +719,7 @@ def eraseIdxSzAux (a : Array α) (i : Nat) (r : Array α) (heq : r.size = a.size
|
||||
eraseIdxSzAux a (i+1) (r.swap idx idx1) ((size_swap r idx idx1).trans heq)
|
||||
else
|
||||
⟨r.pop, (size_pop r).trans (heq ▸ rfl)⟩
|
||||
termination_by _ => r.size - i
|
||||
termination_by r.size - i
|
||||
|
||||
def eraseIdx' (a : Array α) (i : Fin a.size) : { r : Array α // r.size = a.size - 1 } :=
|
||||
eraseIdxSzAux a (i.val + 1) a rfl
|
||||
@@ -726,10 +738,10 @@ def erase [BEq α] (as : Array α) (a : α) : Array α :=
|
||||
loop as ⟨j', by rw [size_swap]; exact j'.2⟩
|
||||
else
|
||||
as
|
||||
termination_by j.1
|
||||
let j := as.size
|
||||
let as := as.push a
|
||||
loop as ⟨j, size_push .. ▸ j.lt_succ_self⟩
|
||||
termination_by loop j => j.1
|
||||
|
||||
/-- Insert element `a` at position `i`. Panics if `i` is not `i ≤ as.size`. -/
|
||||
def insertAt! (as : Array α) (i : Nat) (a : α) : Array α :=
|
||||
@@ -779,7 +791,7 @@ def isPrefixOfAux [BEq α] (as bs : Array α) (hle : as.size ≤ bs.size) (i : N
|
||||
false
|
||||
else
|
||||
true
|
||||
termination_by _ => as.size - i
|
||||
termination_by as.size - i
|
||||
|
||||
/-- Return true iff `as` is a prefix of `bs`.
|
||||
That is, `bs = as ++ t` for some `t : List α`.-/
|
||||
@@ -800,7 +812,7 @@ private def allDiffAux [BEq α] (as : Array α) (i : Nat) : Bool :=
|
||||
allDiffAuxAux as as[i] i h && allDiffAux as (i+1)
|
||||
else
|
||||
true
|
||||
termination_by _ => as.size - i
|
||||
termination_by as.size - i
|
||||
|
||||
def allDiff [BEq α] (as : Array α) : Bool :=
|
||||
allDiffAux as 0
|
||||
@@ -815,7 +827,7 @@ def allDiff [BEq α] (as : Array α) : Bool :=
|
||||
cs
|
||||
else
|
||||
cs
|
||||
termination_by _ => as.size - i
|
||||
termination_by as.size - i
|
||||
|
||||
@[inline] def zipWith (as : Array α) (bs : Array β) (f : α → β → γ) : Array γ :=
|
||||
zipWithAux f as bs 0 #[]
|
||||
|
||||
@@ -47,7 +47,7 @@ where
|
||||
have hlt : i < as.size := Nat.lt_of_le_of_ne hle h
|
||||
let b ← f as[i]
|
||||
go (i+1) ⟨acc.val.push b, by simp [acc.property]⟩ hlt
|
||||
termination_by go i _ _ => as.size - i
|
||||
termination_by as.size - i
|
||||
|
||||
@[inline] private unsafe def mapMonoMImp [Monad m] (as : Array α) (f : α → m α) : m (Array α) :=
|
||||
go 0 as
|
||||
|
||||
@@ -20,7 +20,7 @@ theorem eq_of_isEqvAux [DecidableEq α] (a b : Array α) (hsz : a.size = b.size)
|
||||
· have heq : i = a.size := Nat.le_antisymm hi (Nat.ge_of_not_lt h)
|
||||
subst heq
|
||||
exact absurd (Nat.lt_of_lt_of_le high low) (Nat.lt_irrefl j)
|
||||
termination_by _ => a.size - i
|
||||
termination_by a.size - i
|
||||
|
||||
theorem eq_of_isEqv [DecidableEq α] (a b : Array α) : Array.isEqv a b (fun x y => x = y) → a = b := by
|
||||
simp [Array.isEqv]
|
||||
@@ -36,7 +36,7 @@ theorem isEqvAux_self [DecidableEq α] (a : Array α) (i : Nat) : Array.isEqvAux
|
||||
split
|
||||
case inl h => simp [h, isEqvAux_self a (i+1)]
|
||||
case inr h => simp [h]
|
||||
termination_by _ => a.size - i
|
||||
termination_by a.size - i
|
||||
|
||||
theorem isEqv_self [DecidableEq α] (a : Array α) : Array.isEqv a a (fun x y => x = y) = true := by
|
||||
simp [isEqv, isEqvAux_self]
|
||||
|
||||
@@ -26,8 +26,8 @@ def qpartition (as : Array α) (lt : α → α → Bool) (lo hi : Nat) : Nat ×
|
||||
else
|
||||
let as := as.swap! i hi
|
||||
(i, as)
|
||||
termination_by hi - j
|
||||
loop as lo lo
|
||||
termination_by _ => hi - j
|
||||
|
||||
@[inline] partial def qsort (as : Array α) (lt : α → α → Bool) (low := 0) (high := as.size - 1) : Array α :=
|
||||
let rec @[specialize] sort (as : Array α) (low high : Nat) :=
|
||||
|
||||
@@ -81,7 +81,7 @@ def isEmpty (s : ByteArray) : Bool :=
|
||||
If `exact` is `false`, the capacity will be doubled when grown. -/
|
||||
@[extern "lean_byte_array_copy_slice"]
|
||||
def copySlice (src : @& ByteArray) (srcOff : Nat) (dest : ByteArray) (destOff len : Nat) (exact : Bool := true) : ByteArray :=
|
||||
⟨dest.data.extract 0 destOff ++ src.data.extract srcOff (srcOff + len) ++ dest.data.extract (destOff + len) dest.data.size⟩
|
||||
⟨dest.data.extract 0 destOff ++ src.data.extract srcOff (srcOff + len) ++ dest.data.extract (destOff + min len (src.data.size - srcOff)) dest.data.size⟩
|
||||
|
||||
def extract (a : ByteArray) (b e : Nat) : ByteArray :=
|
||||
a.copySlice b empty 0 (e - b)
|
||||
|
||||
@@ -100,7 +100,7 @@ instance : ShiftLeft (Fin n) where
|
||||
instance : ShiftRight (Fin n) where
|
||||
shiftRight := Fin.shiftRight
|
||||
|
||||
instance : OfNat (Fin (no_index (n+1))) i where
|
||||
instance instOfNat : OfNat (Fin (no_index (n+1))) i where
|
||||
ofNat := Fin.ofNat i
|
||||
|
||||
instance : Inhabited (Fin (no_index (n+1))) where
|
||||
|
||||
@@ -26,6 +26,8 @@ opaque floatSpec : FloatSpec := {
|
||||
decLe := fun _ _ => inferInstanceAs (Decidable True)
|
||||
}
|
||||
|
||||
/-- Native floating point type, corresponding to the IEEE 754 *binary64* format
|
||||
(`double` in C or `f64` in Rust). -/
|
||||
structure Float where
|
||||
val : floatSpec.float
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ attribute [extern "lean_int_neg_succ_of_nat"] Int.negSucc
|
||||
|
||||
instance : Coe Nat Int := ⟨Int.ofNat⟩
|
||||
|
||||
instance : OfNat Int n where
|
||||
instance instOfNat : OfNat Int n where
|
||||
ofNat := Int.ofNat n
|
||||
|
||||
namespace Int
|
||||
|
||||
@@ -124,7 +124,8 @@ def appendTR (as bs : List α) : List α :=
|
||||
induction as with
|
||||
| nil => rfl
|
||||
| cons a as ih =>
|
||||
simp [reverseAux, List.append, ih, reverseAux_reverseAux]
|
||||
rw [reverseAux, reverseAux_reverseAux]
|
||||
simp [List.append, ih, reverseAux]
|
||||
|
||||
instance : Append (List α) := ⟨List.append⟩
|
||||
|
||||
@@ -557,16 +558,22 @@ def takeWhile (p : α → Bool) : (xs : List α) → List α
|
||||
/--
|
||||
`O(|l|)`. Returns true if `p` is true for any element of `l`.
|
||||
* `any p [a, b, c] = p a || p b || p c`
|
||||
|
||||
Short-circuits upon encountering the first `true`.
|
||||
-/
|
||||
@[inline] def any (l : List α) (p : α → Bool) : Bool :=
|
||||
foldr (fun a r => p a || r) false l
|
||||
def any : List α -> (α → Bool) -> Bool
|
||||
| [], _ => false
|
||||
| h :: t, p => p h || any t p
|
||||
|
||||
/--
|
||||
`O(|l|)`. Returns true if `p` is true for every element of `l`.
|
||||
* `all p [a, b, c] = p a && p b && p c`
|
||||
|
||||
Short-circuits upon encountering the first `false`.
|
||||
-/
|
||||
@[inline] def all (l : List α) (p : α → Bool) : Bool :=
|
||||
foldr (fun a r => p a && r) true l
|
||||
def all : List α -> (α → Bool) -> Bool
|
||||
| [], _ => true
|
||||
| h :: t, p => p h && all t p
|
||||
|
||||
/--
|
||||
`O(|l|)`. Returns true if `true` is an element of the list of booleans `l`.
|
||||
|
||||
@@ -8,14 +8,14 @@ import Init.Data.Nat.Div
|
||||
|
||||
namespace Nat
|
||||
|
||||
private def gcdF (x : Nat) : (∀ x₁, x₁ < x → Nat → Nat) → Nat → Nat :=
|
||||
match x with
|
||||
| 0 => fun _ y => y
|
||||
| succ x => fun f y => f (y % succ x) (mod_lt _ (zero_lt_succ _)) (succ x)
|
||||
|
||||
@[extern "lean_nat_gcd"]
|
||||
def gcd (a b : @& Nat) : Nat :=
|
||||
WellFounded.fix (measure id).wf gcdF a b
|
||||
def gcd (m n : @& Nat) : Nat :=
|
||||
if m = 0 then
|
||||
n
|
||||
else
|
||||
gcd (n % m) m
|
||||
termination_by m
|
||||
decreasing_by simp_wf; apply mod_lt _ (zero_lt_of_ne_zero _); assumption
|
||||
|
||||
@[simp] theorem gcd_zero_left (y : Nat) : gcd 0 y = y :=
|
||||
rfl
|
||||
|
||||
@@ -21,8 +21,8 @@ where
|
||||
go (power * 2) (Nat.mul_pos h (by decide))
|
||||
else
|
||||
power
|
||||
termination_by go p h => n - p
|
||||
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
|
||||
termination_by n - power
|
||||
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
|
||||
|
||||
def isPowerOfTwo (n : Nat) := ∃ k, n = 2 ^ k
|
||||
|
||||
@@ -48,7 +48,7 @@ where
|
||||
split
|
||||
. exact isPowerOfTwo_go (power*2) (Nat.mul_pos h₁ (by decide)) (Nat.mul2_isPowerOfTwo_of_isPowerOfTwo h₂)
|
||||
. assumption
|
||||
termination_by isPowerOfTwo_go p _ _ => n - p
|
||||
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
|
||||
termination_by n - power
|
||||
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
|
||||
|
||||
end Nat
|
||||
|
||||
@@ -6,7 +6,7 @@ Authors: Leonardo de Moura
|
||||
prelude
|
||||
import Init.Meta
|
||||
import Init.Data.Float
|
||||
import Init.Data.Nat
|
||||
import Init.Data.Nat.Log2
|
||||
|
||||
/-- For decimal and scientific numbers (e.g., `1.23`, `3.12e10`).
|
||||
Examples:
|
||||
|
||||
@@ -76,10 +76,12 @@ macro_rules
|
||||
end Range
|
||||
end Std
|
||||
|
||||
theorem Membership.mem.upper {i : Nat} {r : Std.Range} (h : i ∈ r) : i < r.stop := by
|
||||
simp [Membership.mem] at h
|
||||
exact h.2
|
||||
theorem Membership.mem.upper {i : Nat} {r : Std.Range} (h : i ∈ r) : i < r.stop := h.2
|
||||
|
||||
theorem Membership.mem.lower {i : Nat} {r : Std.Range} (h : i ∈ r) : r.start ≤ i := by
|
||||
simp [Membership.mem] at h
|
||||
exact h.1
|
||||
theorem Membership.mem.lower {i : Nat} {r : Std.Range} (h : i ∈ r) : r.start ≤ i := h.1
|
||||
|
||||
theorem Membership.get_elem_helper {i n : Nat} {r : Std.Range} (h₁ : i ∈ r) (h₂ : r.stop = n) :
|
||||
i < n := h₂ ▸ h₁.2
|
||||
|
||||
macro_rules
|
||||
| `(tactic| get_elem_tactic_trivial) => `(tactic| apply Membership.get_elem_helper; assumption; rfl)
|
||||
|
||||
@@ -159,7 +159,7 @@ def posOfAux (s : String) (c : Char) (stopPos : Pos) (pos : Pos) : Pos :=
|
||||
have := Nat.sub_lt_sub_left h (lt_next s pos)
|
||||
posOfAux s c stopPos (s.next pos)
|
||||
else pos
|
||||
termination_by _ => stopPos.1 - pos.1
|
||||
termination_by stopPos.1 - pos.1
|
||||
|
||||
@[inline] def posOf (s : String) (c : Char) : Pos :=
|
||||
posOfAux s c s.endPos 0
|
||||
@@ -171,7 +171,7 @@ def revPosOfAux (s : String) (c : Char) (pos : Pos) : Option Pos :=
|
||||
let pos := s.prev pos
|
||||
if s.get pos == c then some pos
|
||||
else revPosOfAux s c pos
|
||||
termination_by _ => pos.1
|
||||
termination_by pos.1
|
||||
|
||||
def revPosOf (s : String) (c : Char) : Option Pos :=
|
||||
revPosOfAux s c s.endPos
|
||||
@@ -183,7 +183,7 @@ def findAux (s : String) (p : Char → Bool) (stopPos : Pos) (pos : Pos) : Pos :
|
||||
have := Nat.sub_lt_sub_left h (lt_next s pos)
|
||||
findAux s p stopPos (s.next pos)
|
||||
else pos
|
||||
termination_by _ => stopPos.1 - pos.1
|
||||
termination_by stopPos.1 - pos.1
|
||||
|
||||
@[inline] def find (s : String) (p : Char → Bool) : Pos :=
|
||||
findAux s p s.endPos 0
|
||||
@@ -195,7 +195,7 @@ def revFindAux (s : String) (p : Char → Bool) (pos : Pos) : Option Pos :=
|
||||
let pos := s.prev pos
|
||||
if p (s.get pos) then some pos
|
||||
else revFindAux s p pos
|
||||
termination_by _ => pos.1
|
||||
termination_by pos.1
|
||||
|
||||
def revFind (s : String) (p : Char → Bool) : Option Pos :=
|
||||
revFindAux s p s.endPos
|
||||
@@ -213,8 +213,8 @@ def firstDiffPos (a b : String) : Pos :=
|
||||
have := Nat.sub_lt_sub_left h (lt_next a i)
|
||||
loop (a.next i)
|
||||
else i
|
||||
termination_by stopPos.1 - i.1
|
||||
loop 0
|
||||
termination_by loop => stopPos.1 - i.1
|
||||
|
||||
@[extern "lean_string_utf8_extract"]
|
||||
def extract : (@& String) → (@& Pos) → (@& Pos) → String
|
||||
@@ -240,7 +240,7 @@ where
|
||||
splitAux s p i' i' (s.extract b i :: r)
|
||||
else
|
||||
splitAux s p b (s.next i) r
|
||||
termination_by _ => s.endPos.1 - i.1
|
||||
termination_by s.endPos.1 - i.1
|
||||
|
||||
@[specialize] def split (s : String) (p : Char → Bool) : List String :=
|
||||
splitAux s p 0 0 []
|
||||
@@ -260,7 +260,7 @@ def splitOnAux (s sep : String) (b : Pos) (i : Pos) (j : Pos) (r : List String)
|
||||
splitOnAux s sep b i j r
|
||||
else
|
||||
splitOnAux s sep b (s.next i) 0 r
|
||||
termination_by _ => s.endPos.1 - i.1
|
||||
termination_by s.endPos.1 - i.1
|
||||
|
||||
def splitOn (s : String) (sep : String := " ") : List String :=
|
||||
if sep == "" then [s] else splitOnAux s sep 0 0 0 []
|
||||
@@ -369,7 +369,7 @@ def offsetOfPosAux (s : String) (pos : Pos) (i : Pos) (offset : Nat) : Nat :=
|
||||
else
|
||||
have := Nat.sub_lt_sub_left (Nat.gt_of_not_le (mt decide_eq_true h)) (lt_next s _)
|
||||
offsetOfPosAux s pos (s.next i) (offset+1)
|
||||
termination_by _ => s.endPos.1 - i.1
|
||||
termination_by s.endPos.1 - i.1
|
||||
|
||||
def offsetOfPos (s : String) (pos : Pos) : Nat :=
|
||||
offsetOfPosAux s pos 0 0
|
||||
@@ -379,7 +379,7 @@ def offsetOfPos (s : String) (pos : Pos) : Nat :=
|
||||
have := Nat.sub_lt_sub_left h (lt_next s i)
|
||||
foldlAux f s stopPos (s.next i) (f a (s.get i))
|
||||
else a
|
||||
termination_by _ => stopPos.1 - i.1
|
||||
termination_by stopPos.1 - i.1
|
||||
|
||||
@[inline] def foldl {α : Type u} (f : α → Char → α) (init : α) (s : String) : α :=
|
||||
foldlAux f s s.endPos 0 init
|
||||
@@ -392,7 +392,7 @@ termination_by _ => stopPos.1 - i.1
|
||||
let a := f (s.get i) a
|
||||
foldrAux f a s i begPos
|
||||
else a
|
||||
termination_by _ => i.1
|
||||
termination_by i.1
|
||||
|
||||
@[inline] def foldr {α : Type u} (f : Char → α → α) (init : α) (s : String) : α :=
|
||||
foldrAux f init s s.endPos 0
|
||||
@@ -404,7 +404,7 @@ termination_by _ => i.1
|
||||
have := Nat.sub_lt_sub_left h (lt_next s i)
|
||||
anyAux s stopPos p (s.next i)
|
||||
else false
|
||||
termination_by _ => stopPos.1 - i.1
|
||||
termination_by stopPos.1 - i.1
|
||||
|
||||
@[inline] def any (s : String) (p : Char → Bool) : Bool :=
|
||||
anyAux s s.endPos p 0
|
||||
@@ -463,7 +463,7 @@ theorem mapAux_lemma (s : String) (i : Pos) (c : Char) (h : ¬s.atEnd i) :
|
||||
have := mapAux_lemma s i c h
|
||||
let s := s.set i c
|
||||
mapAux f (s.next i) s
|
||||
termination_by _ => s.endPos.1 - i.1
|
||||
termination_by s.endPos.1 - i.1
|
||||
|
||||
@[inline] def map (f : Char → Char) (s : String) : String :=
|
||||
mapAux f 0 s
|
||||
@@ -490,7 +490,7 @@ where
|
||||
have := Nat.sub_lt_sub_left h (Nat.add_lt_add_left (one_le_csize c₁) off1.1)
|
||||
c₁ == c₂ && loop (off1 + c₁) (off2 + c₂) stop1
|
||||
else true
|
||||
termination_by loop => stop1.1 - off1.1
|
||||
termination_by stop1.1 - off1.1
|
||||
|
||||
/-- Return true iff `p` is a prefix of `s` -/
|
||||
def isPrefixOf (p : String) (s : String) : Bool :=
|
||||
@@ -512,8 +512,8 @@ def replace (s pattern replacement : String) : String :=
|
||||
else
|
||||
have := Nat.sub_lt_sub_left this (lt_next s pos)
|
||||
loop acc accStop (s.next pos)
|
||||
termination_by s.endPos.1 - pos.1
|
||||
loop "" 0 0
|
||||
termination_by loop => s.endPos.1 - pos.1
|
||||
|
||||
end String
|
||||
|
||||
@@ -612,8 +612,8 @@ def splitOn (s : Substring) (sep : String := " ") : List Substring :=
|
||||
else
|
||||
s.extract b i :: r
|
||||
r.reverse
|
||||
termination_by s.bsize - i.1
|
||||
loop 0 0 0 []
|
||||
termination_by loop => s.bsize - i.1
|
||||
|
||||
@[inline] def foldl {α : Type u} (f : α → Char → α) (init : α) (s : Substring) : α :=
|
||||
match s with
|
||||
@@ -640,7 +640,7 @@ def contains (s : Substring) (c : Char) : Bool :=
|
||||
takeWhileAux s stopPos p (s.next i)
|
||||
else i
|
||||
else i
|
||||
termination_by _ => stopPos.1 - i.1
|
||||
termination_by stopPos.1 - i.1
|
||||
|
||||
@[inline] def takeWhile : Substring → (Char → Bool) → Substring
|
||||
| ⟨s, b, e⟩, p =>
|
||||
@@ -661,7 +661,7 @@ termination_by _ => stopPos.1 - i.1
|
||||
if !p c then i
|
||||
else takeRightWhileAux s begPos p i'
|
||||
else i
|
||||
termination_by _ => i.1
|
||||
termination_by i.1
|
||||
|
||||
@[inline] def takeRightWhile : Substring → (Char → Bool) → Substring
|
||||
| ⟨s, b, e⟩, p =>
|
||||
|
||||
@@ -5,7 +5,6 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Fin.Basic
|
||||
import Init.System.Platform
|
||||
|
||||
open Nat
|
||||
|
||||
@@ -39,7 +38,7 @@ def UInt8.shiftRight (a b : UInt8) : UInt8 := ⟨a.val >>> (modn b 8).val⟩
|
||||
def UInt8.lt (a b : UInt8) : Prop := a.val < b.val
|
||||
def UInt8.le (a b : UInt8) : Prop := a.val ≤ b.val
|
||||
|
||||
instance : OfNat UInt8 n := ⟨UInt8.ofNat n⟩
|
||||
instance UInt8.instOfNat : OfNat UInt8 n := ⟨UInt8.ofNat n⟩
|
||||
instance : Add UInt8 := ⟨UInt8.add⟩
|
||||
instance : Sub UInt8 := ⟨UInt8.sub⟩
|
||||
instance : Mul UInt8 := ⟨UInt8.mul⟩
|
||||
@@ -110,8 +109,7 @@ def UInt16.shiftRight (a b : UInt16) : UInt16 := ⟨a.val >>> (modn b 16).val⟩
|
||||
def UInt16.lt (a b : UInt16) : Prop := a.val < b.val
|
||||
def UInt16.le (a b : UInt16) : Prop := a.val ≤ b.val
|
||||
|
||||
|
||||
instance : OfNat UInt16 n := ⟨UInt16.ofNat n⟩
|
||||
instance UInt16.instOfNat : OfNat UInt16 n := ⟨UInt16.ofNat n⟩
|
||||
instance : Add UInt16 := ⟨UInt16.add⟩
|
||||
instance : Sub UInt16 := ⟨UInt16.sub⟩
|
||||
instance : Mul UInt16 := ⟨UInt16.mul⟩
|
||||
@@ -152,6 +150,14 @@ instance : Min UInt16 := minOfLe
|
||||
def UInt32.ofNat (n : @& Nat) : UInt32 := ⟨Fin.ofNat n⟩
|
||||
@[extern "lean_uint32_of_nat"]
|
||||
def UInt32.ofNat' (n : Nat) (h : n < UInt32.size) : UInt32 := ⟨⟨n, h⟩⟩
|
||||
/--
|
||||
Converts the given natural number to `UInt32`, but returns `2^32 - 1` for natural numbers `>= 2^32`.
|
||||
-/
|
||||
def UInt32.ofNatTruncate (n : Nat) : UInt32 :=
|
||||
if h : n < UInt32.size then
|
||||
UInt32.ofNat' n h
|
||||
else
|
||||
UInt32.ofNat' (UInt32.size - 1) (by decide)
|
||||
abbrev Nat.toUInt32 := UInt32.ofNat
|
||||
@[extern "lean_uint32_add"]
|
||||
def UInt32.add (a b : UInt32) : UInt32 := ⟨a.val + b.val⟩
|
||||
@@ -184,7 +190,7 @@ def UInt8.toUInt32 (a : UInt8) : UInt32 := a.toNat.toUInt32
|
||||
@[extern "lean_uint16_to_uint32"]
|
||||
def UInt16.toUInt32 (a : UInt16) : UInt32 := a.toNat.toUInt32
|
||||
|
||||
instance : OfNat UInt32 n := ⟨UInt32.ofNat n⟩
|
||||
instance UInt32.instOfNat : OfNat UInt32 n := ⟨UInt32.ofNat n⟩
|
||||
instance : Add UInt32 := ⟨UInt32.add⟩
|
||||
instance : Sub UInt32 := ⟨UInt32.sub⟩
|
||||
instance : Mul UInt32 := ⟨UInt32.mul⟩
|
||||
@@ -244,7 +250,7 @@ def UInt16.toUInt64 (a : UInt16) : UInt64 := a.toNat.toUInt64
|
||||
@[extern "lean_uint32_to_uint64"]
|
||||
def UInt32.toUInt64 (a : UInt32) : UInt64 := a.toNat.toUInt64
|
||||
|
||||
instance : OfNat UInt64 n := ⟨UInt64.ofNat n⟩
|
||||
instance UInt64.instOfNat : OfNat UInt64 n := ⟨UInt64.ofNat n⟩
|
||||
instance : Add UInt64 := ⟨UInt64.add⟩
|
||||
instance : Sub UInt64 := ⟨UInt64.sub⟩
|
||||
instance : Mul UInt64 := ⟨UInt64.mul⟩
|
||||
@@ -322,7 +328,7 @@ def USize.toUInt32 (a : USize) : UInt32 := a.toNat.toUInt32
|
||||
def USize.lt (a b : USize) : Prop := a.val < b.val
|
||||
def USize.le (a b : USize) : Prop := a.val ≤ b.val
|
||||
|
||||
instance : OfNat USize n := ⟨USize.ofNat n⟩
|
||||
instance USize.instOfNat : OfNat USize n := ⟨USize.ofNat n⟩
|
||||
instance : Add USize := ⟨USize.add⟩
|
||||
instance : Sub USize := ⟨USize.sub⟩
|
||||
instance : Mul USize := ⟨USize.mul⟩
|
||||
|
||||
@@ -800,9 +800,40 @@ partial def decodeStrLitAux (s : String) (i : String.Pos) (acc : String) : Optio
|
||||
else
|
||||
decodeStrLitAux s i (acc.push c)
|
||||
|
||||
def decodeStrLit (s : String) : Option String :=
|
||||
decodeStrLitAux s ⟨1⟩ ""
|
||||
/--
|
||||
Takes a raw string literal, counts the number of `#`'s after the `r`, and interprets it as a string.
|
||||
The position `i` should start at `1`, which is the character after the leading `r`.
|
||||
The algorithm is simple: we are given `r##...#"...string..."##...#` with zero or more `#`s.
|
||||
By counting the number of leading `#`'s, we can extract the `...string...`.
|
||||
-/
|
||||
partial def decodeRawStrLitAux (s : String) (i : String.Pos) (num : Nat) : String :=
|
||||
let c := s.get i
|
||||
let i := s.next i
|
||||
if c == '#' then
|
||||
decodeRawStrLitAux s i (num + 1)
|
||||
else
|
||||
s.extract i ⟨s.utf8ByteSize - (num + 1)⟩
|
||||
|
||||
/--
|
||||
Takes the string literal lexical syntax parsed by the parser and interprets it as a string.
|
||||
This is where escape sequences are processed for example.
|
||||
The string `s` is is either a plain string literal or a raw string literal.
|
||||
|
||||
If it returns `none` then the string literal is ill-formed, which indicates a bug in the parser.
|
||||
The function is not required to return `none` if the string literal is ill-formed.
|
||||
-/
|
||||
def decodeStrLit (s : String) : Option String :=
|
||||
if s.get 0 == 'r' then
|
||||
decodeRawStrLitAux s ⟨1⟩ 0
|
||||
else
|
||||
decodeStrLitAux s ⟨1⟩ ""
|
||||
|
||||
/--
|
||||
If the provided `Syntax` is a string literal, returns the string it represents.
|
||||
|
||||
Even if the `Syntax` is a `str` node, the function may return `none` if its internally ill-formed.
|
||||
The parser should always create well-formed `str` nodes.
|
||||
-/
|
||||
def isStrLit? (stx : Syntax) : Option String :=
|
||||
match isLit? strLitKind stx with
|
||||
| some val => decodeStrLit val
|
||||
@@ -1008,7 +1039,7 @@ where
|
||||
go (i+1) (args.push (quote xs[i]))
|
||||
else
|
||||
Syntax.mkCApp (Name.mkStr2 "Array" ("mkArray" ++ toString xs.size)) args
|
||||
termination_by go i _ => xs.size - i
|
||||
termination_by xs.size - i
|
||||
|
||||
instance [Quote α `term] : Quote (Array α) `term where
|
||||
quote := quoteArray
|
||||
|
||||
@@ -170,6 +170,20 @@ See [Theorem Proving in Lean 4][tpil4] for more information.
|
||||
-/
|
||||
syntax (name := calcTactic) "calc" calcSteps : tactic
|
||||
|
||||
/--
|
||||
Denotes a term that was omitted by the pretty printer.
|
||||
This is only used for pretty printing, and it cannot be elaborated.
|
||||
The presence of `⋯` is controlled by the `pp.deepTerms` and `pp.deepTerms.threshold`
|
||||
options.
|
||||
-/
|
||||
syntax "⋯" : term
|
||||
|
||||
macro_rules | `(⋯) => Macro.throwError "\
|
||||
Error: The '⋯' token is used by the pretty printer to indicate omitted terms, \
|
||||
and it cannot be elaborated. \
|
||||
Its presence in pretty printing output is controlled by the 'pp.deepTerms' and \
|
||||
`pp.deepTerms.threshold` options."
|
||||
|
||||
@[app_unexpander Unit.unit] def unexpandUnit : Lean.PrettyPrinter.Unexpander
|
||||
| `($(_)) => `(())
|
||||
|
||||
@@ -177,9 +191,13 @@ syntax (name := calcTactic) "calc" calcSteps : tactic
|
||||
| `($(_)) => `([])
|
||||
|
||||
@[app_unexpander List.cons] def unexpandListCons : Lean.PrettyPrinter.Unexpander
|
||||
| `($(_) $x []) => `([$x])
|
||||
| `($(_) $x [$xs,*]) => `([$x, $xs,*])
|
||||
| _ => throw ()
|
||||
| `($(_) $x $tail) =>
|
||||
match tail with
|
||||
| `([]) => `([$x])
|
||||
| `([$xs,*]) => `([$x, $xs,*])
|
||||
| `(⋯) => `([$x, $tail]) -- Unexpands to `[x, y, z, ⋯]` for `⋯ : List α`
|
||||
| _ => throw ()
|
||||
| _ => throw ()
|
||||
|
||||
@[app_unexpander List.toArray] def unexpandListToArray : Lean.PrettyPrinter.Unexpander
|
||||
| `($(_) [$xs,*]) => `(#[$xs,*])
|
||||
|
||||
@@ -9,9 +9,9 @@ set_option linter.missingDocs true -- keep it documented
|
||||
/-!
|
||||
# Init.Prelude
|
||||
|
||||
This is the first file in the lean import hierarchy. It is responsible for setting
|
||||
up basic definitions, most of which lean already has "built in knowledge" about,
|
||||
so it is important that they be set up in exactly this way. (For example, lean will
|
||||
This is the first file in the Lean import hierarchy. It is responsible for setting
|
||||
up basic definitions, most of which Lean already has "built in knowledge" about,
|
||||
so it is important that they be set up in exactly this way. (For example, Lean will
|
||||
use `PUnit` in the desugaring of `do` notation, or in the pattern match compiler.)
|
||||
|
||||
-/
|
||||
@@ -24,7 +24,7 @@ The identity function. `id` takes an implicit argument `α : Sort u`
|
||||
|
||||
Although this may look like a useless function, one application of the identity
|
||||
function is to explicitly put a type on an expression. If `e` has type `T`,
|
||||
and `T'` is definitionally equal to `T`, then `@id T' e` typechecks, and lean
|
||||
and `T'` is definitionally equal to `T`, then `@id T' e` typechecks, and Lean
|
||||
knows that this expression has type `T'` rather than `T`. This can make a
|
||||
difference for typeclass inference, since `T` and `T'` may have different
|
||||
typeclass instances on them. `show T' from e` is sugar for an `@id T' e`
|
||||
@@ -66,6 +66,19 @@ example (b : Bool) : Function.const Bool 10 b = 10 :=
|
||||
@[inline] def Function.const {α : Sort u} (β : Sort v) (a : α) : β → α :=
|
||||
fun _ => a
|
||||
|
||||
/--
|
||||
The encoding of `let_fun x := v; b` is `letFun v (fun x => b)`.
|
||||
This is equal to `(fun x => b) v`, so the value of `x` is not accessible to `b`.
|
||||
This is in contrast to `let x := v; b`, where the value of `x` is accessible to `b`.
|
||||
|
||||
There is special support for `letFun`.
|
||||
Both WHNF and `simp` are aware of `letFun` and can reduce it when zeta reduction is enabled,
|
||||
despite the fact it is marked `irreducible`.
|
||||
For metaprogramming, the function `Lean.Expr.letFun?` can be used to recognize a `let_fun` expression
|
||||
to extract its parts as if it were a `let` expression.
|
||||
-/
|
||||
@[irreducible] def letFun {α : Sort u} {β : α → Sort v} (v : α) (f : (x : α) → β x) : β v := f v
|
||||
|
||||
set_option checkBinderAnnotations false in
|
||||
/--
|
||||
`inferInstance` synthesizes a value of any target type by typeclass
|
||||
@@ -274,9 +287,9 @@ inductive Eq : α → α → Prop where
|
||||
same as `Eq.refl` except that it takes `a` implicitly instead of explicitly.
|
||||
|
||||
This is a more powerful theorem than it may appear at first, because although
|
||||
the statement of the theorem is `a = a`, lean will allow anything that is
|
||||
the statement of the theorem is `a = a`, Lean will allow anything that is
|
||||
definitionally equal to that type. So, for instance, `2 + 2 = 4` is proven in
|
||||
lean by `rfl`, because both sides are the same up to definitional equality.
|
||||
Lean by `rfl`, because both sides are the same up to definitional equality.
|
||||
-/
|
||||
@[match_pattern] def rfl {α : Sort u} {a : α} : Eq a a := Eq.refl a
|
||||
|
||||
@@ -584,7 +597,7 @@ For example, the `Membership` class is defined as:
|
||||
class Membership (α : outParam (Type u)) (γ : Type v)
|
||||
```
|
||||
This means that whenever a typeclass goal of the form `Membership ?α ?γ` comes
|
||||
up, lean will wait to solve it until `?γ` is known, but then it will run
|
||||
up, Lean will wait to solve it until `?γ` is known, but then it will run
|
||||
typeclass inference, and take the first solution it finds, for any value of `?α`,
|
||||
which thereby determines what `?α` should be.
|
||||
|
||||
@@ -699,13 +712,13 @@ nonempty, then `fun i => Classical.choice (h i) : ∀ i, α i` is a family of
|
||||
chosen elements. This is actually a bit stronger than the ZFC choice axiom;
|
||||
this is sometimes called "[global choice](https://en.wikipedia.org/wiki/Axiom_of_global_choice)".
|
||||
|
||||
In lean, we use the axiom of choice to derive the law of excluded middle
|
||||
In Lean, we use the axiom of choice to derive the law of excluded middle
|
||||
(see `Classical.em`), so it will often show up in axiom listings where you
|
||||
may not expect. You can use `#print axioms my_thm` to find out if a given
|
||||
theorem depends on this or other axioms.
|
||||
|
||||
This axiom can be used to construct "data", but obviously there is no algorithm
|
||||
to compute it, so lean will require you to mark any definition that would
|
||||
to compute it, so Lean will require you to mark any definition that would
|
||||
involve executing `Classical.choice` or other axioms as `noncomputable`, and
|
||||
will not produce any executable code for such definitions.
|
||||
-/
|
||||
@@ -930,7 +943,7 @@ determines how to evaluate `c` to true or false. Write `if h : c then t else e`
|
||||
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
|
||||
that `c` is true/false.
|
||||
|
||||
Because lean uses a strict (call-by-value) evaluation strategy, the signature of this
|
||||
Because Lean uses a strict (call-by-value) evaluation strategy, the signature of this
|
||||
function is problematic in that it would require `t` and `e` to be evaluated before
|
||||
calling the `ite` function, which would cause both sides of the `if` to be evaluated.
|
||||
Even if the result is discarded, this would be a big performance problem,
|
||||
@@ -1020,7 +1033,7 @@ You can prove a theorem `P n` about `n : Nat` by `induction n`, which will
|
||||
expect a proof of the theorem for `P 0`, and a proof of `P (succ i)` assuming
|
||||
a proof of `P i`. The same method also works to define functions by recursion
|
||||
on natural numbers: induction and recursion are two expressions of the same
|
||||
operation from lean's point of view.
|
||||
operation from Lean's point of view.
|
||||
|
||||
```
|
||||
open Nat
|
||||
@@ -1056,14 +1069,14 @@ instance : Inhabited Nat where
|
||||
|
||||
/--
|
||||
The class `OfNat α n` powers the numeric literal parser. If you write
|
||||
`37 : α`, lean will attempt to synthesize `OfNat α 37`, and will generate
|
||||
`37 : α`, Lean will attempt to synthesize `OfNat α 37`, and will generate
|
||||
the term `(OfNat.ofNat 37 : α)`.
|
||||
|
||||
There is a bit of infinite regress here since the desugaring apparently
|
||||
still contains a literal `37` in it. The type of expressions contains a
|
||||
primitive constructor for "raw natural number literals", which you can directly
|
||||
access using the macro `nat_lit 37`. Raw number literals are always of type `Nat`.
|
||||
So it would be more correct to say that lean looks for an instance of
|
||||
So it would be more correct to say that Lean looks for an instance of
|
||||
`OfNat α (nat_lit 37)`, and it generates the term `(OfNat.ofNat (nat_lit 37) : α)`.
|
||||
-/
|
||||
class OfNat (α : Type u) (_ : Nat) where
|
||||
@@ -1767,7 +1780,7 @@ Gets the word size of the platform. That is, whether the platform is 64 or 32 bi
|
||||
|
||||
This function is opaque because we cannot guarantee at compile time that the target
|
||||
will have the same size as the host, and also because we would like to avoid
|
||||
typechecking being architecture-dependent. Nevertheless, lean only works on
|
||||
typechecking being architecture-dependent. Nevertheless, Lean only works on
|
||||
64 and 32 bit systems so we can encode this as a fact available for proof purposes.
|
||||
-/
|
||||
@[extern "lean_system_platform_nbits"] opaque System.Platform.getNumBits : Unit → Subtype fun (n : Nat) => Or (Eq n 32) (Eq n 64) :=
|
||||
@@ -2213,9 +2226,10 @@ returns `a` if `opt = some a` and `dflt` otherwise.
|
||||
This function is `@[macro_inline]`, so `dflt` will not be evaluated unless
|
||||
`opt` turns out to be `none`.
|
||||
-/
|
||||
@[macro_inline] def Option.getD : Option α → α → α
|
||||
| some x, _ => x
|
||||
| none, e => e
|
||||
@[macro_inline] def Option.getD (opt : Option α) (dflt : α) : α :=
|
||||
match opt with
|
||||
| some x => x
|
||||
| none => dflt
|
||||
|
||||
/--
|
||||
Map a function over an `Option` by applying the function to the contained
|
||||
@@ -2504,7 +2518,7 @@ attribute [nospecialize] Inhabited
|
||||
|
||||
/--
|
||||
The class `GetElem cont idx elem dom` implements the `xs[i]` notation.
|
||||
When you write this, given `xs : cont` and `i : idx`, lean looks for an instance
|
||||
When you write this, given `xs : cont` and `i : idx`, Lean looks for an instance
|
||||
of `GetElem cont idx elem dom`. Here `elem` is the type of `xs[i]`, while
|
||||
`dom` is whatever proof side conditions are required to make this applicable.
|
||||
For example, the instance for arrays looks like
|
||||
@@ -2544,7 +2558,7 @@ export GetElem (getElem)
|
||||
with elements from `α`. This type has special support in the runtime.
|
||||
|
||||
An array has a size and a capacity; the size is `Array.size` but the capacity
|
||||
is not observable from lean code. Arrays perform best when unshared; as long
|
||||
is not observable from Lean code. Arrays perform best when unshared; as long
|
||||
as they are used "linearly" all updates will be performed destructively on the
|
||||
array, so it has comparable performance to mutable arrays in imperative
|
||||
programming languages.
|
||||
@@ -3217,7 +3231,7 @@ instance (σ : Type u) (m : Type u → Type v) [MonadStateOf σ m] : MonadState
|
||||
/--
|
||||
`modify (f : σ → σ)` applies the function `f` to the state.
|
||||
|
||||
It is equivalent to `do put (f (← get))`, but `modify f` may be preferable
|
||||
It is equivalent to `do set (f (← get))`, but `modify f` may be preferable
|
||||
because the former does not use the state linearly (without sufficient inlining).
|
||||
-/
|
||||
@[always_inline, inline]
|
||||
|
||||
@@ -10,6 +10,7 @@ import Init.Core
|
||||
set_option linter.missingDocs true -- keep it documented
|
||||
|
||||
theorem of_eq_true (h : p = True) : p := h ▸ trivial
|
||||
theorem of_eq_false (h : p = False) : ¬ p := fun hp => False.elim (h.mp hp)
|
||||
|
||||
theorem eq_true (h : p) : p = True :=
|
||||
propext ⟨fun _ => trivial, fun _ => h⟩
|
||||
@@ -84,6 +85,13 @@ theorem dite_congr {_ : Decidable b} [Decidable c]
|
||||
@[simp] theorem ite_false (a b : α) : (if False then a else b) = b := rfl
|
||||
@[simp] theorem dite_true {α : Sort u} {t : True → α} {e : ¬ True → α} : (dite True t e) = t True.intro := rfl
|
||||
@[simp] theorem dite_false {α : Sort u} {t : False → α} {e : ¬ False → α} : (dite False t e) = e not_false := rfl
|
||||
section SimprocHelperLemmas
|
||||
set_option simprocs false
|
||||
theorem ite_cond_eq_true {α : Sort u} {c : Prop} {_ : Decidable c} (a b : α) (h : c = True) : (if c then a else b) = a := by simp [h]
|
||||
theorem ite_cond_eq_false {α : Sort u} {c : Prop} {_ : Decidable c} (a b : α) (h : c = False) : (if c then a else b) = b := by simp [h]
|
||||
theorem dite_cond_eq_true {α : Sort u} {c : Prop} {_ : Decidable c} {t : c → α} {e : ¬ c → α} (h : c = True) : (dite c t e) = t (of_eq_true h) := by simp [h]
|
||||
theorem dite_cond_eq_false {α : Sort u} {c : Prop} {_ : Decidable c} {t : c → α} {e : ¬ c → α} (h : c = False) : (dite c t e) = e (of_eq_false h) := by simp [h]
|
||||
end SimprocHelperLemmas
|
||||
@[simp] theorem ite_self {α : Sort u} {c : Prop} {d : Decidable c} (a : α) : ite c a a = a := by cases d <;> rfl
|
||||
@[simp] theorem and_self (p : Prop) : (p ∧ p) = p := propext ⟨(·.1), fun h => ⟨h, h⟩⟩
|
||||
@[simp] theorem and_true (p : Prop) : (p ∧ True) = p := propext ⟨(·.1), (⟨·, trivial⟩)⟩
|
||||
|
||||
129
src/Init/Simproc.lean
Normal file
129
src/Init/Simproc.lean
Normal file
@@ -0,0 +1,129 @@
|
||||
/-
|
||||
Copyright (c) 2023 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
|
||||
-/
|
||||
prelude
|
||||
import Init.NotationExtra
|
||||
|
||||
namespace Lean.Parser
|
||||
/--
|
||||
A user-defined simplification procedure used by the `simp` tactic, and its variants.
|
||||
Here is an example.
|
||||
```lean
|
||||
simproc reduce_add (_ + _) := fun e => do
|
||||
unless (e.isAppOfArity ``HAdd.hAdd 6) do return none
|
||||
let some n ← getNatValue? (e.getArg! 4) | return none
|
||||
let some m ← getNatValue? (e.getArg! 5) | return none
|
||||
return some (.done { expr := mkNatLit (n+m) })
|
||||
```
|
||||
The `simp` tactic invokes `reduce_add` whenever it finds a term of the form `_ + _`.
|
||||
The simplification procedures are stored in an (imperfect) discrimination tree.
|
||||
The procedure should **not** assume the term `e` perfectly matches the given pattern.
|
||||
The body of a simplification procedure must have type `Simproc`, which is an alias for
|
||||
`Expr → SimpM (Option Step)`.
|
||||
You can instruct the simplifier to apply the procedure before its sub-expressions
|
||||
have been simplified by using the modifier `↓` before the procedure name. Example.
|
||||
```lean
|
||||
simproc ↓ reduce_add (_ + _) := fun e => ...
|
||||
```
|
||||
Simplification procedures can be also scoped or local.
|
||||
-/
|
||||
syntax (docComment)? attrKind "simproc " (Tactic.simpPre <|> Tactic.simpPost)? ("[" ident,* "]")? ident " (" term ")" " := " term : command
|
||||
|
||||
/--
|
||||
A user-defined simplification procedure declaration. To activate this procedure in `simp` tactic,
|
||||
we must provide it as an argument, or use the command `attribute` to set its `[simproc]` attribute.
|
||||
-/
|
||||
syntax (docComment)? "simproc_decl " ident " (" term ")" " := " term : command
|
||||
|
||||
/--
|
||||
A builtin simplification procedure.
|
||||
-/
|
||||
syntax (docComment)? attrKind "builtin_simproc " (Tactic.simpPre <|> Tactic.simpPost)? ("[" ident,* "]")? ident " (" term ")" " := " term : command
|
||||
|
||||
/--
|
||||
A builtin simplification procedure declaration.
|
||||
-/
|
||||
syntax (docComment)? "builtin_simproc_decl " ident " (" term ")" " := " term : command
|
||||
|
||||
/--
|
||||
Auxiliary command for associating a pattern with a simplification procedure.
|
||||
-/
|
||||
syntax (name := simprocPattern) "simproc_pattern% " term " => " ident : command
|
||||
|
||||
/--
|
||||
Auxiliary command for associating a pattern with a builtin simplification procedure.
|
||||
-/
|
||||
syntax (name := simprocPatternBuiltin) "builtin_simproc_pattern% " term " => " ident : command
|
||||
|
||||
namespace Attr
|
||||
/--
|
||||
Auxiliary attribute for simplification procedures.
|
||||
-/
|
||||
syntax (name := simprocAttr) "simproc" (Tactic.simpPre <|> Tactic.simpPost)? : attr
|
||||
|
||||
/--
|
||||
Auxiliary attribute for symbolic evaluation procedures.
|
||||
-/
|
||||
syntax (name := sevalprocAttr) "sevalproc" (Tactic.simpPre <|> Tactic.simpPost)? : attr
|
||||
|
||||
/--
|
||||
Auxiliary attribute for builtin simplification procedures.
|
||||
-/
|
||||
syntax (name := simprocBuiltinAttr) "builtin_simproc" (Tactic.simpPre <|> Tactic.simpPost)? : attr
|
||||
|
||||
/--
|
||||
Auxiliary attribute for builtin symbolic evaluation procedures.
|
||||
-/
|
||||
syntax (name := sevalprocBuiltinAttr) "builtin_sevalproc" (Tactic.simpPre <|> Tactic.simpPost)? : attr
|
||||
|
||||
end Attr
|
||||
|
||||
macro_rules
|
||||
| `($[$doc?:docComment]? simproc_decl $n:ident ($pattern:term) := $body) => do
|
||||
let simprocType := `Lean.Meta.Simp.Simproc
|
||||
`($[$doc?:docComment]? def $n:ident : $(mkIdent simprocType) := $body
|
||||
simproc_pattern% $pattern => $n)
|
||||
|
||||
macro_rules
|
||||
| `($[$doc?:docComment]? builtin_simproc_decl $n:ident ($pattern:term) := $body) => do
|
||||
let simprocType := `Lean.Meta.Simp.Simproc
|
||||
`($[$doc?:docComment]? def $n:ident : $(mkIdent simprocType) := $body
|
||||
builtin_simproc_pattern% $pattern => $n)
|
||||
|
||||
macro_rules
|
||||
| `($[$doc?:docComment]? $kind:attrKind simproc $[$pre?]? $[ [ $ids?:ident,* ] ]? $n:ident ($pattern:term) := $body) => do
|
||||
let mut cmds := #[(← `($[$doc?:docComment]? simproc_decl $n ($pattern) := $body))]
|
||||
let pushDefault (cmds : Array (TSyntax `command)) : MacroM (Array (TSyntax `command)) := do
|
||||
return cmds.push (← `(attribute [$kind simproc $[$pre?]?] $n))
|
||||
if let some ids := ids? then
|
||||
for id in ids.getElems do
|
||||
let idName := id.getId
|
||||
let (attrName, attrKey) :=
|
||||
if idName == `simp then
|
||||
(`simprocAttr, "simproc")
|
||||
else if idName == `seval then
|
||||
(`sevalprocAttr, "sevalproc")
|
||||
else
|
||||
let idName := idName.appendAfter "_proc"
|
||||
(`Parser.Attr ++ idName, idName.toString)
|
||||
let attrStx : TSyntax `attr := ⟨mkNode attrName #[mkAtom attrKey, mkOptionalNode pre?]⟩
|
||||
cmds := cmds.push (← `(attribute [$kind $attrStx] $n))
|
||||
else
|
||||
cmds ← pushDefault cmds
|
||||
return mkNullNode cmds
|
||||
|
||||
macro_rules
|
||||
| `($[$doc?:docComment]? $kind:attrKind builtin_simproc $[$pre?]? $n:ident ($pattern:term) := $body) => do
|
||||
`($[$doc?:docComment]? builtin_simproc_decl $n ($pattern) := $body
|
||||
attribute [$kind builtin_simproc $[$pre?]?] $n)
|
||||
| `($[$doc?:docComment]? $kind:attrKind builtin_simproc $[$pre?]? [seval] $n:ident ($pattern:term) := $body) => do
|
||||
`($[$doc?:docComment]? builtin_simproc_decl $n ($pattern) := $body
|
||||
attribute [$kind builtin_sevalproc $[$pre?]?] $n)
|
||||
| `($[$doc?:docComment]? $kind:attrKind builtin_simproc $[$pre?]? [simp, seval] $n:ident ($pattern:term) := $body) => do
|
||||
`($[$doc?:docComment]? builtin_simproc_decl $n ($pattern) := $body
|
||||
attribute [$kind builtin_simproc $[$pre?]?] $n
|
||||
attribute [$kind builtin_sevalproc $[$pre?]?] $n)
|
||||
|
||||
end Lean.Parser
|
||||
@@ -101,6 +101,21 @@ def withFileName (p : FilePath) (fname : String) : FilePath :=
|
||||
| none => ⟨fname⟩
|
||||
| some p => p / fname
|
||||
|
||||
/-- Appends the extension `ext` to a path `p`.
|
||||
|
||||
`ext` should not contain a leading `.`, as this function adds one.
|
||||
If `ext` is the empty string, no `.` is added.
|
||||
|
||||
Unlike `System.FilePath.withExtension`, this does not remove any existing extension. -/
|
||||
def addExtension (p : FilePath) (ext : String) : FilePath :=
|
||||
match p.fileName with
|
||||
| none => p
|
||||
| some fname => p.withFileName (if ext.isEmpty then fname else fname ++ "." ++ ext)
|
||||
|
||||
/-- Replace the current extension in a path `p` with `ext`.
|
||||
|
||||
`ext` should not contain a `.`, as this function adds one.
|
||||
If `ext` is the empty string, no `.` is added. -/
|
||||
def withExtension (p : FilePath) (ext : String) : FilePath :=
|
||||
match p.fileStem with
|
||||
| none => p
|
||||
|
||||
@@ -117,20 +117,23 @@ opaque asTask (act : BaseIO α) (prio := Task.Priority.default) : BaseIO (Task
|
||||
|
||||
/-- See `BaseIO.asTask`. -/
|
||||
@[extern "lean_io_map_task"]
|
||||
opaque mapTask (f : α → BaseIO β) (t : Task α) (prio := Task.Priority.default) : BaseIO (Task β) :=
|
||||
opaque mapTask (f : α → BaseIO β) (t : Task α) (prio := Task.Priority.default) (sync := false) :
|
||||
BaseIO (Task β) :=
|
||||
Task.pure <$> f t.get
|
||||
|
||||
/-- See `BaseIO.asTask`. -/
|
||||
@[extern "lean_io_bind_task"]
|
||||
opaque bindTask (t : Task α) (f : α → BaseIO (Task β)) (prio := Task.Priority.default) : BaseIO (Task β) :=
|
||||
opaque bindTask (t : Task α) (f : α → BaseIO (Task β)) (prio := Task.Priority.default)
|
||||
(sync := false) : BaseIO (Task β) :=
|
||||
f t.get
|
||||
|
||||
def mapTasks (f : List α → BaseIO β) (tasks : List (Task α)) (prio := Task.Priority.default) : BaseIO (Task β) :=
|
||||
def mapTasks (f : List α → BaseIO β) (tasks : List (Task α)) (prio := Task.Priority.default)
|
||||
(sync := false) : BaseIO (Task β) :=
|
||||
go tasks []
|
||||
where
|
||||
go
|
||||
| t::ts, as =>
|
||||
BaseIO.bindTask t (fun a => go ts (a :: as)) prio
|
||||
BaseIO.bindTask t (fun a => go ts (a :: as)) prio sync
|
||||
| [], as => f as.reverse |>.asTask prio
|
||||
|
||||
end BaseIO
|
||||
@@ -142,16 +145,20 @@ namespace EIO
|
||||
act.toBaseIO.asTask prio
|
||||
|
||||
/-- `EIO` specialization of `BaseIO.mapTask`. -/
|
||||
@[inline] def mapTask (f : α → EIO ε β) (t : Task α) (prio := Task.Priority.default) : BaseIO (Task (Except ε β)) :=
|
||||
BaseIO.mapTask (fun a => f a |>.toBaseIO) t prio
|
||||
@[inline] def mapTask (f : α → EIO ε β) (t : Task α) (prio := Task.Priority.default)
|
||||
(sync := false) : BaseIO (Task (Except ε β)) :=
|
||||
BaseIO.mapTask (fun a => f a |>.toBaseIO) t prio sync
|
||||
|
||||
/-- `EIO` specialization of `BaseIO.bindTask`. -/
|
||||
@[inline] def bindTask (t : Task α) (f : α → EIO ε (Task (Except ε β))) (prio := Task.Priority.default) : BaseIO (Task (Except ε β)) :=
|
||||
BaseIO.bindTask t (fun a => f a |>.catchExceptions fun e => return Task.pure <| Except.error e) prio
|
||||
@[inline] def bindTask (t : Task α) (f : α → EIO ε (Task (Except ε β)))
|
||||
(prio := Task.Priority.default) (sync := false) : BaseIO (Task (Except ε β)) :=
|
||||
BaseIO.bindTask t (fun a => f a |>.catchExceptions fun e => return Task.pure <| Except.error e)
|
||||
prio sync
|
||||
|
||||
/-- `EIO` specialization of `BaseIO.mapTasks`. -/
|
||||
@[inline] def mapTasks (f : List α → EIO ε β) (tasks : List (Task α)) (prio := Task.Priority.default) : BaseIO (Task (Except ε β)) :=
|
||||
BaseIO.mapTasks (fun as => f as |>.toBaseIO) tasks prio
|
||||
@[inline] def mapTasks (f : List α → EIO ε β) (tasks : List (Task α))
|
||||
(prio := Task.Priority.default) (sync := false) : BaseIO (Task (Except ε β)) :=
|
||||
BaseIO.mapTasks (fun as => f as |>.toBaseIO) tasks prio sync
|
||||
|
||||
end EIO
|
||||
|
||||
@@ -184,16 +191,19 @@ def sleep (ms : UInt32) : BaseIO Unit :=
|
||||
EIO.asTask act prio
|
||||
|
||||
/-- `IO` specialization of `EIO.mapTask`. -/
|
||||
@[inline] def mapTask (f : α → IO β) (t : Task α) (prio := Task.Priority.default) : BaseIO (Task (Except IO.Error β)) :=
|
||||
EIO.mapTask f t prio
|
||||
@[inline] def mapTask (f : α → IO β) (t : Task α) (prio := Task.Priority.default) (sync := false) :
|
||||
BaseIO (Task (Except IO.Error β)) :=
|
||||
EIO.mapTask f t prio sync
|
||||
|
||||
/-- `IO` specialization of `EIO.bindTask`. -/
|
||||
@[inline] def bindTask (t : Task α) (f : α → IO (Task (Except IO.Error β))) (prio := Task.Priority.default) : BaseIO (Task (Except IO.Error β)) :=
|
||||
EIO.bindTask t f prio
|
||||
@[inline] def bindTask (t : Task α) (f : α → IO (Task (Except IO.Error β)))
|
||||
(prio := Task.Priority.default) (sync := false) : BaseIO (Task (Except IO.Error β)) :=
|
||||
EIO.bindTask t f prio sync
|
||||
|
||||
/-- `IO` specialization of `EIO.mapTasks`. -/
|
||||
@[inline] def mapTasks (f : List α → IO β) (tasks : List (Task α)) (prio := Task.Priority.default) : BaseIO (Task (Except IO.Error β)) :=
|
||||
EIO.mapTasks f tasks prio
|
||||
@[inline] def mapTasks (f : List α → IO β) (tasks : List (Task α)) (prio := Task.Priority.default)
|
||||
(sync := false) : BaseIO (Task (Except IO.Error β)) :=
|
||||
EIO.mapTasks f tasks prio sync
|
||||
|
||||
/-- Check if the task's cancellation flag has been set by calling `IO.cancel` or dropping the last reference to the task. -/
|
||||
@[extern "lean_io_check_canceled"] opaque checkCanceled : BaseIO Bool
|
||||
|
||||
@@ -5,6 +5,7 @@ Authors: Leonardo de Moura
|
||||
-/
|
||||
prelude
|
||||
import Init.Data.Nat.Basic
|
||||
import Init.Data.String.Basic
|
||||
|
||||
namespace System
|
||||
namespace Platform
|
||||
@@ -17,5 +18,10 @@ def isWindows : Bool := getIsWindows ()
|
||||
def isOSX : Bool := getIsOSX ()
|
||||
def isEmscripten : Bool := getIsEmscripten ()
|
||||
|
||||
@[extern "lean_system_platform_target"] opaque getTarget : Unit → String
|
||||
|
||||
/-- The LLVM target triple of the current platform. Empty if missing at Lean compile time. -/
|
||||
def target : String := getTarget ()
|
||||
|
||||
end Platform
|
||||
end System
|
||||
|
||||
@@ -39,8 +39,75 @@ be a `let` or function type.
|
||||
syntax (name := intro) "intro" notFollowedBy("|") (ppSpace colGt term:max)* : tactic
|
||||
|
||||
/--
|
||||
`intros x...` behaves like `intro x...`, but then keeps introducing (anonymous)
|
||||
hypotheses until goal is not of a function type.
|
||||
Introduces zero or more hypotheses, optionally naming them.
|
||||
|
||||
- `intros` is equivalent to repeatedly applying `intro`
|
||||
until the goal is not an obvious candidate for `intro`, which is to say
|
||||
that so long as the goal is a `let` or a pi type (e.g. an implication, function, or universal quantifier),
|
||||
the `intros` tactic will introduce an anonymous hypothesis.
|
||||
This tactic does not unfold definitions.
|
||||
|
||||
- `intros x y ...` is equivalent to `intro x y ...`,
|
||||
introducing hypotheses for each supplied argument and unfolding definitions as necessary.
|
||||
Each argument can be either an identifier or a `_`.
|
||||
An identifier indicates a name to use for the corresponding introduced hypothesis,
|
||||
and a `_` indicates that the hypotheses should be introduced anonymously.
|
||||
|
||||
## Examples
|
||||
|
||||
Basic properties:
|
||||
```lean
|
||||
def AllEven (f : Nat → Nat) := ∀ n, f n % 2 = 0
|
||||
|
||||
-- Introduces the two obvious hypotheses automatically
|
||||
example : ∀ (f : Nat → Nat), AllEven f → AllEven (fun k => f (k + 1)) := by
|
||||
intros
|
||||
/- Tactic state
|
||||
f✝ : Nat → Nat
|
||||
a✝ : AllEven f✝
|
||||
⊢ AllEven fun k => f✝ (k + 1) -/
|
||||
sorry
|
||||
|
||||
-- Introduces exactly two hypotheses, naming only the first
|
||||
example : ∀ (f : Nat → Nat), AllEven f → AllEven (fun k => f (k + 1)) := by
|
||||
intros g _
|
||||
/- Tactic state
|
||||
g : Nat → Nat
|
||||
a✝ : AllEven g
|
||||
⊢ AllEven fun k => g (k + 1) -/
|
||||
sorry
|
||||
|
||||
-- Introduces exactly three hypotheses, which requires unfolding `AllEven`
|
||||
example : ∀ (f : Nat → Nat), AllEven f → AllEven (fun k => f (k + 1)) := by
|
||||
intros f h n
|
||||
/- Tactic state
|
||||
f : Nat → Nat
|
||||
h : AllEven f
|
||||
n : Nat
|
||||
⊢ (fun k => f (k + 1)) n % 2 = 0 -/
|
||||
apply h
|
||||
```
|
||||
|
||||
Implications:
|
||||
```lean
|
||||
example (p q : Prop) : p → q → p := by
|
||||
intros
|
||||
/- Tactic state
|
||||
a✝¹ : p
|
||||
a✝ : q
|
||||
⊢ p -/
|
||||
assumption
|
||||
```
|
||||
|
||||
Let bindings:
|
||||
```lean
|
||||
example : let n := 1; let k := 2; n + k = 3 := by
|
||||
intros
|
||||
/- n✝ : Nat := 1
|
||||
k✝ : Nat := 2
|
||||
⊢ n✝ + k✝ = 3 -/
|
||||
rfl
|
||||
```
|
||||
-/
|
||||
syntax (name := intros) "intros" (ppSpace colGt (ident <|> hole))* : tactic
|
||||
|
||||
@@ -268,8 +335,8 @@ macro "rfl'" : tactic => `(tactic| set_option smartUnfolding false in with_unfol
|
||||
/--
|
||||
`ac_rfl` proves equalities up to application of an associative and commutative operator.
|
||||
```
|
||||
instance : IsAssociative (α := Nat) (.+.) := ⟨Nat.add_assoc⟩
|
||||
instance : IsCommutative (α := Nat) (.+.) := ⟨Nat.add_comm⟩
|
||||
instance : Associative (α := Nat) (.+.) := ⟨Nat.add_assoc⟩
|
||||
instance : Commutative (α := Nat) (.+.) := ⟨Nat.add_comm⟩
|
||||
|
||||
example (a b c d : Nat) : a + b + c + d = d + (b + c) + a := by ac_rfl
|
||||
```
|
||||
@@ -355,9 +422,9 @@ Using `rw (config := {occs := .pos L}) [e]`,
|
||||
where `L : List Nat`, you can control which "occurrences" are rewritten.
|
||||
(This option applies to each rule, so usually this will only be used with a single rule.)
|
||||
Occurrences count from `1`.
|
||||
At the first occurrence, whether allowed or not,
|
||||
arguments of the rewrite rule `e` may be instantiated,
|
||||
At each allowed occurrence, arguments of the rewrite rule `e` may be instantiated,
|
||||
restricting which later rewrites can be found.
|
||||
(Disallowed occurrences do not result in instantiation.)
|
||||
`{occs := .neg L}` allows skipping specified occurrences.
|
||||
-/
|
||||
syntax (name := rewriteSeq) "rewrite" (config)? rwRuleSeq (location)? : tactic
|
||||
@@ -559,7 +626,7 @@ You can use `with` to provide the variables names for each constructor.
|
||||
- `induction e`, where `e` is an expression instead of a variable,
|
||||
generalizes `e` in the goal, and then performs induction on the resulting variable.
|
||||
- `induction e using r` allows the user to specify the principle of induction that should be used.
|
||||
Here `r` should be a theorem whose result type must be of the form `C t`,
|
||||
Here `r` should be a term whose result type must be of the form `C t`,
|
||||
where `C` is a bound variable and `t` is a (possibly empty) sequence of bound variables
|
||||
- `induction e generalizing z₁ ... zₙ`, where `z₁ ... zₙ` are variables in the local context,
|
||||
generalizes over `z₁ ... zₙ` before applying the induction but then introduces them in each goal.
|
||||
@@ -567,7 +634,7 @@ You can use `with` to provide the variables names for each constructor.
|
||||
- Given `x : Nat`, `induction x with | zero => tac₁ | succ x' ih => tac₂`
|
||||
uses tactic `tac₁` for the `zero` case, and `tac₂` for the `succ` case.
|
||||
-/
|
||||
syntax (name := induction) "induction " term,+ (" using " ident)?
|
||||
syntax (name := induction) "induction " term,+ (" using " term)?
|
||||
(" generalizing" (ppSpace colGt term:max)+)? (inductionAlts)? : tactic
|
||||
|
||||
/-- A `generalize` argument, of the form `term = x` or `h : term = x`. -/
|
||||
@@ -610,7 +677,7 @@ You can use `with` to provide the variables names for each constructor.
|
||||
performs cases on `e` as above, but also adds a hypothesis `h : e = ...` to each hypothesis,
|
||||
where `...` is the constructor instance for that particular case.
|
||||
-/
|
||||
syntax (name := cases) "cases " casesTarget,+ (" using " ident)? (inductionAlts)? : tactic
|
||||
syntax (name := cases) "cases " casesTarget,+ (" using " term)? (inductionAlts)? : tactic
|
||||
|
||||
/-- `rename_i x_1 ... x_n` renames the last `n` inaccessible names using the given names. -/
|
||||
syntax (name := renameI) "rename_i" (ppSpace colGt binderIdent)+ : tactic
|
||||
@@ -753,7 +820,7 @@ end Tactic
|
||||
|
||||
namespace Attr
|
||||
/--
|
||||
Theorems tagged with the `simp` attribute are by the simplifier
|
||||
Theorems tagged with the `simp` attribute are used by the simplifier
|
||||
(i.e., the `simp` tactic, and its variants) to simplify expressions occurring in your goals.
|
||||
We call theorems tagged with the `simp` attribute "simp theorems" or "simp lemmas".
|
||||
Lean maintains a database/index containing all active simp theorems.
|
||||
|
||||
@@ -330,7 +330,7 @@ private def AttributeExtension.mkInitial : IO AttributeExtensionState := do
|
||||
|
||||
unsafe def mkAttributeImplOfConstantUnsafe (env : Environment) (opts : Options) (declName : Name) : Except String AttributeImpl :=
|
||||
match env.find? declName with
|
||||
| none => throw ("unknow constant '" ++ toString declName ++ "'")
|
||||
| none => throw ("unknown constant '" ++ toString declName ++ "'")
|
||||
| some info =>
|
||||
match info.type with
|
||||
| Expr.const `Lean.AttributeImpl _ => env.evalConst AttributeImpl opts declName
|
||||
|
||||
@@ -75,10 +75,10 @@ def eqvLetValue (e₁ e₂ : LetValue) : EqvM Bool := do
|
||||
go (i+1)
|
||||
else
|
||||
x
|
||||
termination_by params₁.size - i
|
||||
go 0
|
||||
else
|
||||
return false
|
||||
termination_by go i => params₁.size - i
|
||||
|
||||
def sortAlts (alts : Array Alt) : Array Alt :=
|
||||
alts.qsort fun
|
||||
@@ -133,4 +133,4 @@ Return `true` if `c₁` and `c₂` are alpha equivalent.
|
||||
def Code.alphaEqv (c₁ c₂ : Code) : Bool :=
|
||||
AlphaEqv.eqv c₁ c₂ |>.run {}
|
||||
|
||||
end Lean.Compiler.LCNF
|
||||
end Lean.Compiler.LCNF
|
||||
|
||||
@@ -181,8 +181,8 @@ def expandCodeDecls (decls : Array CodeDecl) (body : LetValue) : CompilerM Expr
|
||||
go (i+1) (subst.push value)
|
||||
else
|
||||
(body.toExpr.abstract xs).instantiateRev subst
|
||||
termination_by values.size - i
|
||||
return go 0 #[]
|
||||
termination_by go => values.size - i
|
||||
|
||||
/--
|
||||
Create the "key" that uniquely identifies a code specialization.
|
||||
|
||||
@@ -658,7 +658,9 @@ where
|
||||
visit (f.beta e.getAppArgs)
|
||||
|
||||
visitApp (e : Expr) : M Arg := do
|
||||
if let .const declName _ := e.getAppFn then
|
||||
if let some (args, n, t, v, b) := e.letFunAppArgs? then
|
||||
visitCore <| mkAppN (.letE n t v b (nonDep := true)) args
|
||||
else if let .const declName _ := e.getAppFn then
|
||||
if declName == ``Quot.lift then
|
||||
visitQuotLift e
|
||||
else if declName == ``Quot.mk then
|
||||
@@ -725,11 +727,8 @@ where
|
||||
pushElement (.fun funDecl)
|
||||
return .fvar funDecl.fvarId
|
||||
|
||||
visitMData (mdata : MData) (e : Expr) : M Arg := do
|
||||
if let some (.app (.lam n t b ..) v) := letFunAnnotation? (.mdata mdata e) then
|
||||
visitLet (.letE n t v b (nonDep := true)) #[]
|
||||
else
|
||||
visit e
|
||||
visitMData (_mdata : MData) (e : Expr) : M Arg := do
|
||||
visit e
|
||||
|
||||
visitProj (s : Name) (i : Nat) (e : Expr) : M Arg := do
|
||||
match (← visit e) with
|
||||
|
||||
@@ -234,7 +234,7 @@ where
|
||||
throwError "invalid instantiateForall, too many parameters"
|
||||
else
|
||||
return type
|
||||
termination_by go i _ => ps.size - i
|
||||
termination_by ps.size - i
|
||||
|
||||
/--
|
||||
Return `true` if `type` is a predicate.
|
||||
|
||||
@@ -315,7 +315,7 @@ private def checkUnsupported [Monad m] [MonadEnv m] [MonadError m] (decl : Decla
|
||||
| _ => pure ()
|
||||
|
||||
register_builtin_option compiler.enableNew : Bool := {
|
||||
defValue := true
|
||||
defValue := false
|
||||
group := "compiler"
|
||||
descr := "(compiler) enable the new code generator, this should have no significant effect on your code but it does help to test the new code generator; unset to only use the old code generator instead"
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ def filterPairsM {m} [Monad m] {α} (a : Array α) (f : α → α → m (Bool ×
|
||||
let mut numRemoved := 0
|
||||
for h1 : i in [:a.size] do for h2 : j in [i+1:a.size] do
|
||||
unless removed[i]! || removed[j]! do
|
||||
let xi := a[i]'h1.2
|
||||
let xj := a[j]'h2.2
|
||||
let xi := a[i]
|
||||
let xj := a[j]
|
||||
let (keepi, keepj) ← f xi xj
|
||||
unless keepi do
|
||||
numRemoved := numRemoved + 1
|
||||
@@ -40,7 +40,7 @@ def filterPairsM {m} [Monad m] {α} (a : Array α) (f : α → α → m (Bool ×
|
||||
let mut a' := Array.mkEmpty numRemoved
|
||||
for h : i in [:a.size] do
|
||||
unless removed[i]! do
|
||||
a' := a'.push (a[i]'h.2)
|
||||
a' := a'.push a[i]
|
||||
return a'
|
||||
|
||||
end Array
|
||||
|
||||
@@ -89,7 +89,7 @@ def moveEntries [Hashable α] (i : Nat) (source : Array (AssocList α β)) (targ
|
||||
let target := es.foldl (reinsertAux hash) target
|
||||
moveEntries (i+1) source target
|
||||
else target
|
||||
termination_by _ i source _ => source.size - i
|
||||
termination_by source.size - i
|
||||
|
||||
def expand [Hashable α] (size : Nat) (buckets : HashMapBucket α β) : HashMapImp α β :=
|
||||
let bucketsNew : HashMapBucket α β := ⟨
|
||||
@@ -227,3 +227,17 @@ def ofListWith (l : List (α × β)) (f : β → β → β) : HashMap α β :=
|
||||
match m.find? p.fst with
|
||||
| none => m.insert p.fst p.snd
|
||||
| some v => m.insert p.fst $ f v p.snd)
|
||||
end Lean.HashMap
|
||||
|
||||
/--
|
||||
Groups all elements `x`, `y` in `xs` with `key x == key y` into the same array
|
||||
`(xs.groupByKey key).find! (key x)`. Groups preserve the relative order of elements in `xs`.
|
||||
-/
|
||||
def Array.groupByKey [BEq α] [Hashable α] (key : β → α) (xs : Array β)
|
||||
: Lean.HashMap α (Array β) := Id.run do
|
||||
let mut groups := ∅
|
||||
for x in xs do
|
||||
let group := groups.findD (key x) #[]
|
||||
groups := groups.erase (key x) -- make `group` referentially unique
|
||||
groups := groups.insert (key x) (group.push x)
|
||||
return groups
|
||||
|
||||
@@ -80,7 +80,7 @@ def moveEntries [Hashable α] (i : Nat) (source : Array (List α)) (target : Has
|
||||
moveEntries (i+1) source target
|
||||
else
|
||||
target
|
||||
termination_by _ i source _ => source.size - i
|
||||
termination_by source.size - i
|
||||
|
||||
def expand [Hashable α] (size : Nat) (buckets : HashSetBucket α) : HashSetImp α :=
|
||||
let bucketsNew : HashSetBucket α := ⟨
|
||||
|
||||
@@ -78,6 +78,38 @@ structure Command where
|
||||
arguments? : Option (Array Json) := none
|
||||
deriving ToJson, FromJson
|
||||
|
||||
/-- A snippet is a string that gets inserted into a document,
|
||||
and can afterwards be edited by the user in a structured way.
|
||||
|
||||
Snippets contain instructions that
|
||||
specify how this structured editing should proceed.
|
||||
They are expressed in a domain-specific language
|
||||
based on one from TextMate,
|
||||
including the following constructs:
|
||||
- Designated positions for subsequent user input,
|
||||
called "tab stops" after their most frequently-used keybinding.
|
||||
They are denoted with `$1`, `$2`, and so forth.
|
||||
`$0` denotes where the cursor should be positioned after all edits are completed,
|
||||
defaulting to the end of the string.
|
||||
Snippet tab stops are unrelated to tab stops used for indentation.
|
||||
- Tab stops with default values, called _placeholders_, written `${1:default}`.
|
||||
The default may itself contain a tab stop or a further placeholder
|
||||
and multiple options to select from may be provided
|
||||
by surrounding them with `|`s and separating them with `,`,
|
||||
as in `${1|if $2 then $3 else $4,if let $2 := $3 then $4 else $5|}`.
|
||||
- One of a set of predefined variables that are replaced with their values.
|
||||
This includes the current line number (`$TM_LINE_NUMBER`)
|
||||
or the text that was selected when the snippet was invoked (`$TM_SELECTED_TEXT`).
|
||||
- Formatting instructions to modify variables using regular expressions
|
||||
or a set of predefined filters.
|
||||
|
||||
The full syntax and semantics of snippets,
|
||||
including the available variables and the rules for escaping control characters,
|
||||
are described in the [LSP specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax). -/
|
||||
structure SnippetString where
|
||||
value : String
|
||||
deriving ToJson, FromJson
|
||||
|
||||
/-- A textual edit applicable to a text document.
|
||||
|
||||
[reference](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEdit) -/
|
||||
@@ -87,6 +119,21 @@ structure TextEdit where
|
||||
range : Range
|
||||
/-- The string to be inserted. For delete operations use an empty string. -/
|
||||
newText : String
|
||||
/-- If this field is present and the editor supports it,
|
||||
`newText` is ignored
|
||||
and an interactive snippet edit is performed instead.
|
||||
|
||||
The use of snippets in `TextEdit`s
|
||||
is a Lean-specific extension to the LSP standard,
|
||||
so `newText` should still be set to a correct value
|
||||
as fallback in case the editor does not support this feature.
|
||||
Even editors that support snippets may not always use them;
|
||||
for instance, if the file is not already open,
|
||||
VS Code will perform a normal text edit in the background instead. -/
|
||||
/- NOTE: Similar functionality may be added to LSP in the future:
|
||||
see [issue #592](https://github.com/microsoft/language-server-protocol/issues/592).
|
||||
If such an addition occurs, this field should be deprecated. -/
|
||||
leanExtSnippet? : Option SnippetString := none
|
||||
/-- Identifier for annotated edit.
|
||||
|
||||
`WorkspaceEdit` has a `changeAnnotations` field that maps these identifiers to a `ChangeAnnotation`.
|
||||
|
||||
@@ -74,6 +74,7 @@ structure ServerCapabilities where
|
||||
declarationProvider : Bool := false
|
||||
typeDefinitionProvider : Bool := false
|
||||
referencesProvider : Bool := false
|
||||
callHierarchyProvider : Bool := false
|
||||
renameProvider? : Option RenameOptions := none
|
||||
workspaceSymbolProvider : Bool := false
|
||||
foldingRangeProvider : Bool := false
|
||||
|
||||
@@ -8,6 +8,8 @@ Authors: Joscha Mennicken
|
||||
import Lean.Expr
|
||||
import Lean.Data.Lsp.Basic
|
||||
|
||||
set_option linter.missingDocs true -- keep it documented
|
||||
|
||||
/-! This file contains types for communication between the watchdog and the
|
||||
workers. These messages are not visible externally to users of the LSP server.
|
||||
-/
|
||||
@@ -17,17 +19,27 @@ namespace Lean.Lsp
|
||||
/-! Most reference-related types have custom FromJson/ToJson implementations to
|
||||
reduce the size of the resulting JSON. -/
|
||||
|
||||
/--
|
||||
Identifier of a reference.
|
||||
-/
|
||||
inductive RefIdent where
|
||||
/-- Named identifier. These are used in all references that are globally available. -/
|
||||
| const : Name → RefIdent
|
||||
| fvar : FVarId → RefIdent
|
||||
/-- Unnamed identifier. These are used for all local references. -/
|
||||
| fvar : FVarId → RefIdent
|
||||
deriving BEq, Hashable, Inhabited
|
||||
|
||||
namespace RefIdent
|
||||
|
||||
/-- Converts the reference identifier to a string by prefixing it with a symbol. -/
|
||||
def toString : RefIdent → String
|
||||
| RefIdent.const n => s!"c:{n}"
|
||||
| RefIdent.fvar id => s!"f:{id.name}"
|
||||
|
||||
/--
|
||||
Converts the string representation of a reference identifier back to a reference identifier.
|
||||
The string representation must have been created by `RefIdent.toString`.
|
||||
-/
|
||||
def fromString (s : String) : Except String RefIdent := do
|
||||
let sPrefix := s.take 2
|
||||
let sName := s.drop 2
|
||||
@@ -43,33 +55,92 @@ def fromString (s : String) : Except String RefIdent := do
|
||||
| "f:" => return RefIdent.fvar <| FVarId.mk name
|
||||
| _ => throw "string must start with 'c:' or 'f:'"
|
||||
|
||||
instance : FromJson RefIdent where
|
||||
fromJson?
|
||||
| (s : String) => fromString s
|
||||
| j => Except.error s!"expected a String, got {j}"
|
||||
|
||||
instance : ToJson RefIdent where
|
||||
toJson ident := toString ident
|
||||
|
||||
end RefIdent
|
||||
|
||||
/-- Information about the declaration surrounding a reference. -/
|
||||
structure RefInfo.ParentDecl where
|
||||
/-- Name of the declaration surrounding a reference. -/
|
||||
name : Name
|
||||
/-- Range of the declaration surrounding a reference. -/
|
||||
range : Lsp.Range
|
||||
/-- Selection range of the declaration surrounding a reference. -/
|
||||
selectionRange : Lsp.Range
|
||||
deriving ToJson
|
||||
|
||||
/--
|
||||
Denotes the range of a reference, as well as the parent declaration of the reference.
|
||||
If the reference is itself a declaration, then it contains no parent declaration.
|
||||
-/
|
||||
structure RefInfo.Location where
|
||||
/-- Range of the reference. -/
|
||||
range : Lsp.Range
|
||||
/-- Parent declaration of the reference. `none` if the reference is itself a declaration. -/
|
||||
parentDecl? : Option RefInfo.ParentDecl
|
||||
|
||||
/-- Definition site and usage sites of a reference. Obtained from `Lean.Server.RefInfo`. -/
|
||||
structure RefInfo where
|
||||
definition : Option Lsp.Range
|
||||
usages : Array Lsp.Range
|
||||
/-- Definition site of the reference. May be `none` when we cannot find a definition site. -/
|
||||
definition? : Option RefInfo.Location
|
||||
/-- Usage sites of the reference. -/
|
||||
usages : Array RefInfo.Location
|
||||
|
||||
instance : ToJson RefInfo where
|
||||
toJson i :=
|
||||
let rangeToList (r : Lsp.Range) : List Nat :=
|
||||
[r.start.line, r.start.character, r.end.line, r.end.character]
|
||||
let parentDeclToList (d : RefInfo.ParentDecl) : List Json :=
|
||||
let name := d.name.toString |> toJson
|
||||
let range := rangeToList d.range |>.map toJson
|
||||
let selectionRange := rangeToList d.selectionRange |>.map toJson
|
||||
[name] ++ range ++ selectionRange
|
||||
let locationToList (l : RefInfo.Location) : List Json :=
|
||||
let range := rangeToList l.range |>.map toJson
|
||||
let parentDecl := l.parentDecl?.map parentDeclToList |>.getD []
|
||||
range ++ parentDecl
|
||||
Json.mkObj [
|
||||
("definition", toJson $ i.definition.map rangeToList),
|
||||
("usages", toJson $ i.usages.map rangeToList)
|
||||
("definition", toJson $ i.definition?.map locationToList),
|
||||
("usages", toJson $ i.usages.map locationToList)
|
||||
]
|
||||
|
||||
instance : FromJson RefInfo where
|
||||
fromJson? j := do
|
||||
let listToRange (l : List Nat) : Except String Lsp.Range := match l with
|
||||
let toRange : List Nat → Except String Lsp.Range
|
||||
| [sLine, sChar, eLine, eChar] => pure ⟨⟨sLine, sChar⟩, ⟨eLine, eChar⟩⟩
|
||||
| _ => throw s!"Expected list of length 4, not {l.length}"
|
||||
let definition ← j.getObjValAs? (Option $ List Nat) "definition"
|
||||
let definition ← match definition with
|
||||
| l => throw s!"Expected list of length 4, not {l.length}"
|
||||
let toParentDecl (a : Array Json) : Except String RefInfo.ParentDecl := do
|
||||
let name := String.toName <| ← fromJson? a[0]!
|
||||
let range ← a[1:5].toArray.toList |>.mapM fromJson?
|
||||
let range ← toRange range
|
||||
let selectionRange ← a[5:].toArray.toList |>.mapM fromJson?
|
||||
let selectionRange ← toRange selectionRange
|
||||
return ⟨name, range, selectionRange⟩
|
||||
let toLocation (l : List Json) : Except String RefInfo.Location := do
|
||||
let l := l.toArray
|
||||
if l.size != 4 && l.size != 13 then
|
||||
.error "Expected list of length 4 or 13, not {l.size}"
|
||||
let range ← l[:4].toArray.toList |>.mapM fromJson?
|
||||
let range ← toRange range
|
||||
if l.size == 13 then
|
||||
let parentDecl ← toParentDecl l[4:].toArray
|
||||
return ⟨range, parentDecl⟩
|
||||
else
|
||||
return ⟨range, none⟩
|
||||
|
||||
let definition? ← j.getObjValAs? (Option $ List Json) "definition"
|
||||
let definition? ← match definition? with
|
||||
| none => pure none
|
||||
| some list => some <$> listToRange list
|
||||
let usages ← j.getObjValAs? (Array $ List Nat) "usages"
|
||||
let usages ← usages.mapM listToRange
|
||||
pure { definition, usages }
|
||||
| some list => some <$> toLocation list
|
||||
let usages ← j.getObjValAs? (Array $ List Json) "usages"
|
||||
let usages ← usages.mapM toLocation
|
||||
pure { definition?, usages }
|
||||
|
||||
/-- References from a single module/file -/
|
||||
def ModuleRefs := HashMap RefIdent RefInfo
|
||||
@@ -88,7 +159,8 @@ instance : FromJson ModuleRefs where
|
||||
Contains the file's definitions and references. -/
|
||||
structure LeanIleanInfoParams where
|
||||
/-- Version of the file these references are from. -/
|
||||
version : Nat
|
||||
version : Nat
|
||||
/-- All references for the file. -/
|
||||
references : ModuleRefs
|
||||
deriving FromJson, ToJson
|
||||
|
||||
|
||||
@@ -36,16 +36,16 @@ instance : FromJson CompletionItemKind where
|
||||
|
||||
structure InsertReplaceEdit where
|
||||
newText : String
|
||||
insert : Range
|
||||
insert : Range
|
||||
replace : Range
|
||||
deriving FromJson, ToJson
|
||||
|
||||
structure CompletionItem where
|
||||
label : String
|
||||
detail? : Option String := none
|
||||
label : String
|
||||
detail? : Option String := none
|
||||
documentation? : Option MarkupContent := none
|
||||
kind? : Option CompletionItemKind := none
|
||||
textEdit? : Option InsertReplaceEdit := none
|
||||
kind? : Option CompletionItemKind := none
|
||||
textEdit? : Option InsertReplaceEdit := none
|
||||
/-
|
||||
tags? : CompletionItemTag[]
|
||||
deprecated? : boolean
|
||||
@@ -63,7 +63,7 @@ structure CompletionItem where
|
||||
|
||||
structure CompletionList where
|
||||
isIncomplete : Bool
|
||||
items : Array CompletionItem
|
||||
items : Array CompletionItem
|
||||
deriving FromJson, ToJson
|
||||
|
||||
structure CompletionParams extends TextDocumentPositionParams where
|
||||
@@ -74,7 +74,7 @@ structure Hover where
|
||||
/- NOTE we should also accept MarkedString/MarkedString[] here
|
||||
but they are deprecated, so maybe can get away without. -/
|
||||
contents : MarkupContent
|
||||
range? : Option Range := none
|
||||
range? : Option Range := none
|
||||
deriving ToJson, FromJson
|
||||
|
||||
structure HoverParams extends TextDocumentPositionParams
|
||||
@@ -153,45 +153,76 @@ inductive SymbolKind where
|
||||
| event
|
||||
| operator
|
||||
| typeParameter
|
||||
deriving BEq, Hashable, Inhabited
|
||||
|
||||
instance : FromJson SymbolKind where
|
||||
fromJson?
|
||||
| 1 => .ok .file
|
||||
| 2 => .ok .module
|
||||
| 3 => .ok .namespace
|
||||
| 4 => .ok .package
|
||||
| 5 => .ok .class
|
||||
| 6 => .ok .method
|
||||
| 7 => .ok .property
|
||||
| 8 => .ok .field
|
||||
| 9 => .ok .constructor
|
||||
| 10 => .ok .enum
|
||||
| 11 => .ok .interface
|
||||
| 12 => .ok .function
|
||||
| 13 => .ok .variable
|
||||
| 14 => .ok .constant
|
||||
| 15 => .ok .string
|
||||
| 16 => .ok .number
|
||||
| 17 => .ok .boolean
|
||||
| 18 => .ok .array
|
||||
| 19 => .ok .object
|
||||
| 20 => .ok .key
|
||||
| 21 => .ok .null
|
||||
| 22 => .ok .enumMember
|
||||
| 23 => .ok .struct
|
||||
| 24 => .ok .event
|
||||
| 25 => .ok .operator
|
||||
| 26 => .ok .typeParameter
|
||||
| j => .error s!"invalid symbol kind {j}"
|
||||
|
||||
instance : ToJson SymbolKind where
|
||||
toJson
|
||||
| SymbolKind.file => 1
|
||||
| SymbolKind.module => 2
|
||||
| SymbolKind.namespace => 3
|
||||
| SymbolKind.package => 4
|
||||
| SymbolKind.class => 5
|
||||
| SymbolKind.method => 6
|
||||
| SymbolKind.property => 7
|
||||
| SymbolKind.field => 8
|
||||
| SymbolKind.constructor => 9
|
||||
| SymbolKind.enum => 10
|
||||
| SymbolKind.interface => 11
|
||||
| SymbolKind.function => 12
|
||||
| SymbolKind.variable => 13
|
||||
| SymbolKind.constant => 14
|
||||
| SymbolKind.string => 15
|
||||
| SymbolKind.number => 16
|
||||
| SymbolKind.boolean => 17
|
||||
| SymbolKind.array => 18
|
||||
| SymbolKind.object => 19
|
||||
| SymbolKind.key => 20
|
||||
| SymbolKind.null => 21
|
||||
| SymbolKind.enumMember => 22
|
||||
| SymbolKind.struct => 23
|
||||
| SymbolKind.event => 24
|
||||
| SymbolKind.operator => 25
|
||||
| SymbolKind.typeParameter => 26
|
||||
toJson
|
||||
| .file => 1
|
||||
| .module => 2
|
||||
| .namespace => 3
|
||||
| .package => 4
|
||||
| .class => 5
|
||||
| .method => 6
|
||||
| .property => 7
|
||||
| .field => 8
|
||||
| .constructor => 9
|
||||
| .enum => 10
|
||||
| .interface => 11
|
||||
| .function => 12
|
||||
| .variable => 13
|
||||
| .constant => 14
|
||||
| .string => 15
|
||||
| .number => 16
|
||||
| .boolean => 17
|
||||
| .array => 18
|
||||
| .object => 19
|
||||
| .key => 20
|
||||
| .null => 21
|
||||
| .enumMember => 22
|
||||
| .struct => 23
|
||||
| .event => 24
|
||||
| .operator => 25
|
||||
| .typeParameter => 26
|
||||
|
||||
structure DocumentSymbolAux (Self : Type) where
|
||||
name : String
|
||||
detail? : Option String := none
|
||||
kind : SymbolKind
|
||||
name : String
|
||||
detail? : Option String := none
|
||||
kind : SymbolKind
|
||||
-- tags? : Array SymbolTag
|
||||
range : Range
|
||||
range : Range
|
||||
selectionRange : Range
|
||||
children? : Option (Array Self) := none
|
||||
deriving ToJson
|
||||
children? : Option (Array Self) := none
|
||||
deriving FromJson, ToJson
|
||||
|
||||
inductive DocumentSymbol where
|
||||
| mk (sym : DocumentSymbolAux DocumentSymbol)
|
||||
@@ -212,18 +243,56 @@ instance : ToJson DocumentSymbolResult where
|
||||
|
||||
inductive SymbolTag where
|
||||
| deprecated
|
||||
deriving BEq, Hashable, Inhabited
|
||||
|
||||
instance : FromJson SymbolTag where
|
||||
fromJson?
|
||||
| 1 => .ok .deprecated
|
||||
| j => .error s!"unknown symbol tag {j}"
|
||||
|
||||
instance : ToJson SymbolTag where
|
||||
toJson
|
||||
| SymbolTag.deprecated => 1
|
||||
toJson
|
||||
| .deprecated => 1
|
||||
|
||||
structure SymbolInformation where
|
||||
name : String
|
||||
kind : SymbolKind
|
||||
tags : Array SymbolTag := #[]
|
||||
location : Location
|
||||
name : String
|
||||
kind : SymbolKind
|
||||
tags : Array SymbolTag := #[]
|
||||
location : Location
|
||||
containerName? : Option String := none
|
||||
deriving ToJson
|
||||
deriving FromJson, ToJson
|
||||
|
||||
structure CallHierarchyPrepareParams extends TextDocumentPositionParams
|
||||
deriving FromJson, ToJson
|
||||
|
||||
structure CallHierarchyItem where
|
||||
name : String
|
||||
kind : SymbolKind
|
||||
tags? : Option (Array SymbolTag) := none
|
||||
detail? : Option String := none
|
||||
uri : DocumentUri
|
||||
range : Range
|
||||
selectionRange : Range
|
||||
-- data? : Option unknown
|
||||
deriving FromJson, ToJson, BEq, Hashable, Inhabited
|
||||
|
||||
structure CallHierarchyIncomingCallsParams where
|
||||
item : CallHierarchyItem
|
||||
deriving FromJson, ToJson
|
||||
|
||||
structure CallHierarchyIncomingCall where
|
||||
«from» : CallHierarchyItem
|
||||
fromRanges : Array Range
|
||||
deriving FromJson, ToJson, Inhabited
|
||||
|
||||
structure CallHierarchyOutgoingCallsParams where
|
||||
item : CallHierarchyItem
|
||||
deriving FromJson, ToJson
|
||||
|
||||
structure CallHierarchyOutgoingCall where
|
||||
to : CallHierarchyItem
|
||||
fromRanges : Array Range
|
||||
deriving FromJson, ToJson, Inhabited
|
||||
|
||||
inductive SemanticTokenType where
|
||||
-- Used by Lean
|
||||
@@ -304,14 +373,14 @@ example {v : SemanticTokenModifier} : open SemanticTokenModifier in
|
||||
cases v <;> native_decide
|
||||
|
||||
structure SemanticTokensLegend where
|
||||
tokenTypes : Array String
|
||||
tokenTypes : Array String
|
||||
tokenModifiers : Array String
|
||||
deriving FromJson, ToJson
|
||||
|
||||
structure SemanticTokensOptions where
|
||||
legend : SemanticTokensLegend
|
||||
range : Bool
|
||||
full : Bool /- | {
|
||||
range : Bool
|
||||
full : Bool /- | {
|
||||
delta?: boolean;
|
||||
} -/
|
||||
deriving FromJson, ToJson
|
||||
@@ -322,12 +391,12 @@ structure SemanticTokensParams where
|
||||
|
||||
structure SemanticTokensRangeParams where
|
||||
textDocument : TextDocumentIdentifier
|
||||
range : Range
|
||||
range : Range
|
||||
deriving FromJson, ToJson
|
||||
|
||||
structure SemanticTokens where
|
||||
resultId? : Option String := none
|
||||
data : Array Nat
|
||||
data : Array Nat
|
||||
deriving FromJson, ToJson
|
||||
|
||||
structure FoldingRangeParams where
|
||||
@@ -343,12 +412,12 @@ instance : ToJson FoldingRangeKind where
|
||||
toJson
|
||||
| FoldingRangeKind.comment => "comment"
|
||||
| FoldingRangeKind.imports => "imports"
|
||||
| FoldingRangeKind.region => "region"
|
||||
| FoldingRangeKind.region => "region"
|
||||
|
||||
structure FoldingRange where
|
||||
startLine : Nat
|
||||
endLine : Nat
|
||||
kind? : Option FoldingRangeKind := none
|
||||
endLine : Nat
|
||||
kind? : Option FoldingRangeKind := none
|
||||
deriving ToJson
|
||||
|
||||
structure RenameOptions where
|
||||
|
||||
@@ -8,6 +8,7 @@ import Init.Data.String
|
||||
import Init.Data.Array
|
||||
import Lean.Data.Lsp.Basic
|
||||
import Lean.Data.Position
|
||||
import Lean.DeclarationRange
|
||||
|
||||
/-! LSP uses UTF-16 for indexing, so we need to provide some primitives
|
||||
to interact with Lean strings using UTF-16 indices. -/
|
||||
@@ -62,24 +63,37 @@ end String
|
||||
namespace Lean
|
||||
namespace FileMap
|
||||
|
||||
private def lineStartPos (text : FileMap) (line : Nat) : String.Pos :=
|
||||
if h : line < text.positions.size then
|
||||
text.positions.get ⟨line, h⟩
|
||||
else if text.positions.isEmpty then
|
||||
0
|
||||
else
|
||||
text.positions.back
|
||||
|
||||
/-- Computes an UTF-8 offset into `text.source`
|
||||
from an LSP-style 0-indexed (ln, col) position. -/
|
||||
def lspPosToUtf8Pos (text : FileMap) (pos : Lsp.Position) : String.Pos :=
|
||||
let colPos :=
|
||||
if h : pos.line < text.positions.size then
|
||||
text.positions.get ⟨pos.line, h⟩
|
||||
else if text.positions.isEmpty then
|
||||
0
|
||||
else
|
||||
text.positions.back
|
||||
let chr := text.source.utf16PosToCodepointPosFrom pos.character colPos
|
||||
text.source.codepointPosToUtf8PosFrom colPos chr
|
||||
let lineStartPos := lineStartPos text pos.line
|
||||
let chr := text.source.utf16PosToCodepointPosFrom pos.character lineStartPos
|
||||
text.source.codepointPosToUtf8PosFrom lineStartPos chr
|
||||
|
||||
def leanPosToLspPos (text : FileMap) : Lean.Position → Lsp.Position
|
||||
| ⟨ln, col⟩ => ⟨ln-1, text.source.codepointPosToUtf16PosFrom col (text.positions.get! $ ln - 1)⟩
|
||||
| ⟨line, col⟩ =>
|
||||
⟨line - 1, text.source.codepointPosToUtf16PosFrom col (lineStartPos text (line - 1))⟩
|
||||
|
||||
def utf8PosToLspPos (text : FileMap) (pos : String.Pos) : Lsp.Position :=
|
||||
text.leanPosToLspPos (text.toPosition pos)
|
||||
|
||||
end FileMap
|
||||
end Lean
|
||||
|
||||
/--
|
||||
Convert the Lean `DeclarationRange` to an LSP `Range` by turning the 1-indexed line numbering into a
|
||||
0-indexed line numbering and converting the character offset within the line to a UTF-16 indexed
|
||||
offset.
|
||||
-/
|
||||
def Lean.DeclarationRange.toLspRange (r : Lean.DeclarationRange) : Lsp.Range := {
|
||||
start := ⟨r.pos.line - 1, r.charUtf16⟩
|
||||
«end» := ⟨r.endPos.line - 1, r.endCharUtf16⟩
|
||||
}
|
||||
|
||||
@@ -29,9 +29,16 @@ instance : ToExpr Position where
|
||||
|
||||
end Position
|
||||
|
||||
/-- Content of a file together with precalculated positions of newlines. -/
|
||||
structure FileMap where
|
||||
/-- The content of the file. -/
|
||||
source : String
|
||||
/-- The positions of newline characters.
|
||||
The first entry is always `0` and the last always the index of the last character.
|
||||
In particular, if the last character is a newline, that index will appear twice. -/
|
||||
positions : Array String.Pos
|
||||
/-- The line numbers associated with the `positions`.
|
||||
Has the same length as `positions` and is always of the form `#[1, 2, …, n-1, n-1]`. -/
|
||||
lines : Array Nat
|
||||
deriving Inhabited
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ def appendTrees : RBNode α β → RBNode α β → RBNode α β
|
||||
| bc => balLeft a kx vx (node black bc ky vy d)
|
||||
| a, node red b kx vx c => node red (appendTrees a b) kx vx c
|
||||
| node red a kx vx b, c => node red a kx vx (appendTrees b c)
|
||||
termination_by _ x y => x.size + y.size
|
||||
termination_by x y => x.size + y.size
|
||||
|
||||
section Erase
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ def toList (m : SMap α β) : List (α × β) :=
|
||||
|
||||
end SMap
|
||||
|
||||
def List.toSMap [BEq α] [Hashable α] (es : List (α × β)) : SMap α β :=
|
||||
def _root_.List.toSMap [BEq α] [Hashable α] (es : List (α × β)) : SMap α β :=
|
||||
es.foldl (init := {}) fun s (a, b) => s.insert a b
|
||||
|
||||
instance {_ : BEq α} {_ : Hashable α} [Repr α] [Repr β] : Repr (SMap α β) where
|
||||
|
||||
@@ -36,13 +36,12 @@ where
|
||||
if it.atEnd then r
|
||||
else if it.curr == ' ' || it.curr == '\t' then consumeSpaces n it.next r
|
||||
else saveLine it r
|
||||
termination_by (it, 1)
|
||||
saveLine (it : String.Iterator) (r : String) : String :=
|
||||
if it.atEnd then r
|
||||
else if it.curr == '\n' then consumeSpaces n it.next (r.push '\n')
|
||||
else saveLine it.next (r.push it.curr)
|
||||
termination_by
|
||||
consumeSpaces n it r => (it, 1)
|
||||
saveLine it r => (it, 0)
|
||||
termination_by (it, 0)
|
||||
|
||||
def removeLeadingSpaces (s : String) : String :=
|
||||
let n := findLeadingSpacesSize s
|
||||
|
||||
@@ -690,10 +690,10 @@ builtin_initialize elabAsElim : TagAttribute ←
|
||||
(applicationTime := .afterCompilation)
|
||||
fun declName => do
|
||||
let go : MetaM Unit := do
|
||||
discard <| getElimInfo declName
|
||||
let info ← getConstInfo declName
|
||||
if (← hasOptAutoParams info.type) then
|
||||
throwError "[elab_as_elim] attribute cannot be used in declarations containing optional and auto parameters"
|
||||
discard <| getElimInfo declName
|
||||
go.run' {} {}
|
||||
|
||||
/-! # Eliminator-like function application elaborator -/
|
||||
@@ -937,6 +937,7 @@ def elabAppArgs (f : Expr) (namedArgs : Array NamedArg) (args : Array Arg)
|
||||
where
|
||||
/-- Return `some info` if we should elaborate as an eliminator. -/
|
||||
elabAsElim? : TermElabM (Option ElimInfo) := do
|
||||
unless (← read).heedElabAsElim do return none
|
||||
if explicit || ellipsis then return none
|
||||
let .const declName _ := f | return none
|
||||
unless (← shouldElabAsElim declName) do return none
|
||||
@@ -957,8 +958,7 @@ where
|
||||
The idea is that the contribute to motive inference. See comment at `ElamElim.Context.extraArgsPos`.
|
||||
-/
|
||||
getElabAsElimExtraArgsPos (elimInfo : ElimInfo) : MetaM (Array Nat) := do
|
||||
let cinfo ← getConstInfo elimInfo.name
|
||||
forallTelescope cinfo.type fun xs type => do
|
||||
forallTelescope elimInfo.elimType fun xs type => do
|
||||
let resultArgs := type.getAppArgs
|
||||
let mut extraArgsPos := #[]
|
||||
for i in [:xs.size] do
|
||||
|
||||
@@ -6,6 +6,7 @@ Authors: Leonardo de Moura
|
||||
import Lean.Elab.Quotation.Precheck
|
||||
import Lean.Elab.Term
|
||||
import Lean.Elab.BindersUtil
|
||||
import Lean.Elab.PreDefinition.WF.TerminationHint
|
||||
|
||||
namespace Lean.Elab.Term
|
||||
open Meta
|
||||
@@ -570,7 +571,8 @@ def expandMatchAltsIntoMatchTactic (ref : Syntax) (matchAlts : Syntax) : MacroM
|
||||
-/
|
||||
def expandMatchAltsWhereDecls (matchAltsWhereDecls : Syntax) : MacroM Syntax :=
|
||||
let matchAlts := matchAltsWhereDecls[0]
|
||||
let whereDeclsOpt := matchAltsWhereDecls[1]
|
||||
-- matchAltsWhereDecls[1] is the termination hints, collected elsewhere
|
||||
let whereDeclsOpt := matchAltsWhereDecls[2]
|
||||
let rec loop (i : Nat) (discrs : Array Syntax) : MacroM Syntax :=
|
||||
match i with
|
||||
| 0 => do
|
||||
@@ -668,12 +670,11 @@ def elabLetDeclAux (id : Syntax) (binders : Array Syntax) (typeStx : Syntax) (va
|
||||
let body ← instantiateMVars body
|
||||
mkLetFVars #[x] body (usedLetOnly := usedLetOnly)
|
||||
else
|
||||
let f ← withLocalDecl id.getId (kind := kind) .default type fun x => do
|
||||
withLocalDecl id.getId (kind := kind) .default type fun x => do
|
||||
addLocalVarInfo id x
|
||||
let body ← elabTermEnsuringType body expectedType?
|
||||
let body ← instantiateMVars body
|
||||
mkLambdaFVars #[x] body (usedLetOnly := false)
|
||||
pure <| mkLetFunAnnotation (mkApp f val)
|
||||
mkLetFun x val body
|
||||
if elabBodyFirst then
|
||||
forallBoundedTelescope type binders.size fun xs type => do
|
||||
-- the original `fvars` from above are gone, so add back info manually
|
||||
|
||||
@@ -241,7 +241,8 @@ where
|
||||
|
||||
/--
|
||||
Helper method for elaborating terms such as `(.+.)` where a constant name is expected.
|
||||
This method is usually used to implement tactics that function names as arguments (e.g., `simp`).
|
||||
This method is usually used to implement tactics that take function names as arguments
|
||||
(e.g., `simp`).
|
||||
-/
|
||||
def elabCDotFunctionAlias? (stx : Term) : TermElabM (Option Expr) := do
|
||||
let some stx ← liftMacroM <| expandCDotArg? stx | pure none
|
||||
|
||||
@@ -238,10 +238,11 @@ private def mkInfoTree (elaborator : Name) (stx : Syntax) (trees : PersistentArr
|
||||
let s ← get
|
||||
let scope := s.scopes.head!
|
||||
let tree := InfoTree.node (Info.ofCommandInfo { elaborator, stx }) trees
|
||||
return InfoTree.context {
|
||||
let ctx := PartialContextInfo.commandCtx {
|
||||
env := s.env, fileMap := ctx.fileMap, mctx := {}, currNamespace := scope.currNamespace,
|
||||
openDecls := scope.openDecls, options := scope.opts, ngen := s.ngen
|
||||
} tree
|
||||
}
|
||||
return InfoTree.context ctx tree
|
||||
|
||||
private def elabCommandUsing (s : State) (stx : Syntax) : List (KeyedDeclsAttribute.AttributeEntry CommandElab) → CommandElabM Unit
|
||||
| [] => withInfoTreeContext (mkInfoTree := mkInfoTree `no_elab stx) <| throwError "unexpected syntax{indentD stx}"
|
||||
|
||||
@@ -187,15 +187,6 @@ def elabClassInductive (modifiers : Modifiers) (stx : Syntax) : CommandElabM Uni
|
||||
let v ← classInductiveSyntaxToView modifiers stx
|
||||
elabInductiveViews #[v]
|
||||
|
||||
def getTerminationHints (stx : Syntax) : TerminationHints :=
|
||||
let decl := stx[1]
|
||||
let k := decl.getKind
|
||||
if k == ``Parser.Command.def || k == ``Parser.Command.abbrev || k == ``Parser.Command.theorem || k == ``Parser.Command.instance then
|
||||
let args := decl.getArgs
|
||||
{ terminationBy? := args[args.size - 2]!.getOptional?, decreasingBy? := args[args.size - 1]!.getOptional? }
|
||||
else
|
||||
{}
|
||||
|
||||
@[builtin_command_elab declaration]
|
||||
def elabDeclaration : CommandElab := fun stx => do
|
||||
match (← liftMacroM <| expandDeclNamespace? stx) with
|
||||
@@ -219,7 +210,7 @@ def elabDeclaration : CommandElab := fun stx => do
|
||||
let modifiers ← elabModifiers stx[0]
|
||||
elabStructure modifiers decl
|
||||
else if isDefLike decl then
|
||||
elabMutualDef #[stx] (getTerminationHints stx)
|
||||
elabMutualDef #[stx]
|
||||
else
|
||||
throwError "unexpected declaration"
|
||||
|
||||
@@ -332,21 +323,10 @@ def expandMutualPreamble : Macro := fun stx =>
|
||||
|
||||
@[builtin_command_elab «mutual»]
|
||||
def elabMutual : CommandElab := fun stx => do
|
||||
let hints := { terminationBy? := stx[3].getOptional?, decreasingBy? := stx[4].getOptional? }
|
||||
if isMutualInductive stx then
|
||||
if let some bad := hints.terminationBy? then
|
||||
throwErrorAt bad "invalid 'termination_by' in mutually inductive datatype declaration"
|
||||
if let some bad := hints.decreasingBy? then
|
||||
throwErrorAt bad "invalid 'decreasing_by' in mutually inductive datatype declaration"
|
||||
elabMutualInductive stx[1].getArgs
|
||||
else if isMutualDef stx then
|
||||
for arg in stx[1].getArgs do
|
||||
let argHints := getTerminationHints arg
|
||||
if let some bad := argHints.terminationBy? then
|
||||
throwErrorAt bad "invalid 'termination_by' in 'mutual' block, it must be used after the 'end' keyword"
|
||||
if let some bad := argHints.decreasingBy? then
|
||||
throwErrorAt bad "invalid 'decreasing_by' in 'mutual' block, it must be used after the 'end' keyword"
|
||||
elabMutualDef stx[1].getArgs hints
|
||||
elabMutualDef stx[1].getArgs
|
||||
else
|
||||
throwError "invalid mutual block: either all elements of the block must be inductive declarations, or they must all be definitions/theorems/abbrevs"
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ def mkDefViewOfOpaque (modifiers : Modifiers) (stx : Syntax) : CommandElabM DefV
|
||||
| some val => pure val
|
||||
| none =>
|
||||
let val ← if modifiers.isUnsafe then `(default_or_ofNonempty% unsafe) else `(default_or_ofNonempty%)
|
||||
pure <| mkNode ``Parser.Command.declValSimple #[ mkAtomFrom stx ":=", val ]
|
||||
`(Parser.Command.declValSimple| := $val)
|
||||
return {
|
||||
ref := stx, kind := DefKind.opaque, modifiers := modifiers,
|
||||
declId := stx[1], binders := binders, type? := some type, value := val
|
||||
|
||||
@@ -57,7 +57,10 @@ where
|
||||
let b := mkIdent (← mkFreshUserName `b)
|
||||
ctorArgs1 := ctorArgs1.push a
|
||||
ctorArgs2 := ctorArgs2.push b
|
||||
if (← inferType x).isAppOf indVal.name then
|
||||
let xType ← inferType x
|
||||
if (← isProp xType) then
|
||||
continue
|
||||
if xType.isAppOf indVal.name then
|
||||
rhs ← `($rhs && $(mkIdent auxFunName):ident $a:ident $b:ident)
|
||||
else
|
||||
rhs ← `($rhs && $a:ident == $b:ident)
|
||||
@@ -91,9 +94,9 @@ def mkMutualBlock (ctx : Context) : TermElabM Syntax := do
|
||||
$auxDefs:command*
|
||||
end)
|
||||
|
||||
private def mkBEqInstanceCmds (declNames : Array Name) : TermElabM (Array Syntax) := do
|
||||
let ctx ← mkContext "beq" declNames[0]!
|
||||
let cmds := #[← mkMutualBlock ctx] ++ (← mkInstanceCmds ctx `BEq declNames)
|
||||
private def mkBEqInstanceCmds (declName : Name) : TermElabM (Array Syntax) := do
|
||||
let ctx ← mkContext "beq" declName
|
||||
let cmds := #[← mkMutualBlock ctx] ++ (← mkInstanceCmds ctx `BEq #[declName])
|
||||
trace[Elab.Deriving.beq] "\n{cmds}"
|
||||
return cmds
|
||||
|
||||
@@ -109,14 +112,18 @@ private def mkBEqEnumCmd (name : Name): TermElabM (Array Syntax) := do
|
||||
|
||||
open Command
|
||||
|
||||
def mkBEqInstance (declName : Name) : CommandElabM Unit := do
|
||||
let cmds ← liftTermElabM <|
|
||||
if (← isEnumType declName) then
|
||||
mkBEqEnumCmd declName
|
||||
else
|
||||
mkBEqInstanceCmds declName
|
||||
cmds.forM elabCommand
|
||||
|
||||
def mkBEqInstanceHandler (declNames : Array Name) : CommandElabM Bool := do
|
||||
if declNames.size == 1 && (← isEnumType declNames[0]!) then
|
||||
let cmds ← liftTermElabM <| mkBEqEnumCmd declNames[0]!
|
||||
cmds.forM elabCommand
|
||||
return true
|
||||
else if (← declNames.allM isInductive) && declNames.size > 0 then
|
||||
let cmds ← liftTermElabM <| mkBEqInstanceCmds declNames
|
||||
cmds.forM elabCommand
|
||||
if (← declNames.allM isInductive) then
|
||||
for declName in declNames do
|
||||
mkBEqInstance declName
|
||||
return true
|
||||
else
|
||||
return false
|
||||
|
||||
@@ -67,11 +67,12 @@ where
|
||||
let b := mkIdent (← mkFreshUserName `b)
|
||||
ctorArgs1 := ctorArgs1.push a
|
||||
ctorArgs2 := ctorArgs2.push b
|
||||
let xType ← inferType x
|
||||
let indValNum :=
|
||||
ctx.typeInfos.findIdx?
|
||||
((← inferType x).isAppOf ∘ ConstantVal.name ∘ InductiveVal.toConstantVal)
|
||||
(xType.isAppOf ∘ ConstantVal.name ∘ InductiveVal.toConstantVal)
|
||||
let recField := indValNum.map (ctx.auxFunNames[·]!)
|
||||
let isProof := (← inferType (← inferType x)).isProp
|
||||
let isProof ← isProp xType
|
||||
todo := todo.push (a, b, recField, isProof)
|
||||
patterns := patterns.push (← `(@$(mkIdent ctorName₁):ident $ctorArgs1:term*))
|
||||
patterns := patterns.push (← `(@$(mkIdent ctorName₁):ident $ctorArgs2:term*))
|
||||
@@ -186,12 +187,15 @@ def mkDecEqEnum (declName : Name) : CommandElabM Unit := do
|
||||
trace[Elab.Deriving.decEq] "\n{cmd}"
|
||||
elabCommand cmd
|
||||
|
||||
def mkDecEqInstanceHandler (declNames : Array Name) : CommandElabM Bool := do
|
||||
if (← isEnumType declNames[0]!) then
|
||||
mkDecEqEnum declNames[0]!
|
||||
def mkDecEqInstance (declName : Name) : CommandElabM Bool := do
|
||||
if (← isEnumType declName) then
|
||||
mkDecEqEnum declName
|
||||
return true
|
||||
else
|
||||
mkDecEq declNames[0]!
|
||||
mkDecEq declName
|
||||
|
||||
def mkDecEqInstanceHandler (declNames : Array Name) : CommandElabM Bool := do
|
||||
declNames.foldlM (fun b n => andM (pure b) (mkDecEqInstance n)) true
|
||||
|
||||
builtin_initialize
|
||||
registerDerivingHandler `DecidableEq mkDecEqInstanceHandler
|
||||
|
||||
@@ -19,60 +19,58 @@ def mkJsonField (n : Name) : CoreM (Bool × Term) := do
|
||||
let s₁ := s.dropRightWhile (· == '?')
|
||||
return (s != s₁, Syntax.mkStrLit s₁)
|
||||
|
||||
def mkToJsonInstanceHandler (declNames : Array Name) : CommandElabM Bool := do
|
||||
if declNames.size == 1 then
|
||||
if isStructure (← getEnv) declNames[0]! then
|
||||
let cmds ← liftTermElabM do
|
||||
let ctx ← mkContext "toJson" declNames[0]!
|
||||
let header ← mkHeader ``ToJson 1 ctx.typeInfos[0]!
|
||||
let fields := getStructureFieldsFlattened (← getEnv) declNames[0]! (includeSubobjectFields := false)
|
||||
let fields ← fields.mapM fun field => do
|
||||
let (isOptField, nm) ← mkJsonField field
|
||||
let target := mkIdent header.targetNames[0]!
|
||||
if isOptField then ``(opt $nm ($target).$(mkIdent field))
|
||||
else ``([($nm, toJson ($target).$(mkIdent field))])
|
||||
let cmd ← `(private def $(mkIdent ctx.auxFunNames[0]!):ident $header.binders:bracketedBinder* : Json :=
|
||||
mkObj <| List.join [$fields,*])
|
||||
return #[cmd] ++ (← mkInstanceCmds ctx ``ToJson declNames)
|
||||
cmds.forM elabCommand
|
||||
return true
|
||||
else
|
||||
let indVal ← getConstInfoInduct declNames[0]!
|
||||
let cmds ← liftTermElabM do
|
||||
let ctx ← mkContext "toJson" declNames[0]!
|
||||
let toJsonFuncId := mkIdent ctx.auxFunNames[0]!
|
||||
-- Return syntax to JSONify `id`, either via `ToJson` or recursively
|
||||
-- if `id`'s type is the type we're deriving for.
|
||||
let mkToJson (id : Ident) (type : Expr) : TermElabM Term := do
|
||||
if type.isAppOf indVal.name then `($toJsonFuncId:ident $id:ident)
|
||||
else ``(toJson $id:ident)
|
||||
let header ← mkHeader ``ToJson 1 ctx.typeInfos[0]!
|
||||
let discrs ← mkDiscrs header indVal
|
||||
let alts ← mkAlts indVal fun ctor args userNames => do
|
||||
let ctorStr := ctor.name.eraseMacroScopes.getString!
|
||||
match args, userNames with
|
||||
| #[], _ => ``(toJson $(quote ctorStr))
|
||||
| #[(x, t)], none => ``(mkObj [($(quote ctorStr), $(← mkToJson x t))])
|
||||
| xs, none =>
|
||||
let xs ← xs.mapM fun (x, t) => mkToJson x t
|
||||
``(mkObj [($(quote ctorStr), Json.arr #[$[$xs:term],*])])
|
||||
| xs, some userNames =>
|
||||
let xs ← xs.mapIdxM fun idx (x, t) => do
|
||||
`(($(quote userNames[idx]!.eraseMacroScopes.getString!), $(← mkToJson x t)))
|
||||
``(mkObj [($(quote ctorStr), mkObj [$[$xs:term],*])])
|
||||
let auxTerm ← `(match $[$discrs],* with $alts:matchAlt*)
|
||||
let auxCmd ←
|
||||
if ctx.usePartial then
|
||||
let letDecls ← mkLocalInstanceLetDecls ctx ``ToJson header.argNames
|
||||
let auxTerm ← mkLet letDecls auxTerm
|
||||
`(private partial def $toJsonFuncId:ident $header.binders:bracketedBinder* : Json := $auxTerm)
|
||||
else
|
||||
`(private def $toJsonFuncId:ident $header.binders:bracketedBinder* : Json := $auxTerm)
|
||||
return #[auxCmd] ++ (← mkInstanceCmds ctx ``ToJson declNames)
|
||||
cmds.forM elabCommand
|
||||
return true
|
||||
def mkToJsonInstance (declName : Name) : CommandElabM Bool := do
|
||||
if isStructure (← getEnv) declName then
|
||||
let cmds ← liftTermElabM do
|
||||
let ctx ← mkContext "toJson" declName
|
||||
let header ← mkHeader ``ToJson 1 ctx.typeInfos[0]!
|
||||
let fields := getStructureFieldsFlattened (← getEnv) declName (includeSubobjectFields := false)
|
||||
let fields ← fields.mapM fun field => do
|
||||
let (isOptField, nm) ← mkJsonField field
|
||||
let target := mkIdent header.targetNames[0]!
|
||||
if isOptField then ``(opt $nm ($target).$(mkIdent field))
|
||||
else ``([($nm, toJson ($target).$(mkIdent field))])
|
||||
let cmd ← `(private def $(mkIdent ctx.auxFunNames[0]!):ident $header.binders:bracketedBinder* : Json :=
|
||||
mkObj <| List.join [$fields,*])
|
||||
return #[cmd] ++ (← mkInstanceCmds ctx ``ToJson #[declName])
|
||||
cmds.forM elabCommand
|
||||
return true
|
||||
else
|
||||
return false
|
||||
let indVal ← getConstInfoInduct declName
|
||||
let cmds ← liftTermElabM do
|
||||
let ctx ← mkContext "toJson" declName
|
||||
let toJsonFuncId := mkIdent ctx.auxFunNames[0]!
|
||||
-- Return syntax to JSONify `id`, either via `ToJson` or recursively
|
||||
-- if `id`'s type is the type we're deriving for.
|
||||
let mkToJson (id : Ident) (type : Expr) : TermElabM Term := do
|
||||
if type.isAppOf indVal.name then `($toJsonFuncId:ident $id:ident)
|
||||
else ``(toJson $id:ident)
|
||||
let header ← mkHeader ``ToJson 1 ctx.typeInfos[0]!
|
||||
let discrs ← mkDiscrs header indVal
|
||||
let alts ← mkAlts indVal fun ctor args userNames => do
|
||||
let ctorStr := ctor.name.eraseMacroScopes.getString!
|
||||
match args, userNames with
|
||||
| #[], _ => ``(toJson $(quote ctorStr))
|
||||
| #[(x, t)], none => ``(mkObj [($(quote ctorStr), $(← mkToJson x t))])
|
||||
| xs, none =>
|
||||
let xs ← xs.mapM fun (x, t) => mkToJson x t
|
||||
``(mkObj [($(quote ctorStr), Json.arr #[$[$xs:term],*])])
|
||||
| xs, some userNames =>
|
||||
let xs ← xs.mapIdxM fun idx (x, t) => do
|
||||
`(($(quote userNames[idx]!.eraseMacroScopes.getString!), $(← mkToJson x t)))
|
||||
``(mkObj [($(quote ctorStr), mkObj [$[$xs:term],*])])
|
||||
let auxTerm ← `(match $[$discrs],* with $alts:matchAlt*)
|
||||
let auxCmd ←
|
||||
if ctx.usePartial then
|
||||
let letDecls ← mkLocalInstanceLetDecls ctx ``ToJson header.argNames
|
||||
let auxTerm ← mkLet letDecls auxTerm
|
||||
`(private partial def $toJsonFuncId:ident $header.binders:bracketedBinder* : Json := $auxTerm)
|
||||
else
|
||||
`(private def $toJsonFuncId:ident $header.binders:bracketedBinder* : Json := $auxTerm)
|
||||
return #[auxCmd] ++ (← mkInstanceCmds ctx ``ToJson #[declName])
|
||||
cmds.forM elabCommand
|
||||
return true
|
||||
|
||||
where
|
||||
mkAlts
|
||||
(indVal : InductiveVal)
|
||||
@@ -103,54 +101,51 @@ where
|
||||
let rhs ← rhs ctorInfo binders (if userNames.size == binders.size then some userNames else none)
|
||||
`(matchAltExpr| | $[$patterns:term],* => $rhs:term)
|
||||
|
||||
def mkFromJsonInstanceHandler (declNames : Array Name) : CommandElabM Bool := do
|
||||
if declNames.size == 1 then
|
||||
let declName := declNames[0]!
|
||||
if isStructure (← getEnv) declName then
|
||||
let cmds ← liftTermElabM do
|
||||
let ctx ← mkContext "fromJson" declName
|
||||
let header ← mkHeader ``FromJson 0 ctx.typeInfos[0]!
|
||||
let fields := getStructureFieldsFlattened (← getEnv) declName (includeSubobjectFields := false)
|
||||
let getters ← fields.mapM (fun field => do
|
||||
let getter ← `(getObjValAs? j _ $(Prod.snd <| ← mkJsonField field))
|
||||
let getter ← `(doElem| Except.mapError (fun s => (toString $(quote declName)) ++ "." ++ (toString $(quote field)) ++ ": " ++ s) <| $getter)
|
||||
return getter
|
||||
)
|
||||
let fields := fields.map mkIdent
|
||||
let cmd ← `(private def $(mkIdent ctx.auxFunNames[0]!):ident $header.binders:bracketedBinder* (j : Json)
|
||||
: Except String $(← mkInductiveApp ctx.typeInfos[0]! header.argNames) := do
|
||||
$[let $fields:ident ← $getters]*
|
||||
return { $[$fields:ident := $(id fields)],* })
|
||||
return #[cmd] ++ (← mkInstanceCmds ctx ``FromJson declNames)
|
||||
cmds.forM elabCommand
|
||||
return true
|
||||
else
|
||||
let indVal ← getConstInfoInduct declName
|
||||
let cmds ← liftTermElabM do
|
||||
let ctx ← mkContext "fromJson" declName
|
||||
let header ← mkHeader ``FromJson 0 ctx.typeInfos[0]!
|
||||
let fromJsonFuncId := mkIdent ctx.auxFunNames[0]!
|
||||
let alts ← mkAlts indVal fromJsonFuncId
|
||||
let mut auxTerm ← alts.foldrM (fun xs x => `(Except.orElseLazy $xs (fun _ => $x))) (← `(Except.error "no inductive constructor matched"))
|
||||
if ctx.usePartial then
|
||||
let letDecls ← mkLocalInstanceLetDecls ctx ``FromJson header.argNames
|
||||
auxTerm ← mkLet letDecls auxTerm
|
||||
-- FromJson is not structurally recursive even non-nested recursive inductives,
|
||||
-- so we also use `partial` then.
|
||||
let auxCmd ←
|
||||
if ctx.usePartial || indVal.isRec then
|
||||
`(private partial def $fromJsonFuncId:ident $header.binders:bracketedBinder* (json : Json)
|
||||
: Except String $(← mkInductiveApp ctx.typeInfos[0]! header.argNames) :=
|
||||
$auxTerm)
|
||||
else
|
||||
`(private def $fromJsonFuncId:ident $header.binders:bracketedBinder* (json : Json)
|
||||
: Except String $(← mkInductiveApp ctx.typeInfos[0]! header.argNames) :=
|
||||
$auxTerm)
|
||||
return #[auxCmd] ++ (← mkInstanceCmds ctx ``FromJson declNames)
|
||||
cmds.forM elabCommand
|
||||
return true
|
||||
def mkFromJsonInstance (declName : Name) : CommandElabM Bool := do
|
||||
if isStructure (← getEnv) declName then
|
||||
let cmds ← liftTermElabM do
|
||||
let ctx ← mkContext "fromJson" declName
|
||||
let header ← mkHeader ``FromJson 0 ctx.typeInfos[0]!
|
||||
let fields := getStructureFieldsFlattened (← getEnv) declName (includeSubobjectFields := false)
|
||||
let getters ← fields.mapM (fun field => do
|
||||
let getter ← `(getObjValAs? j _ $(Prod.snd <| ← mkJsonField field))
|
||||
let getter ← `(doElem| Except.mapError (fun s => (toString $(quote declName)) ++ "." ++ (toString $(quote field)) ++ ": " ++ s) <| $getter)
|
||||
return getter
|
||||
)
|
||||
let fields := fields.map mkIdent
|
||||
let cmd ← `(private def $(mkIdent ctx.auxFunNames[0]!):ident $header.binders:bracketedBinder* (j : Json)
|
||||
: Except String $(← mkInductiveApp ctx.typeInfos[0]! header.argNames) := do
|
||||
$[let $fields:ident ← $getters]*
|
||||
return { $[$fields:ident := $(id fields)],* })
|
||||
return #[cmd] ++ (← mkInstanceCmds ctx ``FromJson #[declName])
|
||||
cmds.forM elabCommand
|
||||
return true
|
||||
else
|
||||
return false
|
||||
let indVal ← getConstInfoInduct declName
|
||||
let cmds ← liftTermElabM do
|
||||
let ctx ← mkContext "fromJson" declName
|
||||
let header ← mkHeader ``FromJson 0 ctx.typeInfos[0]!
|
||||
let fromJsonFuncId := mkIdent ctx.auxFunNames[0]!
|
||||
let alts ← mkAlts indVal fromJsonFuncId
|
||||
let mut auxTerm ← alts.foldrM (fun xs x => `(Except.orElseLazy $xs (fun _ => $x))) (← `(Except.error "no inductive constructor matched"))
|
||||
if ctx.usePartial then
|
||||
let letDecls ← mkLocalInstanceLetDecls ctx ``FromJson header.argNames
|
||||
auxTerm ← mkLet letDecls auxTerm
|
||||
-- FromJson is not structurally recursive even non-nested recursive inductives,
|
||||
-- so we also use `partial` then.
|
||||
let auxCmd ←
|
||||
if ctx.usePartial || indVal.isRec then
|
||||
`(private partial def $fromJsonFuncId:ident $header.binders:bracketedBinder* (json : Json)
|
||||
: Except String $(← mkInductiveApp ctx.typeInfos[0]! header.argNames) :=
|
||||
$auxTerm)
|
||||
else
|
||||
`(private def $fromJsonFuncId:ident $header.binders:bracketedBinder* (json : Json)
|
||||
: Except String $(← mkInductiveApp ctx.typeInfos[0]! header.argNames) :=
|
||||
$auxTerm)
|
||||
return #[auxCmd] ++ (← mkInstanceCmds ctx ``FromJson #[declName])
|
||||
cmds.forM elabCommand
|
||||
return true
|
||||
|
||||
where
|
||||
mkAlts (indVal : InductiveVal) (fromJsonFuncId : Ident) : TermElabM (Array Term) := do
|
||||
let alts ←
|
||||
@@ -188,6 +183,12 @@ where
|
||||
let alts := alts.qsort (fun (_, x) (_, y) => x < y)
|
||||
return alts.map Prod.fst
|
||||
|
||||
def mkToJsonInstanceHandler (declNames : Array Name) : CommandElabM Bool := do
|
||||
declNames.foldlM (fun b n => andM (pure b) (mkToJsonInstance n)) true
|
||||
|
||||
def mkFromJsonInstanceHandler (declNames : Array Name) : CommandElabM Bool := do
|
||||
declNames.foldlM (fun b n => andM (pure b) (mkFromJsonInstance n)) true
|
||||
|
||||
builtin_initialize
|
||||
registerDerivingHandler ``ToJson mkToJsonInstanceHandler
|
||||
registerDerivingHandler ``FromJson mkFromJsonInstanceHandler
|
||||
|
||||
@@ -75,16 +75,17 @@ def mkHashFuncs (ctx : Context) : TermElabM Syntax := do
|
||||
auxDefs := auxDefs.push (← mkAuxFunction ctx i)
|
||||
`(mutual $auxDefs:command* end)
|
||||
|
||||
private def mkHashableInstanceCmds (declNames : Array Name) : TermElabM (Array Syntax) := do
|
||||
let ctx ← mkContext "hash" declNames[0]!
|
||||
let cmds := #[← mkHashFuncs ctx] ++ (← mkInstanceCmds ctx `Hashable declNames)
|
||||
private def mkHashableInstanceCmds (declName : Name) : TermElabM (Array Syntax) := do
|
||||
let ctx ← mkContext "hash" declName
|
||||
let cmds := #[← mkHashFuncs ctx] ++ (← mkInstanceCmds ctx `Hashable #[declName])
|
||||
trace[Elab.Deriving.hashable] "\n{cmds}"
|
||||
return cmds
|
||||
|
||||
def mkHashableHandler (declNames : Array Name) : CommandElabM Bool := do
|
||||
if (← declNames.allM isInductive) && declNames.size > 0 then
|
||||
let cmds ← liftTermElabM <| mkHashableInstanceCmds declNames
|
||||
cmds.forM elabCommand
|
||||
if (← declNames.allM isInductive) then
|
||||
for declName in declNames do
|
||||
let cmds ← liftTermElabM <| mkHashableInstanceCmds declName
|
||||
cmds.forM elabCommand
|
||||
return true
|
||||
else
|
||||
return false
|
||||
|
||||
@@ -86,18 +86,19 @@ def mkMutualBlock (ctx : Context) : TermElabM Syntax := do
|
||||
$auxDefs:command*
|
||||
end)
|
||||
|
||||
private def mkOrdInstanceCmds (declNames : Array Name) : TermElabM (Array Syntax) := do
|
||||
let ctx ← mkContext "ord" declNames[0]!
|
||||
let cmds := #[← mkMutualBlock ctx] ++ (← mkInstanceCmds ctx `Ord declNames)
|
||||
private def mkOrdInstanceCmds (declName : Name) : TermElabM (Array Syntax) := do
|
||||
let ctx ← mkContext "ord" declName
|
||||
let cmds := #[← mkMutualBlock ctx] ++ (← mkInstanceCmds ctx `Ord #[declName])
|
||||
trace[Elab.Deriving.ord] "\n{cmds}"
|
||||
return cmds
|
||||
|
||||
open Command
|
||||
|
||||
def mkOrdInstanceHandler (declNames : Array Name) : CommandElabM Bool := do
|
||||
if (← declNames.allM isInductive) && declNames.size > 0 then
|
||||
let cmds ← liftTermElabM <| mkOrdInstanceCmds declNames
|
||||
cmds.forM elabCommand
|
||||
if (← declNames.allM isInductive) then
|
||||
for declName in declNames do
|
||||
let cmds ← liftTermElabM <| mkOrdInstanceCmds declName
|
||||
cmds.forM elabCommand
|
||||
return true
|
||||
else
|
||||
return false
|
||||
|
||||
@@ -104,18 +104,19 @@ def mkMutualBlock (ctx : Context) : TermElabM Syntax := do
|
||||
$auxDefs:command*
|
||||
end)
|
||||
|
||||
private def mkReprInstanceCmds (declNames : Array Name) : TermElabM (Array Syntax) := do
|
||||
let ctx ← mkContext "repr" declNames[0]!
|
||||
let cmds := #[← mkMutualBlock ctx] ++ (← mkInstanceCmds ctx `Repr declNames)
|
||||
private def mkReprInstanceCmd (declName : Name) : TermElabM (Array Syntax) := do
|
||||
let ctx ← mkContext "repr" declName
|
||||
let cmds := #[← mkMutualBlock ctx] ++ (← mkInstanceCmds ctx `Repr #[declName])
|
||||
trace[Elab.Deriving.repr] "\n{cmds}"
|
||||
return cmds
|
||||
|
||||
open Command
|
||||
|
||||
def mkReprInstanceHandler (declNames : Array Name) : CommandElabM Bool := do
|
||||
if (← declNames.allM isInductive) && declNames.size > 0 then
|
||||
let cmds ← liftTermElabM <| mkReprInstanceCmds declNames
|
||||
cmds.forM elabCommand
|
||||
if (← declNames.allM isInductive) then
|
||||
for declName in declNames do
|
||||
let cmds ← liftTermElabM <| mkReprInstanceCmd declName
|
||||
cmds.forM elabCommand
|
||||
return true
|
||||
else
|
||||
return false
|
||||
|
||||
@@ -16,8 +16,9 @@ namespace Lean.Elab.Deriving.SizeOf
|
||||
open Command
|
||||
|
||||
def mkSizeOfHandler (declNames : Array Name) : CommandElabM Bool := do
|
||||
if (← declNames.allM isInductive) && declNames.size > 0 then
|
||||
liftTermElabM <| Meta.mkSizeOfInstances declNames[0]!
|
||||
if (← declNames.allM isInductive) then
|
||||
for declName in declNames do
|
||||
liftTermElabM <| Meta.mkSizeOfInstances declName
|
||||
return true
|
||||
else
|
||||
return false
|
||||
|
||||
@@ -1368,7 +1368,7 @@ mutual
|
||||
else
|
||||
pure doElems.toArray
|
||||
let contSeq := mkDoSeq contSeq
|
||||
let auxDo ← `(do let __discr := $val; match __discr with | $pattern:term => $contSeq | _ => $elseSeq)
|
||||
let auxDo ← `(do match $val:term with | $pattern:term => $contSeq | _ => $elseSeq)
|
||||
doSeqToCode <| getDoSeqElems (getDoSeq auxDo)
|
||||
|
||||
/-- Generate `CodeBlock` for `doReassignArrow; doElems`
|
||||
|
||||
@@ -118,7 +118,7 @@ def runFrontend
|
||||
if let some ileanFileName := ileanFileName? then
|
||||
let trees := s.commandState.infoState.trees.toArray
|
||||
let references := Lean.Server.findModuleRefs inputCtx.fileMap trees (localVars := false)
|
||||
let ilean := { module := mainModuleName, references : Lean.Server.Ilean }
|
||||
let ilean := { module := mainModuleName, references := ← references.toLspModuleRefs : Lean.Server.Ilean }
|
||||
IO.FS.writeFile ileanFileName $ Json.compress $ toJson ilean
|
||||
|
||||
pure (s.commandState.env, !s.commandState.messages.hasErrors)
|
||||
|
||||
@@ -6,11 +6,11 @@ Authors: Wojciech Nawrocki, Leonardo de Moura, Sebastian Ullrich
|
||||
-/
|
||||
import Lean.Meta.PPGoal
|
||||
|
||||
namespace Lean.Elab.ContextInfo
|
||||
namespace Lean.Elab.CommandContextInfo
|
||||
|
||||
variable [Monad m] [MonadEnv m] [MonadMCtx m] [MonadOptions m] [MonadResolveName m] [MonadNameGenerator m]
|
||||
|
||||
def saveNoFileMap : m ContextInfo := return {
|
||||
def saveNoFileMap : m CommandContextInfo := return {
|
||||
env := (← getEnv)
|
||||
fileMap := default
|
||||
mctx := (← getMCtx)
|
||||
@@ -20,11 +20,32 @@ def saveNoFileMap : m ContextInfo := return {
|
||||
ngen := (← getNGen)
|
||||
}
|
||||
|
||||
def save [MonadFileMap m] : m ContextInfo := do
|
||||
def save [MonadFileMap m] : m CommandContextInfo := do
|
||||
let ctx ← saveNoFileMap
|
||||
return { ctx with fileMap := (← getFileMap) }
|
||||
|
||||
end ContextInfo
|
||||
end CommandContextInfo
|
||||
|
||||
/--
|
||||
Merges the `inner` partial context into the `outer` context s.t. fields of the `inner` context
|
||||
overwrite fields of the `outer` context. Panics if the invariant described in the documentation
|
||||
for `PartialContextInfo` is violated.
|
||||
|
||||
When traversing an `InfoTree`, this function should be used to combine the context of outer
|
||||
nodes with the partial context of their subtrees. This ensures that the traversal has the context
|
||||
from the inner node to the root node of the `InfoTree` available, with partial contexts of
|
||||
inner nodes taking priority over contexts of outer nodes.
|
||||
-/
|
||||
def PartialContextInfo.mergeIntoOuter?
|
||||
: (inner : PartialContextInfo) → (outer? : Option ContextInfo) → Option ContextInfo
|
||||
| .commandCtx info, none =>
|
||||
some { info with }
|
||||
| .parentDeclCtx _, none =>
|
||||
panic! "Unexpected incomplete InfoTree context info."
|
||||
| .commandCtx innerInfo, some outer =>
|
||||
some { outer with toCommandContextInfo := innerInfo }
|
||||
| .parentDeclCtx innerParentDecl, some outer =>
|
||||
some { outer with parentDecl? := innerParentDecl }
|
||||
|
||||
def CompletionInfo.stx : CompletionInfo → Syntax
|
||||
| dot i .. => i.stx
|
||||
@@ -142,7 +163,7 @@ def MacroExpansionInfo.format (ctx : ContextInfo) (info : MacroExpansionInfo) :
|
||||
return f!"Macro expansion\n{stx}\n===>\n{output}"
|
||||
|
||||
def UserWidgetInfo.format (info : UserWidgetInfo) : Format :=
|
||||
f!"UserWidget {info.widgetId}\n{Std.ToFormat.format info.props}"
|
||||
f!"UserWidget {info.id}\n{Std.ToFormat.format <| info.props.run' {}}"
|
||||
|
||||
def FVarAliasInfo.format (info : FVarAliasInfo) : Format :=
|
||||
f!"FVarAlias {info.userName.eraseMacroScopes}"
|
||||
@@ -150,6 +171,9 @@ def FVarAliasInfo.format (info : FVarAliasInfo) : Format :=
|
||||
def FieldRedeclInfo.format (ctx : ContextInfo) (info : FieldRedeclInfo) : Format :=
|
||||
f!"FieldRedecl @ {formatStxRange ctx info.stx}"
|
||||
|
||||
def OmissionInfo.format (ctx : ContextInfo) (info : OmissionInfo) : IO Format := do
|
||||
return f!"Omission @ {← TermInfo.format ctx info.toTermInfo}"
|
||||
|
||||
def Info.format (ctx : ContextInfo) : Info → IO Format
|
||||
| ofTacticInfo i => i.format ctx
|
||||
| ofTermInfo i => i.format ctx
|
||||
@@ -162,6 +186,7 @@ def Info.format (ctx : ContextInfo) : Info → IO Format
|
||||
| ofCustomInfo i => pure <| Std.ToFormat.format i
|
||||
| ofFVarAliasInfo i => pure <| i.format
|
||||
| ofFieldRedeclInfo i => pure <| i.format ctx
|
||||
| ofOmissionInfo i => i.format ctx
|
||||
|
||||
def Info.toElabInfo? : Info → Option ElabInfo
|
||||
| ofTacticInfo i => some i.toElabInfo
|
||||
@@ -175,6 +200,7 @@ def Info.toElabInfo? : Info → Option ElabInfo
|
||||
| ofCustomInfo _ => none
|
||||
| ofFVarAliasInfo _ => none
|
||||
| ofFieldRedeclInfo _ => none
|
||||
| ofOmissionInfo i => some i.toElabInfo
|
||||
|
||||
/--
|
||||
Helper function for propagating the tactic metavariable context to its children nodes.
|
||||
@@ -197,7 +223,7 @@ def Info.updateContext? : Option ContextInfo → Info → Option ContextInfo
|
||||
partial def InfoTree.format (tree : InfoTree) (ctx? : Option ContextInfo := none) : IO Format := do
|
||||
match tree with
|
||||
| hole id => return .nestD f!"• ?{toString id.name}"
|
||||
| context i t => format t i
|
||||
| context i t => format t <| i.mergeIntoOuter? ctx?
|
||||
| node i cs => match ctx? with
|
||||
| none => return "• <context-not-available>"
|
||||
| some ctx =>
|
||||
@@ -308,20 +334,52 @@ def withInfoTreeContext [MonadFinally m] (x : m α) (mkInfoTree : PersistentArra
|
||||
@[inline] def withInfoContext [MonadFinally m] (x : m α) (mkInfo : m Info) : m α := do
|
||||
withInfoTreeContext x (fun trees => do return InfoTree.node (← mkInfo) trees)
|
||||
|
||||
/-- Resets the trees state `t₀`, runs `x` to produce a new trees
|
||||
state `t₁` and sets the state to be `t₀ ++ (InfoTree.context Γ <$> t₁)`
|
||||
where `Γ` is the context derived from the monad state. -/
|
||||
def withSaveInfoContext [MonadNameGenerator m] [MonadFinally m] [MonadEnv m] [MonadOptions m] [MonadMCtx m] [MonadResolveName m] [MonadFileMap m] (x : m α) : m α := do
|
||||
if (← getInfoState).enabled then
|
||||
let treesSaved ← getResetInfoTrees
|
||||
Prod.fst <$> MonadFinally.tryFinally' x fun _ => do
|
||||
let st ← getInfoState
|
||||
let trees ← st.trees.mapM fun tree => do
|
||||
let tree := tree.substitute st.assignment
|
||||
pure <| InfoTree.context (← ContextInfo.save) tree
|
||||
modifyInfoTrees fun _ => treesSaved ++ trees
|
||||
else
|
||||
x
|
||||
private def withSavedPartialInfoContext [MonadFinally m]
|
||||
(x : m α)
|
||||
(ctx? : m (Option PartialContextInfo))
|
||||
: m α := do
|
||||
if !(← getInfoState).enabled then
|
||||
return ← x
|
||||
let treesSaved ← getResetInfoTrees
|
||||
Prod.fst <$> MonadFinally.tryFinally' x fun _ => do
|
||||
let st ← getInfoState
|
||||
let trees ← st.trees.mapM fun tree => do
|
||||
let tree := tree.substitute st.assignment
|
||||
match (← ctx?) with
|
||||
| none =>
|
||||
pure tree
|
||||
| some ctx =>
|
||||
pure <| InfoTree.context ctx tree
|
||||
modifyInfoTrees fun _ => treesSaved ++ trees
|
||||
|
||||
/--
|
||||
Resets the trees state `t₀`, runs `x` to produce a new trees state `t₁` and sets the state to be
|
||||
`t₀ ++ (InfoTree.context (PartialContextInfo.commandCtx Γ) <$> t₁)` where `Γ` is the context derived
|
||||
from the monad state.
|
||||
-/
|
||||
def withSaveInfoContext
|
||||
[MonadNameGenerator m]
|
||||
[MonadFinally m]
|
||||
[MonadEnv m]
|
||||
[MonadOptions m]
|
||||
[MonadMCtx m]
|
||||
[MonadResolveName m]
|
||||
[MonadFileMap m]
|
||||
(x : m α)
|
||||
: m α := do
|
||||
withSavedPartialInfoContext x do
|
||||
return some <| .commandCtx (← CommandContextInfo.save)
|
||||
|
||||
/--
|
||||
Resets the trees state `t₀`, runs `x` to produce a new trees state `t₁` and sets the state to be
|
||||
`t₀ ++ (InfoTree.context (PartialContextInfo.parentDeclCtx Γ) <$> t₁)` where `Γ` is the parent decl
|
||||
name provided by `MonadParentDecl m`.
|
||||
-/
|
||||
def withSaveParentDeclInfoContext [MonadFinally m] [MonadParentDecl m] (x : m α) : m α := do
|
||||
withSavedPartialInfoContext x do
|
||||
let some declName ← getParentDeclName?
|
||||
| return none
|
||||
return some <| .parentDeclCtx declName
|
||||
|
||||
def getInfoHoleIdAssignment? (mvarId : MVarId) : m (Option InfoTree) :=
|
||||
return (← getInfoState).assignment[mvarId]
|
||||
|
||||
@@ -9,13 +9,17 @@ import Lean.Data.OpenDecl
|
||||
import Lean.MetavarContext
|
||||
import Lean.Environment
|
||||
import Lean.Data.Json
|
||||
import Lean.Server.Rpc.Basic
|
||||
import Lean.Widget.Types
|
||||
|
||||
namespace Lean.Elab
|
||||
|
||||
/-- Context after executing `liftTermElabM`.
|
||||
Note that the term information collected during elaboration may contain metavariables, and their
|
||||
assignments are stored at `mctx`. -/
|
||||
structure ContextInfo where
|
||||
/--
|
||||
Context after executing `liftTermElabM`.
|
||||
Note that the term information collected during elaboration may contain metavariables, and their
|
||||
assignments are stored at `mctx`.
|
||||
-/
|
||||
structure CommandContextInfo where
|
||||
env : Environment
|
||||
fileMap : FileMap
|
||||
mctx : MetavarContext := {}
|
||||
@@ -24,6 +28,31 @@ structure ContextInfo where
|
||||
openDecls : List OpenDecl := []
|
||||
ngen : NameGenerator -- We must save the name generator to implement `ContextInfo.runMetaM` and making we not create `MVarId`s used in `mctx`.
|
||||
|
||||
/--
|
||||
Context from the root of the `InfoTree` up to this node.
|
||||
Note that the term information collected during elaboration may contain metavariables, and their
|
||||
assignments are stored at `mctx`.
|
||||
-/
|
||||
structure ContextInfo extends CommandContextInfo where
|
||||
parentDecl? : Option Name := none
|
||||
|
||||
/--
|
||||
Context for a sub-`InfoTree`.
|
||||
|
||||
Within `InfoTree`, this must fulfill the invariant that every non-`commandCtx` `PartialContextInfo`
|
||||
node is always contained within a `commandCtx` node.
|
||||
-/
|
||||
inductive PartialContextInfo where
|
||||
| commandCtx (info : CommandContextInfo)
|
||||
/--
|
||||
Context for the name of the declaration that surrounds nodes contained within this `context` node.
|
||||
For example, this makes the name of the surrounding declaration available in `InfoTree` nodes
|
||||
corresponding to the terms within the declaration.
|
||||
-/
|
||||
| parentDeclCtx (parentDecl : Name)
|
||||
-- TODO: More constructors for the different kinds of scopes `commandCtx` is currently
|
||||
-- used for (e.g. eliminating `Info.updateContext?` would be nice!).
|
||||
|
||||
/-- Base structure for `TermInfo`, `CommandInfo` and `TacticInfo`. -/
|
||||
structure ElabInfo where
|
||||
/-- The name of the elaborator that created this info. -/
|
||||
@@ -95,17 +124,12 @@ structure CustomInfo where
|
||||
stx : Syntax
|
||||
value : Dynamic
|
||||
|
||||
/-- An info that represents a user-widget.
|
||||
User-widgets are custom pieces of code that run on the editor client.
|
||||
You can learn about user widgets at `src/Lean/Widget/UserWidget`
|
||||
-/
|
||||
structure UserWidgetInfo where
|
||||
/-- Information about a user widget associated with a syntactic span.
|
||||
This must be a panel widget.
|
||||
A panel widget is a widget that can be displayed
|
||||
in the infoview alongside the goal state. -/
|
||||
structure UserWidgetInfo extends Widget.WidgetInstance where
|
||||
stx : Syntax
|
||||
/-- Id of `WidgetSource` object to use. -/
|
||||
widgetId : Name
|
||||
/-- Json representing the props to be loaded in to the component. -/
|
||||
props : Json
|
||||
deriving Inhabited
|
||||
|
||||
/--
|
||||
Specifies that the given free variables should be considered semantically identical.
|
||||
@@ -130,6 +154,15 @@ structure Bar extends Foo :=
|
||||
structure FieldRedeclInfo where
|
||||
stx : Syntax
|
||||
|
||||
/--
|
||||
Denotes information for the term `⋯` that is emitted by the delaborator when omitting a term
|
||||
due to `pp.deepTerms false`. Omission needs to be treated differently from regular terms because
|
||||
it has to be delaborated differently in `Lean.Widget.InteractiveDiagnostics.infoToInteractive`:
|
||||
Regular terms are delaborated explicitly, whereas omitted terms are simply to be expanded with
|
||||
regular delaboration settings.
|
||||
-/
|
||||
structure OmissionInfo extends TermInfo
|
||||
|
||||
/-- Header information for a node in `InfoTree`. -/
|
||||
inductive Info where
|
||||
| ofTacticInfo (i : TacticInfo)
|
||||
@@ -143,6 +176,7 @@ inductive Info where
|
||||
| ofCustomInfo (i : CustomInfo)
|
||||
| ofFVarAliasInfo (i : FVarAliasInfo)
|
||||
| ofFieldRedeclInfo (i : FieldRedeclInfo)
|
||||
| ofOmissionInfo (i : OmissionInfo)
|
||||
deriving Inhabited
|
||||
|
||||
/-- The InfoTree is a structure that is generated during elaboration and used
|
||||
@@ -167,8 +201,8 @@ inductive Info where
|
||||
`hole`s which are filled in later in the same way that unassigned metavariables are.
|
||||
-/
|
||||
inductive InfoTree where
|
||||
/-- The context object is created by `liftTermElabM` at `Command.lean` -/
|
||||
| context (i : ContextInfo) (t : InfoTree)
|
||||
/-- The context object is created at appropriate points during elaboration -/
|
||||
| context (i : PartialContextInfo) (t : InfoTree)
|
||||
/-- The children contain information for nested term elaboration and tactic evaluation -/
|
||||
| node (i : Info) (children : PersistentArray InfoTree)
|
||||
/-- The elaborator creates holes (aka metavariables) for tactics and postponed terms -/
|
||||
@@ -194,7 +228,7 @@ structure InfoState where
|
||||
trees : PersistentArray InfoTree := {}
|
||||
deriving Inhabited
|
||||
|
||||
class MonadInfoTree (m : Type → Type) where
|
||||
class MonadInfoTree (m : Type → Type) where
|
||||
getInfoState : m InfoState
|
||||
modifyInfoState : (InfoState → InfoState) → m Unit
|
||||
|
||||
@@ -207,4 +241,9 @@ instance [MonadLift m n] [MonadInfoTree m] : MonadInfoTree n where
|
||||
def setInfoState [MonadInfoTree m] (s : InfoState) : m Unit :=
|
||||
modifyInfoState fun _ => s
|
||||
|
||||
class MonadParentDecl (m : Type → Type) where
|
||||
getParentDeclName? : m (Option Name)
|
||||
|
||||
export MonadParentDecl (getParentDeclName?)
|
||||
|
||||
end Lean.Elab
|
||||
|
||||
@@ -21,6 +21,7 @@ structure LetRecDeclView where
|
||||
type : Expr
|
||||
mvar : Expr -- auxiliary metavariable used to lift the 'let rec'
|
||||
valStx : Syntax
|
||||
termination : WF.TerminationHints
|
||||
|
||||
structure LetRecView where
|
||||
decls : Array LetRecDeclView
|
||||
@@ -59,7 +60,9 @@ private def mkLetRecDeclView (letRec : Syntax) : TermElabM LetRecView := do
|
||||
pure decl[4]
|
||||
else
|
||||
liftMacroM <| expandMatchAltsIntoMatch decl decl[3]
|
||||
pure { ref := declId, attrs, shortDeclName, declName, binderIds, type, mvar, valStx : LetRecDeclView }
|
||||
let termination ← WF.elabTerminationHints ⟨attrDeclStx[3]⟩
|
||||
pure { ref := declId, attrs, shortDeclName, declName, binderIds, type, mvar, valStx,
|
||||
termination : LetRecDeclView }
|
||||
else
|
||||
throwUnsupportedSyntax
|
||||
return { decls, body := letRec[3] }
|
||||
@@ -91,18 +94,23 @@ private def registerLetRecsToLift (views : Array LetRecDeclView) (fvars : Array
|
||||
throwError "'{view.declName}' has already been declared"
|
||||
let lctx ← getLCtx
|
||||
let localInstances ← getLocalInstances
|
||||
let toLift := views.mapIdx fun i view => {
|
||||
ref := view.ref
|
||||
fvarId := fvars[i]!.fvarId!
|
||||
attrs := view.attrs
|
||||
shortDeclName := view.shortDeclName
|
||||
declName := view.declName
|
||||
lctx
|
||||
localInstances
|
||||
type := view.type
|
||||
val := values[i]!
|
||||
mvarId := view.mvar.mvarId!
|
||||
: LetRecToLift }
|
||||
|
||||
let toLift ← views.mapIdxM fun i view => do
|
||||
let value := values[i]!
|
||||
let termination ← view.termination.checkVars view.binderIds.size value
|
||||
pure {
|
||||
ref := view.ref
|
||||
fvarId := fvars[i]!.fvarId!
|
||||
attrs := view.attrs
|
||||
shortDeclName := view.shortDeclName
|
||||
declName := view.declName
|
||||
lctx
|
||||
localInstances
|
||||
type := view.type
|
||||
val := value
|
||||
mvarId := view.mvar.mvarId!
|
||||
termination := termination
|
||||
: LetRecToLift }
|
||||
modify fun s => { s with letRecsToLift := toLift.toList ++ s.letRecsToLift }
|
||||
|
||||
@[builtin_term_elab «letrec»] def elabLetRec : TermElab := fun stx expectedType? => do
|
||||
|
||||
@@ -13,6 +13,7 @@ import Lean.Elab.Match
|
||||
import Lean.Elab.DefView
|
||||
import Lean.Elab.Deriving.Basic
|
||||
import Lean.Elab.PreDefinition.Main
|
||||
import Lean.Elab.PreDefinition.WF.TerminationHint
|
||||
import Lean.Elab.DeclarationRange
|
||||
|
||||
namespace Lean.Elab
|
||||
@@ -221,14 +222,16 @@ private def expandWhereStructInst : Macro
|
||||
/-
|
||||
Recall that
|
||||
```
|
||||
def declValSimple := leading_parser " :=\n" >> termParser >> optional Term.whereDecls
|
||||
def declValSimple := leading_parser " :=\n" >> termParser >> Termination.suffix >> optional Term.whereDecls
|
||||
def declValEqns := leading_parser Term.matchAltsWhereDecls
|
||||
def declVal := declValSimple <|> declValEqns <|> Term.whereDecls
|
||||
```
|
||||
|
||||
The `Termination.suffix` is ignored here, and extracted in `declValToTerminationHint`.
|
||||
-/
|
||||
private def declValToTerm (declVal : Syntax) : MacroM Syntax := withRef declVal do
|
||||
if declVal.isOfKind ``Parser.Command.declValSimple then
|
||||
expandWhereDeclsOpt declVal[2] declVal[1]
|
||||
expandWhereDeclsOpt declVal[3] declVal[1]
|
||||
else if declVal.isOfKind ``Parser.Command.declValEqns then
|
||||
expandMatchAltsWhereDecls declVal[0]
|
||||
else if declVal.isOfKind ``Parser.Command.whereStructInst then
|
||||
@@ -238,6 +241,15 @@ private def declValToTerm (declVal : Syntax) : MacroM Syntax := withRef declVal
|
||||
else
|
||||
Macro.throwErrorAt declVal "unexpected declaration body"
|
||||
|
||||
/-- Elaborates the termination hints in a `declVal` syntax. -/
|
||||
private def declValToTerminationHint (declVal : Syntax) : TermElabM WF.TerminationHints :=
|
||||
if declVal.isOfKind ``Parser.Command.declValSimple then
|
||||
WF.elabTerminationHints ⟨declVal[2]⟩
|
||||
else if declVal.isOfKind ``Parser.Command.declValEqns then
|
||||
WF.elabTerminationHints ⟨declVal[0][1]⟩
|
||||
else
|
||||
return .none
|
||||
|
||||
private def elabFunValues (headers : Array DefViewElabHeader) : TermElabM (Array Expr) :=
|
||||
headers.mapM fun header => withDeclName header.declName <| withLevelNames header.levelNames do
|
||||
let valStx ← liftMacroM <| declValToTerm header.valueStx
|
||||
@@ -629,6 +641,8 @@ def pushMain (preDefs : Array PreDefinition) (sectionVars : Array Expr) (mainHea
|
||||
: TermElabM (Array PreDefinition) :=
|
||||
mainHeaders.size.foldM (init := preDefs) fun i preDefs => do
|
||||
let header := mainHeaders[i]!
|
||||
let termination ← declValToTerminationHint header.valueStx
|
||||
let termination ← termination.checkVars header.numParams mainVals[i]!
|
||||
let value ← mkLambdaFVars sectionVars mainVals[i]!
|
||||
let type ← mkForallFVars sectionVars header.type
|
||||
return preDefs.push {
|
||||
@@ -637,7 +651,7 @@ def pushMain (preDefs : Array PreDefinition) (sectionVars : Array Expr) (mainHea
|
||||
declName := header.declName
|
||||
levelParams := [], -- we set it later
|
||||
modifiers := header.modifiers
|
||||
type, value
|
||||
type, value, termination
|
||||
}
|
||||
|
||||
def pushLetRecs (preDefs : Array PreDefinition) (letRecClosures : List LetRecClosure) (kind : DefKind) (modifiers : Modifiers) : MetaM (Array PreDefinition) :=
|
||||
@@ -655,7 +669,8 @@ def pushLetRecs (preDefs : Array PreDefinition) (letRecClosures : List LetRecClo
|
||||
declName := c.toLift.declName
|
||||
levelParams := [] -- we set it later
|
||||
modifiers := { modifiers with attrs := c.toLift.attrs }
|
||||
kind, type, value
|
||||
kind, type, value,
|
||||
termination := c.toLift.termination
|
||||
}
|
||||
|
||||
def getKindForLetRecs (mainHeaders : Array DefViewElabHeader) : DefKind :=
|
||||
@@ -766,7 +781,7 @@ partial def checkForHiddenUnivLevels (allUserLevelNames : List Name) (preDefs :
|
||||
for preDef in preDefs do
|
||||
checkPreDef preDef
|
||||
|
||||
def elabMutualDef (vars : Array Expr) (views : Array DefView) (hints : TerminationHints) : TermElabM Unit :=
|
||||
def elabMutualDef (vars : Array Expr) (views : Array DefView) : TermElabM Unit :=
|
||||
if isExample views then
|
||||
withoutModifyingEnv do
|
||||
-- save correct environment in info tree
|
||||
@@ -805,7 +820,7 @@ where
|
||||
for preDef in preDefs do
|
||||
trace[Elab.definition] "after eraseAuxDiscr, {preDef.declName} : {preDef.type} :=\n{preDef.value}"
|
||||
checkForHiddenUnivLevels allUserLevelNames preDefs
|
||||
addPreDefinitions preDefs hints
|
||||
addPreDefinitions preDefs
|
||||
processDeriving headers
|
||||
|
||||
processDeriving (headers : Array DefViewElabHeader) := do
|
||||
@@ -820,13 +835,13 @@ where
|
||||
end Term
|
||||
namespace Command
|
||||
|
||||
def elabMutualDef (ds : Array Syntax) (hints : TerminationHints) : CommandElabM Unit := do
|
||||
def elabMutualDef (ds : Array Syntax) : CommandElabM Unit := do
|
||||
let views ← ds.mapM fun d => do
|
||||
let modifiers ← elabModifiers d[0]
|
||||
if ds.size > 1 && modifiers.isNonrec then
|
||||
throwErrorAt d "invalid use of 'nonrec' modifier in 'mutual' block"
|
||||
mkDefView modifiers d[1]
|
||||
runTermElabM fun vars => Term.elabMutualDef vars views hints
|
||||
runTermElabM fun vars => Term.elabMutualDef vars views
|
||||
|
||||
end Command
|
||||
end Lean.Elab
|
||||
|
||||
@@ -146,7 +146,6 @@ partial def collect (stx : Syntax) : M Syntax := withRef stx <| withFreshMacroSc
|
||||
```
|
||||
def namedPattern := check... >> trailing_parser "@" >> optional (atomic (ident >> ":")) >> termParser
|
||||
```
|
||||
TODO: pattern variable for equality proof
|
||||
-/
|
||||
let id := stx[0]
|
||||
discard <| processVar id
|
||||
|
||||
@@ -8,11 +8,13 @@ import Lean.Util.CollectLevelParams
|
||||
import Lean.Meta.AbstractNestedProofs
|
||||
import Lean.Elab.RecAppSyntax
|
||||
import Lean.Elab.DefView
|
||||
import Lean.Elab.PreDefinition.WF.TerminationHint
|
||||
|
||||
namespace Lean.Elab
|
||||
open Meta
|
||||
open Term
|
||||
|
||||
|
||||
/--
|
||||
A (potentially recursive) definition.
|
||||
The elaborator converts it into Kernel definitions using many different strategies.
|
||||
@@ -25,6 +27,7 @@ structure PreDefinition where
|
||||
declName : Name
|
||||
type : Expr
|
||||
value : Expr
|
||||
termination : WF.TerminationHints
|
||||
deriving Inhabited
|
||||
|
||||
def instantiateMVarsAtPreDecls (preDefs : Array PreDefinition) : TermElabM (Array PreDefinition) :=
|
||||
|
||||
@@ -21,16 +21,25 @@ structure EqnInfoCore where
|
||||
value : Expr
|
||||
deriving Inhabited
|
||||
|
||||
partial def expand : Expr → Expr
|
||||
| Expr.letE _ _ v b _ => expand (b.instantiate1 v)
|
||||
| Expr.mdata _ b => expand b
|
||||
| e => e
|
||||
/--
|
||||
Zeta reduces `let` and `let_fun` while consuming metadata.
|
||||
Returns true if progress is made.
|
||||
-/
|
||||
partial def expand (progress : Bool) (e : Expr) : Bool × Expr :=
|
||||
match e with
|
||||
| Expr.letE _ _ v b _ => expand true (b.instantiate1 v)
|
||||
| Expr.mdata _ b => expand true b
|
||||
| e =>
|
||||
if let some (_, _, v, b) := e.letFun? then
|
||||
expand true (b.instantiate1 v)
|
||||
else
|
||||
(progress, e)
|
||||
|
||||
def expandRHS? (mvarId : MVarId) : MetaM (Option MVarId) := do
|
||||
let target ← mvarId.getType'
|
||||
let some (_, lhs, rhs) := target.eq? | return none
|
||||
unless rhs.isLet || rhs.isMData do return none
|
||||
return some (← mvarId.replaceTargetDefEq (← mkEq lhs (expand rhs)))
|
||||
let (true, rhs') := expand false rhs | return none
|
||||
return some (← mvarId.replaceTargetDefEq (← mkEq lhs rhs'))
|
||||
|
||||
def funext? (mvarId : MVarId) : MetaM (Option MVarId) := do
|
||||
let target ← mvarId.getType'
|
||||
|
||||
@@ -13,11 +13,6 @@ namespace Lean.Elab
|
||||
open Meta
|
||||
open Term
|
||||
|
||||
structure TerminationHints where
|
||||
terminationBy? : Option Syntax := none
|
||||
decreasingBy? : Option Syntax := none
|
||||
deriving Inhabited
|
||||
|
||||
private def addAndCompilePartial (preDefs : Array PreDefinition) (useSorry := false) : TermElabM Unit := do
|
||||
for preDef in preDefs do
|
||||
trace[Elab.definition] "processing {preDef.declName}"
|
||||
@@ -94,15 +89,12 @@ private def addAsAxioms (preDefs : Array PreDefinition) : TermElabM Unit := do
|
||||
applyAttributesOf #[preDef] AttributeApplicationTime.afterTypeChecking
|
||||
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
|
||||
|
||||
def addPreDefinitions (preDefs : Array PreDefinition) (hints : TerminationHints) : TermElabM Unit := withLCtx {} {} do
|
||||
def addPreDefinitions (preDefs : Array PreDefinition) : TermElabM Unit := withLCtx {} {} do
|
||||
for preDef in preDefs do
|
||||
trace[Elab.definition.body] "{preDef.declName} : {preDef.type} :=\n{preDef.value}"
|
||||
let preDefs ← preDefs.mapM ensureNoUnassignedMVarsAtPreDef
|
||||
let preDefs ← betaReduceLetRecApps preDefs
|
||||
let cliques := partitionPreDefs preDefs
|
||||
let mut terminationBy ← liftMacroM <| WF.expandTerminationBy hints.terminationBy? (cliques.map fun ds => ds.map (·.declName))
|
||||
let mut decreasingBy ← liftMacroM <| WF.expandTerminationHint hints.decreasingBy? (cliques.map fun ds => ds.map (·.declName))
|
||||
let mut hasErrors := false
|
||||
for preDefs in cliques do
|
||||
trace[Elab.definition.scc] "{preDefs.map (·.declName)}"
|
||||
if preDefs.size == 1 && isNonRecursive preDefs[0]! then
|
||||
@@ -116,35 +108,31 @@ def addPreDefinitions (preDefs : Array PreDefinition) (hints : TerminationHints)
|
||||
addNonRec preDef
|
||||
else
|
||||
addAndCompileNonRec preDef
|
||||
preDef.termination.ensureNone "not recursive"
|
||||
else if preDefs.any (·.modifiers.isUnsafe) then
|
||||
addAndCompileUnsafe preDefs
|
||||
preDefs.forM (·.termination.ensureNone "unsafe")
|
||||
else if preDefs.any (·.modifiers.isPartial) then
|
||||
for preDef in preDefs do
|
||||
if preDef.modifiers.isPartial && !(← whnfD preDef.type).isForall then
|
||||
withRef preDef.ref <| throwError "invalid use of 'partial', '{preDef.declName}' is not a function{indentExpr preDef.type}"
|
||||
addAndCompilePartial preDefs
|
||||
preDefs.forM (·.termination.ensureNone "partial")
|
||||
else
|
||||
try
|
||||
let mut wf? := none
|
||||
let mut decrTactic? := none
|
||||
if let some wf := terminationBy.find? (preDefs.map (·.declName)) then
|
||||
wf? := some wf
|
||||
terminationBy := terminationBy.markAsUsed (preDefs.map (·.declName))
|
||||
if let some { ref, value := decrTactic } := decreasingBy.find? (preDefs.map (·.declName)) then
|
||||
decrTactic? := some (← withRef ref `(by $(⟨decrTactic⟩)))
|
||||
decreasingBy := decreasingBy.markAsUsed (preDefs.map (·.declName))
|
||||
if wf?.isSome || decrTactic?.isSome then
|
||||
wfRecursion preDefs wf? decrTactic?
|
||||
let hasHints := preDefs.any fun preDef =>
|
||||
preDef.termination.decreasing_by?.isSome || preDef.termination.termination_by?.isSome
|
||||
if hasHints then
|
||||
wfRecursion preDefs
|
||||
else
|
||||
withRef (preDefs[0]!.ref) <| mapError
|
||||
(orelseMergeErrors
|
||||
(structuralRecursion preDefs)
|
||||
(wfRecursion preDefs none none))
|
||||
(wfRecursion preDefs))
|
||||
(fun msg =>
|
||||
let preDefMsgs := preDefs.toList.map (MessageData.ofExpr $ mkConst ·.declName)
|
||||
m!"fail to show termination for{indentD (MessageData.joinSep preDefMsgs Format.line)}\nwith errors\n{msg}")
|
||||
catch ex =>
|
||||
hasErrors := true
|
||||
logException ex
|
||||
let s ← saveState
|
||||
try
|
||||
@@ -159,9 +147,6 @@ def addPreDefinitions (preDefs : Array PreDefinition) (hints : TerminationHints)
|
||||
else if preDefs.all fun preDef => preDef.kind == DefKind.theorem then
|
||||
addAsAxioms preDefs
|
||||
catch _ => s.restore
|
||||
unless hasErrors do
|
||||
liftMacroM <| terminationBy.ensureAllUsed
|
||||
liftMacroM <| decreasingBy.ensureAllUsed
|
||||
|
||||
builtin_initialize
|
||||
registerTraceClass `Elab.definition.body
|
||||
|
||||
@@ -42,7 +42,7 @@ where
|
||||
go mvarId
|
||||
else if let some mvarId ← whnfReducibleLHS? mvarId then
|
||||
go mvarId
|
||||
else match (← simpTargetStar mvarId {}).1 with
|
||||
else match (← simpTargetStar mvarId {} (simprocs := {})).1 with
|
||||
| TacticResultCNM.closed => return ()
|
||||
| TacticResultCNM.modified mvarId => go mvarId
|
||||
| TacticResultCNM.noChange =>
|
||||
|
||||
@@ -41,14 +41,12 @@ def preprocess (e : Expr) (recFnName : Name) : CoreM Expr :=
|
||||
return .visit e.headBeta
|
||||
else
|
||||
return .continue)
|
||||
(post := fun e =>
|
||||
match e with
|
||||
| .app (.mdata m f) a =>
|
||||
(post := fun e => do
|
||||
if e.isApp && e.getAppFn.isMData then
|
||||
let .mdata m f := e.getAppFn | unreachable!
|
||||
if m.isRecApp then
|
||||
return .done (.mdata m (.app f a))
|
||||
else
|
||||
return .done e
|
||||
| _ => return .done e)
|
||||
return .done (.mdata m (f.beta e.getAppArgs))
|
||||
return .continue)
|
||||
|
||||
|
||||
end Lean.Elab.Structural
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user